diff --git a/dmd2/aggregate.h b/dmd2/aggregate.h index 6deae43e..c9f69be1 100644 --- a/dmd2/aggregate.h +++ b/dmd2/aggregate.h @@ -74,7 +74,7 @@ struct AggregateDeclaration : ScopeDsymbol bool isdeprecated; // !=0 if deprecated #if DMDV2 - int isnested; // !=0 if is nested + bool isnested; // !=0 if is nested VarDeclaration *vthis; // 'this' parameter if this aggregate is nested #endif // Special member functions @@ -100,6 +100,7 @@ struct AggregateDeclaration : ScopeDsymbol Expression *getRTInfo; // pointer to GC info generated by object.RTInfo(this) AggregateDeclaration(Loc loc, Identifier *id); + void setScope(Scope *sc); void semantic2(Scope *sc); void semantic3(Scope *sc); void inlineScan(); @@ -118,7 +119,7 @@ struct AggregateDeclaration : ScopeDsymbol void emitComment(Scope *sc); void toJsonBuffer(OutBuffer *buf); - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); // For access checking virtual PROT getAccess(Dsymbol *smember); // determine access to smember @@ -194,8 +195,9 @@ struct StructDeclaration : AggregateDeclaration FuncDeclaration *buildCpCtor(Scope *sc); FuncDeclaration *buildXopEquals(Scope *sc); + void makeNested(); #endif - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); PROT getAccess(Dsymbol *smember); // determine access to smember @@ -285,7 +287,7 @@ struct ClassDeclaration : AggregateDeclaration int isscope; // !=0 if this is an auto class int isabstract; // !=0 if abstract class #if DMDV1 - int isnested; // !=0 if is nested + bool isnested; // !=0 if is nested VarDeclaration *vthis; // 'this' parameter if this class is nested #endif int inuse; // to prevent recursive attempts @@ -319,7 +321,7 @@ struct ClassDeclaration : AggregateDeclaration virtual int vtblOffset(); const char *kind(); char *mangle(); - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); PROT getAccess(Dsymbol *smember); // determine access to smember diff --git a/dmd2/apply.c b/dmd2/apply.c index f5a5ede8..e05a343e 100644 --- a/dmd2/apply.c +++ b/dmd2/apply.c @@ -34,45 +34,33 @@ int Expression::apply(fp_t fp, void *param) } /****************************** - * Perform apply() on an array of Expressions. + * Perform apply() on an t if not null */ - -int arrayExpressionApply(Expressions *a, fp_t fp, void *param) +template +int condApply(T* t, fp_t fp, void* param) { - //printf("arrayExpressionApply(%p)\n", a); - if (a) - { - for (size_t i = 0; i < a->dim; i++) - { Expression *e = (*a)[i]; - - if (e) - { - if (e->apply(fp, param)) - return 1; - } - } - } - return 0; + return t ? t->apply(fp, param) : 0; } + int NewExp::apply(int (*fp)(Expression *, void *), void *param) { //printf("NewExp::apply(): %s\n", toChars()); - return ((thisexp ? thisexp->apply(fp, param) : 0) || - arrayExpressionApply(newargs, fp, param) || - arrayExpressionApply(arguments, fp, param) || - (*fp)(this, param)); + return condApply(thisexp, fp, param) || + condApply(newargs, fp, param) || + condApply(arguments, fp, param) || + (*fp)(this, param); } int NewAnonClassExp::apply(int (*fp)(Expression *, void *), void *param) { //printf("NewAnonClassExp::apply(): %s\n", toChars()); - return ((thisexp ? thisexp->apply(fp, param) : 0) || - arrayExpressionApply(newargs, fp, param) || - arrayExpressionApply(arguments, fp, param) || - (*fp)(this, param)); + return condApply(thisexp, fp, param) || + condApply(newargs, fp, param) || + condApply(arguments, fp, param) || + (*fp)(this, param); } int UnaExp::apply(fp_t fp, void *param) @@ -92,7 +80,7 @@ int AssertExp::apply(fp_t fp, void *param) { //printf("CallExp::apply(fp_t fp, void *param): %s\n", toChars()); return e1->apply(fp, param) || - (msg ? msg->apply(fp, param) : 0) || + condApply(msg, fp, param) || (*fp)(this, param); } @@ -101,7 +89,7 @@ int CallExp::apply(fp_t fp, void *param) { //printf("CallExp::apply(fp_t fp, void *param): %s\n", toChars()); return e1->apply(fp, param) || - arrayExpressionApply(arguments, fp, param) || + condApply(arguments, fp, param) || (*fp)(this, param); } @@ -110,7 +98,7 @@ int ArrayExp::apply(fp_t fp, void *param) { //printf("ArrayExp::apply(fp_t fp, void *param): %s\n", toChars()); return e1->apply(fp, param) || - arrayExpressionApply(arguments, fp, param) || + condApply(arguments, fp, param) || (*fp)(this, param); } @@ -118,37 +106,37 @@ int ArrayExp::apply(fp_t fp, void *param) int SliceExp::apply(fp_t fp, void *param) { return e1->apply(fp, param) || - (lwr ? lwr->apply(fp, param) : 0) || - (upr ? upr->apply(fp, param) : 0) || + condApply(lwr, fp, param) || + condApply(upr, fp, param) || (*fp)(this, param); } int ArrayLiteralExp::apply(fp_t fp, void *param) { - return arrayExpressionApply(elements, fp, param) || + return condApply(elements, fp, param) || (*fp)(this, param); } int AssocArrayLiteralExp::apply(fp_t fp, void *param) { - return arrayExpressionApply(keys, fp, param) || - arrayExpressionApply(values, fp, param) || + return condApply(keys, fp, param) || + condApply(values, fp, param) || (*fp)(this, param); } int StructLiteralExp::apply(fp_t fp, void *param) { - return arrayExpressionApply(elements, fp, param) || + return condApply(elements, fp, param) || (*fp)(this, param); } int TupleExp::apply(fp_t fp, void *param) { - return arrayExpressionApply(exps, fp, param) || + return condApply(exps, fp, param) || (*fp)(this, param); } diff --git a/dmd2/argtypes.c b/dmd2/argtypes.c index ffa38a86..5f1579ad 100644 --- a/dmd2/argtypes.c +++ b/dmd2/argtypes.c @@ -370,6 +370,8 @@ TypeTuple *TypeStruct::toArgTypes() unsigned off2 = f->offset; if (ft1) off2 = 8; + if (!t2 && off2 != 8) + goto Lmemory; assert(t2 || off2 == 8); t2 = argtypemerge(t2, ft2, off2 - 8); if (!t2) diff --git a/dmd2/arraytypes.h b/dmd2/arraytypes.h index be854a62..e40009ac 100644 --- a/dmd2/arraytypes.h +++ b/dmd2/arraytypes.h @@ -64,6 +64,8 @@ typedef ArrayBase CompoundStatements; typedef ArrayBase GotoCaseStatements; +typedef ArrayBase ReturnStatements; + typedef ArrayBase TemplateInstances; //typedef ArrayBase Strings; diff --git a/dmd2/attrib.c b/dmd2/attrib.c index 39d2e7ae..be830812 100644 --- a/dmd2/attrib.c +++ b/dmd2/attrib.c @@ -373,13 +373,13 @@ void AttribDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; for (size_t i = 0; i < decl->dim; i++) { Dsymbol *s = (*decl)[i]; - - buf->writestring(" "); s->toCBuffer(buf, hgs); } + buf->level--; buf->writeByte('}'); } } @@ -552,6 +552,44 @@ void StorageClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) AttribDeclaration::toCBuffer(buf, hgs); } +/********************************* DeprecatedDeclaration ****************************/ + +DeprecatedDeclaration::DeprecatedDeclaration(Expression *msg, Dsymbols *decl) + : StorageClassDeclaration(STCdeprecated, decl) +{ + this->msg = msg; +} + +Dsymbol *DeprecatedDeclaration::syntaxCopy(Dsymbol *s) +{ + assert(!s); + return new DeprecatedDeclaration(msg->syntaxCopy(), Dsymbol::arraySyntaxCopy(decl)); +} + +void DeprecatedDeclaration::setScope(Scope *sc) +{ + assert(msg); + char *depmsg = NULL; + StringExp *se = msg->toString(); + if (se) + depmsg = (char *)se->string; + else + msg->error("string expected, not '%s'", msg->toChars()); + + Scope *scx = sc->push(); + scx->depmsg = depmsg; + StorageClassDeclaration::setScope(scx); + scx->pop(); +} + +void DeprecatedDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("deprecated("); + msg->toCBuffer(buf, hgs); + buf->writestring(") "); + AttribDeclaration::toCBuffer(buf, hgs); +} + /********************************* LinkDeclaration ****************************/ LinkDeclaration::LinkDeclaration(enum LINK p, Dsymbols *decl) @@ -699,17 +737,10 @@ void ProtDeclaration::protectionToCBuffer(OutBuffer *buf, enum PROT protection) { const char *p; - switch (protection) - { - case PROTprivate: p = "private"; break; - case PROTpackage: p = "package"; break; - case PROTprotected: p = "protected"; break; - case PROTpublic: p = "public"; break; - case PROTexport: p = "export"; break; - default: - assert(0); - break; - } + p = Pprotectionnames[protection]; + + assert(p); + buf->writestring(p); buf->writeByte(' '); } @@ -887,16 +918,16 @@ void AnonDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->printf(isunion ? "union" : "struct"); buf->writestring("\n{\n"); + buf->level++; if (decl) { for (size_t i = 0; i < decl->dim; i++) { Dsymbol *s = (*decl)[i]; - - //buf->writestring(" "); s->toCBuffer(buf, hgs); } } + buf->level--; buf->writestring("}\n"); } @@ -939,6 +970,7 @@ void PragmaDeclaration::setScope(Scope *sc) { Expression *e = (*args)[0]; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; StringExp* se = e->toString(); @@ -978,6 +1010,7 @@ void PragmaDeclaration::semantic(Scope *sc) Expression *e = (*args)[i]; e = e->semantic(sc); + e = resolveProperties(sc, e); if (e->op != TOKerror && e->op != TOKtype) e = e->ctfeInterpret(); if (e->op == TOKerror) @@ -1005,6 +1038,7 @@ void PragmaDeclaration::semantic(Scope *sc) Expression *e = (*args)[0]; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; if (e->op == TOKerror) @@ -1047,6 +1081,7 @@ void PragmaDeclaration::semantic(Scope *sc) e = (*args)[1]; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); e = e->toString(); if (e && ((StringExp *)e)->sz == 1) @@ -1069,6 +1104,7 @@ void PragmaDeclaration::semantic(Scope *sc) { Expression *e = (*args)[0]; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; Dsymbol *sa = getDsymbol(e); @@ -1106,6 +1142,7 @@ void PragmaDeclaration::semantic(Scope *sc) unsigned errors_save = global.errors; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); if (i == 0) printf(" ("); @@ -1349,16 +1386,16 @@ void ConditionalDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; if (decl) { for (size_t i = 0; i < decl->dim; i++) { Dsymbol *s = (*decl)[i]; - - buf->writestring(" "); s->toCBuffer(buf, hgs); } } + buf->level--; buf->writeByte('}'); if (elsedecl) { @@ -1367,13 +1404,13 @@ void ConditionalDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; for (size_t i = 0; i < elsedecl->dim; i++) { Dsymbol *s = (*elsedecl)[i]; - - buf->writestring(" "); s->toCBuffer(buf, hgs); } + buf->level--; buf->writeByte('}'); } } @@ -1420,7 +1457,7 @@ Dsymbols *StaticIfDeclaration::include(Scope *sc, ScopeDsymbol *sd) { Dsymbol *s = (*d)[i]; - s->setScope(sc); + s->setScope(scope); } } return d; @@ -1580,3 +1617,124 @@ const char *CompileDeclaration::kind() } +/***************************** UserAttributeDeclaration *****************************/ + +UserAttributeDeclaration::UserAttributeDeclaration(Expressions *atts, Dsymbols *decl) + : AttribDeclaration(decl) +{ + //printf("UserAttributeDeclaration()\n"); + this->atts = atts; +} + +Dsymbol *UserAttributeDeclaration::syntaxCopy(Dsymbol *s) +{ + //printf("UserAttributeDeclaration::syntaxCopy('%s')\n", toChars()); + assert(!s); + Expressions *atts = Expression::arraySyntaxCopy(this->atts); + return new UserAttributeDeclaration(atts, Dsymbol::arraySyntaxCopy(decl)); +} + +void UserAttributeDeclaration::semantic(Scope *sc) +{ + //printf("UserAttributeDeclaration::semantic() %p\n", this); + atts = arrayExpressionSemantic(atts, sc); + + if (decl) + { + Scope *newsc = sc; +#if 1 + if (atts && atts->dim) + { + // create new one for changes + newsc = new Scope(*sc); + newsc->flags &= ~SCOPEfree; + + // Create new uda that is the concatenation of the previous + newsc->userAttributes = concat(newsc->userAttributes, atts); + } +#endif + for (size_t i = 0; i < decl->dim; i++) + { Dsymbol *s = (*decl)[i]; + + s->semantic(newsc); + } + if (newsc != sc) + { + sc->offset = newsc->offset; + newsc->pop(); + } + } +} + +Expressions *UserAttributeDeclaration::concat(Expressions *udas1, Expressions *udas2) +{ + Expressions *udas; + if (!udas1 || udas1->dim == 0) + udas = udas2; + else if (!udas2 || udas2->dim == 0) + udas = udas1; + else + { + /* Create a new tuple that combines them + * (do not append to left operand, as this is a copy-on-write operation) + */ + udas = new Expressions(); + udas->push(new TupleExp(0, udas1)); + udas->push(new TupleExp(0, udas2)); + } + return udas; +} + +void UserAttributeDeclaration::setScope(Scope *sc) +{ + //printf("UserAttributeDeclaration::setScope() %p\n", this); + if (decl) + { + Scope *newsc = sc; +#if 1 + if (atts && atts->dim) + { + // create new one for changes + newsc = new Scope(*sc); + newsc->flags &= ~SCOPEfree; + + // Append new atts to old one + if (!newsc->userAttributes || newsc->userAttributes->dim == 0) + newsc->userAttributes = atts; + else + { + // Create a tuple that combines them + Expressions *exps = new Expressions(); + exps->push(new TupleExp(0, newsc->userAttributes)); + exps->push(new TupleExp(0, atts)); + newsc->userAttributes = exps; + } + } +#endif + for (size_t i = 0; i < decl->dim; i++) + { Dsymbol *s = (*decl)[i]; + + s->setScope(newsc); // yes, the only difference from semantic() + } + if (newsc != sc) + { + sc->offset = newsc->offset; + newsc->pop(); + } + } +} + +void UserAttributeDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) +{ + buf->writestring("@("); + argsToCBuffer(buf, atts, hgs); + buf->writeByte(')'); + AttribDeclaration::toCBuffer(buf, hgs); +} + +const char *UserAttributeDeclaration::kind() +{ + return "UserAttribute"; +} + + diff --git a/dmd2/attrib.h b/dmd2/attrib.h index 2f929fee..b40f06c7 100644 --- a/dmd2/attrib.h +++ b/dmd2/attrib.h @@ -67,7 +67,7 @@ struct AttribDeclaration : Dsymbol #endif }; -struct StorageClassDeclaration: AttribDeclaration +struct StorageClassDeclaration : AttribDeclaration { StorageClass stc; @@ -81,6 +81,16 @@ struct StorageClassDeclaration: AttribDeclaration static void stcToCBuffer(OutBuffer *buf, StorageClass stc); }; +struct DeprecatedDeclaration : StorageClassDeclaration +{ + Expression *msg; + + DeprecatedDeclaration(Expression *msg, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void setScope(Scope *sc); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); +}; + struct LinkDeclaration : AttribDeclaration { enum LINK linkage; @@ -204,4 +214,21 @@ struct CompileDeclaration : AttribDeclaration const char *kind(); }; +/** + * User defined attributes look like: + * [ args, ... ] + */ +struct UserAttributeDeclaration : AttribDeclaration +{ + Expressions *atts; + + UserAttributeDeclaration(Expressions *atts, Dsymbols *decl); + Dsymbol *syntaxCopy(Dsymbol *s); + void semantic(Scope *sc); + void setScope(Scope *sc); + static Expressions *concat(Expressions *udas1, Expressions *udas2); + void toCBuffer(OutBuffer *buf, HdrGenState *hgs); + const char *kind(); +}; + #endif /* DMD_ATTRIB_H */ diff --git a/dmd2/builtin.c b/dmd2/builtin.c index c1494b5f..ee51b891 100644 --- a/dmd2/builtin.c +++ b/dmd2/builtin.c @@ -24,16 +24,6 @@ #include "id.h" #include "module.h" -#if __FreeBSD__ -extern "C" -{ - longdouble sinl(longdouble); - longdouble cosl(longdouble); - longdouble tanl(longdouble); - longdouble sqrtl(longdouble); -} -#endif - #if DMDV2 /********************************** @@ -46,7 +36,7 @@ enum BUILTIN FuncDeclaration::isBuiltin() static const char FeZe2[] = "FNaNbNeeZe"; // @trusted pure nothrow real function(real) static const char FuintZint[] = "FNaNbNfkZi"; // @safe pure nothrow int function(uint) static const char FuintZuint[] = "FNaNbNfkZk"; // @safe pure nothrow uint function(uint) - static const char FulongZulong[] = "FNaNbkZk"; // pure nothrow int function(ulong) + //static const char FulongZulong[] = "FNaNbkZk"; // pure nothrow int function(ulong) static const char FulongZint[] = "FNaNbNfmZi"; // @safe pure nothrow int function(uint) static const char FrealrealZreal [] = "FNaNbNfeeZe"; // @safe pure nothrow real function(real, real) static const char FrealZlong [] = "FNaNbNfeZl"; // @safe pure nothrow long function(real) @@ -228,6 +218,7 @@ Expression *eval_builtin(Loc loc, enum BUILTIN builtin, Expressions *arguments) if (arg0->op == TOKint64) e = new IntegerExp(loc, eval_bswap(arg0), arg0->type); break; + default: break; } return e; } diff --git a/dmd2/cast.c b/dmd2/cast.c index 02e1f8b5..64dfb1df 100644 --- a/dmd2/cast.c +++ b/dmd2/cast.c @@ -70,7 +70,7 @@ Expression *Expression::implicitCastTo(Scope *sc, Type *t) } #endif #if DMDV2 - if (match == MATCHconst && t == type->constOf()) + if (match == MATCHconst && type->constConv(t)) { Expression *e = copy(); e->type = t; @@ -769,7 +769,9 @@ MATCH FuncExp::implicitConvTo(Type *t) { //printf("FuncExp::implicitConvTo type = %p %s, t = %s\n", type, type ? type->toChars() : NULL, t->toChars()); Expression *e = inferType(t, 1); - if (e) + if (e && + (t->ty == Tdelegate || + t->ty == Tpointer && t->nextOf()->ty == Tfunction)) { if (e != this) return e->implicitConvTo(t); @@ -777,13 +779,10 @@ MATCH FuncExp::implicitConvTo(Type *t) /* MATCHconst: Conversion from implicit to explicit function pointer * MATCHconvert: Conversion from impliict funciton pointer to delegate */ - if (tok == TOKreserved && type->ty == Tpointer && - (t->ty == Tpointer || t->ty == Tdelegate)) + if (fd->tok == TOKreserved && // fbody doesn't have a frame pointer + (type->equals(t) || type->nextOf()->covariant(t->nextOf()) == 1)) { - if (type == t) - return MATCHexact; - if (type->nextOf()->covariant(t->nextOf()) == 1) - return t->ty == Tpointer ? MATCHconst : MATCHconvert; + return t->ty == Tpointer ? MATCHconst : MATCHconvert; } } return Expression::implicitConvTo(t); @@ -856,6 +855,122 @@ MATCH CastExp::implicitConvTo(Type *t) return result; } +MATCH NewExp::implicitConvTo(Type *t) +{ +#if 0 + printf("NewExp::implicitConvTo(this=%s, type=%s, t=%s)\n", + toChars(), type->toChars(), t->toChars()); +#endif + MATCH match = Expression::implicitConvTo(t); + if (match != MATCHnomatch) + return match; + + /* The return from new() is special in that it might be a unique pointer. + * If we can prove it is, allow the following implicit conversions: + * mutable => immutable + * non-shared => shared + * shared => non-shared + */ + + Type *typeb = type->toBasetype(); + Type *tb = t->toBasetype(); + + if (tb->ty == Tclass) + { + //printf("%s => %s\n", type->castMod(0)->toChars(), t->castMod(0)->toChars()); + match = type->castMod(0)->implicitConvTo(t->castMod(0)); + if (!match) + goto Lnomatch; + + // Regardless, don't allow immutable to be implicitly converted to mutable + if (tb->isMutable() && !typeb->isMutable()) + goto Lnomatch; + + // All the fields must be convertible as well + ClassDeclaration *cd = ((TypeClass *)tb)->sym; + + cd->size(loc); // resolve any forward references + + /* The following is excessively conservative, but be very + * careful in loosening them up. + */ + if (cd->isNested() || + cd->isInterfaceDeclaration() || + cd->ctor || + cd->baseClass != ClassDeclaration::object) + goto Lnomatch; + + for (size_t i = 0; i < cd->fields.dim; i++) + { Dsymbol *sm = cd->fields[i]; + Declaration *d = sm->isDeclaration(); + if (d->storage_class & STCref || d->hasPointers()) + goto Lnomatch; + } + return (match == MATCHexact) ? MATCHconst : match; + } + else if ((tb->ty == Tpointer || tb->ty == Tarray) && + (typeb->ty == Tpointer || typeb->ty == Tarray)) + { + Type *typen = type->nextOf()->toBasetype(); + Type *tn = tb->nextOf()->toBasetype(); + + //printf("%s => %s\n", typen->castMod(0)->toChars(), tn->castMod(0)->toChars()); + { + /* Determine if the match failure was solely due to a difference + * in the mod bits, by rebuilding type and t without mod bits and + * retrying the implicit conversion. + */ + Type *tn2 = tn->castMod(0); // cast off mod bits + Type *typen2 = typen->castMod(0); + Type *t2 = (tb->ty == Tpointer) ? tn2->pointerTo() : tn2->arrayOf(); + Type *type2 = (typeb->ty == Tpointer) ? typen2->pointerTo() : typen2->arrayOf(); + match = type2->implicitConvTo(t2); + if (!match) + goto Lnomatch; + } + + // Regardless, don't allow immutable to be implicitly converted to mutable + if (tn->isMutable() && !typen->isMutable()) + goto Lnomatch; + + if (tn->isTypeBasic()) + ; + else if (tn->ty == Tstruct) + { + // All the fields must be convertible as well + StructDeclaration *sd = ((TypeStruct *)tn)->sym; + + sd->size(loc); // resolve any forward references + + /* The following is excessively conservative, but be very + * careful in loosening them up. + */ + + if (sd->isNested() || + sd->ctor) + goto Lnomatch; + + for (size_t i = 0; i < sd->fields.dim; i++) + { Dsymbol *sm = sd->fields[i]; + Declaration *d = sm->isDeclaration(); + if (d->storage_class & STCref || d->hasPointers()) + goto Lnomatch; + } + } + else + { + /* More fruit left on the table, such as pointers to immutable. + */ + goto Lnomatch; + } + + return (match == MATCHexact) ? MATCHconst : match; + } + + Lnomatch: + return MATCHnomatch; +} + /* ==================== castTo ====================== */ /************************************** @@ -945,6 +1060,12 @@ Expression *Expression::castTo(Scope *sc, Type *t) e = e->semantic(sc); return e; } + else if (typeb->implicitConvTo(tb) == MATCHconst && t == type->constOf()) + { + Expression *e = copy(); + e->type = t; + return e; + } e = new CastExp(loc, e, tb); } } @@ -1132,7 +1253,7 @@ Expression *StringExp::castTo(Scope *sc, Type *t) if (committed) goto Lcast; -#define X(tf,tt) ((tf) * 256 + (tt)) +#define X(tf,tt) ((int)(tf) * 256 + (int)(tt)) { OutBuffer buffer; size_t newlen = 0; @@ -1258,12 +1379,9 @@ L2: if (dim2 != se->len) { // Copy when changing the string literal - unsigned newsz = se->sz; - void *s; - int d; - - d = (dim2 < se->len) ? dim2 : se->len; - s = (unsigned char *)mem.malloc((dim2 + 1) * newsz); + size_t newsz = se->sz; + size_t d = (dim2 < se->len) ? dim2 : se->len; + void *s = (unsigned char *)mem.malloc((dim2 + 1) * newsz); memcpy(s, se->string, d * newsz); // Extend with 0, add terminating 0 memset((char *)s + d * newsz, 0, (dim2 + 1 - d) * newsz); @@ -1634,8 +1752,15 @@ Expression *FuncExp::castTo(Scope *sc, Type *t) //printf("FuncExp::castTo type = %s, t = %s\n", type->toChars(), t->toChars()); Expression *e = inferType(t, 1); if (e) - { if (e != this) + { + if (e != this) e = e->castTo(sc, t); + else if (!e->type->equals(t)) + { + assert(e->type->nextOf()->covariant(t->nextOf()) == 1); + e = e->copy(); + e->type = t; + } return e; } return Expression::castTo(sc, t); @@ -1805,10 +1930,14 @@ Expression *FuncExp::inferType(Type *to, int flag, TemplateParameters *tparams) FuncLiteralDeclaration *fld = td->onemember->isFuncLiteralDeclaration(); assert(fld); if (!fld->type->nextOf() && tfv->next) - fld->treq = tfv; + fld->treq = to; TemplateInstance *ti = new TemplateInstance(loc, td, tiargs); e = (new ScopeExp(loc, ti))->semantic(td->scope); + + // Reset inference target for the later re-semantic + fld->treq = NULL; + if (e->op == TOKfunction) { FuncExp *fe = (FuncExp *)e; assert(fe->td == NULL); @@ -2255,16 +2384,16 @@ Lagain: Lcc: while (1) { - int i1 = e2->implicitConvTo(t1); - int i2 = e1->implicitConvTo(t2); + MATCH i1 = e2->implicitConvTo(t1); + MATCH i2 = e1->implicitConvTo(t2); if (i1 && i2) { // We have the case of class vs. void*, so pick class if (t1->ty == Tpointer) - i1 = 0; + i1 = MATCHnomatch; else if (t2->ty == Tpointer) - i2 = 0; + i2 = MATCHnomatch; } if (i2) @@ -2331,8 +2460,8 @@ Lcc: if (!ts1->sym->aliasthis && !ts2->sym->aliasthis) goto Lincompatible; - int i1 = 0; - int i2 = 0; + MATCH i1 = MATCHnomatch; + MATCH i2 = MATCHnomatch; Expression *e1b = NULL; Expression *e2b = NULL; diff --git a/dmd2/class.c b/dmd2/class.c index 6721e925..55cbb050 100644 --- a/dmd2/class.c +++ b/dmd2/class.c @@ -27,6 +27,7 @@ #include "module.h" #include "expression.h" #include "statement.h" +#include "template.h" /********************************* ClassDeclaration ****************************/ @@ -305,6 +306,7 @@ void ClassDeclaration::semantic(Scope *sc) { isdeprecated = true; } + userAttributes = sc->userAttributes; if (sc->linkage == LINKcpp) error("cannot create C++ classes"); @@ -533,11 +535,12 @@ void ClassDeclaration::semantic(Scope *sc) */ if (vthis) // if inheriting from nested class { // Use the base class's 'this' member - isnested = 1; + isnested = true; if (storage_class & STCstatic) error("static class cannot inherit from nested class %s", baseClass->toChars()); if (toParent2() != baseClass->toParent2() && (!toParent2() || + !baseClass->toParent2()->getType() || !baseClass->toParent2()->getType()->isBaseOf(toParent2()->getType(), NULL))) { if (toParent2()) @@ -553,7 +556,7 @@ void ClassDeclaration::semantic(Scope *sc) baseClass->toChars(), baseClass->toParent2()->toChars()); } - isnested = 0; + isnested = false; } } else if (!(storage_class & STCstatic)) @@ -565,7 +568,7 @@ void ClassDeclaration::semantic(Scope *sc) if (ad || fd) - { isnested = 1; + { isnested = true; Type *t; if (ad) t = ad->handle; @@ -628,6 +631,7 @@ void ClassDeclaration::semantic(Scope *sc) { sc->offset = PTRSIZE * 2; // allow room for __vptr and __monitor alignsize = PTRSIZE; } + sc->userAttributes = NULL; structsize = sc->offset; Scope scsave = *sc; size_t members_dim = members->dim; @@ -733,8 +737,9 @@ void ClassDeclaration::semantic(Scope *sc) if (!ctor && baseClass && baseClass->ctor) { //printf("Creating default this(){} for class %s\n", toChars()); - Type *tf = new TypeFunction(NULL, NULL, 0, LINKd, 0); + Type *tf = new TypeFunction(NULL, NULL, 0, LINKd, 0); CtorDeclaration *ctor = new CtorDeclaration(loc, 0, 0, tf); + ctor->isImplicit = true; ctor->fbody = new CompoundStatement(0, new Statements()); members->push(ctor); ctor->addMember(sc, this, 1); @@ -787,7 +792,30 @@ void ClassDeclaration::semantic(Scope *sc) Module::dprogress++; dtor = buildDtor(sc); + if (Dsymbol *assign = search_function(this, Id::assign)) + { + Expression *e = new NullExp(loc, type); // dummy rvalue + Expressions *arguments = new Expressions(); + arguments->push(e); + // check identity opAssign exists + FuncDeclaration *fd = assign->isFuncDeclaration(); + if (fd) + { fd = fd->overloadResolve(loc, e, arguments, 1); + if (fd && !(fd->storage_class & STCdisable)) + goto Lassignerr; + } + + if (TemplateDeclaration *td = assign->isTemplateDeclaration()) + { fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1+2); + if (fd && !(fd->storage_class & STCdisable)) + goto Lassignerr; + } + +Lassignerr: + if (fd && !(fd->storage_class & STCdisable)) + error("identity assignment operator overload is illegal"); + } sc->pop(); #if 0 // Do not call until toObjfile() because of forward references @@ -831,13 +859,13 @@ void ClassDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; - - buf->writestring(" "); s->toCBuffer(buf, hgs); } + buf->level--; buf->writestring("}"); } else @@ -1280,6 +1308,7 @@ void InterfaceDeclaration::semantic(Scope *sc) { isdeprecated = true; } + userAttributes = sc->userAttributes; // Expand any tuples in baseclasses[] for (size_t i = 0; i < baseclasses->dim; ) @@ -1423,6 +1452,7 @@ void InterfaceDeclaration::semantic(Scope *sc) sc->explicitProtection = 0; // structalign = sc->structalign; sc->offset = PTRSIZE * 2; + sc->userAttributes = NULL; structsize = sc->offset; inuse++; @@ -1472,11 +1502,9 @@ void InterfaceDeclaration::semantic(Scope *sc) int InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) { - unsigned j; - //printf("%s.InterfaceDeclaration::isBaseOf(cd = '%s')\n", toChars(), cd->toChars()); assert(!baseClass); - for (j = 0; j < cd->interfaces_dim; j++) + for (size_t j = 0; j < cd->interfaces_dim; j++) { BaseClass *b = cd->interfaces[j]; @@ -1510,7 +1538,7 @@ int InterfaceDeclaration::isBaseOf(ClassDeclaration *cd, int *poffset) int InterfaceDeclaration::isBaseOf(BaseClass *bc, int *poffset) { //printf("%s.InterfaceDeclaration::isBaseOf(bc = '%s')\n", toChars(), bc->base->toChars()); - for (unsigned j = 0; j < bc->baseInterfaces_dim; j++) + for (size_t j = 0; j < bc->baseInterfaces_dim; j++) { BaseClass *b = &bc->baseInterfaces[j]; @@ -1680,7 +1708,7 @@ void BaseClass::copyBaseInterfaces(BaseClasses *vtblInterfaces) baseInterfaces = (BaseClass *)mem.calloc(baseInterfaces_dim, sizeof(BaseClass)); //printf("%s.copyBaseInterfaces()\n", base->toChars()); - for (int i = 0; i < baseInterfaces_dim; i++) + for (size_t i = 0; i < baseInterfaces_dim; i++) { BaseClass *b = &baseInterfaces[i]; BaseClass *b2 = base->interfaces[i]; diff --git a/dmd2/clone.c b/dmd2/clone.c index bfd167ae..37fa2a13 100644 --- a/dmd2/clone.c +++ b/dmd2/clone.c @@ -34,8 +34,9 @@ int StructDeclaration::needOpAssign() { #define X 0 if (X) printf("StructDeclaration::needOpAssign() %s\n", toChars()); + if (hasIdentityAssign) - goto Ldontneed; + goto Lneed; // because has identity==elaborate opAssign if (dtor || postblit) goto Lneed; @@ -74,25 +75,51 @@ Lneed: /****************************************** * Build opAssign for struct. - * S* opAssign(S s) { ... } + * ref S opAssign(S s) { ... } * * Note that s will be constructed onto the stack, probably copy-constructed. * Then, the body is: - * S tmp = *this; // bit copy - * *this = s; // bit copy + * S tmp = this; // bit copy + * this = s; // bit copy * tmp.dtor(); * Instead of running the destructor on s, run it on tmp instead. */ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) { + Dsymbol *assign = search_function(this, Id::assign); + if (assign) + { + /* check identity opAssign exists + */ + Expression *er = new NullExp(loc, type); // dummy rvalue + Expression *el = new IdentifierExp(loc, Id::p); // dummy lvalue + el->type = type; + Expressions ar; ar.push(er); + Expressions al; al.push(el); + if (FuncDeclaration *fd = assign->isFuncDeclaration()) + { + FuncDeclaration *f = fd->overloadResolve(loc, er, &ar, 1); + if (f == NULL) f = fd->overloadResolve(loc, er, &al, 1); + if (f) + return (f->storage_class & STCdisable) ? NULL : f; + } + if (TemplateDeclaration *td = assign->isTemplateDeclaration()) + { + FuncDeclaration *f = td->deduceFunctionTemplate(sc, loc, NULL, er, &ar, 1); + if (f == NULL) f = td->deduceFunctionTemplate(sc, loc, NULL, er, &al, 1); + if (f) + return (f->storage_class & STCdisable) ? NULL : f; + } + // Even if non-identity opAssign is defined, built-in identity opAssign + // will be defined. (Is this an exception of operator overloading rule?) + } + if (!needOpAssign()) return NULL; //printf("StructDeclaration::buildOpAssign() %s\n", toChars()); - FuncDeclaration *fop = NULL; - Parameters *fparams = new Parameters; fparams->push(new Parameter(STCnodtor, type, Id::p, NULL)); Type *ftype = new TypeFunction(fparams, handle, FALSE, LINKd); @@ -100,7 +127,7 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) ((TypeFunction *)ftype)->isref = 1; #endif - fop = new FuncDeclaration(loc, 0, Id::assign, STCundefined, ftype); + FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::assign, STCundefined, ftype); Expression *e = NULL; if (postblit) @@ -160,7 +187,6 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) AssignExp *ec = new AssignExp(0, new DotVarExp(0, new ThisExp(0), v, 0), new DotVarExp(0, new IdentifierExp(0, Id::p), v, 0)); - ec->op = TOKblit; e = Expression::combine(e, ec); } } @@ -174,15 +200,24 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) fop->fbody = new CompoundStatement(0, s1, s2); - members->push(fop); - fop->addMember(sc, this, 1); + Dsymbol *s = fop; + if (assign && assign->isTemplateDeclaration()) + { + // Wrap a template around the function declaration + TemplateParameters *tpl = new TemplateParameters(); + Dsymbols *decldefs = new Dsymbols(); + decldefs->push(s); + TemplateDeclaration *tempdecl = + new TemplateDeclaration(assign->loc, fop->ident, tpl, NULL, decldefs, 0); + s = tempdecl; + } + members->push(s); + s->addMember(sc, this, 1); sc = sc->push(); sc->stc = 0; sc->linkage = LINKd; - - fop->semantic(sc); - + s->semantic(sc); sc->pop(); //printf("-StructDeclaration::buildOpAssign() %s\n", toChars()); @@ -357,10 +392,10 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) parameters->push(new Parameter(STCin, Type::tvoidptr, Id::p, NULL)); parameters->push(new Parameter(STCin, Type::tvoidptr, Id::q, NULL)); TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); - tf = (TypeFunction *)tf->semantic(loc, sc); + tf = (TypeFunction *)tf->semantic(0, sc); Identifier *id = Lexer::idPool("__xopEquals"); - FuncDeclaration *fop = new FuncDeclaration(loc, 0, id, STCstatic, tf); + FuncDeclaration *fop = new FuncDeclaration(0, 0, id, STCstatic, tf); Expression *e = new CallExp(0, new DotIdExp(0, @@ -370,7 +405,7 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) new PtrExp(0, new CastExp(0, new IdentifierExp(0, Id::q), type->pointerTo()->constOf()))); - fop->fbody = new ReturnStatement(loc, e); + fop->fbody = new ReturnStatement(0, e); size_t index = members->dim; members->push(fop); @@ -395,9 +430,9 @@ FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) if (!xerreq) { - Expression *e = new IdentifierExp(loc, Id::empty); - e = new DotIdExp(loc, e, Id::object); - e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals")); + Expression *e = new IdentifierExp(0, Id::empty); + e = new DotIdExp(0, e, Id::object); + e = new DotIdExp(0, e, Lexer::idPool("_xopEquals")); e = e->semantic(sc); Dsymbol *s = getDsymbol(e); FuncDeclaration *fd = s->isFuncDeclaration(); @@ -567,8 +602,7 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) */ if (e || (stc & STCdisable)) { //printf("Building __fieldPostBlit()\n"); - PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, Lexer::idPool("__fieldPostBlit")); - dd->storage_class |= stc; + PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, stc, Lexer::idPool("__fieldPostBlit")); dd->fbody = new ExpStatement(0, e); postblits.shift(dd); members->push(dd); @@ -598,8 +632,7 @@ FuncDeclaration *StructDeclaration::buildPostBlit(Scope *sc) ex = new CallExp(0, ex); e = Expression::combine(e, ex); } - PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, Lexer::idPool("__aggrPostBlit")); - dd->storage_class |= stc; + PostBlitDeclaration *dd = new PostBlitDeclaration(loc, 0, stc, Lexer::idPool("__aggrPostBlit")); dd->fbody = new ExpStatement(0, e); members->push(dd); dd->semantic(sc); diff --git a/dmd2/cond.c b/dmd2/cond.c index f54fd467..5846ee6c 100644 --- a/dmd2/cond.c +++ b/dmd2/cond.c @@ -266,6 +266,7 @@ int StaticIfCondition::include(Scope *sc, ScopeDsymbol *s) sc->sd = s; // s gets any addMember() sc->flags |= SCOPEstaticif; Expression *e = exp->semantic(sc); + e = resolveProperties(sc, e); sc->pop(); if (!e->type->checkBoolean()) { diff --git a/dmd2/constfold.c b/dmd2/constfold.c index b114885e..cafe9e87 100644 --- a/dmd2/constfold.c +++ b/dmd2/constfold.c @@ -910,6 +910,14 @@ Expression *Equal(enum TOK op, Type *type, Expression *e1, Expression *e2) break; } } +#if !IN_LLVM + // LDC_FIXME: Implement this. + if (cmp && es1->type->needsNested()) + { + if ((es1->sinit != NULL) != (es2->sinit != NULL)) + cmp = 0; + } +#endif } #if 0 // Should handle this else if (e1->op == TOKarrayliteral && e2->op == TOKstring) @@ -1495,7 +1503,7 @@ Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) /* Set a slice of char array literal 'existingAE' from a string 'newval'. * existingAE[firstIndex..firstIndex+newval.length] = newval. */ -void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex) +void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex) { size_t newlen = newval->len; size_t sz = newval->sz; @@ -1521,7 +1529,7 @@ void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *n /* Set a slice of string 'existingSE' from a char array literal 'newae'. * existingSE[firstIndex..firstIndex+newae.length] = newae. */ -void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex) +void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, size_t firstIndex) { unsigned char *s = (unsigned char *)existingSE->string; for (size_t j = 0; j < newae->elements->dim; j++) @@ -1542,7 +1550,7 @@ void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *n /* Set a slice of string 'existingSE' from a string 'newstr'. * existingSE[firstIndex..firstIndex+newstr.length] = newstr. */ -void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex) +void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, size_t firstIndex) { unsigned char *s = (unsigned char *)existingSE->string; size_t sz = existingSE->sz; @@ -1621,7 +1629,7 @@ Expression *Cat(Type *type, Expression *e1, Expression *e2) StringExp *es; if (t->nextOf()) t = t->nextOf()->toBasetype(); - int sz = t->size(); + size_t sz = t->size(); dinteger_t v = e->toInteger(); @@ -1740,7 +1748,7 @@ Expression *Cat(Type *type, Expression *e1, Expression *e2) void *s; StringExp *es1 = (StringExp *)e1; StringExp *es; - int sz = es1->sz; + size_t sz = es1->sz; dinteger_t v = e2->toInteger(); // Is it a concatentation of homogenous types? diff --git a/dmd2/cppmangle.c b/dmd2/cppmangle.c index f29eaa83..48e37094 100644 --- a/dmd2/cppmangle.c +++ b/dmd2/cppmangle.c @@ -133,8 +133,7 @@ void cpp_mangle_name(OutBuffer *buf, CppMangleState *cms, Dsymbol *s) { s->error("C++ static variables not supported"); } - else - if (fd->isConst()) + else if (fd->type->isConst()) buf->writeByte('K'); prefix_name(buf, cms, p); @@ -411,38 +410,51 @@ void TypeClass::toCppMangle(OutBuffer *buf, CppMangleState *cms) } } +struct ArgsCppMangleCtx +{ + OutBuffer *buf; + CppMangleState *cms; + size_t cnt; +}; +static int argsCppMangleDg(void *ctx, size_t n, Parameter *arg) +{ + ArgsCppMangleCtx *p = (ArgsCppMangleCtx *)ctx; + + Type *t = arg->type; + if (arg->storageClass & (STCout | STCref)) + t = t->referenceTo(); + else if (arg->storageClass & STClazy) + { // Mangle as delegate + Type *td = new TypeFunction(NULL, t, 0, LINKd); + td = new TypeDelegate(td); + t = t->merge(); + } + if (t->ty == Tsarray) + { // Mangle static arrays as pointers + t = t->pointerTo(); + } + + /* If it is a basic, enum or struct type, + * then don't mark it const + */ + if ((t->ty == Tenum || t->ty == Tstruct || t->isTypeBasic()) && t->isConst()) + t->mutableOf()->toCppMangle(p->buf, p->cms); + else + t->toCppMangle(p->buf, p->cms); + + p->cnt++; + return 0; +} void Parameter::argsCppMangle(OutBuffer *buf, CppMangleState *cms, Parameters *arguments, int varargs) -{ int n = 0; +{ + size_t n = 0; if (arguments) { - for (size_t i = 0; i < arguments->dim; i++) - { Parameter *arg = (*arguments)[i]; - Type *t = arg->type; - if (arg->storageClass & (STCout | STCref)) - t = t->referenceTo(); - else if (arg->storageClass & STClazy) - { // Mangle as delegate - Type *td = new TypeFunction(NULL, t, 0, LINKd); - td = new TypeDelegate(td); - t = t->merge(); - } - if (t->ty == Tsarray) - { // Mangle static arrays as pointers - t = t->pointerTo(); - } - - /* If it is a basic, enum or struct type, - * then don't mark it const - */ - if ((t->ty == Tenum || t->ty == Tstruct || t->isTypeBasic()) && t->isConst()) - t->mutableOf()->toCppMangle(buf, cms); - else - t->toCppMangle(buf, cms); - - n++; - } + ArgsCppMangleCtx ctx = { buf, cms, 0 }; + foreach(arguments, &argsCppMangleDg, &ctx); + n = ctx.cnt; } if (varargs) buf->writestring("z"); diff --git a/dmd2/ctfe.h b/dmd2/ctfe.h new file mode 100644 index 00000000..d5d72908 --- /dev/null +++ b/dmd2/ctfe.h @@ -0,0 +1,243 @@ +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#ifndef DMD_CTFE_H +#define DMD_CTFE_H + +#ifdef __DMC__ +#pragma once +#endif /* __DMC__ */ + + +/** + Global status of the CTFE engine. Mostly used for performance diagnostics + */ +struct CtfeStatus +{ + static int callDepth; // current number of recursive calls + 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 +}; + + +/** Expression subclasses which only exist in CTFE */ + +#define TOKclassreference ((TOK)(TOKMAX+1)) +#define TOKthrownexception ((TOK)(TOKMAX+2)) + +/** + A 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 *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + char *toChars(); + ClassDeclaration *originalClass(); + VarDeclaration *getFieldAt(unsigned index); + + /// Return index of the field, or -1 if not found + int getFieldIndex(Type *fieldtype, unsigned fieldoffset); + /// Return index of the field, or -1 if not found + /// Same as getFieldIndex, but checks for a direct match with the VarDeclaration + int findFieldIndexByName(VarDeclaration *v); +}; + +/// Return index of the field, or -1 if not found +/// Same as getFieldIndex, but checks for a direct match with the VarDeclaration +int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v); + + +/** An uninitialized value + */ +struct VoidInitExp : Expression +{ + VarDeclaration *var; + + VoidInitExp(VarDeclaration *var, Type *type); + char *toChars(); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); +}; + + +/** 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 *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); + char *toChars(); + /// Generate an error message when this exception is not caught + void generateUncaughtError(); +}; + + +/// True if 'e' is EXP_CANT_INTERPRET, or an exception +bool exceptionOrCantInterpret(Expression *e); + +// Used for debugging only +void showCtfeExpr(Expression *e, int level = 0); + +/// Return true if this is a valid CTFE expression +bool isCtfeValueValid(Expression *newval); + + +/// Given expr, which evaluates to an array/AA/string literal, +/// return true if it needs to be copied +bool needToCopyLiteral(Expression *expr); + +/// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. +/// This value will be used for in-place modification. +Expression *copyLiteral(Expression *e); + +/// Set this literal to the given type, copying it if necessary +Expression *paintTypeOntoLiteral(Type *type, Expression *lit); + +/// Convert from a CTFE-internal slice, into a normal Expression +Expression *resolveSlice(Expression *e); + +/// Determine the array length, without interpreting the expression. +uinteger_t resolveArrayLength(Expression *e); + +/// Create an array literal consisting of 'elem' duplicated 'dim' times. +ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Loc loc, Type *type, + Expression *elem, size_t dim); + +/// Create a string literal consisting of 'value' duplicated 'dim' times. +StringExp *createBlockDuplicatedStringLiteral(Loc loc, Type *type, + unsigned value, size_t dim, int sz); + + +/* Set dest = src, where both dest and src are container value literals + * (ie, struct literals, or static arrays (can be an array literal or a string) + * Assignment is recursively in-place. + * Purpose: any reference to a member of 'dest' will remain valid after the + * assignment. + */ +void assignInPlace(Expression *dest, Expression *src); + +/// Set all elements of 'ae' to 'val'. ae may be a multidimensional array. +/// If 'wantRef', all elements of ae will hold references to the same val. +void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef); + +/// Duplicate the elements array, then set field 'indexToChange' = newelem. +Expressions *changeOneElement(Expressions *oldelems, size_t indexToChange, Expression *newelem); + +/// 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); + +/// Given an AA literal aae, set arr[index] = newval and return the new array. +Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae, + Expression *index, Expression *newval); + +/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length +/// oldlen, change its length to newlen. If the newlen is longer than oldlen, +/// all new elements will be set to the default initializer for the element type. +Expression *changeArrayLiteralLength(Loc loc, TypeArray *arrayType, + Expression *oldval, size_t oldlen, size_t newlen); + + + +/// Return true if t is a pointer (not a function pointer) +bool isPointer(Type *t); + +// For CTFE only. Returns true if 'e' is TRUE or a non-null pointer. +int isTrueBool(Expression *e); + +/// 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); + +/// Given pointer e, return the memory block expression it points to, +/// and set ofs to the offset within that memory block. +Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs); + +/// Return true if agg1 and agg2 are pointers to the same memory block +bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2); + +// return e1 - e2 as an integer, or error if not possible +Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2); + +/// Return 1 if true, 0 if false +/// -1 if comparison is illegal because they point to non-comparable memory blocks +int comparePointers(Loc loc, enum TOK op, Type *type, Expression *agg1, dinteger_t ofs1, Expression *agg2, dinteger_t ofs2); + +// Return eptr op e2, where eptr is a pointer, e2 is an integer, +// and op is TOKadd or TOKmin +Expression *pointerArithmetic(Loc loc, enum TOK op, Type *type, + Expression *eptr, Expression *e2); + +// True if conversion from type 'from' to 'to' involves a reinterpret_cast +// floating point -> integer or integer -> floating point +bool isFloatIntPaint(Type *to, Type *from); + +// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. +Expression *paintFloatInt(Expression *fromVal, Type *to); + +/// Return true if t is an AA, or AssociativeArray!(key, value) +bool isAssocArray(Type *t); + +/// Given a template AA type, extract the corresponding built-in AA type +TypeAArray *toBuiltinAAType(Type *t); + +/* Given an AA literal 'ae', and a key 'e2': + * Return ae[e2] if present, or NULL if not found. + * Return EXP_CANT_INTERPRET on error. + */ +Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2); + +/*********************************************** + In-place integer operations +***********************************************/ + +/// e = OP e +void intUnary(TOK op, IntegerExp *e); + +/// dest = e1 OP e2; +void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp *e2); + + +/*********************************************** + COW const-folding operations +***********************************************/ + +/// Return true if non-pointer expression e can be compared +/// with >,is, ==, etc, using ctfeCmp, ctfeEquals, ctfeIdentity +bool isCtfeComparable(Expression *e); + +/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 +int ctfeEqual(Loc loc, enum TOK op, Expression *e1, Expression *e2); + +/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 +int ctfeIdentity(Loc loc, enum TOK op, Expression *e1, Expression *e2); + +/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 +int ctfeCmp(Loc loc, enum TOK op, Expression *e1, Expression *e2); + +/// Returns e1 ~ e2. Resolves slices before concatenation. +Expression *ctfeCat(Type *type, Expression *e1, Expression *e2); + +/// Same as for constfold.Index, except that it only works for static arrays, +/// dynamic arrays, and strings. +Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx); + +/// Cast 'e' of type 'type' to type 'to'. +Expression *ctfeCast(Loc loc, Type *type, Type *to, Expression *e); + + +#endif /* DMD_CTFE_H */ diff --git a/dmd2/ctfeexpr.c b/dmd2/ctfeexpr.c new file mode 100644 index 00000000..64b9012e --- /dev/null +++ b/dmd2/ctfeexpr.c @@ -0,0 +1,2091 @@ +// Compiler implementation of the D programming language +// Copyright (c) 1999-2012 by Digital Mars +// All Rights Reserved +// written by Walter Bright +// http://www.digitalmars.com +// License for redistribution is by either the Artistic License +// in artistic.txt, or the GNU General Public License in gnu.txt. +// See the included readme.txt for details. + +#include +#include +#include +#include // mem{cpy|set}() + +#include "rmem.h" + +#include "expression.h" +#include "declaration.h" +#include "aggregate.h" +// for AssocArray +#include "id.h" +#include "template.h" +#include "ctfe.h" + +int RealEquals(real_t x1, real_t x2); + +/************** ClassReferenceExp ********************************************/ + +ClassReferenceExp::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 *ClassReferenceExp::interpret(InterState *istate, CtfeGoal goal) +{ + //printf("ClassReferenceExp::interpret() %s\n", value->toChars()); + return this; +} + +char *ClassReferenceExp::toChars() +{ + return value->toChars(); +} + +ClassDeclaration *ClassReferenceExp::originalClass() +{ + return value->sd->isClassDeclaration(); +} + +VarDeclaration *ClassReferenceExp::getFieldAt(unsigned index) +{ + ClassDeclaration *cd = originalClass(); + unsigned fieldsSoFar = 0; + while (index - fieldsSoFar >= cd->fields.dim) + { fieldsSoFar += cd->fields.dim; + cd = cd->baseClass; + } + return cd->fields[index - fieldsSoFar]; +} + +// Return index of the field, or -1 if not found +int ClassReferenceExp::getFieldIndex(Type *fieldtype, unsigned fieldoffset) +{ + ClassDeclaration *cd = originalClass(); + unsigned 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[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; +} + +// Return index of the field, or -1 if not found +// Same as getFieldIndex, but checks for a direct match with the VarDeclaration +int ClassReferenceExp::findFieldIndexByName(VarDeclaration *v) +{ + 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[j - fieldsSoFar]; + VarDeclaration *v2 = s->isVarDeclaration(); + if (v == v2) + { return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar); + } + } + return -1; +} + +/************** VoidInitExp ********************************************/ + +VoidInitExp::VoidInitExp(VarDeclaration *var, Type *type) + : Expression(var->loc, TOKvoid, sizeof(VoidInitExp)) +{ + this->var = var; + this->type = var->type; +} + +char *VoidInitExp::toChars() +{ + return (char *)"void"; +} + +Expression *VoidInitExp::interpret(InterState *istate, CtfeGoal goal) +{ + error("CTFE internal error: trying to read uninitialized variable"); + assert(0); + return EXP_CANT_INTERPRET; +} + +// Return index of the field, or -1 if not found +// Same as getFieldIndex, but checks for a direct match with the VarDeclaration +int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v) +{ + for (int i = 0; i < sd->fields.dim; ++i) + { + if (sd->fields[i] == v) + return i; + } + return -1; +} + +/************** ThrownExceptionExp ********************************************/ + +ThrownExceptionExp::ThrownExceptionExp(Loc loc, ClassReferenceExp *victim) : Expression(loc, TOKthrownexception, sizeof(ThrownExceptionExp)) +{ + this->thrown = victim; + this->type = victim->type; +} + +Expression *ThrownExceptionExp::interpret(InterState *istate, CtfeGoal) +{ + assert(0); // This should never be interpreted + return this; +} + +char *ThrownExceptionExp::toChars() +{ + return (char *)"CTFE ThrownException"; +} + +// Generate an error message when this exception is not caught +void ThrownExceptionExp::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)) + errorSupplemental(loc, "thrown from here"); +} + + +// 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; +} + +/************** Aggregate literals (AA/string/array/struct) ******************/ + +// Given expr, which evaluates to an array/AA/string literal, +// return true if it needs to be copied +bool needToCopyLiteral(Expression *expr) +{ + for (;;) + { + switch (expr->op) + { + case TOKarrayliteral: + return !((ArrayLiteralExp *)expr)->ownedByCtfe; + case TOKassocarrayliteral: + return !((AssocArrayLiteralExp *)expr)->ownedByCtfe; + case TOKstructliteral: + return !((StructLiteralExp *)expr)->ownedByCtfe; + case TOKstring: + case TOKthis: + case TOKvar: + return false; + case TOKassign: + return false; + case TOKindex: + case TOKdotvar: + case TOKslice: + case TOKcast: + expr = ((UnaExp *)expr)->e1; + continue; + case TOKcat: + return needToCopyLiteral(((BinExp *)expr)->e1) || + needToCopyLiteral(((BinExp *)expr)->e2); + case TOKcatass: + expr = ((BinExp *)expr)->e2; + continue; + default: + return false; + } + } +} + +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++) + newelems->tdata()[i] = copyLiteral(oldelems->tdata()[i]); + return newelems; +} + +// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. +// This value will be used for in-place modification. +Expression *copyLiteral(Expression *e) +{ + if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp! + { + StringExp *se = (StringExp *)e; + unsigned char *s; + s = (unsigned char *)mem.calloc(se->len + 1, se->sz); + memcpy(s, se->string, se->len * se->sz); + StringExp *se2 = new StringExp(se->loc, s, se->len); + se2->committed = se->committed; + se2->postfix = se->postfix; + se2->type = se->type; + se2->sz = se->sz; + se2->ownedByCtfe = true; + return se2; + } + else if (e->op == TOKarrayliteral) + { + ArrayLiteralExp *ae = (ArrayLiteralExp *)e; + ArrayLiteralExp *r = new ArrayLiteralExp(e->loc, + copyLiteralArray(ae->elements)); + r->type = e->type; + r->ownedByCtfe = true; + return r; + } + else if (e->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + AssocArrayLiteralExp *r = new AssocArrayLiteralExp(e->loc, + copyLiteralArray(aae->keys), copyLiteralArray(aae->values)); + r->type = e->type; + r->ownedByCtfe = true; + return r; + } + /* syntaxCopy doesn't work for struct literals, because of a nasty special + * case: block assignment is permitted inside struct literals, eg, + * an int[4] array can be initialized with a single int. + */ + else if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + Expressions *oldelems = se->elements; + Expressions * newelems = new Expressions(); + newelems->setDim(oldelems->dim); + for (size_t i = 0; i < newelems->dim; i++) + { + Expression *m = oldelems->tdata()[i]; + // We need the struct definition to detect block assignment + AggregateDeclaration *sd = se->sd; + Dsymbol *s = sd->fields[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + // If it is a void assignment, use the default initializer + if (!m) + m = v->type->voidInitLiteral(v); + if (m->op == TOKslice) + m = resolveSlice(m); + if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray) + { + // Block assignment from inside struct literals + TypeSArray *tsa = (TypeSArray *)v->type; + uinteger_t length = tsa->dim->toInteger(); + m = createBlockDuplicatedArrayLiteral(e->loc, v->type, m, (size_t)length); + } + else if (v->type->ty != Tarray && v->type->ty!=Taarray) // NOTE: do not copy array references + m = copyLiteral(m); + newelems->tdata()[i] = m; + } +#if DMDV2 + StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems, se->stype); +#else + StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems); +#endif + r->type = e->type; + r->ownedByCtfe = true; + return r; + } + else if (e->op == TOKfunction || e->op == TOKdelegate + || e->op == TOKsymoff || e->op == TOKnull + || e->op == TOKvar + || e->op == TOKint64 || e->op == TOKfloat64 + || e->op == TOKchar || e->op == TOKcomplex80 + || e->op == TOKvoid) + { // Simple value types + Expression *r = e->syntaxCopy(); + r->type = e->type; + return r; + } + else if ( isPointer(e->type) ) + { // For pointers, we only do a shallow copy. + Expression *r; + if (e->op == TOKaddress) + r = new AddrExp(e->loc, ((AddrExp *)e)->e1); + else if (e->op == TOKindex) + r = new IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2); + else if (e->op == TOKdotvar) + r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1, + ((DotVarExp *)e)->var +#if DMDV2 + , ((DotVarExp *)e)->hasOverloads +#endif + ); + else + assert(0); + r->type = e->type; + return r; + } + else if (e->op == TOKslice) + { // Array slices only do a shallow copy + Expression *r = new SliceExp(e->loc, ((SliceExp *)e)->e1, + ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); + 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()); + assert(0); + return e; + } +} + +/* Deal with type painting. + * Type painting is a major nuisance: we can't just set + * e->type = type, because that would change the original literal. + * But, we can't simply copy the literal either, because that would change + * the values of any pointers. + */ +Expression *paintTypeOntoLiteral(Type *type, Expression *lit) +{ + if (lit->type == type) + return lit; + Expression *e; + if (lit->op == TOKslice) + { + SliceExp *se = (SliceExp *)lit; + e = new SliceExp(lit->loc, se->e1, se->lwr, se->upr); + } + else if (lit->op == TOKindex) + { + IndexExp *ie = (IndexExp *)lit; + e = new IndexExp(lit->loc, ie->e1, ie->e2); + } + else if (lit->op == TOKarrayliteral) + { + e = new SliceExp(lit->loc, lit, + new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); + } + else if (lit->op == TOKstring) + { + // For strings, we need to introduce another level of indirection + e = new SliceExp(lit->loc, lit, + new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); + } + else if (lit->op == TOKassocarrayliteral) + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit; + // TODO: we should be creating a reference to this AAExp, not + // just a ref to the keys and values. + bool wasOwned = aae->ownedByCtfe; + aae = new AssocArrayLiteralExp(lit->loc, aae->keys, aae->values); + aae->ownedByCtfe = wasOwned; + e = aae; + } + else + { // Can't type paint from struct to struct*; this needs another + // level of indirection + if (lit->op == TOKstructliteral && isPointer(type) ) + lit->error("CTFE internal error painting %s", type->toChars()); + e = copyLiteral(lit); + } + e->type = type; + return e; +} + +Expression *resolveSlice(Expression *e) +{ + if ( ((SliceExp *)e)->e1->op == TOKnull) + return ((SliceExp *)e)->e1; + return Slice(e->type, ((SliceExp *)e)->e1, + ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); +} + +/* Determine the array length, without interpreting it. + * e must be an array literal, or a slice + * It's very wasteful to resolve the slice when we only + * need the length. + */ +uinteger_t resolveArrayLength(Expression *e) +{ + if (e->op == TOKnull) + return 0; + if (e->op == TOKslice) + { uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger(); + uinteger_t iup = ((SliceExp *)e)->upr->toInteger(); + return iup - ilo; + } + if (e->op == TOKstring) + { return ((StringExp *)e)->len; + } + if (e->op == TOKarrayliteral) + { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; + return ale->elements ? ale->elements->dim : 0; + } + if (e->op == TOKassocarrayliteral) + { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e; + return ale->keys->dim; + } + assert(0); + return 0; +} + +/****************************** + * Helper for NewExp + * Create an array literal consisting of 'elem' duplicated 'dim' times. + */ +ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Loc loc, Type *type, + Expression *elem, size_t dim) +{ + Expressions *elements = new Expressions(); + elements->setDim(dim); + bool mustCopy = needToCopyLiteral(elem); + for (size_t i = 0; i < dim; i++) + { if (mustCopy) + elem = copyLiteral(elem); + (*elements)[i] = elem; + } + ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements); + ae->type = type; + ae->ownedByCtfe = true; + return ae; +} + +/****************************** + * Helper for NewExp + * Create a string literal consisting of 'value' duplicated 'dim' times. + */ +StringExp *createBlockDuplicatedStringLiteral(Loc loc, Type *type, + unsigned value, size_t dim, int sz) +{ + unsigned char *s; + s = (unsigned char *)mem.calloc(dim + 1, sz); + for (size_t elemi = 0; elemi < dim; ++elemi) + { + switch (sz) + { + case 1: s[elemi] = value; break; + case 2: ((unsigned short *)s)[elemi] = value; break; + case 4: ((unsigned *)s)[elemi] = value; break; + default: assert(0); + } + } + StringExp *se = new StringExp(loc, s, dim); + se->type = type; + se->sz = sz; + se->committed = true; + se->ownedByCtfe = true; + return se; +} + +// Return true if t is an AA, or AssociativeArray!(key, value) +bool isAssocArray(Type *t) +{ + t = t->toBasetype(); + if (t->ty == Taarray) + return true; +#if DMDV2 + if (t->ty != Tstruct) + return false; + StructDeclaration *sym = ((TypeStruct *)t)->sym; + if (sym->ident == Id::AssociativeArray && sym->parent && + sym->parent->parent && + sym->parent->parent->ident == Id::object) + { + return true; + } +#endif + return false; +} + +// Given a template AA type, extract the corresponding built-in AA type +TypeAArray *toBuiltinAAType(Type *t) +{ + t = t->toBasetype(); + if (t->ty == Taarray) + return (TypeAArray *)t; +#if DMDV2 + assert(t->ty == Tstruct); + StructDeclaration *sym = ((TypeStruct *)t)->sym; + assert(sym->ident == Id::AssociativeArray); + TemplateInstance *tinst = sym->parent->isTemplateInstance(); + assert(tinst); + return new TypeAArray((Type *)(tinst->tiargs->tdata()[1]), (Type *)(tinst->tiargs->tdata()[0])); +#else + assert(0); + return NULL; +#endif +} + +/************** Pointer operations ************************************/ + +// Return true if t is a pointer (not a function pointer) +bool isPointer(Type *t) +{ + Type * tb = t->toBasetype(); + return tb->ty == Tpointer && tb->nextOf()->ty != Tfunction; +} + +// For CTFE only. Returns true if 'e' is TRUE or a non-null pointer. +int isTrueBool(Expression *e) +{ + return e->isBool(TRUE) || ((e->type->ty == Tpointer || e->type->ty == Tclass) + && e->op != TOKnull); +} + +/* 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 *getAggregateFromPointer(Expression *e, dinteger_t *ofs) +{ + *ofs = 0; + if (e->op == TOKaddress) + e = ((AddrExp *)e)->e1; + if (e->op == TOKdotvar) + { + Expression *ex = ((DotVarExp *)e)->e1; + VarDeclaration *v = ((DotVarExp *)e)->var->isVarDeclaration(); + assert(v); + StructLiteralExp *se = ex->op == TOKclassreference ? ((ClassReferenceExp *)ex)->value : (StructLiteralExp *)ex; + // We can't use getField, because it makes a copy + unsigned i; + if (ex->op == TOKclassreference) + i = ((ClassReferenceExp *)ex)->getFieldIndex(e->type, v->offset); + else + i = se->getFieldIndex(e->type, v->offset); + e = se->elements->tdata()[i]; + } + if (e->op == TOKindex) + { + IndexExp *ie = (IndexExp *)e; + // Note that each AA element is part of its own memory block + if ((ie->e1->type->ty == Tarray || ie->e1->type->ty == Tsarray + || ie->e1->op == TOKstring || ie->e1->op==TOKarrayliteral) && + ie->e2->op == TOKint64) + { + *ofs = ie->e2->toInteger(); + return ie->e1; + } + } + return e; +} + +/** Return true if agg1 and agg2 are pointers to the same memory block +*/ +bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2) +{ + // Note that type painting can occur with VarExp, so we + // must compare the variables being pointed to. + return agg1 == agg2 || + (agg1->op == TOKvar && agg2->op == TOKvar && + ((VarExp *)agg1)->var == ((VarExp *)agg2)->var); +} + +// return e1 - e2 as an integer, or error if not possible +Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2) +{ + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + if (agg1 == agg2) + { + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + return new IntegerExp(loc, (ofs1-ofs2)*sz, type); + } + else if (agg1->op == TOKstring && agg2->op == TOKstring) + { + if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string) + { + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + return new IntegerExp(loc, (ofs1-ofs2)*sz, type); + } + } + error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract " + "pointers to two different memory blocks", + e1->toChars(), e2->toChars()); + return EXP_CANT_INTERPRET; +} + +// Return eptr op e2, where eptr is a pointer, e2 is an integer, +// and op is TOKadd or TOKmin +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; + Expression *agg1 = getAggregateFromPointer(eptr, &ofs1); + if (agg1->op != TOKstring && agg1->op != TOKarrayliteral) + { + error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); + return EXP_CANT_INTERPRET; + } + ofs2 = e2->toInteger(); + Type *pointee = ((TypePointer *)agg1->type)->next; + dinteger_t sz = pointee->size(); + Expression *dollar = ArrayLength(Type::tsize_t, agg1); + assert(dollar != EXP_CANT_INTERPRET); + dinteger_t len = dollar->toInteger(); + + Expression *val = agg1; + TypeArray *tar = (TypeArray *)val->type; + sinteger_t indx = ofs1; +#if IN_LLVM // LDC: llvm uses typesafe pointer arithmetic + if (op == TOKadd || op == TOKaddass || op == TOKplusplus) + indx += ofs2; + else if (op == TOKmin || op == TOKminass || op == TOKminusminus) + indx -= ofs2; +#else + if (op == TOKadd || op == TOKaddass || op == TOKplusplus) + indx = indx + ofs2/sz; + else if (op == TOKmin || op == TOKminass || op == TOKminusminus) + indx -= ofs2/sz; +#endif + else + { + error(loc, "CTFE Internal compiler error: bad pointer operation"); + return EXP_CANT_INTERPRET; + } + if (val->op != TOKarrayliteral && val->op != TOKstring) + { + error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars()); + return EXP_CANT_INTERPRET; + } + if (indx < 0 || indx > len) + { + error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", indx, len); + return EXP_CANT_INTERPRET; + } + + IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); + IndexExp *ie = new IndexExp(loc, val, ofs); + ie->type = type; + return ie; +} + +// Return 1 if true, 0 if false +// -1 if comparison is illegal because they point to non-comparable memory blocks +int comparePointers(Loc loc, enum TOK op, Type *type, Expression *agg1, dinteger_t ofs1, + Expression *agg2, dinteger_t ofs2) +{ + if ( pointToSameMemoryBlock(agg1, agg2) ) + { + dinteger_t cm = ofs1 - ofs2; + dinteger_t n; + dinteger_t zero = 0; + switch(op) + { + case TOKlt: n = (ofs1 < ofs2); break; + case TOKle: n = (ofs1 <= ofs2); break; + case TOKgt: n = (ofs1 > ofs2); break; + case TOKge: n = (ofs1 >= ofs2); break; + case TOKidentity: + case TOKequal: n = (ofs1 == ofs2); break; + case TOKnotidentity: + case TOKnotequal: n = (ofs1 != ofs2); break; + default: + assert(0); + } + return n; + } + bool null1 = ( agg1->op == TOKnull ); + bool null2 = ( agg2->op == TOKnull ); + + int cmp; + if (null1 || null2) + { + switch (op) + { + case TOKlt: cmp = null1 && !null2; break; + case TOKgt: cmp = !null1 && null2; break; + case TOKle: cmp = null1; break; + case TOKge: cmp = null2; break; + case TOKidentity: + case TOKequal: + case TOKnotidentity: // 'cmp' gets inverted below + case TOKnotequal: + cmp = (null1 == null2); + break; + } + } + else + { + switch(op) + { + case TOKidentity: + case TOKequal: + case TOKnotidentity: // 'cmp' gets inverted below + case TOKnotequal: + cmp = 0; + break; + default: + return -1; // memory blocks are different + } + } + if (op == TOKnotidentity || op == TOKnotequal) + cmp ^= 1; + return cmp; +} + +union UnionFloatInt +{ + float f; + d_int32 x; +}; + +union UnionDoubleLong +{ + double f; + d_int64 x; +}; + +// True if conversion from type 'from' to 'to' involves a reinterpret_cast +// floating point -> integer or integer -> floating point +bool isFloatIntPaint(Type *to, Type *from) +{ + return (from->size() == to->size()) && + ( (from->isintegral() && to->isfloating()) + || (from->isfloating() && to->isintegral()) ); +} + +// Reinterpret float/int value 'fromVal' as a float/integer of type 'to'. +Expression *paintFloatInt(Expression *fromVal, Type *to) +{ + if (exceptionOrCantInterpret(fromVal)) + return fromVal; + + if (to->size() == 4) + { + UnionFloatInt u; + if (to->isintegral()) + { + u.f = fromVal->toReal(); + return new IntegerExp(fromVal->loc, ldouble(u.x), to); + } + else + { + u.x = fromVal->toInteger(); + return new RealExp(fromVal->loc, ldouble(u.f), to); + } + } + else if (to->size() == 8) + { + UnionDoubleLong v; + if (to->isintegral()) + { + v.f = fromVal->toReal(); + return new IntegerExp(fromVal->loc, v.x, to); + } + else + { + v.x = fromVal->toInteger(); + return new RealExp(fromVal->loc, ldouble(v.f), to); + } + } + assert(0); + return NULL; // avoid warning +} + + +/*********************************************** + Primitive integer operations +***********************************************/ + +/** e = OP e +*/ +void intUnary(TOK op, IntegerExp *e) +{ + switch (op) + { + case TOKneg: + e->value = -e->value; + break; + case TOKtilde: + e->value = ~e->value; + break; + } +} + +/** dest = e1 OP e2; +*/ +void intBinary(TOK op, IntegerExp *dest, Type *type, IntegerExp *e1, IntegerExp *e2) +{ + dinteger_t result; + switch (op) + { + case TOKand: + result = e1->value & e2->value; + break; + case TOKor: + result = e1->value | e2->value; + break; + case TOKxor: + result = e1->value ^ e2->value; + break; + case TOKadd: + result = e1->value + e2->value; + break; + case TOKmin: + result = e1->value - e2->value; + break; + case TOKmul: + result = e1->value * e2->value; + break; + case TOKdiv: + { sinteger_t n1 = e1->value; + sinteger_t n2 = e2->value; + + if (n2 == 0) + { e2->error("divide by 0"); + result = 1; + } + else if (e1->type->isunsigned() || e2->type->isunsigned()) + result = ((d_uns64) n1) / ((d_uns64) n2); + else + result = n1 / n2; + } + break; + case TOKmod: + { sinteger_t n1 = e1->value; + sinteger_t n2 = e2->value; + + if (n2 == 0) + { e2->error("divide by 0"); + n2 = 1; + } + if (n2 == -1 && !type->isunsigned()) + { // Check for int.min % -1 + if (n1 == 0xFFFFFFFF80000000ULL && type->toBasetype()->ty != Tint64) + { + e2->error("integer overflow: int.min % -1"); + n2 = 1; + } + else if (n1 == 0x8000000000000000LL) // long.min % -1 + { + e2->error("integer overflow: long.min % -1"); + n2 = 1; + } + } + if (e1->type->isunsigned() || e2->type->isunsigned()) + result = ((d_uns64) n1) % ((d_uns64) n2); + else + result = n1 % n2; + } + break; + case TOKpow: + { dinteger_t n = e2->value; + if (!e2->type->isunsigned() && (sinteger_t)n < 0) + { + e2->error("integer ^^ -integer: total loss of precision"); + n = 1; + } + uinteger_t r = e1->value; + result = 1; + while (n != 0) + { + if (n & 1) + result = result * r; + n >>= 1; + r = r * r; + } + } + break; + case TOKshl: + result = e1->value << e2->value; + break; + case TOKshr: + { dinteger_t value = e1->value; + dinteger_t dcount = e2->value; + assert(dcount <= 0xFFFFFFFF); + unsigned count = (unsigned)dcount; + switch (e1->type->toBasetype()->ty) + { + case Tint8: + result = (d_int8)(value) >> count; + break; + + case Tuns8: + case Tchar: + result = (d_uns8)(value) >> count; + break; + + case Tint16: + result = (d_int16)(value) >> count; + break; + + case Tuns16: + case Twchar: + result = (d_uns16)(value) >> count; + break; + + case Tint32: + result = (d_int32)(value) >> count; + break; + + case Tuns32: + case Tdchar: + result = (d_uns32)(value) >> count; + break; + + case Tint64: + result = (d_int64)(value) >> count; + break; + + case Tuns64: + result = (d_uns64)(value) >> count; + break; + default: + assert(0); + } + } + break; + case TOKushr: + { dinteger_t value = e1->value; + dinteger_t dcount = e2->value; + assert(dcount <= 0xFFFFFFFF); + unsigned count = (unsigned)dcount; + switch (e1->type->toBasetype()->ty) + { + case Tint8: + case Tuns8: + case Tchar: + // Possible only with >>>=. >>> always gets promoted to int. + result = (value & 0xFF) >> count; + break; + + case Tint16: + case Tuns16: + case Twchar: + // Possible only with >>>=. >>> always gets promoted to int. + result = (value & 0xFFFF) >> count; + break; + + case Tint32: + case Tuns32: + case Tdchar: + result = (value & 0xFFFFFFFF) >> count; + break; + + case Tint64: + case Tuns64: + result = (d_uns64)(value) >> count; + break; + + default: + assert(0); + } + } + break; + case TOKequal: + case TOKidentity: + result = (e1->value == e2->value); + break; + case TOKnotequal: + case TOKnotidentity: + result = (e1->value != e2->value); + break; + default: + assert(0); + } + dest->value = result; + dest->type = type; +} + + +/******** Constant folding, with support for CTFE ***************************/ + +/// Return true if non-pointer expression e can be compared +/// with >,is, ==, etc, using ctfeCmp, ctfeEqual, ctfeIdentity +bool isCtfeComparable(Expression *e) +{ + Expression *x = e; + if (x->op == TOKslice) + x = ((SliceExp *)e)->e1; + + if (x->isConst() != 1 && + x->op != TOKnull && + x->op != TOKstring && + x->op != TOKarrayliteral && + x->op != TOKstructliteral && + x->op != TOKclassreference) + { + return false; + } + return true; +} + +/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 +int intUnsignedCmp(TOK op, d_uns64 n1, d_uns64 n2) +{ + int n; + switch (op) + { + case TOKlt: n = n1 < n2; break; + case TOKle: n = n1 <= n2; break; + case TOKgt: n = n1 > n2; break; + case TOKge: n = n1 >= n2; break; + + case TOKleg: n = 1; break; + case TOKlg: n = n1 != n2; break; + case TOKunord: n = 0; break; + case TOKue: n = n1 == n2; break; + case TOKug: n = n1 > n2; break; + case TOKuge: n = n1 >= n2; break; + case TOKul: n = n1 < n2; break; + case TOKule: n = n1 <= n2; break; + + default: + assert(0); + } + return n; +} + +/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 +int intSignedCmp(TOK op, sinteger_t n1, sinteger_t n2) +{ + int n; + switch (op) + { + case TOKlt: n = n1 < n2; break; + case TOKle: n = n1 <= n2; break; + case TOKgt: n = n1 > n2; break; + case TOKge: n = n1 >= n2; break; + + case TOKleg: n = 1; break; + case TOKlg: n = n1 != n2; break; + case TOKunord: n = 0; break; + case TOKue: n = n1 == n2; break; + case TOKug: n = n1 > n2; break; + case TOKuge: n = n1 >= n2; break; + case TOKul: n = n1 < n2; break; + case TOKule: n = n1 <= n2; break; + + default: + assert(0); + } + return n; +} + +/// Returns e1 OP e2; where OP is ==, !=, <, >=, etc. Result is 0 or 1 +int realCmp(TOK op, real_t r1, real_t r2) +{ + int n; +#if __DMC__ + // DMC is the only compiler I know of that handles NAN arguments + // correctly in comparisons. + switch (op) + { + case TOKlt: n = r1 < r2; break; + case TOKle: n = r1 <= r2; break; + case TOKgt: n = r1 > r2; break; + case TOKge: n = r1 >= r2; break; + + case TOKleg: n = r1 <>= r2; break; + case TOKlg: n = r1 <> r2; break; + case TOKunord: n = r1 !<>= r2; break; + case TOKue: n = r1 !<> r2; break; + case TOKug: n = r1 !<= r2; break; + case TOKuge: n = r1 !< r2; break; + case TOKul: n = r1 !>= r2; break; + case TOKule: n = r1 !> r2; break; + + default: + assert(0); + } +#else + // Don't rely on compiler, handle NAN arguments separately + if (Port::isNan(r1) || Port::isNan(r2)) // if unordered + { + switch (op) + { + case TOKlt: n = 0; break; + case TOKle: n = 0; break; + case TOKgt: n = 0; break; + case TOKge: n = 0; break; + + case TOKleg: n = 0; break; + case TOKlg: n = 0; break; + case TOKunord: n = 1; break; + case TOKue: n = 1; break; + case TOKug: n = 1; break; + case TOKuge: n = 1; break; + case TOKul: n = 1; break; + case TOKule: n = 1; break; + + default: + assert(0); + } + } + else + { + switch (op) + { + case TOKlt: n = r1 < r2; break; + case TOKle: n = r1 <= r2; break; + case TOKgt: n = r1 > r2; break; + case TOKge: n = r1 >= r2; break; + + case TOKleg: n = 1; break; + case TOKlg: n = r1 != r2; break; + case TOKunord: n = 0; break; + case TOKue: n = r1 == r2; break; + case TOKug: n = r1 > r2; break; + case TOKuge: n = r1 >= r2; break; + case TOKul: n = r1 < r2; break; + case TOKule: n = r1 <= r2; break; + + default: + assert(0); + } + } +#endif + return n; +} + +int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2); + +/* Conceptually the same as memcmp(e1, e2). + * e1 and e2 may be strings, arrayliterals, or slices. + * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. + * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. + */ +int ctfeCmpArrays(Loc loc, Expression *e1, Expression *e2, uinteger_t len) +{ + // Resolve slices, if necessary + uinteger_t lo1 = 0; + uinteger_t lo2 = 0; + + Expression *x = e1; + if (x->op == TOKslice) + { lo1 = ((SliceExp *)x)->lwr->toInteger(); + x = ((SliceExp*)x)->e1; + } + StringExp *se1 = (x->op == TOKstring) ? (StringExp *)x : 0; + ArrayLiteralExp *ae1 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0; + + x = e2; + if (x->op == TOKslice) + { lo2 = ((SliceExp *)x)->lwr->toInteger(); + x = ((SliceExp*)x)->e1; + } + StringExp *se2 = (x->op == TOKstring) ? (StringExp *)x : 0; + ArrayLiteralExp *ae2 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0; + + // Now both must be either TOKarrayliteral or TOKstring + if (se1 && se2) + return sliceCmpStringWithString(se1, se2, lo1, lo2, len); + if (se1 && ae2) + return sliceCmpStringWithArray(se1, ae2, lo1, lo2, len); + if (se2 && ae1) + return -sliceCmpStringWithArray(se2, ae1, lo2, lo1, len); + + assert (ae1 && ae2); + // Comparing two array literals. This case is potentially recursive. + // If they aren't strings, we just need an equality check rather than + // a full cmp. + bool needCmp = ae1->type->nextOf()->isintegral(); + for (size_t i = 0; i < len; i++) + { Expression *ee1 = (*ae1->elements)[lo1 + i]; + Expression *ee2 = (*ae2->elements)[lo2 + i]; + if (needCmp) + { sinteger_t c = ee1->toInteger() - ee2->toInteger(); + if (c > 0) + return 1; + if (c < 0) + return -1; + } + else + { if (ctfeRawCmp(loc, ee1, ee2)) + return 1; + } + } + return 0; +} + +bool isArray(Expression *e) +{ + return e->op == TOKarrayliteral || e->op == TOKstring || + e->op == TOKslice || e->op == TOKnull; +} + +/* For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. + * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. + */ +int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2) +{ + if (e1->op == TOKclassreference || e2->op == TOKclassreference) + { if (e1->op == TOKclassreference && e2->op == TOKclassreference && + ((ClassReferenceExp *)e1)->value == ((ClassReferenceExp *)e2)->value) + return 0; + return 1; + } + if (e1->op == TOKnull && e2->op == TOKnull) + return 0; + + if (e1->type->ty == Tpointer && e2->type->ty == Tpointer) + { // Can only be an equality test. + if (e1->op == TOKnull && e2->op == TOKnull) + return 0; + dinteger_t ofs1, ofs2; + Expression *agg1 = getAggregateFromPointer(e1, &ofs1); + Expression *agg2 = getAggregateFromPointer(e2, &ofs2); + if ((agg1 == agg2) || (agg1->op == TOKvar && agg2->op == TOKvar && + ((VarExp *)agg1)->var == ((VarExp *)agg2)->var)) + { if (ofs1 == ofs2) + return 0; + } + return 1; + } + if (isArray(e1) && isArray(e2)) + { + uinteger_t len1 = resolveArrayLength(e1); + uinteger_t len2 = resolveArrayLength(e2); + // workaround for dmc optimizer bug calculating wrong len for + // uinteger_t len = (len1 < len2 ? len1 : len2); + // if(len == 0) ... + if(len1 > 0 && len2 > 0) + { + uinteger_t len = (len1 < len2 ? len1 : len2); + int res = ctfeCmpArrays(loc, e1, e2, len); + if (res != 0) + return res; + } + return len1 - len2; + } + if (e1->type->isintegral()) + { + return e1->toInteger() != e2->toInteger(); + } + real_t r1; + real_t r2; + if (e1->type->isreal()) + { + r1 = e1->toReal(); + r2 = e2->toReal(); + goto L1; + } + else if (e1->type->isimaginary()) + { + r1 = e1->toImaginary(); + r2 = e2->toImaginary(); + L1: +#if __DMC__ + return (r1 != r2); +#else + if (Port::isNan(r1) || Port::isNan(r2)) // if unordered + { + return 1; + } + else + { + return (r1 != r2); + } +#endif + } + else if (e1->type->iscomplex()) + { + return e1->toComplex() != e2->toComplex(); + } + + if (e1->op == TOKstructliteral && e2->op == TOKstructliteral) + { StructLiteralExp *es1 = (StructLiteralExp *)e1; + StructLiteralExp *es2 = (StructLiteralExp *)e2; + // For structs, we only need to return 0 or 1 (< and > aren't legal). + + if (es1->sd != es2->sd) + return 1; + else if ((!es1->elements || !es1->elements->dim) && + (!es2->elements || !es2->elements->dim)) + return 0; // both arrays are empty + else if (!es1->elements || !es2->elements) + return 1; + else if (es1->elements->dim != es2->elements->dim) + return 1; + else + { + for (size_t i = 0; i < es1->elements->dim; i++) + { Expression *ee1 = (*es1->elements)[i]; + Expression *ee2 = (*es2->elements)[i]; + + if (ee1 == ee2) + continue; + if (!ee1 || !ee2) + return 1; + int cmp = ctfeRawCmp(loc, ee1, ee2); + if (cmp) + return 1; + } + return 0; // All elements are equal + } + } + error(loc, "CTFE internal error: bad compare"); + assert(0); + return 0; +} + + +/// Evaluate ==, !=. Resolves slices before comparing. Returns 0 or 1 +int ctfeEqual(Loc loc, enum TOK op, Expression *e1, Expression *e2) +{ + int cmp = !ctfeRawCmp(loc, e1, e2); + if (op == TOKnotequal) + cmp ^= 1; + return cmp; +} + + +/// Evaluate is, !is. Resolves slices before comparing. Returns 0 or 1 +int ctfeIdentity(Loc loc, enum TOK op, Expression *e1, Expression *e2) +{ + int cmp; + if (e1->op == TOKnull) + { + cmp = (e2->op == TOKnull); + } + else if (e2->op == TOKnull) + { + cmp = 0; + } + else if (e1->op == TOKsymoff && e2->op == TOKsymoff) + { + SymOffExp *es1 = (SymOffExp *)e1; + SymOffExp *es2 = (SymOffExp *)e2; + cmp = (es1->var == es2->var && es1->offset == es2->offset); + } + else if (e1->type->isreal()) + cmp = RealEquals(e1->toReal(), e2->toReal()); + else if (e1->type->isimaginary()) + cmp = RealEquals(e1->toImaginary(), e2->toImaginary()); + else if (e1->type->iscomplex()) + { complex_t v1 = e1->toComplex(); + complex_t v2 = e2->toComplex(); + cmp = RealEquals(creall(v1), creall(v2)) && + RealEquals(cimagl(v1), cimagl(v1)); + } + else + cmp = !ctfeRawCmp(loc, e1, e2); + + if (op == TOKnotidentity || op == TOKnotequal) + cmp ^= 1; + return cmp; +} + + +/// Evaluate >,<=, etc. Resolves slices before comparing. Returns 0 or 1 +int ctfeCmp(Loc loc, enum TOK op, Expression *e1, Expression *e2) +{ + int n; + if (e1->type->isString() && e2->type->isString()) + { + int cmp = ctfeRawCmp(loc, e1, e2); + switch (op) + { + case TOKlt: n = cmp < 0; break; + case TOKle: n = cmp <= 0; break; + case TOKgt: n = cmp > 0; break; + case TOKge: n = cmp >= 0; break; + + case TOKleg: n = 1; break; + case TOKlg: n = cmp != 0; break; + case TOKunord: n = 0; break; + case TOKue: n = cmp == 0; break; + case TOKug: n = cmp > 0; break; + case TOKuge: n = cmp >= 0; break; + case TOKul: n = cmp < 0; break; + case TOKule: n = cmp <= 0; break; + + default: + assert(0); + } + } + else if (e1->type->isreal()) + { + n = realCmp(op, e1->toReal(), e2->toReal()); + } + else if (e1->type->isimaginary()) + { + n = realCmp(op, e1->toImaginary(), e2->toImaginary()); + } + else if (e1->type->isunsigned() || e2->type->isunsigned()) + { + n = intUnsignedCmp(op, e1->toInteger(), e2->toInteger()); + } + else + { + n = intSignedCmp(op, e1->toInteger(), e2->toInteger()); + } + return n; +} + + +Expression *ctfeCat(Type *type, Expression *e1, Expression *e2) +{ + Loc loc = e1->loc; + Type *t1 = e1->type->toBasetype(); + Type *t2 = e2->type->toBasetype(); + Expression *e; + if (e2->op == TOKstring && e1->op == TOKarrayliteral && + t1->nextOf()->isintegral()) + { + // [chars] ~ string => string (only valid for CTFE) + StringExp *es1 = (StringExp *)e2; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e1; + size_t len = es1->len + es2->elements->dim; + int sz = es1->sz; + + void *s = mem.malloc((len + 1) * sz); + memcpy((char *)s + sz * es2->elements->dim, es1->string, es1->len * sz); + for (size_t i = 0; i < es2->elements->dim; i++) + { Expression *es2e = es2->elements->tdata()[i]; + if (es2e->op != TOKint64) + return EXP_CANT_INTERPRET; + dinteger_t v = es2e->toInteger(); + memcpy((unsigned char *)s + i * sz, &v, sz); + } + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + StringExp *es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = 0; + es->type = type; + e = es; + return e; + } + else if (e1->op == TOKstring && e2->op == TOKarrayliteral && + t2->nextOf()->isintegral()) + { + // string ~ [chars] => string (only valid for CTFE) + // Concatenate the strings + StringExp *es1 = (StringExp *)e1; + ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; + size_t len = es1->len + es2->elements->dim; + int sz = es1->sz; + + void *s = mem.malloc((len + 1) * sz); + memcpy(s, es1->string, es1->len * sz); + for (size_t i = 0; i < es2->elements->dim; i++) + { Expression *es2e = es2->elements->tdata()[i]; + if (es2e->op != TOKint64) + return EXP_CANT_INTERPRET; + dinteger_t v = es2e->toInteger(); + memcpy((unsigned char *)s + (es1->len + i) * sz, &v, sz); + } + + // Add terminating 0 + memset((unsigned char *)s + len * sz, 0, sz); + + StringExp *es = new StringExp(loc, s, len); + es->sz = sz; + es->committed = 0; //es1->committed; + es->type = type; + e = es; + return e; + } + return Cat(type, e1, e2); +} + +/* Given an AA literal 'ae', and a key 'e2': + * Return ae[e2] if present, or NULL if not found. + */ +Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2) +{ + /* Search the keys backwards, in case there are duplicate keys + */ + for (size_t i = ae->keys->dim; i;) + { + i--; + Expression *ekey = ae->keys->tdata()[i]; + int eq = ctfeEqual(loc, TOKequal, ekey, e2); + if (eq) + { + return ae->values->tdata()[i]; + } + } + return NULL; +} + +/* Same as for constfold.Index, except that it only works for static arrays, + * dynamic arrays, and strings. We know that e1 is an + * interpreted CTFE expression, so it cannot have side-effects. + */ +Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx) +{ //printf("ctfeIndex(e1 = %s)\n", e1->toChars()); + assert(e1->type); + if (e1->op == TOKstring) + { StringExp *es1 = (StringExp *)e1; + if (indx >= es1->len) + { + error(loc, "string index %ju is out of bounds [0 .. %zu]", indx, es1->len); + return EXP_CANT_INTERPRET; + } + else + return new IntegerExp(loc, es1->charAt(indx), type); + } + assert(e1->op == TOKarrayliteral); + ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; + if (indx >= ale->elements->dim) + { + error(loc, "array index %ju is out of bounds %s[0 .. %u]", indx, e1->toChars(), ale->elements->dim); + return EXP_CANT_INTERPRET; + } + Expression *e = ale->elements->tdata()[indx]; + return paintTypeOntoLiteral(type, 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 + return new NullExp(loc, to); + } + Expression *r = Cast(type, to, e); + if (r == EXP_CANT_INTERPRET) + error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars()); + if (e->op == TOKarrayliteral) + ((ArrayLiteralExp *)e)->ownedByCtfe = true; + if (e->op == TOKstring) + ((StringExp *)e)->ownedByCtfe = true; + return r; +} + +/******** Assignment helper functions ***************************/ + +/* Set dest = src, where both dest and src are container value literals + * (ie, struct literals, or static arrays (can be an array literal or a string) + * Assignment is recursively in-place. + * Purpose: any reference to a member of 'dest' will remain valid after the + * assignment. + */ +void assignInPlace(Expression *dest, Expression *src) +{ + assert(dest->op == TOKstructliteral || dest->op == TOKarrayliteral || + dest->op == TOKstring); + Expressions *oldelems; + Expressions *newelems; + if (dest->op == TOKstructliteral) + { + assert(dest->op == src->op); + oldelems = ((StructLiteralExp *)dest)->elements; + newelems = ((StructLiteralExp *)src)->elements; + } + else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral) + { + oldelems = ((ArrayLiteralExp *)dest)->elements; + newelems = ((ArrayLiteralExp *)src)->elements; + } + else if (dest->op == TOKstring && src->op == TOKstring) + { + sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0); + return; + } + else if (dest->op == TOKarrayliteral && src->op == TOKstring) + { + sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0); + return; + } + else if (src->op == TOKarrayliteral && dest->op == TOKstring) + { + sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0); + return; + } + else assert(0); + + assert(oldelems->dim == newelems->dim); + + for (size_t i= 0; i < oldelems->dim; ++i) + { + Expression *e = newelems->tdata()[i]; + Expression *o = oldelems->tdata()[i]; + if (e->op == TOKstructliteral) + { + assert(o->op == e->op); + assignInPlace(o, e); + } + else if (e->type->ty == Tsarray && o->type->ty == Tsarray && e->op != TOKvoid) + { + assignInPlace(o, e); + } + else + { + oldelems->tdata()[i] = newelems->tdata()[i]; + } + } +} + +void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef) +{ + assert( ae->type->ty == Tsarray || ae->type->ty == Tarray); +#if DMDV2 + Type *desttype = ((TypeArray *)ae->type)->next->castMod(0); + bool directblk = (val->type->toBasetype()->castMod(0)) == desttype; +#else + Type *desttype = ((TypeArray *)ae->type)->next; + bool directblk = (val->type->toBasetype()) == desttype; +#endif + + bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral + || val->op == TOKstring); + + for (size_t k = 0; k < ae->elements->dim; k++) + { + if (!directblk && ae->elements->tdata()[k]->op == TOKarrayliteral) + { + recursiveBlockAssign((ArrayLiteralExp *)ae->elements->tdata()[k], val, wantRef); + } + else + { + if (wantRef || cow) + ae->elements->tdata()[k] = val; + else + assignInPlace(ae->elements->tdata()[k], val); + } + } +} + +// Duplicate the elements array, then set field 'indexToChange' = newelem. +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++) + { + if (j == indexToChange) + (*expsx)[j] = newelem; + else + (*expsx)[j] = oldelems->tdata()[j]; + } + return expsx; +} + +// 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) +{ + int fieldi = se->getFieldIndex(newval->type, offset); + if (fieldi == -1) + return EXP_CANT_INTERPRET; + /* Create new struct literal reflecting updated fieldi + */ + Expressions *expsx = changeOneElement(se->elements, fieldi, newval); + StructLiteralExp * ee = new StructLiteralExp(se->loc, se->sd, expsx); + ee->type = se->type; + ee->ownedByCtfe = 1; + return ee; +} + +// Given an AA literal aae, set arr[index] = newval and return the new array. +Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae, + Expression *index, Expression *newval) +{ + /* Create new associative array literal reflecting updated key/value + */ + Expressions *keysx = aae->keys; + Expressions *valuesx = aae->values; + int updated = 0; + for (size_t j = valuesx->dim; j; ) + { j--; + Expression *ekey = aae->keys->tdata()[j]; + int eq = ctfeEqual(loc, TOKequal, ekey, index); + if (eq) + { valuesx->tdata()[j] = newval; + updated = 1; + } + } + if (!updated) + { // Append index/newval to keysx[]/valuesx[] + valuesx->push(newval); + keysx->push(index); + } + return newval; +} + +/// Given array literal oldval of type ArrayLiteralExp or StringExp, of length +/// oldlen, change its length to newlen. If the newlen is longer than oldlen, +/// all new elements will be set to the default initializer for the element type. +Expression *changeArrayLiteralLength(Loc loc, TypeArray *arrayType, + Expression *oldval, size_t oldlen, size_t newlen) +{ + Type *elemType = elemType = arrayType->next; + assert(elemType); + Expression *defaultElem = elemType->defaultInitLiteral(loc); + Expressions *elements = new Expressions(); + elements->setDim(newlen); + + // Resolve slices + size_t indxlo = 0; + if (oldval->op == TOKslice) + { indxlo = ((SliceExp *)oldval)->lwr->toInteger(); + oldval = ((SliceExp *)oldval)->e1; + } + size_t copylen = oldlen < newlen ? oldlen : newlen; + if (oldval->op == TOKstring) + { + StringExp *oldse = (StringExp *)oldval; + unsigned char *s = (unsigned char *)mem.calloc(newlen + 1, oldse->sz); + memcpy(s, oldse->string, copylen * oldse->sz); + unsigned defaultValue = (unsigned)(defaultElem->toInteger()); + for (size_t elemi = copylen; elemi < newlen; ++elemi) + { + switch (oldse->sz) + { + case 1: s[indxlo + elemi] = defaultValue; break; + case 2: ((unsigned short *)s)[indxlo + elemi] = defaultValue; break; + case 4: ((unsigned *)s)[indxlo + elemi] = defaultValue; break; + default: assert(0); + } + } + StringExp *se = new StringExp(loc, s, newlen); + se->type = arrayType; + se->sz = oldse->sz; + se->committed = oldse->committed; + se->ownedByCtfe = true; + return se; + } + else + { + if (oldlen !=0) + assert(oldval->op == TOKarrayliteral); + ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval; + for (size_t i = 0; i < copylen; i++) + (*elements)[i] = (*ae->elements)[indxlo + i]; + if (elemType->ty == Tstruct || elemType->ty == Tsarray) + { /* If it is an aggregate literal representing a value type, + * we need to create a unique copy for each element + */ + for (size_t i = copylen; i < newlen; i++) + (*elements)[i] = copyLiteral(defaultElem); + } + else + { + for (size_t i = copylen; i < newlen; i++) + (*elements)[i] = defaultElem; + } + ArrayLiteralExp *aae = new ArrayLiteralExp(loc, elements); + aae->type = arrayType; + aae->ownedByCtfe = true; + return aae; + } +} + + +/*************************** CTFE Sanity Checks ***************************/ + + +bool isCtfeValueValid(Expression *newval) +{ + if ( +#if DMDV2 + newval->type->ty == Tnull || +#endif + isPointer(newval->type) ) + { + if (newval->op == TOKaddress || newval->op == TOKnull || + newval->op == TOKstring) + return true; + if (newval->op == TOKindex) + { + Expression *g = ((IndexExp *)newval)->e1; + if (g->op == TOKarrayliteral || g->op == TOKstring || + g->op == TOKassocarrayliteral) + return true; + } + if (newval->op == TOKvar) + return true; + if (newval->type->nextOf()->ty == Tarray && newval->op == TOKslice) + return true; + if (newval->op == TOKint64) + return true; // Result of a cast, but cannot be dereferenced + // else it must be a reference + } + if (newval->op == TOKclassreference || (newval->op == TOKnull && newval->type->ty == Tclass)) + return true; + if (newval->op == TOKvar) + { + VarExp *ve = (VarExp *)newval; + VarDeclaration *vv = ve->var->isVarDeclaration(); + // Must not be a reference to a reference + if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar)) + return true; + } + if (newval->op == TOKdotvar) + { + if (((DotVarExp *)newval)->e1->op == TOKstructliteral) + { + assert(((StructLiteralExp *)((DotVarExp *)newval)->e1)->ownedByCtfe); + return true; + } + } + if (newval->op == TOKindex) + { + IndexExp *ie = (IndexExp *)newval; + if (ie->e2->op == TOKint64) + { + if (ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) + return true; + } + if (ie->e1->op == TOKassocarrayliteral) + return true; + // BUG: Happens ONLY in ref foreach. Should tighten this. + if (ie->e2->op == TOKvar) + return true; + } + if (newval->op == TOKfunction) return true; // function/delegate literal + if (newval->op == TOKdelegate) return true; + if (newval->op == TOKsymoff) // function pointer + { + if (((SymOffExp *)newval)->var->isFuncDeclaration()) + return true; + } +#if IN_LLVM + if (newval->op == TOKaddress) { // function pointer + AddrExp *ae = (AddrExp *)newval; + if (ae->e1->op == TOKvar) { + if (((VarExp *)ae->e1)->var->isFuncDeclaration()) + return true; + } + } +#endif + + if (newval->op == TOKint64 || newval->op == TOKfloat64 || + newval->op == TOKchar || newval->op == TOKcomplex80) + return true; + + // References + + if (newval->op == TOKstructliteral) + assert(((StructLiteralExp *)newval)->ownedByCtfe); + if (newval->op == TOKarrayliteral) + assert(((ArrayLiteralExp *)newval)->ownedByCtfe); + if (newval->op == TOKassocarrayliteral) + assert(((AssocArrayLiteralExp *)newval)->ownedByCtfe); + + if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || + (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || + (newval->op == TOKnull)) + { return true; + } + // 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) + if (newval->op == TOKdotvar || newval->op == TOKindex) + return true; // actually must be null + + if (newval->op == TOKslice) + { + SliceExp *se = (SliceExp *)newval; + assert(se->lwr && se->lwr != EXP_CANT_INTERPRET && se->lwr->op == TOKint64); + assert(se->upr && se->upr != EXP_CANT_INTERPRET && se->upr->op == TOKint64); + assert(se->e1->op == TOKarrayliteral || se->e1->op == TOKstring); + return true; + } + if (newval->op == TOKvoid) + { + return true; + } + newval->error("CTFE internal error: illegal value %s", newval->toChars()); + return false; +} + +// Used for debugging only +void showCtfeExpr(Expression *e, int level) +{ + for (int i = level; i>0; --i) printf(" "); + Expressions *elements = NULL; + // We need the struct definition to detect block assignment + StructDeclaration *sd = NULL; + 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); + } + else if (e->op == TOKarrayliteral) + { + elements = ((ArrayLiteralExp *)e)->elements; + printf("ARRAY LITERAL type=%s %p:\n", e->type->toChars(), + e); + } + else if (e->op == TOKassocarrayliteral) + { + printf("AA LITERAL type=%s %p:\n", e->type->toChars(), + e); + } + else if (e->op == TOKstring) + { + printf("STRING %s %p\n", e->toChars(), + ((StringExp *)e)->string); + } + else if (e->op == TOKslice) + { + printf("SLICE %p: %s\n", e, e->toChars()); + showCtfeExpr(((SliceExp *)e)->e1, level + 1); + } + else if (e->op == TOKvar) + { + printf("VAR %p %s\n", e, e->toChars()); + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + if (v && v->getValue()) + showCtfeExpr(v->getValue(), level + 1); + } + else if (isPointer(e->type)) + { + // This is potentially recursive. We mustn't try to print the thing we're pointing to. + if (e->op == TOKindex) + printf("POINTER %p into %p [%s]\n", e, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2->toChars()); + else if (e->op == TOKdotvar) + printf("POINTER %p to %p .%s\n", e, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var->toChars()); + else + printf("POINTER %p: %s\n", e, e->toChars()); + } + else + printf("VALUE %p: %s\n", e, e->toChars()); + + if (elements) + { + size_t fieldsSoFar = 0; + for (size_t i = 0; i < elements->dim; i++) + { Expression *z = NULL; + Dsymbol *s = NULL; + if (i > 15) { + int nelements = elements->dim; + printf("...(total %d elements)\n", nelements); + return; + } + if (sd) + { s = sd->fields[i]; + z = (*elements)[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[i - fieldsSoFar]; + assert((elements->dim + i) >= (fieldsSoFar + cd->fields.dim)); + size_t indx = (elements->dim - fieldsSoFar)- cd->fields.dim + i; + assert(indx < elements->dim); + z = (*elements)[indx]; + } + if (!z) { + for (int j = level; j>0; --j) printf(" "); + printf(" void\n"); + continue; + } + + if (s) + { + VarDeclaration *v = s->isVarDeclaration(); + assert(v); + // If it is a void assignment, use the default initializer + if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray) + { + for (int j = level; --j;) printf(" "); + printf(" field: block initalized static array\n"); + continue; + } + } + showCtfeExpr(z, level + 1); + } + } +} + +/*************************** Void initialization ***************************/ + +Expression *Type::voidInitLiteral(VarDeclaration *var) +{ + return new VoidInitExp(var, this); +} + +Expression *TypeSArray::voidInitLiteral(VarDeclaration *var) +{ + Expression *elem = next->voidInitLiteral(var); + + // For aggregate value types (structs, static arrays) we must + // create an a separate copy for each element. + bool mustCopy = (elem->op == TOKarrayliteral || elem->op == TOKstructliteral); + + Expressions *elements = new Expressions(); + size_t d = dim->toInteger(); + elements->setDim(d); + for (size_t i = 0; i < d; i++) + { if (mustCopy && i > 0) + elem = copyLiteral(elem); + (*elements)[i] = elem; + } + ArrayLiteralExp *ae = new ArrayLiteralExp(var->loc, elements); + ae->type = this; + ae->ownedByCtfe = true; + return ae; +} + +Expression *TypeStruct::voidInitLiteral(VarDeclaration *var) +{ + Expressions *exps = new Expressions(); + exps->setDim(sym->fields.dim); + for (size_t i = 0; i < sym->fields.dim; i++) + { + (*exps)[i] = sym->fields[i]->type->voidInitLiteral(var); + } + StructLiteralExp *se = new StructLiteralExp(var->loc, sym, exps); + se->type = this; + se->ownedByCtfe = true; + return se; +} diff --git a/dmd2/declaration.c b/dmd2/declaration.c index d82248c6..ac7f65fa 100644 --- a/dmd2/declaration.c +++ b/dmd2/declaration.c @@ -24,6 +24,51 @@ #include "statement.h" #include "hdrgen.h" +AggregateDeclaration *isAggregate(Type *t); // from opover.c + +void checkFrameAccess(Loc loc, Scope *sc, AggregateDeclaration *ad) +{ + if (!ad->isnested) + return; + + Dsymbol *s = sc->func; + if (s) + { + Dsymbol *sparent = ad->toParent2(); + //printf("ad = %p %s [%s], parent:%p\n", ad, ad->toChars(), ad->loc.toChars(), ad->parent); + //printf("sparent = %p %s [%s], parent: %s\n", sparent, sparent->toChars(), sparent->loc.toChars(), sparent->parent->toChars()); + + while (s) + { + if (s == sparent) // hit! + { + // Is it better moving this check to AggregateDeclaration:semantic? + for (size_t i = 0; i < ad->fields.dim; i++) + { VarDeclaration *vd = ad->fields[i]->isVarDeclaration(); + if (vd) + if (AggregateDeclaration *ad2 = isAggregate(vd->type)) + if (ad2->isStructDeclaration()) + checkFrameAccess(loc, sc, ad2); + } + return; + } + + if (FuncDeclaration *fd = s->isFuncDeclaration()) + { + if (!fd->isThis() && !fd->isNested()) + break; + } + if (AggregateDeclaration *ad2 = s->isAggregateDeclaration()) + { + if (ad2->storage_class & STCstatic) + break; + } + s = s->toParent2(); + } + } + error(loc, "cannot access frame pointer of %s", ad->toPrettyChars()); +} + /********************************* Declaration ****************************/ Declaration::Declaration(Identifier *id) @@ -85,40 +130,26 @@ enum PROT Declaration::prot() #if DMDV2 -void Declaration::checkModify(Loc loc, Scope *sc, Type *t) +int Declaration::checkModify(Loc loc, Scope *sc, Type *t) { - if (sc->incontract && isParameter()) + if ((sc->flags & SCOPEcontract) && isParameter()) error(loc, "cannot modify parameter '%s' in contract", toChars()); - if (sc->incontract && isResult()) + if ((sc->flags & SCOPEcontract) && isResult()) error(loc, "cannot modify result '%s' in contract", toChars()); if (isCtorinit() && !t->isMutable() || (storage_class & STCnodefaultctor)) { // It's only modifiable if inside the right constructor - modifyFieldVar(loc, sc, isVarDeclaration(), NULL); + return modifyFieldVar(loc, sc, isVarDeclaration(), NULL); } else { VarDeclaration *v = isVarDeclaration(); - if (v && v->canassign == 0) - { - const char *p = NULL; - if (isConst()) - p = "const"; - else if (isImmutable()) - p = "immutable"; - else if (isWild()) - p = "inout"; - else if (storage_class & STCmanifest) - p = "enum"; - else if (!t->isAssignable()) - p = "struct with immutable members"; - if (p) - { error(loc, "cannot modify %s", p); - } - } + if (v && v->canassign) + return TRUE; } + return FALSE; } #endif @@ -304,6 +335,7 @@ Dsymbol *TypedefDeclaration::syntaxCopy(Dsymbol *s) void TypedefDeclaration::semantic(Scope *sc) { //printf("TypedefDeclaration::semantic(%s) sem = %d\n", toChars(), sem); + userAttributes = sc->userAttributes; if (sem == SemanticStart) { sem = SemanticIn; parent = sc->parent; @@ -332,6 +364,7 @@ void TypedefDeclaration::semantic(Scope *sc) return; } storage_class |= sc->stc & STCdeprecated; + userAttributes = sc->userAttributes; } else if (sem == SemanticIn) { @@ -430,6 +463,7 @@ Dsymbol *AliasDeclaration::syntaxCopy(Dsymbol *s) sa = new AliasDeclaration(loc, ident, type->syntaxCopy()); else sa = new AliasDeclaration(loc, ident, aliassym->syntaxCopy(NULL)); + sa->storage_class = storage_class; // Syntax copy for header file if (!htype) // Don't overwrite original @@ -470,6 +504,7 @@ void AliasDeclaration::semantic(Scope *sc) storage_class |= sc->stc & STCdeprecated; protection = sc->protection; + userAttributes = sc->userAttributes; // Given: // alias foo.bar.abc def; @@ -534,7 +569,7 @@ void AliasDeclaration::semantic(Scope *sc) //printf("\talias resolved to type %s\n", type->toChars()); } if (overnext) - ScopeDsymbol::multiplyDefined(0, this, overnext); + ScopeDsymbol::multiplyDefined(0, overnext, this); this->inSemantic = 0; if (global.gag && errors != global.errors) @@ -563,7 +598,7 @@ void AliasDeclaration::semantic(Scope *sc) fa->importprot = importprot; #endif if (!fa->overloadInsert(overnext)) - ScopeDsymbol::multiplyDefined(0, f, overnext); + ScopeDsymbol::multiplyDefined(0, overnext, f); overnext = NULL; s = fa; s->parent = sc->parent; @@ -581,7 +616,7 @@ void AliasDeclaration::semantic(Scope *sc) } } if (overnext) - ScopeDsymbol::multiplyDefined(0, this, overnext); + ScopeDsymbol::multiplyDefined(0, overnext, this); if (s == this) { assert(global.errors); @@ -731,7 +766,7 @@ VarDeclaration::VarDeclaration(Loc loc, Type *type, Identifier *id, Initializer aliassym = NULL; onstack = 0; canassign = 0; - ctfeAdrOnStack = (size_t)(-1); + ctfeAdrOnStack = -1; #if DMDV2 rundtor = NULL; edtor = NULL; @@ -813,6 +848,8 @@ void VarDeclaration::semantic(Scope *sc) if (storage_class & STCextern && init) error("extern symbols cannot have initializers"); + userAttributes = sc->userAttributes; + AggregateDeclaration *ad = isThis(); if (ad) storage_class |= ad->storage_class & STC_TYPECTOR; @@ -858,11 +895,11 @@ void VarDeclaration::semantic(Scope *sc) * declarations. */ storage_class &= ~STCauto; - originalType = type; + originalType = type->syntaxCopy(); } else { if (!originalType) - originalType = type; + originalType = type->syntaxCopy(); type = type->semantic(loc, sc); } //printf(" semantic type = %s\n", type ? type->toChars() : "null"); @@ -1262,7 +1299,27 @@ Lnomatch: { // Provide a default initializer //printf("Providing default initializer for '%s'\n", toChars()); - if (type->ty == Tstruct && + if (type->needsNested()) + { + Type *tv = type; + while (tv->toBasetype()->ty == Tsarray) + tv = tv->toBasetype()->nextOf(); + assert(tv->toBasetype()->ty == Tstruct); + + /* Nested struct requires valid enclosing frame pointer. + * In StructLiteralExp::toElem(), it's calculated. + */ + + checkFrameAccess(loc, sc, ((TypeStruct *)tv->toBasetype())->sym); + + Expression *e = tv->defaultInitLiteral(loc); + Expression *e1 = new VarExp(loc, this); + e = new ConstructExp(loc, e1, e); + e = e->semantic(sc); + init = new ExpInitializer(loc, e); + goto Ldtor; + } + else if (type->ty == Tstruct && ((TypeStruct *)type)->sym->zeroInit == 1) { /* If a struct is all zeros, as a special case * set it's initializer to the integer 0. @@ -1278,19 +1335,6 @@ Lnomatch: init = new ExpInitializer(loc, e); goto Ldtor; } - else if (type->ty == Tstruct && - (((TypeStruct *)type)->sym->isnested)) - { - /* Nested struct requires valid enclosing frame pointer. - * In StructLiteralExp::toElem(), it's calculated. - */ - Expression *e = type->defaultInitLiteral(loc); - Expression *e1 = new VarExp(loc, this); - e = new ConstructExp(loc, e1, e); - e = e->semantic(sc); - init = new ExpInitializer(loc, e); - goto Ldtor; - } else if (type->ty == Ttypedef) { TypeTypedef *td = (TypeTypedef *)type; if (td->sym->init) @@ -1501,7 +1545,7 @@ Lnomatch: ei->exp = new CommaExp(loc, e, ei->exp); } else - /* Look for opCall + /* Look for static opCall * See bugzilla 2702 for more discussion */ // Don't cast away invariant or mutability in initializer @@ -1510,8 +1554,8 @@ Lnomatch: */ !(ti->ty == Tstruct && t->toDsymbol(sc) == ti->toDsymbol(sc))) { // Rewrite as e1.call(arguments) - Expression * eCall = new DotIdExp(loc, e1, Id::call); - ei->exp = new CallExp(loc, eCall, ei->exp); + Expression *e = typeDotIdExp(ei->exp->loc, t, Id::call); + ei->exp = new CallExp(loc, e, ei->exp); } } } @@ -1528,8 +1572,7 @@ Lnomatch: } } else if (storage_class & (STCconst | STCimmutable | STCmanifest) || - type->isConst() || type->isImmutable() || - parent->isAggregateDeclaration()) + type->isConst() || type->isImmutable()) { /* Because we may need the results of a const declaration in a * subsequent type, such as an array dimension, before semantic2() @@ -1540,15 +1583,18 @@ Lnomatch: if (!global.errors && !inferred) { unsigned errors = global.startGagging(); - Expression *e; + Expression *exp; Initializer *i2 = init; inuse++; if (ei) { - e = ei->exp->syntaxCopy(); - e = e->semantic(sc); - e = resolveProperties(sc, e); + exp = ei->exp->syntaxCopy(); + exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); #if DMDV2 + Type *tb = type->toBasetype(); + Type *ti = exp->type->toBasetype(); + /* The problem is the following code: * struct CopyTest { * double x; @@ -1560,21 +1606,18 @@ Lnomatch: * static assert(w.x == 55.0); * because the postblit doesn't get run on the initialization of w. */ - - Type *tb2 = e->type->toBasetype(); - if (tb2->ty == Tstruct) - { StructDeclaration *sd = ((TypeStruct *)tb2)->sym; - Type *typeb = type->toBasetype(); + if (ti->ty == Tstruct) + { StructDeclaration *sd = ((TypeStruct *)ti)->sym; /* Look to see if initializer involves a copy constructor * (which implies a postblit) */ if (sd->cpctor && // there is a copy constructor - typeb->equals(tb2)) // rvalue is the same struct + tb->equals(ti)) // rvalue is the same struct { // The only allowable initializer is a (non-copy) constructor - if (e->op == TOKcall) + if (exp->op == TOKcall) { - CallExp *ce = (CallExp *)e; + CallExp *ce = (CallExp *)exp; if (ce->e1->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)ce->e1; @@ -1583,15 +1626,33 @@ Lnomatch: } } global.gag--; - error("of type struct %s uses this(this), which is not allowed in static initialization", typeb->toChars()); + error("of type struct %s uses this(this), which is not allowed in static initialization", tb->toChars()); global.gag++; LNoCopyConstruction: ; } } + + // Look for implicit constructor call + if (tb->ty == Tstruct && + !(ti->ty == Tstruct && tb->toDsymbol(sc) == ti->toDsymbol(sc)) && + !exp->implicitConvTo(type)) + { + StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->ctor) + { // Look for constructor first + // Rewrite as e1.ctor(arguments) + Expression *e; + e = new StructLiteralExp(loc, sd, NULL, NULL); + e = new DotIdExp(loc, e, Id::ctor); + e = new CallExp(loc, e, exp); + e = e->semantic(sc); + exp = e->ctfeInterpret(); + } + } #endif - e = e->implicitCastTo(sc, type); + exp = exp->implicitCastTo(sc, type); } else if (si || ai) { i2 = init->syntaxCopy(); @@ -1610,10 +1671,10 @@ Lnomatch: else if (ei) { if (isDataseg() || (storage_class & STCmanifest)) - e = e->ctfeInterpret(); + exp = exp->ctfeInterpret(); else - e = e->optimize(WANTvalue); - switch (e->op) + exp = exp->optimize(WANTvalue); + switch (exp->op) { case TOKint64: case TOKfloat64: @@ -1622,7 +1683,7 @@ Lnomatch: case TOKassocarrayliteral: case TOKstructliteral: case TOKnull: - ei->exp = e; // no errors, keep result + ei->exp = exp; // no errors, keep result break; default: @@ -1639,6 +1700,11 @@ Lnomatch: init = i2; // no errors, keep result } } + else if (parent->isAggregateDeclaration()) + { + scope = new Scope(*sc); + scope->setNoFree(); + } sc = sc->pop(); } @@ -1765,6 +1831,25 @@ void VarDeclaration::setFieldOffset(AggregateDeclaration *ad, unsigned *poffset, ad->sizeok = SIZEOKfwd; // cannot finish; flag as forward referenced return; } +#if DMDV2 + else if (t->ty == Tsarray) + { + Type *tv = t->toBasetype(); + while (tv->ty == Tsarray) + { + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { + TypeStruct *ts = (TypeStruct *)tv; + if (ad == ts->sym) + { + ad->error("cannot have field %s with same struct type", toChars()); + return; + } + } + } +#endif unsigned memsize = t->size(loc); // size of member unsigned memalignsize = t->alignsize(); // size of member for alignment purposes diff --git a/dmd2/declaration.h b/dmd2/declaration.h index 25586839..30b9161c 100644 --- a/dmd2/declaration.h +++ b/dmd2/declaration.h @@ -152,13 +152,13 @@ struct Declaration : Dsymbol void semantic(Scope *sc); const char *kind(); unsigned size(Loc loc); - void checkModify(Loc loc, Scope *sc, Type *t); + int checkModify(Loc loc, Scope *sc, Type *t); Dsymbol *search(Loc loc, Identifier *ident, int flags); void emitComment(Scope *sc); void toJsonBuffer(OutBuffer *buf); - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); char *mangle(); int isStatic() { return storage_class & STCstatic; } @@ -236,7 +236,7 @@ struct TypedefDeclaration : Declaration Type *htype; Type *hbasetype; - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); #if IN_DMD void toObjFile(int multiobj); // compile to .obj file @@ -278,7 +278,7 @@ struct AliasDeclaration : Declaration Type *htype; Dsymbol *haliassym; - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); AliasDeclaration *isAliasDeclaration() { return this; } }; @@ -289,7 +289,7 @@ struct VarDeclaration : Declaration { Initializer *init; unsigned offset; - int noscope; // no auto semantics + bool noscope; // no auto semantics #if DMDV2 FuncDeclarations nestedrefs; // referenced by these lexically nested functions bool isargptr; // if parameter that _argptr points to @@ -297,15 +297,15 @@ struct VarDeclaration : Declaration int nestedref; // referenced by a lexically nested function #endif structalign_t alignment; - int ctorinit; // it has been initialized in a ctor - int onstack; // 1: it has been allocated on the stack + bool ctorinit; // it has been initialized in a ctor + short onstack; // 1: it has been allocated on the stack // 2: on stack, run destructor anyway int canassign; // it can be assigned to Dsymbol *aliassym; // if redone as alias to another symbol // 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; + int ctfeAdrOnStack; // The various functions are used only to detect compiler CTFE bugs Expression *getValue(); bool hasValue(); @@ -766,16 +766,20 @@ struct FuncDeclaration : Declaration Declaration *overnext; // next in overload list Loc endloc; // location of closing curly bracket int vtblIndex; // for member functions, index into vtbl[] - int naked; // !=0 if naked + bool naked; // !=0 if naked ILS inlineStatusStmt; ILS inlineStatusExp; int inlineNest; // !=0 if nested inline - int isArrayOp; // !=0 if array operation +#if IN_LLVM + char isArrayOp; // 1 if compiler-generated array op, 2 if druntime-provided +#else + bool isArrayOp; // !=0 if array operation +#endif enum PASS semanticRun; int semantic3Errors; // !=0 if errors in semantic3 // this function's frame ptr ForeachStatement *fes; // if foreach body, this is the foreach - int introducing; // !=0 if 'introducing' function + bool introducing; // !=0 if 'introducing' function Type *tintro; // if !=NULL, then this is the type // of the 'introducing' function // this one is overriding @@ -789,12 +793,14 @@ struct FuncDeclaration : Declaration // 8 if there's inline asm // Support for NRVO (named return value optimization) - int nrvo_can; // !=0 means we can do it + bool nrvo_can; // !=0 means we can do it VarDeclaration *nrvo_var; // variable to replace with shidden #if IN_DMD Symbol *shidden; // hidden pointer passed to function #endif + ReturnStatements *returns; + #if DMDV2 enum BUILTIN builtin; // set if this is a known, builtin // function we can evaluate at compile @@ -802,6 +808,7 @@ struct FuncDeclaration : Declaration int tookAddressOf; // set if someone took the address of // this function + bool requiresClosure; // this function needs a closure VarDeclarations closureVars; // local variables in this function // which are referenced by nested // functions @@ -870,7 +877,7 @@ struct FuncDeclaration : Declaration int canInline(int hasthis, int hdrscan = false, int statementsToo = true); Expression *expandInline(InlineScanState *iss, Expression *ethis, Expressions *arguments, Statement **ps); const char *kind(); - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); FuncDeclaration *isUnique(); void checkNestedReference(Scope *sc, Loc loc); int needsClosure(); @@ -990,6 +997,7 @@ struct CtorDeclaration : FuncDeclaration int isVirtual(); int addPreInvariant(); int addPostInvariant(); + bool isImplicit; // implicitly generated ctor CtorDeclaration *isCtorDeclaration() { return this; } }; @@ -997,8 +1005,7 @@ struct CtorDeclaration : FuncDeclaration #if DMDV2 struct PostBlitDeclaration : FuncDeclaration { - PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc = STCundefined); - PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id); + PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); diff --git a/dmd2/dmd_msc.vcproj b/dmd2/dmd_msc.vcproj deleted file mode 100644 index 1d27f7e3..00000000 --- a/dmd2/dmd_msc.vcproj +++ /dev/null @@ -1,2150 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/dmd2/doc.c b/dmd2/doc.c index e9631c42..4b566155 100644 --- a/dmd2/doc.c +++ b/dmd2/doc.c @@ -19,7 +19,7 @@ #include "rmem.h" #include "root.h" -#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include "gnuc.h" #endif @@ -49,10 +49,10 @@ struct Escape struct Section { unsigned char *name; - unsigned namelen; + size_t namelen; unsigned char *body; - unsigned bodylen; + size_t bodylen; int nooutput; @@ -86,8 +86,8 @@ struct DocComment { } static DocComment *parse(Scope *sc, Dsymbol *s, unsigned char *comment); - static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen); - static void parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen); + static void parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, size_t mlen); + static void parseEscapes(Escape **pescapetable, unsigned char *textstart, size_t textlen); void parseSections(unsigned char *comment); void writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf); @@ -98,13 +98,13 @@ int cmp(const char *stringz, void *s, size_t slen); int icmp(const char *stringz, void *s, size_t slen); int isDitto(unsigned char *comment); unsigned char *skipwhitespace(unsigned char *p); -unsigned skiptoident(OutBuffer *buf, size_t i); -unsigned skippastident(OutBuffer *buf, size_t i); -unsigned skippastURL(OutBuffer *buf, size_t i); -void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); -void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); -void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset); -Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len); +size_t skiptoident(OutBuffer *buf, size_t i); +size_t skippastident(OutBuffer *buf, size_t i); +size_t skippastURL(OutBuffer *buf, size_t i); +void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset); +void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset, bool anchor = true); +void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset); +Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, size_t len); int isIdStart(unsigned char *p); int isIdTail(unsigned char *p); @@ -191,6 +191,7 @@ DDOC_PARAM_ID = $(TD $0)\n\ DDOC_PARAM_DESC = $(TD $0)\n\ DDOC_BLANKLINE = $(BR)$(BR)\n\ \n\ +DDOC_ANCHOR = \n\ DDOC_PSYMBOL = $(U $0)\n\ DDOC_KEYWORD = $(B $0)\n\ DDOC_PARAM = $(I $0)\n\ @@ -233,7 +234,7 @@ void Module::gendocfile() // Override with the ddoc macro files from the command line for (size_t i = 0; i < global.params.ddocfiles->dim; i++) { - FileName f((*global.params.ddocfiles)[i], 0); + FileName f((*global.params.ddocfiles)[i]); File file(&f); file.readv(); // BUG: convert file contents to UTF-8 before use @@ -302,7 +303,7 @@ void Module::gendocfile() OutBuffer buf2; buf2.writestring("$(DDOC)\n"); - unsigned end = buf2.offset; + size_t end = buf2.offset; macrotable->expand(&buf2, 0, &end, NULL, 0); #if 1 @@ -313,7 +314,7 @@ void Module::gendocfile() buf.setsize(0); buf.reserve(buf2.offset); unsigned char *p = buf2.data; - for (unsigned j = 0; j < buf2.offset; j++) + for (size_t j = 0; j < buf2.offset; j++) { unsigned char c = p[j]; if (c == 0xFF && j + 1 < buf2.offset) @@ -348,9 +349,9 @@ void Module::gendocfile() #else /* Remove all the escape sequences from buf2 */ - { unsigned i = 0; + { size_t i = 0; unsigned char *p = buf2.data; - for (unsigned j = 0; j < buf2.offset; j++) + for (size_t j = 0; j < buf2.offset; j++) { if (p[j] == 0xFF && j + 1 < buf2.offset) { @@ -381,9 +382,9 @@ void Module::gendocfile() * to preserve text literally. This also means macros in the * text won't be expanded. */ -void escapeDdocString(OutBuffer *buf, unsigned start) +void escapeDdocString(OutBuffer *buf, size_t start) { - for (unsigned u = start; u < buf->offset; u++) + for (size_t u = start; u < buf->offset; u++) { unsigned char c = buf->data[u]; switch(c) @@ -415,11 +416,11 @@ void escapeDdocString(OutBuffer *buf, unsigned start) * Fix by replacing unmatched ( with $(LPAREN) and unmatched ) with $(RPAREN). */ -void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc) +void escapeStrayParenthesis(OutBuffer *buf, size_t start, Loc loc) { unsigned par_open = 0; - for (unsigned u = start; u < buf->offset; u++) + for (size_t u = start; u < buf->offset; u++) { unsigned char c = buf->data[u]; switch(c) @@ -432,8 +433,7 @@ void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc) if (par_open == 0) { //stray ')' - if (global.params.warnings) - warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output." + warning(loc, "Ddoc: Stray ')'. This may cause incorrect Ddoc output." " Use $(RPAREN) instead for unpaired right parentheses."); buf->remove(u, 1); //remove the ) buf->insert(u, "$(RPAREN)", 9); //insert this instead @@ -455,7 +455,7 @@ void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc) if (par_open) // if any unmatched lparens { par_open = 0; - for (unsigned u = buf->offset; u > start;) + for (size_t u = buf->offset; u > start;) { u--; unsigned char c = buf->data[u]; switch(c) @@ -468,8 +468,7 @@ void escapeStrayParenthesis(OutBuffer *buf, unsigned start, Loc loc) if (par_open == 0) { //stray '(' - if (global.params.warnings) - warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output." + warning(loc, "Ddoc: Stray '('. This may cause incorrect Ddoc output." " Use $(LPAREN) instead for unpaired left parentheses."); buf->remove(u, 1); //remove the ( buf->insert(u, "$(LPAREN)", 9); //insert this instead @@ -492,12 +491,12 @@ void Dsymbol::emitDitto(Scope *sc) { //printf("Dsymbol::emitDitto() %s %s\n", kind(), toChars()); OutBuffer *buf = sc->docbuf; - unsigned o; + size_t o; OutBuffer b; b.writestring("$(DDOC_DITTO "); o = b.offset; - toDocBuffer(&b); + toDocBuffer(&b, sc); //printf("b: '%.*s'\n", b.offset, b.data); /* If 'this' is a function template, then highlightCode() was * already run by FuncDeclaration::toDocbuffer(). @@ -559,15 +558,8 @@ void ScopeDsymbol::emitMemberComments(Scope *sc) void emitProtection(OutBuffer *buf, PROT prot) { - const char *p; + const char *p = (prot == PROTpublic) ? NULL : Pprotectionnames[prot]; - switch (prot) - { - case PROTpackage: p = "package"; break; - case PROTprotected: p = "protected"; break; - case PROTexport: p = "export"; break; - default: p = NULL; break; - } if (p) buf->printf("%s ", p); } @@ -598,7 +590,7 @@ void Declaration::emitComment(Scope *sc) OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, comment); - unsigned o; + size_t o; if (!dc) { @@ -609,7 +601,7 @@ void Declaration::emitComment(Scope *sc) buf->writestring(ddoc_decl_s); o = buf->offset; - toDocBuffer(buf); + toDocBuffer(buf, sc); highlightCode(sc, this, buf, o); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); @@ -638,7 +630,7 @@ void AggregateDeclaration::emitComment(Scope *sc) dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); - toDocBuffer(buf); + toDocBuffer(buf, sc); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); @@ -680,7 +672,7 @@ void TemplateDeclaration::emitComment(Scope *sc) OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, com); - unsigned o; + size_t o; if (!dc) { @@ -691,7 +683,7 @@ void TemplateDeclaration::emitComment(Scope *sc) buf->writestring(ddoc_decl_s); o = buf->offset; - ss->toDocBuffer(buf); + ss->toDocBuffer(buf, sc); if (ss == this) highlightCode(sc, this, buf, o); sc->lastoffset = buf->offset; @@ -735,7 +727,7 @@ void EnumDeclaration::emitComment(Scope *sc) dc->pmacrotable = &sc->module->macrotable; buf->writestring(ddoc_decl_s); - toDocBuffer(buf); + toDocBuffer(buf, sc); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); @@ -755,7 +747,7 @@ void EnumMember::emitComment(Scope *sc) OutBuffer *buf = sc->docbuf; DocComment *dc = DocComment::parse(sc, this, comment); - unsigned o; + size_t o; if (!dc) { @@ -766,7 +758,7 @@ void EnumMember::emitComment(Scope *sc) buf->writestring(ddoc_decl_s); o = buf->offset; - toDocBuffer(buf); + toDocBuffer(buf, sc); highlightCode(sc, this, buf, o); sc->lastoffset = buf->offset; buf->writestring(ddoc_decl_e); @@ -776,9 +768,45 @@ void EnumMember::emitComment(Scope *sc) buf->writestring(ddoc_decl_dd_e); } +static bool emitAnchorName(OutBuffer *buf, Dsymbol *s) +{ + if (!s || s->isPackage() || s->isModule()) + return false; + + TemplateDeclaration *td; + bool dot; + + // Add parent names first + dot = emitAnchorName(buf, s->parent); + // Eponymous template members can share the parent anchor name + if (s->parent && (td = s->parent->isTemplateDeclaration()) != NULL && + td->onemember == s) + return dot; + if (dot) + buf->writeByte('.'); + // Use "this" not "__ctor" + if (s->isCtorDeclaration() || ((td = s->isTemplateDeclaration()) != NULL && + td->onemember && td->onemember->isCtorDeclaration())) + buf->writestring("this"); + else + { + /* We just want the identifier, not overloads like TemplateDeclaration::toChars. + * We don't want the template parameter list and constraints. */ + buf->writestring(s->Dsymbol::toChars()); + } + return true; +} + +static void emitAnchor(OutBuffer *buf, Dsymbol *s) +{ + buf->writestring("$(DDOC_ANCHOR "); + emitAnchorName(buf, s); + buf->writeByte(')'); +} + /******************************* toDocBuffer **********************************/ -void Dsymbol::toDocBuffer(OutBuffer *buf) +void Dsymbol::toDocBuffer(OutBuffer *buf, Scope *sc) { //printf("Dsymbol::toDocbuffer() %s\n", toChars()); HdrGenState hgs; @@ -795,18 +823,20 @@ void prefix(OutBuffer *buf, Dsymbol *s) if (d) { emitProtection(buf, d->protection); - if (d->isAbstract()) - buf->writestring("abstract "); + if (d->isStatic()) buf->writestring("static "); + else if (d->isFinal()) + buf->writestring("final "); + else if (d->isAbstract()) + buf->writestring("abstract "); + if (d->isConst()) buf->writestring("const "); #if DMDV2 if (d->isImmutable()) buf->writestring("immutable "); #endif - if (d->isFinal()) - buf->writestring("final "); if (d->isSynchronized()) buf->writestring("synchronized "); } @@ -837,12 +867,12 @@ void declarationToDocBuffer(Declaration *decl, OutBuffer *buf, TemplateDeclarati } } -void Declaration::toDocBuffer(OutBuffer *buf) +void Declaration::toDocBuffer(OutBuffer *buf, Scope *sc) { declarationToDocBuffer(this, buf, NULL); } -void AliasDeclaration::toDocBuffer(OutBuffer *buf) +void AliasDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { //printf("AliasDeclaration::toDocbuffer() %s\n", toChars()); if (ident) @@ -852,13 +882,83 @@ void AliasDeclaration::toDocBuffer(OutBuffer *buf) emitProtection(buf, protection); buf->writestring("alias "); + + if (Dsymbol *s = aliassym) // ident alias + { + prettyPrintDsymbol(buf, s, parent); + } + else if (Type *type = getType()) // type alias + { + if (type->ty == Tclass || type->ty == Tstruct || type->ty == Tenum) + { + if (Dsymbol *s = type->toDsymbol(NULL)) // elaborate type + prettyPrintDsymbol(buf, s, parent); + else + buf->writestring(type->toChars()); + } + else + { + // simple type + buf->writestring(type->toChars()); + } + } + + buf->writestring(" "); buf->writestring(toChars()); buf->writestring(";\n"); } } +void parentToBuffer(OutBuffer *buf, Dsymbol *s) +{ + if (s && !s->isPackage() && !s->isModule()) + { + parentToBuffer(buf, s->parent); + buf->writestring(s->toChars()); + buf->writestring("."); + } +} -void TypedefDeclaration::toDocBuffer(OutBuffer *buf) +bool inSameModule(Dsymbol *s, Dsymbol *p) +{ + for ( ; s ; s = s->parent) + { + if (s->isModule()) + break; + } + + for ( ; p ; p = p->parent) + { + if (p->isModule()) + break; + } + + return s == p; +} + +void prettyPrintDsymbol(OutBuffer *buf, Dsymbol *s, Dsymbol *parent) +{ + if (s->parent && (s->parent == parent)) // in current scope -> naked name + { + buf->writestring(s->toChars()); + } + else + if (!inSameModule(s, parent)) // in another module -> full name + { + buf->writestring(s->toPrettyChars()); + } + else // nested in a type in this module -> full name w/o module name + { + // if alias is nested in a user-type use module-scope lookup + if (!parent->isModule() && !parent->isPackage()) + buf->writestring("."); + + parentToBuffer(buf, s->parent); + buf->writestring(s->toChars()); + } +} + +void TypedefDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { if (ident) { @@ -873,7 +973,7 @@ void TypedefDeclaration::toDocBuffer(OutBuffer *buf) } -void FuncDeclaration::toDocBuffer(OutBuffer *buf) +void FuncDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { //printf("FuncDeclaration::toDocbuffer() %s\n", toChars()); if (ident) @@ -885,7 +985,7 @@ void FuncDeclaration::toDocBuffer(OutBuffer *buf) td->onemember == this) { /* It's a function template */ - unsigned o = buf->offset; + size_t o = buf->offset; declarationToDocBuffer(this, buf, td); @@ -893,13 +993,13 @@ void FuncDeclaration::toDocBuffer(OutBuffer *buf) } else { - Declaration::toDocBuffer(buf); + Declaration::toDocBuffer(buf, sc); } } } #if DMDV1 -void CtorDeclaration::toDocBuffer(OutBuffer *buf) +void CtorDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { HdrGenState hgs; @@ -909,10 +1009,11 @@ void CtorDeclaration::toDocBuffer(OutBuffer *buf) } #endif -void AggregateDeclaration::toDocBuffer(OutBuffer *buf) +void AggregateDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { if (ident) { + emitAnchor(buf, this); #if 0 emitProtection(buf, protection); #endif @@ -921,7 +1022,7 @@ void AggregateDeclaration::toDocBuffer(OutBuffer *buf) } } -void StructDeclaration::toDocBuffer(OutBuffer *buf) +void StructDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { //printf("StructDeclaration::toDocbuffer() %s\n", toChars()); if (ident) @@ -934,19 +1035,20 @@ void StructDeclaration::toDocBuffer(OutBuffer *buf) if (parent && (td = parent->isTemplateDeclaration()) != NULL && td->onemember == this) - { unsigned o = buf->offset; - td->toDocBuffer(buf); + { size_t o = buf->offset; + td->toDocBuffer(buf, sc); highlightCode(NULL, this, buf, o); } else { + emitAnchor(buf, this); buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); } buf->writestring(";\n"); } } -void ClassDeclaration::toDocBuffer(OutBuffer *buf) +void ClassDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { //printf("ClassDeclaration::toDocbuffer() %s\n", toChars()); if (ident) @@ -959,12 +1061,13 @@ void ClassDeclaration::toDocBuffer(OutBuffer *buf) if (parent && (td = parent->isTemplateDeclaration()) != NULL && td->onemember == this) - { unsigned o = buf->offset; - td->toDocBuffer(buf); + { size_t o = buf->offset; + td->toDocBuffer(buf, sc); highlightCode(NULL, this, buf, o); } else { + emitAnchor(buf, this); if (isAbstract()) buf->writestring("abstract "); buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); @@ -1000,16 +1103,17 @@ void ClassDeclaration::toDocBuffer(OutBuffer *buf) } -void EnumDeclaration::toDocBuffer(OutBuffer *buf) +void EnumDeclaration::toDocBuffer(OutBuffer *buf, Scope *sc) { if (ident) { + emitAnchor(buf, this); buf->printf("%s $(DDOC_PSYMBOL %s)", kind(), toChars()); buf->writestring(";\n"); } } -void EnumMember::toDocBuffer(OutBuffer *buf) +void EnumMember::toDocBuffer(OutBuffer *buf, Scope *sc) { if (ident) { @@ -1062,10 +1166,10 @@ void DocComment::parseSections(unsigned char *comment) unsigned char *pstart; unsigned char *pend; unsigned char *idstart; - unsigned idlen; + size_t idlen; unsigned char *name = NULL; - unsigned namelen = 0; + size_t namelen = 0; //printf("parseSections('%s')\n", comment); p = comment; @@ -1188,7 +1292,7 @@ void DocComment::writeSections(Scope *sc, Dsymbol *s, OutBuffer *buf) else { buf->writestring("$(DDOC_SUMMARY "); - unsigned o = buf->offset; + size_t o = buf->offset; buf->write(sec->body, sec->bodylen); escapeStrayParenthesis(buf, o, s->loc); highlightText(sc, s, buf, o); @@ -1216,7 +1320,7 @@ void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) "RETURNS", "SEE_ALSO", "STANDARDS", "THROWS", "VERSION" }; - for (int i = 0; i < sizeof(table) / sizeof(table[0]); i++) + for (size_t i = 0; i < sizeof(table) / sizeof(table[0]); i++) { if (icmp(table[i], name, namelen) == 0) { @@ -1228,8 +1332,8 @@ void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) buf->writestring("$(DDOC_SECTION "); // Replace _ characters with spaces buf->writestring("$(DDOC_SECTION_H "); - unsigned o = buf->offset; - for (unsigned u = 0; u < namelen; u++) + size_t o = buf->offset; + for (size_t u = 0; u < namelen; u++) { unsigned char c = name[u]; buf->writeByte((c == '_') ? ' ' : c); } @@ -1241,7 +1345,7 @@ void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) buf->writestring("$(DDOC_DESCRIPTION "); } L1: - unsigned o = buf->offset; + size_t o = buf->offset; buf->write(body, bodylen); escapeStrayParenthesis(buf, o, s->loc); highlightText(sc, s, buf, o); @@ -1254,19 +1358,19 @@ void Section::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) { unsigned char *p = body; - unsigned len = bodylen; + size_t len = bodylen; unsigned char *pend = p + len; unsigned char *tempstart; - unsigned templen; + size_t templen; unsigned char *namestart; - unsigned namelen = 0; // !=0 if line continuation + size_t namelen = 0; // !=0 if line continuation unsigned char *textstart; - unsigned textlen; + size_t textlen; - unsigned o; + size_t o; Parameter *arg; buf->writestring("$(DDOC_PARAMS \n"); @@ -1326,7 +1430,7 @@ void ParamSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) else buf->write(namestart, namelen); escapeStrayParenthesis(buf, o, s->loc); - highlightCode(sc, s, buf, o); + highlightCode(sc, s, buf, o, false); buf->writestring(")\n"); buf->writestring("$(DDOC_PARAM_DESC "); @@ -1384,20 +1488,20 @@ void MacroSection::write(DocComment *dc, Scope *sc, Dsymbol *s, OutBuffer *buf) * name2 = value2 */ -void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, unsigned mlen) +void DocComment::parseMacros(Escape **pescapetable, Macro **pmacrotable, unsigned char *m, size_t mlen) { unsigned char *p = m; - unsigned len = mlen; + size_t len = mlen; unsigned char *pend = p + len; unsigned char *tempstart; - unsigned templen; + size_t templen; unsigned char *namestart; - unsigned namelen = 0; // !=0 if line continuation + size_t namelen = 0; // !=0 if line continuation unsigned char *textstart; - unsigned textlen; + size_t textlen; while (p < pend) { @@ -1509,7 +1613,7 @@ Ldone: * by whitespace and/or commas. */ -void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, unsigned textlen) +void DocComment::parseEscapes(Escape **pescapetable, unsigned char *textstart, size_t textlen) { Escape *escapetable = *pescapetable; if (!escapetable) @@ -1618,7 +1722,7 @@ unsigned char *skipwhitespace(unsigned char *p) * end of buf */ -unsigned skiptoident(OutBuffer *buf, size_t i) +size_t skiptoident(OutBuffer *buf, size_t i) { while (i < buf->offset) { dchar_t c; @@ -1645,7 +1749,7 @@ unsigned skiptoident(OutBuffer *buf, size_t i) * Scan forward past end of identifier. */ -unsigned skippastident(OutBuffer *buf, size_t i) +size_t skippastident(OutBuffer *buf, size_t i) { while (i < buf->offset) { dchar_t c; @@ -1677,10 +1781,10 @@ unsigned skippastident(OutBuffer *buf, size_t i) * index just past it if it is a URL */ -unsigned skippastURL(OutBuffer *buf, size_t i) -{ unsigned length = buf->offset - i; +size_t skippastURL(OutBuffer *buf, size_t i) +{ size_t length = buf->offset - i; unsigned char *p = &buf->data[i]; - unsigned j; + size_t j; unsigned sawdot = 0; if (length > 7 && memicmp((char *)p, "http://", 7) == 0) @@ -1721,7 +1825,7 @@ Lno: /**************************************************** */ -int isKeyword(unsigned char *p, unsigned len) +int isKeyword(unsigned char *p, size_t len) { static const char *table[] = { "true", "false", "null" }; @@ -1736,7 +1840,7 @@ int isKeyword(unsigned char *p, unsigned len) /**************************************************** */ -Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len) +Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, size_t len) { FuncDeclaration *f = s->isFuncDeclaration(); @@ -1771,7 +1875,7 @@ Parameter *isFunctionParameter(Dsymbol *s, unsigned char *p, unsigned len) * Highlight text section. */ -void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) +void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) { //printf("highlightText()\n"); const char *sid = s->ident->toChars(); @@ -1782,11 +1886,11 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) int leadingBlank = 1; int inCode = 0; //int inComment = 0; // in comment - unsigned iCodeStart; // start of code section + size_t iCodeStart; // start of code section - unsigned iLineStart = offset; + size_t iLineStart = offset; - for (unsigned i = offset; i < buf->offset; i++) + for (size_t i = offset; i < buf->offset; i++) { unsigned char c = buf->data[i]; Lcont: @@ -1815,7 +1919,7 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) // Skip over comments if (p[1] == '!' && p[2] == '-' && p[3] == '-') - { unsigned j = i + 4; + { size_t j = i + 4; p += 4; while (1) { @@ -1834,7 +1938,7 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) // Skip over HTML tag if (isalpha(p[1]) || (p[1] == '/' && isalpha(p[2]))) - { unsigned j = i + 2; + { size_t j = i + 2; p += 2; while (1) { @@ -1898,8 +2002,8 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) * inCode tells us if it is start or end of a code section. */ if (leadingBlank) - { int istart = i; - int eollen = 0; + { size_t istart = i; + size_t eollen = 0; leadingBlank = 0; while (1) @@ -1953,7 +2057,7 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) buf->remove(iCodeStart, i - iCodeStart); i = buf->insert(iCodeStart, codebuf.data, codebuf.offset); i = buf->insert(i, ")\n", 2); - i--; + i -= 2; // in next loop, c should be '\n' } else { static char pre[] = "$(D_CODE \n"; @@ -1970,12 +2074,11 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) default: leadingBlank = 0; if (sc && !inCode && isIdStart(&buf->data[i])) - { unsigned j; - - j = skippastident(buf, i); + { + size_t j = skippastident(buf, i); if (j > i) { - unsigned k = skippastURL(buf, i); + size_t k = skippastURL(buf, i); if (k > i) { i = k - 1; break; @@ -2023,13 +2126,21 @@ void highlightText(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) * Highlight code for DDOC section. */ -void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) +void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset, bool anchor) { + if (anchor) + { + OutBuffer ancbuf; + + emitAnchor(&ancbuf, s); + buf->insert(offset, (char *)ancbuf.data, ancbuf.offset); + offset += ancbuf.offset; + } char *sid = s->ident->toChars(); FuncDeclaration *f = s->isFuncDeclaration(); //printf("highlightCode(s = '%s', kind = %s)\n", sid, s->kind()); - for (unsigned i = offset; i < buf->offset; i++) + for (size_t i = offset; i < buf->offset; i++) { unsigned char c = buf->data[i]; const char *se; @@ -2042,9 +2153,8 @@ void highlightCode(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) i--; // point to ';' } else if (isIdStart(&buf->data[i])) - { unsigned j; - - j = skippastident(buf, i); + { + size_t j = skippastident(buf, i); if (j > i) { if (cmp(sid, buf->data + i, j - i) == 0) @@ -2086,7 +2196,7 @@ void highlightCode3(OutBuffer *buf, unsigned char *p, unsigned char *pend) */ -void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, unsigned offset) +void highlightCode2(Scope *sc, Dsymbol *s, OutBuffer *buf, size_t offset) { char *sid = s->ident->toChars(); FuncDeclaration *f = s->isFuncDeclaration(); diff --git a/dmd2/doc.h b/dmd2/doc.h index ffa97fb9..4f2dd660 100644 --- a/dmd2/doc.h +++ b/dmd2/doc.h @@ -15,6 +15,9 @@ #pragma once #endif /* __DMC__ */ -void escapeDdocString(OutBuffer *buf, unsigned start); +void escapeDdocString(OutBuffer *buf, size_t start); +void parentToBuffer(OutBuffer *buf, Dsymbol *s); +bool inSameModule(Dsymbol *s, Dsymbol *p); +void prettyPrintDsymbol(OutBuffer *buf, Dsymbol *s, Dsymbol *parent); #endif diff --git a/dmd2/dsymbol.c b/dmd2/dsymbol.c index 2228feb5..87dd10a6 100644 --- a/dmd2/dsymbol.c +++ b/dmd2/dsymbol.c @@ -31,6 +31,8 @@ #include "import.h" #include "template.h" #include "attrib.h" +#include "enum.h" + #if IN_LLVM #include "../gen/pragma.h" #endif @@ -51,6 +53,7 @@ Dsymbol::Dsymbol() this->comment = NULL; this->scope = NULL; this->errors = false; + this->userAttributes = NULL; #if IN_LLVM this->llvmInternal = LLVMnone; @@ -71,6 +74,8 @@ Dsymbol::Dsymbol(Identifier *ident) this->comment = NULL; this->scope = NULL; this->errors = false; + this->depmsg = NULL; + this->userAttributes = NULL; #if IN_LLVM this->llvmInternal = LLVMnone; @@ -230,10 +235,8 @@ const char *Dsymbol::toPrettyChars() return s; } -char *Dsymbol::locToChars() +Loc& Dsymbol::getLoc() { - OutBuffer buf; - if (!loc.filename) // avoid bug 5861. { Module *m = getModule(); @@ -241,7 +244,12 @@ char *Dsymbol::locToChars() if (m && m->srcfile) loc.filename = m->srcfile->toChars(); } - return loc.toChars(); + return loc; +} + +char *Dsymbol::locToChars() +{ + return getLoc().toChars(); } const char *Dsymbol::kind() @@ -331,6 +339,8 @@ void Dsymbol::setScope(Scope *sc) if (!sc->nofree) sc->setNoFree(); // may need it even after semantic() finishes scope = sc; + if (sc->depmsg) + depmsg = sc->depmsg; } void Dsymbol::importAll(Scope *sc) @@ -494,7 +504,7 @@ void Dsymbol::toCBuffer(OutBuffer *buf, HdrGenState *hgs) unsigned Dsymbol::size(Loc loc) { - error("Dsymbol '%s' has no size\n", toChars()); + error("Dsymbol '%s' has no size", toChars()); return 0; } @@ -516,6 +526,14 @@ AggregateDeclaration *Dsymbol::isAggregateMember() // are we a member of an return NULL; } +AggregateDeclaration *Dsymbol::isAggregateMember2() // are we a member of an aggregate? +{ + Dsymbol *parent = toParent2(); + if (parent && parent->isAggregateDeclaration()) + return (AggregateDeclaration *)parent; + return NULL; +} + ClassDeclaration *Dsymbol::isClassMember() // are we a member of a class? { AggregateDeclaration *ad = isAggregateMember(); @@ -612,17 +630,9 @@ int Dsymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) void Dsymbol::error(const char *format, ...) { - //printf("Dsymbol::error()\n"); - if (!loc.filename) // avoid bug 5861. - { - Module *m = getModule(); - - if (m && m->srcfile) - loc.filename = m->srcfile->toChars(); - } va_list ap; va_start(ap, format); - verror(loc, format, ap, kind(), toPrettyChars()); + ::verror(getLoc(), format, ap, kind(), toPrettyChars()); va_end(ap); } @@ -630,13 +640,29 @@ void Dsymbol::error(Loc loc, const char *format, ...) { va_list ap; va_start(ap, format); - verror(loc, format, ap, kind(), toPrettyChars()); + ::verror(loc, format, ap, kind(), toPrettyChars()); + va_end(ap); +} + +void Dsymbol::deprecation(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::vdeprecation(loc, format, ap, kind(), toPrettyChars()); + va_end(ap); +} + +void Dsymbol::deprecation(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::vdeprecation(getLoc(), format, ap, kind(), toPrettyChars()); va_end(ap); } void Dsymbol::checkDeprecated(Loc loc, Scope *sc) { - if (!global.params.useDeprecated && isDeprecated()) + if (global.params.useDeprecated != 1 && isDeprecated()) { // Don't complain if we're inside a deprecated symbol's scope for (Dsymbol *sp = sc->parent; sp; sp = sp->parent) @@ -654,7 +680,18 @@ void Dsymbol::checkDeprecated(Loc loc, Scope *sc) goto L1; } - error(loc, "is deprecated"); + char *message = NULL; + for (Dsymbol *p = this; p; p = p->parent) + { + message = p->depmsg; + if (message) + break; + } + + if (message) + deprecation(loc, "is deprecated - %s", message); + else + deprecation(loc, "is deprecated"); } L1: @@ -769,7 +806,7 @@ void Dsymbol::addComment(unsigned char *comment) if (!this->comment) this->comment = comment; #if 1 - else if (comment && strcmp((char *)comment, (char *)this->comment)) + else if (comment && strcmp((char *)comment, (char *)this->comment) != 0) { // Concatenate the two this->comment = Lexer::combineComments(this->comment, comment); } @@ -939,11 +976,27 @@ Dsymbol *ScopeDsymbol::search(Loc loc, Identifier *ident, int flags) if (s) { - Declaration *d = s->isDeclaration(); - if (d && d->protection == PROTprivate && - !d->parent->isTemplateMixin() && - !(flags & 2)) - error(loc, "%s is private", d->toPrettyChars()); + if (!(flags & 2)) + { Declaration *d = s->isDeclaration(); + if (d && d->protection == PROTprivate && + !d->parent->isTemplateMixin()) + error(loc, "%s is private", d->toPrettyChars()); + + AggregateDeclaration *ad = s->isAggregateDeclaration(); + if (ad && ad->protection == PROTprivate && + !ad->parent->isTemplateMixin()) + error(loc, "%s is private", ad->toPrettyChars()); + + EnumDeclaration *ed = s->isEnumDeclaration(); + if (ed && ed->protection == PROTprivate && + !ed->parent->isTemplateMixin()) + error(loc, "%s is private", ed->toPrettyChars()); + + TemplateDeclaration *td = s->isTemplateDeclaration(); + if (td && td->protection == PROTprivate && + !td->parent->isTemplateMixin()) + error(loc, "%s is private", td->toPrettyChars()); + } } } return s; @@ -1006,7 +1059,7 @@ void ScopeDsymbol::multiplyDefined(Loc loc, Dsymbol *s1, Dsymbol *s2) } else { - s1->error(loc, "conflicts with %s %s at %s", + s1->error(s1->loc, "conflicts with %s %s at %s", s2->kind(), s2->toPrettyChars(), s2->locToChars()); @@ -1242,13 +1295,10 @@ ArrayScopeSymbol::ArrayScopeSymbol(Scope *sc, TupleDeclaration *s) Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) { //printf("ArrayScopeSymbol::search('%s', flags = %d)\n", ident->toChars(), flags); - if (ident == Id::length || ident == Id::dollar) + if (ident == Id::dollar) { VarDeclaration **pvar; Expression *ce; - if (ident == Id::length && !global.params.useDeprecated) - error("using 'length' inside [ ] is deprecated, use '$' instead"); - L1: if (td) @@ -1294,62 +1344,9 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) * $ 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; + pvar = &ae->lengthVar; + ce = ae->e1; } else /* Didn't find $, look in enclosing scope(s). @@ -1375,15 +1372,87 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) if (!*pvar) // if not already initialized { /* Create variable v and set it to the value of $ */ - VarDeclaration *v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, NULL); + VarDeclaration *v; + Type *t; if (ce->op == TOKtuple) { /* It is for an expression tuple, so the * length will be a const. */ Expression *e = new IntegerExp(0, ((TupleExp *)ce)->exps->dim, Type::tsize_t); - v->init = new ExpInitializer(0, e); + v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, new ExpInitializer(0, e)); v->storage_class |= STCstatic | STCconst; } + else if (ce->type && (t = ce->type->toBasetype()) != NULL && + (t->ty == Tstruct || t->ty == Tclass)) + { // Look for opDollar + assert(exp->op == TOKarray || exp->op == TOKslice); + AggregateDeclaration *ad = NULL; + + if (t->ty == Tclass) + { + ad = ((TypeClass *)t)->sym; + } + else if (t->ty == Tstruct) + { + ad = ((TypeStruct *)t)->sym; + } + assert(ad); + + Dsymbol *s = ad->search(loc, Id::opDollar, 0); + if (!s) // no dollar exists -- search in higher scope + return NULL; + s = s->toAlias(); + + Expression *e = NULL; + // Check for multi-dimensional opDollar(dim) template. + if (TemplateDeclaration *td = s->isTemplateDeclaration()) + { + dinteger_t dim; + if (exp->op == TOKarray) + { + dim = ((ArrayExp *)exp)->currentDimension; + e = ((ArrayExp *)exp)->e1; + } + else if (exp->op == TOKslice) + { + dim = 0; // slices are currently always one-dimensional + e = ((SliceExp *)exp)->e1; + } + assert(e); + + Objects *tdargs = new Objects(); + Expression *edim = new IntegerExp(0, dim, Type::tsize_t); + edim = edim->semantic(sc); + tdargs->push(edim); + + //TemplateInstance *ti = new TemplateInstance(loc, td, tdargs); + //ti->semantic(sc); + + e = new DotTemplateInstanceExp(loc, e, td->ident, tdargs); + } + else + { /* opDollar exists, but it's 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 (exp->op == TOKarray && ((ArrayExp *)exp)->arguments->dim != 1) + { + exp->error("%s only defines opDollar for one dimension", ad->toChars()); + return NULL; + } + Declaration *d = s->isDeclaration(); + assert(d); + e = new DotVarExp(loc, ce, d); + } + e = e->semantic(sc); + if (!e->type) + exp->error("%s has no value", e->toChars()); + t = e->type->toBasetype(); + if (t && t->ty == Tfunction) + e = new CallExp(e->loc, e); + v = new VarDeclaration(loc, NULL, Id::dollar, new ExpInitializer(0, e)); + } else { /* For arrays, $ will either be a compile-time constant * (in which case its value in set during constant-folding), @@ -1392,7 +1461,7 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) */ VoidInitializer *e = new VoidInitializer(0); e->type = Type::tsize_t; - v->init = e; + v = new VarDeclaration(loc, Type::tsize_t, Id::dollar, e); v->storage_class |= STCctfe; // it's never a true static variable } *pvar = v; diff --git a/dmd2/dsymbol.h b/dmd2/dsymbol.h index 28b3fa60..07506121 100644 --- a/dmd2/dsymbol.h +++ b/dmd2/dsymbol.h @@ -120,6 +120,9 @@ enum PROT PROTexport, }; +// this is used for printing the protection in json, traits, docs, etc. +static const char* Pprotectionnames[] = {NULL, "none", "private", "package", "protected", "public", "export"}; + /* State of symbol in winding its way through the passes of the compiler */ enum PASS @@ -148,15 +151,20 @@ struct Dsymbol : Object Loc loc; // where defined Scope *scope; // !=NULL means context to use for semantic() bool errors; // this symbol failed to pass semantic() + char *depmsg; // customized deprecation message + Expressions *userAttributes; // user defined attributes from UserAttributeDeclaration Dsymbol(); Dsymbol(Identifier *); char *toChars(); + Loc& getLoc(); char *locToChars(); int equals(Object *o); int isAnonymous(); - void error(Loc loc, const char *format, ...) IS_PRINTF(3); - void error(const char *format, ...) IS_PRINTF(2); + void error(Loc loc, const char *format, ...); + void error(const char *format, ...); + void deprecation(Loc loc, const char *format, ...); + void deprecation(const char *format, ...); void checkDeprecated(Loc loc, Scope *sc); Module *getModule(); // module where declared Module *getAccessModule(); @@ -189,13 +197,14 @@ struct Dsymbol : Object char *toHChars(); virtual void toHBuffer(OutBuffer *buf, HdrGenState *hgs); virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - virtual void toDocBuffer(OutBuffer *buf); + virtual void toDocBuffer(OutBuffer *buf, Scope *sc); virtual void toJsonBuffer(OutBuffer *buf); virtual unsigned size(Loc loc); virtual int isforwardRef(); virtual void defineRef(Dsymbol *s); virtual AggregateDeclaration *isThis(); // is a 'this' required to access the member AggregateDeclaration *isAggregateMember(); // are we a member of an aggregate? + AggregateDeclaration *isAggregateMember2(); // are we a member of an aggregate? ClassDeclaration *isClassMember(); // are we a member of a class? virtual int isExport(); // is Dsymbol exported? virtual int isImportedSymbol(); // is Dsymbol imported? diff --git a/dmd2/entity.c b/dmd2/entity.c index 98b81141..8cb063dc 100644 --- a/dmd2/entity.c +++ b/dmd2/entity.c @@ -1,5 +1,5 @@ -// Copyright (c) 1999-2009 by Digital Mars +// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -2372,15 +2372,14 @@ static NameId* namesTable[] = { namesS, namesT, namesU, namesV, namesW, namesX, namesY, namesZ, NULL }; -int HtmlNamedEntity(unsigned char *p, int length) +int HtmlNamedEntity(unsigned char *p, size_t length) { int tableIndex = tolower(*p) - 'a'; if (tableIndex >= 0 && tableIndex < 26) { NameId* names = namesTable[tableIndex]; - int i; - for (i = 0; names[i].name; i++) + for (size_t i = 0; names[i].name; i++) { if (strncmp(names[i].name, (char *)p, length) == 0) return names[i].value; diff --git a/dmd2/enum.c b/dmd2/enum.c index 93fc79c4..ecc32504 100644 --- a/dmd2/enum.c +++ b/dmd2/enum.c @@ -115,8 +115,10 @@ void EnumDeclaration::semantic(Scope *sc) if (sc->stc & STCdeprecated) isdeprecated = 1; + userAttributes = sc->userAttributes; parent = sc->parent; + protection = sc->protection; /* The separate, and distinct, cases are: * 1. enum { ... } @@ -347,16 +349,17 @@ void EnumDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; for (size_t i = 0; i < members->dim; i++) { EnumMember *em = (*members)[i]->isEnumMember(); if (!em) continue; - //buf->writestring(" "); em->toCBuffer(buf, hgs); buf->writeByte(','); buf->writenl(); } + buf->level--; buf->writeByte('}'); buf->writenl(); } diff --git a/dmd2/enum.h b/dmd2/enum.h index 1c7d5e9d..5059317f 100644 --- a/dmd2/enum.h +++ b/dmd2/enum.h @@ -29,6 +29,7 @@ struct EnumDeclaration : ScopeDsymbol */ Type *type; // the TypeEnum Type *memtype; // type of the members + enum PROT protection; #if DMDV1 dinteger_t maxval; @@ -61,7 +62,7 @@ struct EnumDeclaration : ScopeDsymbol void emitComment(Scope *sc); void toJsonBuffer(OutBuffer *buf); - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); EnumDeclaration *isEnumDeclaration() { return this; } @@ -92,7 +93,7 @@ struct EnumMember : Dsymbol void emitComment(Scope *sc); void toJsonBuffer(OutBuffer *buf); - void toDocBuffer(OutBuffer *buf); + void toDocBuffer(OutBuffer *buf, Scope *sc); EnumMember *isEnumMember() { return this; } }; diff --git a/dmd2/expression.c b/dmd2/expression.c index 5140464b..29f87d52 100644 --- a/dmd2/expression.c +++ b/dmd2/expression.c @@ -50,7 +50,7 @@ extern "C" char * __cdecl __locale_decpoint; #include "doc.h" #if IN_DMD -Expression *createTypeInfoArray(Scope *sc, Expression *args[], unsigned dim); +Expression *createTypeInfoArray(Scope *sc, Expression *args[], size_t dim); #endif Expression *expandVar(int result, VarDeclaration *v); @@ -245,6 +245,17 @@ Expression *resolveProperties(Scope *sc, Expression *e) ethis = dte->e1; goto L1; } + else if (e->op == TOKimport) + { + Dsymbol *s = ((ScopeExp *)e)->sds; + td = s->isTemplateDeclaration(); + if (td) + { + targsi = NULL; + ethis = NULL; + goto L1; + } + } else if (e->op == TOKtemplate) { td = ((TemplateExp *)e)->td; @@ -269,7 +280,8 @@ Expression *resolveProperties(Scope *sc, Expression *e) goto return_expr; } - if (e->type) + if (e->type && + e->op != TOKtype) // function type is not a property { Type *t = e->type->toBasetype(); @@ -389,6 +401,9 @@ Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NU if (e2) { + // run semantic without gagging + e2 = e2->semantic(sc); + /* .f(e1) = e2 */ Expression *ex = e->syntaxCopy(); @@ -437,6 +452,48 @@ Expression *resolveUFCSProperties(Scope *sc, Expression *e1, Expression *e2 = NU return e; } +/********************************* + * Attempt to find a type property. If failed, attempt to find + * UFCS property. If UFCS found, return expression. Otherwise + * show type property error message. + * Returns non-NULL only if UFCS property found. + */ +Expression * resolveProperty(Scope *sc, Expression **e1, Expression *e2) +{ + enum TOK op = (*e1)->op; + UnaExp *una = (UnaExp *)(*e1); + Type *t = una->e1->type; + int olderrors = global.errors; + una->e1 = una->e1->semantic(sc); + if (global.errors == olderrors && una->e1->type) + { + unsigned errors = global.startGagging(); + // try property gagged + if (op == TOKdotti) + *e1 = ((DotTemplateInstanceExp *)una)->semantic(sc, 1); + else if (op == TOKdot) + *e1 = ((DotIdExp *)una)->semantic(sc, 1); + + if (global.endGagging(errors) || (*e1)->op == TOKerror) + { + (*e1)->op = op; + errors = global.startGagging(); // try UFCS gagged + Expression *e = resolveUFCSProperties(sc, una, e2); + if (!global.endGagging(errors) && (*e1)->op != TOKerror) + return e; // found UFCS + + // try property non-gagged + una->type = t; // restore type + if (op == TOKdotti) + *e1 = ((DotTemplateInstanceExp *)una)->semantic(sc, 1); + else if (op == TOKdot) + *e1 = ((DotIdExp *)una)->semantic(sc, 1); + } + } + + return NULL; +} + /****************************** * Perform semantic() on an array of Expressions. */ @@ -552,7 +609,7 @@ TupleDeclaration *isAliasThisTuple(Expression *e) return NULL; } -int expandAliasThisTuples(Expressions *exps, int starti) +int expandAliasThisTuples(Expressions *exps, size_t starti) { if (!exps || exps->dim == 0) return -1; @@ -585,7 +642,7 @@ int expandAliasThisTuples(Expressions *exps, int starti) printf("\texps[%d] e = %s %s\n", i, Token::tochars[e->op], e->toChars()); } #endif - return u; + return (int)u; } } @@ -636,7 +693,7 @@ Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt Type *t0 = NULL; Expression *e0; - int j0; + size_t j0; for (size_t i = 0; i < exps->dim; i++) { Expression *e = (*exps)[i]; @@ -646,6 +703,8 @@ Expressions *arrayExpressionToCommonType(Scope *sc, Expressions *exps, Type **pt e = new ErrorExp(); } + e = callCpCtor(e->loc, sc, e, 1); + if (t0) { if (t0 != e->type) { @@ -783,6 +842,27 @@ void valueNoDtor(Expression *e) } } +/******************************************** + * Determine if t is an array of structs that need a postblit. + */ +#if DMDV2 +int checkPostblit(Loc loc, Type *t) +{ + t = t->toBasetype(); + while (t->ty == Tsarray) + t = t->nextOf()->toBasetype(); + if (t->ty == Tstruct) + { FuncDeclaration *fd = ((TypeStruct *)t)->sym->postblit; + if (fd) + { if (fd->storage_class & STCdisable) + fd->toParent()->error(loc, "is not copyable because it is annotated with @disable"); + return 1; + } + } + return 0; +} +#endif + /********************************************* * Call copy constructor for struct value argument. */ @@ -808,7 +888,7 @@ Expression *callCpCtor(Loc loc, Scope *sc, Expression *e, int noscope) if (tv->ty == Tstruct) { StructDeclaration *sd = ((TypeStruct *)tv)->sym; - if (sd->cpctor) + if (sd->cpctor && e->isLvalue()) { /* Create a variable tmp, and replace the argument e with: * (tmp = e),tmp @@ -862,14 +942,19 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, { TemplateInstance *spec = fd->isSpeculative(); int olderrs = global.errors; + // If it isn't speculative, we need to show errors + unsigned oldgag = global.gag; + if (global.gag && !spec) + global.gag = 0; fd->semantic3(fd->scope); + global.gag = oldgag; // Update the template instantiation with the number // of errors which occured. if (spec && global.errors != olderrs) spec->errors = global.errors - olderrs; } - unsigned n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) + size_t n = (nargs > nparams) ? nargs : nparams; // n = max(nargs, nparams) unsigned wildmatch = 0; if (ethis && tf->isWild()) @@ -962,7 +1047,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, t = t->semantic(loc, sc); bool isSafe = fd ? fd->isSafe() : tf->trust == TRUSTsafe; VarDeclaration *v = new VarDeclaration(loc, t, id, - isSafe ? NULL : new VoidInitializer(loc)); + (isSafe && sc->func) ? NULL : new VoidInitializer(loc)); v->storage_class |= STCctfe; v->semantic(sc); v->parent = sc->parent; @@ -973,8 +1058,14 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, for (size_t u = i; u < nargs; u++) { Expression *a = (*arguments)[u]; - if (tret && !((TypeArray *)tb)->next->equals(a->type)) - a = a->toDelegate(sc, tret); + TypeArray *ta = (TypeArray *)tb; + if (tret && !ta->next->equals(a->type)) + { if (tret->toBasetype()->ty == Tvoid || + a->implicitConvTo(tret)) + { + a = a->toDelegate(sc, tret); + } + } Expression *e = new VarExp(loc, v); e = new IndexExp(loc, e, new IntegerExp(u + 1 - nparams)); @@ -1075,41 +1166,14 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, } if (p->storageClass & STCref) { - if (arg->op == TOKstructliteral) - { - Identifier *idtmp = Lexer::uniqueId("__tmpsl"); - VarDeclaration *tmp = new VarDeclaration(loc, arg->type, idtmp, new ExpInitializer(0, arg)); - tmp->storage_class |= STCctfe; - Expression *ae = new DeclarationExp(loc, tmp); - Expression *e = new CommaExp(loc, ae, new VarExp(loc, tmp)); - e = e->semantic(sc); - - arg = e; - } - else if (arg->op == TOKcall) - { - CallExp *ce = (CallExp *)arg; - if (ce->e1->op == TOKdotvar && - ((DotVarExp *)ce->e1)->var->isCtorDeclaration()) - { - DotVarExp *dve = (DotVarExp *)ce->e1; - assert(dve->e1->op == TOKcomma); - assert(((CommaExp *)dve->e1)->e2->op == TOKvar); - VarExp *ve = (VarExp *)((CommaExp *)dve->e1)->e2; - VarDeclaration *tmp = ve->var->isVarDeclaration(); - - arg = new CommaExp(arg->loc, arg, new VarExp(loc, tmp)); - arg = arg->semantic(sc); - } - else - arg = arg->toLvalue(sc, arg); - } - else - arg = arg->toLvalue(sc, arg); + arg = arg->toLvalue(sc, arg); } else if (p->storageClass & STCout) { - arg = arg->modifiableLvalue(sc, arg); + Type *t = arg->type; + if (!t->isMutable() || !t->isAssignable(1)) // check blit assignable + arg->error("cannot modify struct %s with immutable members", arg->toChars()); + arg = arg->toLvalue(sc, arg); } else if (p->storageClass & STClazy) { // Convert lazy argument to a delegate @@ -1118,11 +1182,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, else { Type *tb = arg->type->toBasetype(); - if (arg->op == TOKarrayliteral) - { - arg = callCpCtor(loc, sc, arg, 1); - } - else if (tb->ty == Tsarray) + if (tb->ty == Tsarray) { #if !SARRAYVALUE && !IN_LLVM // Convert static arrays to pointers @@ -1190,6 +1250,7 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, } } #endif + arg = arg->optimize(WANTvalue, (p->storageClass & (STCref | STCout)) != 0); } else { @@ -1269,8 +1330,8 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, } #endif arg->rvalue(); + arg = arg->optimize(WANTvalue); } - arg = arg->optimize(WANTvalue); L3: (*arguments)[i] = arg; } @@ -1338,7 +1399,7 @@ void argsToCBuffer(OutBuffer *buf, Expressions *expressions, HdrGenState *hgs) { Expression *e = (*expressions)[i]; if (i) - buf->writeByte(','); + buf->writestring(", "); if (e) expToCBuffer(buf, hgs, e, PREC_assign); } @@ -1487,6 +1548,17 @@ void Expression::warning(const char *format, ...) } } +void Expression::deprecation(const char *format, ...) +{ + if (type != Type::terror) + { + va_list ap; + va_start(ap, format); + ::vdeprecation(loc, format, ap); + va_end( ap ); + } +} + int Expression::rvalue() { if (type && type->toBasetype()->ty == Tvoid) @@ -1598,8 +1670,8 @@ Expression *Expression::modifiableLvalue(Scope *sc, Expression *e) // See if this expression is a modifiable lvalue (i.e. not const) #if DMDV2 - if (type && (!type->isMutable() || !type->isAssignable())) - { error("%s is not mutable", e->toChars()); + if (type && !type->isMutable()) + { e->error("%s is not mutable", e->toChars()); return new ErrorExp(); } #endif @@ -1671,7 +1743,7 @@ void Expression::checkDeprecated(Scope *sc, Dsymbol *s) void Expression::checkPurity(Scope *sc, FuncDeclaration *f) { #if 1 - if (sc->func) + if (sc->func && !sc->intypeof && !(sc->flags & SCOPEdebug)) { /* Given: * void f() @@ -1686,37 +1758,70 @@ void Expression::checkPurity(Scope *sc, FuncDeclaration *f) * g() can call h() but not f() * i() can call h() and g() but not f() */ - FuncDeclaration *outerfunc = sc->func; + // Find the closest pure parent of the calling function - while (outerfunc->toParent2() && - !outerfunc->isPureBypassingInference() && + FuncDeclaration *outerfunc = sc->func; + while ( outerfunc->toParent2() && + !outerfunc->isPureBypassingInference() && outerfunc->toParent2()->isFuncDeclaration()) { outerfunc = outerfunc->toParent2()->isFuncDeclaration(); } + // Find the closest pure parent of the called function + if (getFuncTemplateDecl(f)) + { // The closest pure parent of instantiated template function is + // always itself. + if (!f->isPure() && outerfunc->setImpure()) + error("pure function '%s' cannot call impure function '%s'", + outerfunc->toChars(), f->toChars()); + return; + } FuncDeclaration *calledparent = f; - while (calledparent->toParent2() && !calledparent->isPureBypassingInference() - && calledparent->toParent2()->isFuncDeclaration() ) + while ( calledparent->toParent2() && + !calledparent->isPureBypassingInference() && + calledparent->toParent2()->isFuncDeclaration()) { calledparent = calledparent->toParent2()->isFuncDeclaration(); } + + /* Both escape!allocator and escapeImpl!allocator are impure at [a], + * but they are nested template function that instantiated in test(). + * Then calling them from [a] doesn't break purity. + * It's similar to normal impure nested function inside pure function. + * + * auto escapeImpl(alias fun)() { + * return fun(); + * } + * auto escape(alias fun)() { + * return escape!fun(); + * } + * pure string test() { + * char[] allocator() { return new char[1]; } // impure + * return escape!allocator(); // [a] + * } + */ + if (getFuncTemplateDecl(outerfunc) && + outerfunc->toParent2() == calledparent && + f != calledparent) + { + return; + } + // If the caller has a pure parent, then either the called func must be pure, // OR, they must have the same pure parent. if (/*outerfunc->isPure() &&*/ // comment out because we deduce purity now - !sc->intypeof && - !(sc->flags & SCOPEdebug) && - !(f->isPure() || (calledparent == outerfunc))) + !f->isPure() && calledparent != outerfunc) { if (outerfunc->setImpure()) error("pure function '%s' cannot call impure function '%s'", - outerfunc->toChars(), f->toChars()); + outerfunc->toPrettyChars(), f->toPrettyChars()); } } #else if (sc->func && sc->func->isPure() && !sc->intypeof && !f->isPure()) error("pure function '%s' cannot call impure function '%s'", - sc->func->toChars(), f->toChars()); + sc->func->toPrettyChars(), f->toPrettyChars()); #endif } @@ -1736,7 +1841,9 @@ void Expression::checkPurity(Scope *sc, VarDeclaration *v, Expression *ethis) !(sc->flags & SCOPEdebug) && // allow violations inside debug conditionals v->ident != Id::ctfe && // magic variable never violates pure and safe !v->isImmutable() && // always safe and pure to access immutables... - !(v->isConst() && v->isDataseg() && !v->type->hasPointers()) && // const global value types are immutable + !(v->isConst() && !v->isRef() && (v->isDataseg() || v->isParameter()) && + v->type->implicitConvTo(v->type->invariantOf())) && + // or const global/parameter values which have no mutable indirections !(v->storage_class & STCmanifest) // ...or manifest constants ) { @@ -1808,12 +1915,64 @@ void Expression::checkSafety(Scope *sc, FuncDeclaration *f) !f->isSafe() && !f->isTrusted()) { if (sc->func->setUnsafe()) + { + if (loc.linnum == 0) // e.g. implicitly generated dtor + loc = sc->func->loc; + error("safe function '%s' cannot call system function '%s'", - sc->func->toChars(), f->toChars()); + sc->func->toPrettyChars(), f->toPrettyChars()); + } } } #endif +void Expression::checkModifiable(Scope *sc) +{ + assert(type); + + /* We should call checkCtorInit() first, because this rejects + the modifying parameter and result inside contract. + */ + if (!checkCtorInit(sc)) + { + if (type->isMutable()) + { + if (!type->isAssignable()) + { + error("cannot modify struct %s %s with immutable members", toChars(), type->toChars()); + } + } + else + { + Declaration *var = NULL; + if (op == TOKvar) + var = ((VarExp *)this)->var; + else if (op == TOKdotvar) + var = ((DotVarExp *)this)->var; + if (var && var->storage_class & STCctorinit) + { + const char *p = var->isStatic() ? "static " : ""; + error("can only initialize %sconst member %s inside %sconstructor", + p, var->toChars(), p); + } + else + { + OutBuffer buf; + MODtoBuffer(&buf, type->mod); + error("cannot modify %s expression %s", buf.toChars(), toChars()); + } + } + } +} + +/*************************************** + * Return !=0 if expression is a part of initializing. + */ + +int Expression::checkCtorInit(Scope *sc) +{ + return FALSE; +} /***************************** * Check that expression can be tested for true or false. @@ -2245,7 +2404,7 @@ void IntegerExp::toCBuffer(OutBuffer *buf, HdrGenState *hgs) case Tuns32: L3: - buf->printf("%du", (unsigned)v); + buf->printf("%uu", (unsigned)v); break; case Tint64: @@ -2449,8 +2608,11 @@ void floatToBuffer(OutBuffer *buf, Type *type, real_t value) * to decimal then back again. If it matches, use it. * If it doesn't, fall back to hex, which is * always exact. + * Longest string is for -real.max: + * "-1.18973e+4932\0".length == 17 + * "-0xf.fffffffffffffffp+16380\0".length == 28 */ - char buffer[25]; + char buffer[32]; ld_sprint(buffer, 'g', value); assert(strlen(buffer) < sizeof(buffer)); #if _WIN32 && __DMC__ @@ -2875,8 +3037,6 @@ Lagain: FuncDeclaration *f; FuncLiteralDeclaration *fld; OverloadSet *o; - ClassDeclaration *cd; - ClassDeclaration *thiscd = NULL; Import *imp; Package *pkg; Type *t; @@ -2893,9 +3053,6 @@ Lagain: if (s != olds && !s->isFuncDeclaration()) checkDeprecated(sc, s); - if (sc->func) - thiscd = sc->func->parent->isClassDeclaration(); - // BUG: This should happen after overload resolution for functions, not before if (s->needThis()) { @@ -2963,7 +3120,7 @@ Lagain: } f = s->isFuncDeclaration(); if (f) - { //printf("'%s' is a function\n", f->toChars()); + { f = f->toAliasFunc(); if (!f->originalType && f->scope) // semantic not yet run { @@ -2975,11 +3132,17 @@ Lagain: } // if inferring return type, sematic3 needs to be run - if (f->inferRetType && f->scope && f->type && !f->type->nextOf()) + if (f->scope && (f->inferRetType && f->type && !f->type->nextOf() || + getFuncTemplateDecl(f))) { TemplateInstance *spec = f->isSpeculative(); int olderrs = global.errors; + // If it isn't speculative, we need to show errors + unsigned oldgag = global.gag; + if (global.gag && !spec) + global.gag = 0; f->semantic3(f->scope); + global.gag = oldgag; // Update the template instantiation with the number // of errors which occured. if (spec && global.errors != olderrs) @@ -2996,22 +3159,15 @@ Lagain: error("forward reference to %s", toChars()); return new ErrorExp(); } - return new VarExp(loc, f, hasOverloads); + FuncDeclaration *fd = s->isFuncDeclaration(); + fd->type = f->type; + return new VarExp(loc, fd, hasOverloads); } o = s->isOverloadSet(); if (o) { //printf("'%s' is an overload set\n", o->toChars()); return new OverExp(o); } - cd = s->isClassDeclaration(); - if (cd && thiscd && cd->isBaseOf(thiscd, NULL) && sc->func->needThis()) - { - // We need to add an implicit 'this' if cd is this class or a base class. - DotTypeExp *dte; - - dte = new DotTypeExp(loc, new ThisExp(loc), s); - return dte->semantic(sc); - } imp = s->isImport(); if (imp) { @@ -3230,6 +3386,13 @@ Expression *ThisExp::toLvalue(Scope *sc, Expression *e) return this; } +Expression *ThisExp::modifiableLvalue(Scope *sc, Expression *e) +{ + if (type->toBasetype()->ty == Tclass) + error("Cannot modify '%s'", toChars()); + return Expression::modifiableLvalue(sc, e); +} + /******************************** SuperExp **************************/ SuperExp::SuperExp(Loc loc) @@ -3594,10 +3757,10 @@ int StringExp::compare(Object *obj) assert(se2->op == TOKstring); - int len1 = len; - int len2 = se2->len; + size_t len1 = len; + size_t len2 = se2->len; - //printf("sz = %d, len1 = %d, len2 = %d\n", sz, len1, len2); + //printf("sz = %d, len1 = %d, len2 = %d\n", sz, (int)len1, (int)len2); if (len1 == len2) { switch (sz) @@ -3606,11 +3769,11 @@ int StringExp::compare(Object *obj) return memcmp((char *)string, (char *)se2->string, len1); case 2: - { unsigned u; + { d_wchar *s1 = (d_wchar *)string; d_wchar *s2 = (d_wchar *)se2->string; - for (u = 0; u < len; u++) + for (size_t u = 0; u < len; u++) { if (s1[u] != s2[u]) return s1[u] - s2[u]; @@ -3618,11 +3781,11 @@ int StringExp::compare(Object *obj) } case 4: - { unsigned u; + { d_dchar *s1 = (d_dchar *)string; d_dchar *s2 = (d_dchar *)se2->string; - for (u = 0; u < len; u++) + for (size_t u = 0; u < len; u++) { if (s1[u] != s2[u]) return s1[u] - s2[u]; @@ -3634,7 +3797,7 @@ int StringExp::compare(Object *obj) assert(0); } } - return len1 - len2; + return (int)(len1 - len2); } int StringExp::isBool(int result) @@ -3660,7 +3823,7 @@ Expression *StringExp::toLvalue(Scope *sc, Expression *e) Expression *StringExp::modifiableLvalue(Scope *sc, Expression *e) { - error("Cannot modify '%s'", toChars()); + e->error("Cannot modify '%s'", toChars()); return new ErrorExp(); } @@ -3730,7 +3893,7 @@ void StringExp::toMangleBuffer(OutBuffer *buf) unsigned c; size_t u; unsigned char *q; - unsigned qlen; + size_t qlen; /* Write string in UTF-8 format */ @@ -3771,7 +3934,7 @@ void StringExp::toMangleBuffer(OutBuffer *buf) } buf->reserve(1 + 11 + 2 * qlen); buf->writeByte(m); - buf->printf("%d_", qlen); // nbytes <= 11 + buf->printf("%d_", (int)qlen); // nbytes <= 11 for (unsigned char *p = buf->data + buf->offset, *pend = p + 2 * qlen; p < pend; p += 2, ++q) @@ -3989,6 +4152,8 @@ StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions * : Expression(loc, TOKstructliteral, sizeof(StructLiteralExp)) { this->sd = sd; + if (!elements) + elements = new Expressions(); this->elements = elements; this->stype = stype; #if IN_DMD @@ -3998,6 +4163,7 @@ StructLiteralExp::StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions * this->soffset = 0; this->fillHoles = 1; this->ownedByCtfe = false; + this->ctorinit = 0; #if IN_LLVM constType = NULL; #endif @@ -4061,6 +4227,8 @@ Expression *StructLiteralExp::semantic(Scope *sc) } e = e->implicitCastTo(sc, telem); + if (e->op == TOKerror) + return e; (*elements)[i] = e; } @@ -4096,10 +4264,14 @@ Expression *StructLiteralExp::semantic(Scope *sc) // remove v->scope (see bug 3426) // but not if gagged, for we might be called again. if (!global.gag) - v->scope = NULL; + { v->scope = NULL; + v->init = i2; // save result + } } } } + else if (v->type->needsNested() && ctorinit) + e = v->type->defaultInit(loc); else e = v->type->defaultInitLiteral(loc); offset = v->offset + v->type->size(); @@ -4156,7 +4328,7 @@ Expression *StructLiteralExp::getField(Type *type, unsigned offset) uinteger_t length = tsa->dim->toInteger(); Expressions *z = new Expressions; z->setDim(length); - for (int q = 0; q < length; ++q) + for (size_t q = 0; q < length; ++q) (*z)[q] = e->copy(); e = new ArrayLiteralExp(loc, z); e->type = type; @@ -4166,6 +4338,14 @@ Expression *StructLiteralExp::getField(Type *type, unsigned offset) e = e->copy(); e->type = type; } +#if !IN_LLVM + if (sinit && e->op == TOKstructliteral && + e->type->needsNested()) + { + StructLiteralExp *se = (StructLiteralExp *)e; + se->sinit = se->sd->toInitializer(); + } +#endif } } return e; @@ -4193,7 +4373,7 @@ int StructLiteralExp::getFieldIndex(Type *type, unsigned offset) { Expression *e = (*elements)[i]; if (e) { - return i; + return (int)i; } break; } @@ -4504,6 +4684,8 @@ Lagain: { TypeClass *tc = (TypeClass *)(tb); ClassDeclaration *cd = tc->sym->isClassDeclaration(); + if (cd->scope) + cd->semantic(NULL); if (cd->isInterfaceDeclaration()) { error("cannot create instance of interface %s", cd->toChars()); goto Lerr; @@ -4682,12 +4864,39 @@ Lagain: { TypeStruct *ts = (TypeStruct *)tb; StructDeclaration *sd = ts->sym; - TypeFunction *tf; - + if (sd->scope) + sd->semantic(NULL); if (sd->noDefaultCtor && (!arguments || !arguments->dim)) { error("default construction is disabled for type %s", sd->toChars()); goto Lerr; } + + if (sd->aggNew) + { + // Prepend the uint size argument to newargs[] + Expression *e = new IntegerExp(loc, sd->size(loc), Type::tuns32); + if (!newargs) + newargs = new Expressions(); + newargs->shift(e); + + FuncDeclaration *f = sd->aggNew->overloadResolve(loc, NULL, newargs); + allocator = f->isNewDeclaration(); + assert(allocator); + + TypeFunction *tf = (TypeFunction *)f->type; + unsigned olderrors = global.errors; + functionParameters(loc, sc, tf, NULL, newargs, f); + if (olderrors != global.errors) + return new ErrorExp(); + } + else + { + if (newargs && newargs->dim) + { error("no allocator for %s", sd->toChars()); + goto Lerr; + } + } + FuncDeclaration *f = NULL; if (sd->ctor) f = resolveFuncCall(sc, loc, sd->ctor, NULL, NULL, arguments, 0); @@ -4699,7 +4908,7 @@ Lagain: sd->accessCheck(loc, sc, member); - tf = (TypeFunction *)f->type; + TypeFunction *tf = (TypeFunction *)f->type; type = tf->next; if (!arguments) @@ -4708,49 +4917,32 @@ Lagain: functionParameters(loc, sc, tf, NULL, arguments, f); if (olderrors != global.errors) return new ErrorExp(); - } - else + else if (arguments && arguments->dim) { - if (arguments && arguments->dim) - { error("no constructor for %s", sd->toChars()); - goto Lerr; - } - } + Type *tptr = type->pointerTo(); + /* Rewrite: + * new S(arguments) + * as: + * (((S* __newsl = new S()), (*__newsl = S(arguments))), __newsl) + */ + Identifier *id = Lexer::uniqueId("__newsl"); + ExpInitializer *ei = new ExpInitializer(loc, this); + VarDeclaration *v = new VarDeclaration(loc, tptr, id, ei); + v->storage_class |= STCctfe; + Expression *e = new DeclarationExp(loc, v); + Expression *ve = new VarExp(loc, v); + Expression *se = new StructLiteralExp(loc, sd, arguments, type); + Expression *ae = new ConstructExp(loc, new PtrExp(loc, ve), se); + e = new CommaExp(loc, e, ae); + e = new CommaExp(loc, e, ve); - if (sd->aggNew) - { - // Prepend the uint size argument to newargs[] - Expression *e = new IntegerExp(loc, sd->size(loc), Type::tuns32); - if (!newargs) - newargs = new Expressions(); - newargs->shift(e); + // rewrite this + this->arguments = NULL; + this->type = tptr; - f = sd->aggNew->overloadResolve(loc, NULL, newargs); - allocator = f->isNewDeclaration(); - assert(allocator); - - tf = (TypeFunction *)f->type; - unsigned olderrors = global.errors; - functionParameters(loc, sc, tf, NULL, newargs, f); - if (olderrors != global.errors) - return new ErrorExp(); - -#if 0 - e = new VarExp(loc, f); - e = new CallExp(loc, e, newargs); - e = e->semantic(sc); - e->type = type->pointerTo(); - return e; -#endif - } - else - { - if (newargs && newargs->dim) - { error("no allocator for %s", sd->toChars()); - goto Lerr; - } + return e->semantic(sc); } type = type->pointerTo(); @@ -5070,6 +5262,11 @@ void VarExp::checkEscapeRef() } } +int VarExp::checkCtorInit(Scope *sc) +{ + return var->checkModify(loc, sc, type); +} + int VarExp::isLvalue() { @@ -5092,6 +5289,11 @@ Expression *VarExp::toLvalue(Scope *sc, Expression *e) { error("lazy variables cannot be lvalues"); return new ErrorExp(); } + if (var->ident == Id::ctfe) + { + error("compiler-generated variable __ctfe is not an lvalue"); + return new ErrorExp(); + } return this; } @@ -5103,15 +5305,15 @@ Expression *VarExp::modifiableLvalue(Scope *sc, Expression *e) #if (BUG6652 == 1) VarDeclaration *v = var->isVarDeclaration(); - if (v && (v->storage_class & STCbug6652) && global.params.warnings) - warning("Variable modified in foreach body requires ref storage class"); + if (v && (v->storage_class & STCbug6652) && v->type->isMutable()) + warning("variable modified in foreach body requires ref storage class"); #elif (BUG6652 == 2) VarDeclaration *v = var->isVarDeclaration(); - if (v && (v->storage_class & STCbug6652) && !global.params.useDeprecated) - error("Variable modified in foreach body requires ref storage class"); + if (v && (v->storage_class & STCbug6652) && v->type->isMutable()) + deprecation("variable modified in foreach body requires ref storage class"); #endif - var->checkModify(loc, sc, type); + checkModifiable(sc); // See if this expression is a modifiable lvalue (i.e. not const) return toLvalue(sc, e); @@ -5327,9 +5529,6 @@ Expression *FuncExp::semantic(Scope *sc) (fd->type && fd->type->ty == Tfunction && !fd->type->nextOf())) { fd->semantic3(sc); - - if ( (olderrors == global.errors) && global.params.useInline) - fd->inlineScan(); } } @@ -5465,11 +5664,17 @@ Expression *DeclarationExp::semantic(Scope *sc) */ Dsymbol *s = declaration; - AttribDeclaration *ad = declaration->isAttribDeclaration(); - if (ad) + while (1) { - if (ad->decl && ad->decl->dim == 1) - s = (*ad->decl)[0]; + AttribDeclaration *ad = s->isAttribDeclaration(); + if (ad) + { + if (ad->decl && ad->decl->dim == 1) + { s = (*ad->decl)[0]; + continue; + } + } + break; } if (s->isVarDeclaration()) @@ -5500,7 +5705,7 @@ Expression *DeclarationExp::semantic(Scope *sc) s->toPrettyChars(), sc->func->toChars()); return new ErrorExp(); } - else if (!global.params.useDeprecated) + else { // Disallow shadowing for (Scope *scx = sc->enclosing; scx && scx->func == sc->func; scx = scx->enclosing) @@ -5510,7 +5715,7 @@ Expression *DeclarationExp::semantic(Scope *sc) (s2 = scx->scopesym->symtab->lookup(s->ident)) != NULL && s != s2) { - error("shadowing declaration %s is deprecated", s->toPrettyChars()); + error("is shadowing declaration %s", s->toPrettyChars()); return new ErrorExp(); } } @@ -5534,9 +5739,6 @@ Expression *DeclarationExp::semantic(Scope *sc) if (global.errors == olderrors) { declaration->semantic3(sc); - - if ((global.errors == olderrors) && global.params.useInline) - declaration->inlineScan(); } } @@ -5794,8 +5996,7 @@ Expression *IsExp::semantic(Scope *sc) break; case TOKinvariant: - if (!global.params.useDeprecated) - error("use of 'invariant' rather than 'immutable' is deprecated"); + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); case TOKimmutable: if (!targ->isImmutable()) goto Lno; @@ -6432,7 +6633,7 @@ Expression *CompileExp::semantic(Scope *sc) return e1; if (!e1->type->isString()) { - error("argument to mixin must be a string type, not %s\n", e1->type->toChars()); + error("argument to mixin must be a string type, not %s", e1->type->toChars()); return new ErrorExp(); } e1 = e1->ctfeInterpret(); @@ -6701,6 +6902,7 @@ Expression *DotIdExp::semantic(Scope *sc, int flag) case TOKimport: ds = ((ScopeExp *)e1)->sds; goto L1; case TOKvar: ds = ((VarExp *)e1)->var; goto L1; case TOKdotvar: ds = ((DotVarExp *)e1)->var; goto L1; + default: break; L1: char* s = ds->mangle(); e = new StringExp(loc, s, strlen(s), 'c'); @@ -6717,8 +6919,7 @@ Expression *DotIdExp::semantic(Scope *sc, int flag) } else { - if (e1->op != TOKtype) - e1 = resolveProperties(sc, e1); + e1 = resolveProperties(sc, e1); eleft = NULL; eright = e1; } @@ -6907,7 +7108,7 @@ Expression *DotIdExp::semantic(Scope *sc, int flag) error("undefined identifier '%s'", ident->toChars()); return new ErrorExp(); } - else if (t1b->ty == Tpointer && + else if (t1b->ty == Tpointer && e1->type->ty != Tenum && ident != Id::init && ident != Id::__sizeof && ident != Id::__xalignof && ident != Id::offsetof && ident != Id::mangleof && ident != Id::stringof) @@ -6960,7 +7161,14 @@ Expression *DotIdExp::semantic(Scope *sc, int flag) if (global.endGagging(errors)) // if failed to find the property { e1->type = t1; // kludge to restore type + errors = global.startGagging(); e = resolveUFCSProperties(sc, this); + if (global.endGagging(errors)) + { + // both lookups failed, lookup property again for better error message + e1->type = t1; // restore type + e = t1->dotExp(sc, e1, ident); + } } e = e->semantic(sc); return e; @@ -7034,33 +7242,42 @@ Expression *DotVarExp::semantic(Scope *sc) exps->reserve(tup->objects->dim); for (size_t i = 0; i < tup->objects->dim; i++) { Object *o = (*tup->objects)[i]; - if (o->dyncast() != DYNCAST_EXPRESSION) + Expression *e; + if (o->dyncast() == DYNCAST_EXPRESSION) + { + e = (Expression *)o; + if (e->op == TOKdsymbol) + { + Dsymbol *s = ((DsymbolExp *)e)->s; + if (i == 0 && sc->func && tup->objects->dim > 1 && + e1->hasSideEffect()) + { + Identifier *id = Lexer::uniqueId("__tup"); + ExpInitializer *ei = new ExpInitializer(e1->loc, e1); + VarDeclaration *v = new VarDeclaration(e1->loc, NULL, id, ei); + v->storage_class |= STCctfe | STCref | STCforeach; + + ev = new VarExp(e->loc, v); + e = new CommaExp(e1->loc, new DeclarationExp(e1->loc, v), ev); + e = new DotVarExp(loc, e, s->isDeclaration()); + } + else + e = new DotVarExp(loc, ev, s->isDeclaration()); + } + } + else if (o->dyncast() == DYNCAST_DSYMBOL) + { + e = new DsymbolExp(loc, (Dsymbol *)o); + } + else if (o->dyncast() == DYNCAST_TYPE) + { + e = new TypeExp(loc, (Type *)o); + } + else { error("%s is not an expression", o->toChars()); goto Lerr; } - - Expression *e = (Expression *)o; - if (e->op != TOKdsymbol) - { error("%s is not a member", e->toChars()); - goto Lerr; - } - - Dsymbol *s = ((DsymbolExp *)e)->s; - if (i == 0 && sc->func && tup->objects->dim > 1 && - e1->hasSideEffect()) - { - Identifier *id = Lexer::uniqueId("__tup"); - ExpInitializer *ei = new ExpInitializer(e1->loc, e1); - VarDeclaration *v = new VarDeclaration(e1->loc, NULL, id, ei); - v->storage_class |= STCctfe | STCref | STCforeach; - - ev = new VarExp(e->loc, v); - e = new CommaExp(e1->loc, new DeclarationExp(e1->loc, v), ev); - e = new DotVarExp(loc, e, s->isDeclaration()); - } - else - e = new DotVarExp(loc, ev, s->isDeclaration()); exps->push(e); } Expression *e = new TupleExp(loc, exps); @@ -7118,6 +7335,14 @@ Lerr: return new ErrorExp(); } +int DotVarExp::checkCtorInit(Scope *sc) +{ + if (e1->op == TOKthis) + return modifyFieldVar(loc, sc, var->isVarDeclaration(), e1); + else + return e1->checkCtorInit(sc); +} + int DotVarExp::isLvalue() { @@ -7135,7 +7360,7 @@ Expression *DotVarExp::toLvalue(Scope *sc, Expression *e) * Mark variable v as modified if it is inside a constructor that var * is a field in. */ -void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) +int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) { //printf("modifyFieldVar(var = %s)\n", var->toChars()); Dsymbol *s = sc->func; @@ -7153,6 +7378,7 @@ void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) { var->ctorinit = 1; //printf("setting ctorinit\n"); + return TRUE; } else { @@ -7160,15 +7386,10 @@ void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) { s = s->toParent2(); continue; } - else if (var->storage_class & STCctorinit) - { - const char *p = var->isStatic() ? "static " : ""; - error(loc, "can only initialize %sconst member %s inside %sconstructor", - p, var->toChars(), p); - } } break; } + return FALSE; } Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e) @@ -7179,28 +7400,7 @@ Expression *DotVarExp::modifiableLvalue(Scope *sc, Expression *e) printf("var->type = %s\n", var->type->toChars()); #endif - Type *t1 = e1->type->toBasetype(); - - if (!t1->isMutable() || - (t1->ty == Tpointer && !t1->nextOf()->isMutable()) || - !var->type->isMutable() || - !var->type->isAssignable() || - var->storage_class & STCmanifest - ) - { - if (var->isCtorinit()) - { // It's only modifiable if inside the right constructor - modifyFieldVar(loc, sc, var->isVarDeclaration(), e1); - } - else - { - error("cannot modify const/immutable/inout expression %s", toChars()); - } - } - else if (var->storage_class & STCnodefaultctor) - { - modifyFieldVar(loc, sc, var->isVarDeclaration(), e1); - } + checkModifiable(sc); return this; } @@ -7275,15 +7475,15 @@ Expression *DotTemplateInstanceExp::semantic(Scope *sc, int flag) DotIdExp *die = new DotIdExp(loc, e1, ti->name); if (flag || !e1->type || e1->op == TOKtype || - e1->op == TOKimport && ((ScopeExp *)e1)->sds->isModule()) + (e1->op == TOKimport && ((ScopeExp *)e1)->sds->isModule())) { e = die->semantic(sc, 1); } else { Type *t1b = e1->type->toBasetype(); - if ((t1b->ty == Tarray || t1b->ty == Tsarray || t1b->ty == Taarray || - t1b->ty == Tnull || t1b->isTypeBasic() && t1b->ty != Tvoid)) + if (t1b->ty == Tarray || t1b->ty == Tsarray || t1b->ty == Taarray || + t1b->ty == Tnull || (t1b->isTypeBasic() && t1b->ty != Tvoid)) { /* No built-in type has templatized property, so can short cut. */ @@ -7292,9 +7492,17 @@ Expression *DotTemplateInstanceExp::semantic(Scope *sc, int flag) unsigned errors = global.startGagging(); e = die->semantic(sc, 1); + Type *t = e1->type; if (global.endGagging(errors)) { - return resolveUFCSProperties(sc, this); + errors = global.startGagging(); + e = resolveUFCSProperties(sc, this); + if (!global.endGagging(errors)) + return e; + + // both lookups failed, lookup property again for better error message + e->type = t; // restore type + e = die->semantic(sc, 1); } } @@ -7570,14 +7778,9 @@ Expression *CallExp::resolveUFCS(Scope *sc) return new ErrorExp(); } if (!e->type->isMutable()) - { const char *p = NULL; - if (e->type->isConst()) - p = "const"; - else if (e->type->isImmutable()) - p = "immutable"; - else - p = "inout"; - error("cannot remove key from %s associative array %s", p, e->toChars()); + { OutBuffer buf; + MODtoBuffer(&buf, e->type->mod); + error("cannot remove key from %s associative array %s", buf.toChars(), e->toChars()); return new ErrorExp(); } Expression *key = (*arguments)[0]; @@ -7607,7 +7810,7 @@ Expression *CallExp::resolveUFCS(Scope *sc) } } else if (t->ty == Tarray || t->ty == Tsarray || - t->ty == Tnull || t->isTypeBasic() && t->ty != Tvoid) + t->ty == Tnull || (t->isTypeBasic() && t->ty != Tvoid)) { /* In basic, built-in types don't have normal and templatized * member functions. So can short cut. @@ -7900,11 +8103,21 @@ Lagain: ad = ((TypeStruct *)t1)->sym; #if DMDV2 // First look for constructor - if (ad->ctor && arguments && arguments->dim) + if (e1->op == TOKtype && ad->ctor && (ad->noDefaultCtor || arguments && arguments->dim)) { // Create variable that will get constructed Identifier *idtmp = Lexer::uniqueId("__ctmp"); - VarDeclaration *tmp = new VarDeclaration(loc, t1, idtmp, NULL); + + ExpInitializer *ei = NULL; + if (t1->needsNested()) + { + Expressions *args = new Expressions(); + StructLiteralExp *se = new StructLiteralExp(loc, (StructDeclaration *)ad, args); + se->ctorinit = 1; + ei = new ExpInitializer(loc, se); + } + + VarDeclaration *tmp = new VarDeclaration(loc, t1, idtmp, ei); tmp->storage_class |= STCctfe; Expression *av = new DeclarationExp(loc, tmp); av = new CommaExp(loc, av, new VarExp(loc, tmp)); @@ -8162,6 +8375,8 @@ Lagain: error("constructor calls not allowed in loops or after labels"); if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor)) error("multiple constructor calls"); + if ((sc->callSuper & CSXreturn) && !(sc->callSuper & CSXany_ctor)) + error("an earlier return statement skips constructor"); sc->callSuper |= CSXany_ctor | CSXsuper_ctor; } @@ -8202,6 +8417,8 @@ Lagain: error("constructor calls not allowed in loops or after labels"); if (sc->callSuper & (CSXsuper_ctor | CSXthis_ctor)) error("multiple constructor calls"); + if ((sc->callSuper & CSXreturn) && !(sc->callSuper & CSXany_ctor)) + error("an earlier return statement skips constructor"); sc->callSuper |= CSXany_ctor | CSXthis_ctor; } @@ -8301,13 +8518,21 @@ Lagain: tierror->error("errors instantiating template"); // give better error message return new ErrorExp(); } - if (f->needThis() && hasThis(sc)) + if (f->needThis()) { - // Supply an implicit 'this', as in - // this.ident + if (hasThis(sc)) + { + // Supply an implicit 'this', as in + // this.ident - e1 = new DotTemplateExp(loc, (new ThisExp(loc))->semantic(sc), te->td); - goto Lagain; + e1 = new DotTemplateExp(loc, (new ThisExp(loc))->semantic(sc), te->td); + goto Lagain; + } + else if (!sc->intypeof && !sc->getStructClassScope()) + { + error("need 'this' for %s type %s", f->toChars(), f->type->toChars()); + return new ErrorExp(); + } } e1 = new VarExp(loc, f); @@ -8321,12 +8546,12 @@ Lagain: if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug)) { if (sc->func->setImpure()) - error("pure function '%s' cannot call impure %s '%s'", sc->func->toChars(), p, e1->toChars()); + error("pure function '%s' cannot call impure %s '%s'", sc->func->toPrettyChars(), p, e1->toChars()); } if (sc->func && tf->trust <= TRUSTsystem) { if (sc->func->setUnsafe()) - error("safe function '%s' cannot call system %s '%s'", sc->func->toChars(), p, e1->toChars()); + error("safe function '%s' cannot call system %s '%s'", sc->func->toPrettyChars(), p, e1->toChars()); } if (!tf->callMatch(NULL, arguments)) @@ -8371,23 +8596,55 @@ Lagain: assert(f); if (ve->hasOverloads) - f = f->overloadResolve(loc, NULL, arguments); + f = f->overloadResolve(loc, NULL, arguments, 2); + else + { + TypeFunction *tf = (TypeFunction *)f->type; + if (!tf->callMatch(NULL, arguments)) + { + OutBuffer buf; + + buf.writeByte('('); + if (arguments && arguments->dim) + { + HdrGenState hgs; + + argExpTypesToCBuffer(&buf, arguments, &hgs); + } + buf.writeByte(')'); + + //printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco); + ::error(loc, "%s %s is not callable using argument types %s", + e1->toChars(), Parameter::argsTypesToChars(tf->parameters, tf->varargs), + buf.toChars()); + + return new ErrorExp(); + } + } + + if (f->needThis()) + { + if (hasThis(sc)) + { + // Supply an implicit 'this', as in + // this.ident + + e1 = new DotVarExp(loc, (new ThisExp(loc))->semantic(sc), ve->var); + goto Lagain; + } + else if (!sc->intypeof && !sc->getStructClassScope()) + { + error("need 'this' for %s type %s", f->toChars(), f->type->toChars()); + return new ErrorExp(); + } + } + checkDeprecated(sc, f); #if DMDV2 checkPurity(sc, f); checkSafety(sc, f); #endif f->checkNestedReference(sc, loc); - - if (f->needThis() && hasThis(sc)) - { - // Supply an implicit 'this', as in - // this.ident - - e1 = new DotVarExp(loc, new ThisExp(loc), f); - goto Lagain; - } - accessCheck(loc, sc, NULL, f); ethis = NULL; @@ -8551,7 +8808,14 @@ Expression *AddrExp::semantic(Scope *sc) * otherwise the 'pure' is missing from the type assigned to x. */ - error("forward reference to %s", e1->toChars()); + if (e1->op == TOKvar) + { + VarExp *ve = (VarExp *)e1; + Declaration *d = ve->var; + error("forward reference to %s %s", d->kind(), d->toChars()); + } + else + error("forward reference to %s", e1->toChars()); return new ErrorExp(); } @@ -8567,7 +8831,12 @@ Expression *AddrExp::semantic(Scope *sc) { if (!dve->hasOverloads) f->tookAddressOf++; - Expression *e = new DelegateExp(loc, dve->e1, f, dve->hasOverloads); + + Expression *e; + if ( f->needThis()) + e = new DelegateExp(loc, dve->e1, f, dve->hasOverloads); + else // It is a function pointer. Convert &v.f() --> (v, &V.f()) + e = new CommaExp(loc, dve->e1, new AddrExp(loc, new VarExp(loc, f))); e = e->semantic(sc); return e; } @@ -8705,8 +8974,7 @@ 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()); + deprecation("using * on an array is deprecated; use *(%s).ptr instead", e1->toChars()); type = ((TypeArray *)tb)->next; e1 = e1->castTo(sc, type->pointerTo()); break; @@ -8722,18 +8990,26 @@ Expression *PtrExp::semantic(Scope *sc) return this; } +void PtrExp::checkEscapeRef() +{ + e1->checkEscape(); +} + +int PtrExp::checkCtorInit(Scope *sc) +{ + if (e1->op == TOKsymoff) + { SymOffExp *se = (SymOffExp *)e1; + return se->var->checkModify(loc, sc, type); + } + return FALSE; +} + int PtrExp::isLvalue() { return 1; } - -void PtrExp::checkEscapeRef() -{ - e1->checkEscape(); -} - Expression *PtrExp::toLvalue(Scope *sc, Expression *e) { #if 0 @@ -8751,13 +9027,8 @@ Expression *PtrExp::modifiableLvalue(Scope *sc, Expression *e) { //printf("PtrExp::modifiableLvalue() %s, type %s\n", toChars(), type->toChars()); - if (e1->op == TOKsymoff) - { SymOffExp *se = (SymOffExp *)e1; - se->var->checkModify(loc, sc, type); - //return toLvalue(sc, e); - } - - return Expression::modifiableLvalue(sc, e); + checkModifiable(sc); + return toLvalue(sc, e); } #endif @@ -8996,9 +9267,7 @@ Expression *DeleteExp::semantic(Scope *sc) IndexExp *ae = (IndexExp *)(e1); Type *tb1 = ae->e1->type->toBasetype(); if (tb1->ty == Taarray) - { if (!global.params.useDeprecated) - error("delete aa[key] deprecated, use aa.remove(key)"); - } + error("delete aa[key] deprecated, use aa.remove(key)"); } return this; @@ -9075,6 +9344,13 @@ Expression *CastExp::semantic(Scope *sc) to = to->semantic(loc, sc); if (to == Type::terror) return new ErrorExp(); + if (to->ty == Ttuple) + { + error("cannot cast %s to tuple type %s", e1->toChars(), to->toChars()); + return new ErrorExp(); + } + if (e1->type->ty == Terror) + return new ErrorExp(); if (!to->equals(e1->type)) { @@ -9139,11 +9415,8 @@ Expression *CastExp::semantic(Scope *sc) return new VectorExp(loc, e1, to); } - if (tob->isintegral() && t1b->ty == Tarray && - !global.params.useDeprecated) - { - error("casting %s to %s is deprecated", e1->type->toChars(), to->toChars()); - } + if (tob->isintegral() && t1b->ty == Tarray) + deprecation("casting %s to %s is deprecated", e1->type->toChars(), to->toChars()); } else if (!to) { error("cannot cast tuple"); @@ -9381,17 +9654,15 @@ Lagain: if (search_function(ad, Id::slice)) { // Rewrite as e1.slice(lwr, upr) - e = new DotIdExp(loc, e1, Id::slice); - - if (lwr) - { - assert(upr); - e = new CallExp(loc, e, lwr, upr); - } - else - { assert(!upr); - e = new CallExp(loc, e); + SliceExp *se = resolveOpDollar(sc, this); + Expressions *a = new Expressions(); + assert(!se->lwr || se->upr); + if (se->lwr) + { a->push(se->lwr); + a->push(se->upr); } + e = new DotIdExp(loc, se->e1, Id::slice); + e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } @@ -9478,7 +9749,7 @@ Lagain: { Expression *e = (*te->exps)[j1 + i]; (*exps)[i] = e; } - if (j1 > 0 && j2 - j1 > 0 && sc->func && (*te->exps)[0]->op == TOKdotvar) + if (j1 > 0 && j1 != j2 && sc->func && (*te->exps)[0]->op == TOKdotvar) { Expression *einit = ((DotVarExp *)(*te->exps)[0])->e1->isTemp(); if (einit) @@ -9536,6 +9807,17 @@ void SliceExp::checkEscapeRef() e1->checkEscapeRef(); } +int SliceExp::checkCtorInit(Scope *sc) +{ + if (e1->type->ty == Tsarray || + (e1->op == TOKindex && e1->type->ty != Tarray) || + e1->op == TOKslice) + { + return e1->checkCtorInit(sc); + } + return FALSE; +} + int SliceExp::isLvalue() { @@ -9806,6 +10088,11 @@ void CommaExp::checkEscapeRef() e2->checkEscapeRef(); } +int CommaExp::checkCtorInit(Scope *sc) +{ + return e2->checkCtorInit(sc); +} + int CommaExp::isLvalue() { @@ -9998,6 +10285,17 @@ Lerr: return new ErrorExp(); } +int IndexExp::checkCtorInit(Scope *sc) +{ + if (e1->type->ty == Tsarray || + (e1->op == TOKindex && e1->type->ty != Tarray) || + e1->op == TOKslice) + { + return e1->checkCtorInit(sc); + } + return FALSE; +} + int IndexExp::isLvalue() { @@ -10016,10 +10314,6 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) { //printf("IndexExp::modifiableLvalue(%s)\n", toChars()); modifiable = 1; - if (e1->op == TOKstring) - error("string literals are immutable"); - if (type && (!type->isMutable() || !type->isAssignable())) - error("%s isn't mutable", e->toChars()); Type *t1 = e1->type->toBasetype(); if (t1->ty == Taarray) { TypeAArray *taa = (TypeAArray *)t1; @@ -10028,6 +10322,10 @@ Expression *IndexExp::modifiableLvalue(Scope *sc, Expression *e) error("associative arrays can only be assigned values with immutable keys, not %s", e2->type->toChars()); e1 = e1->modifiableLvalue(sc, e1); } + else + { + checkModifiable(sc); + } return toLvalue(sc, e); } @@ -10215,55 +10513,17 @@ Expression *AssignExp::semantic(Scope *sc) L1: // 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)[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)[i] = x; - sc = sc->pop(); - } + ae = resolveOpDollar(sc, ae); Expressions *a = (Expressions *)ae->arguments->copy(); - a->insert(0, e2); + + Expression *e = new DotIdExp(loc, ae->e1, Id::indexass); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; } -#if 0 // Turned off to allow rewriting (a[i]=value) to (a.opIndex(i)=value) - else - { - // Rewrite (a[i] = value) to (a.opIndex(i, value)) - if (search_function(ad, id)) - { Expression *e = new DotIdExp(loc, ae->e1, id); - - if (1 || !global.params.useDeprecated) - { error("operator [] assignment overload with opIndex(i, value) illegal, use opIndexAssign(value, i)"); - return new ErrorExp(); - } - - e = new CallExp(loc, e, (*ae->arguments)[0], e2); - e = e->semantic(sc); - return e; - } - } -#endif } // No opIndexAssign found yet, but there might be an alias this to try. @@ -10307,17 +10567,16 @@ Expression *AssignExp::semantic(Scope *sc) L2: // Rewrite (a[i..j] = value) to (a.opSliceAssign(value, i, j)) if (search_function(ad, Id::sliceass)) - { Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass); + { + ae = resolveOpDollar(sc, ae); Expressions *a = new Expressions(); - a->push(e2); + assert(!ae->lwr || ae->upr); if (ae->lwr) { a->push(ae->lwr); - assert(ae->upr); a->push(ae->upr); } - else - assert(!ae->upr); + Expression *e = new DotIdExp(loc, ae->e1, Id::sliceass); e = new CallExp(loc, e, a); e = e->semantic(sc); return e; @@ -10342,11 +10601,6 @@ Expression *AssignExp::semantic(Scope *sc) } } - e2 = e2->semantic(sc); - if (e2->op == TOKerror) - return new ErrorExp(); - e2 = resolveProperties(sc, e2); - /* With UFCS, e.f = value * Could mean: * .f(e, value) @@ -10355,33 +10609,24 @@ Expression *AssignExp::semantic(Scope *sc) */ if (e1->op == TOKdotti) { - DotTemplateInstanceExp *dti = (DotTemplateInstanceExp *)e1; - dti->e1 = dti->e1->semantic(sc); - if (!global.errors && dti->e1->type) - { - unsigned errors = global.startGagging(); - e1 = dti->semantic(sc, 1); - if (global.endGagging(errors) || e1->op == TOKerror) - { - return resolveUFCSProperties(sc, dti, e2); - } - } + Expression *e = resolveProperty(sc, &e1, e2); + if (e) return e; } else if (e1->op == TOKdot) { - DotIdExp *die = (DotIdExp *)e1; - die->e1 = die->e1->semantic(sc); - if (!global.errors && die->e1->type) + Expression *e = resolveProperty(sc, &e1, e2); + if (e) return e; + + VarDeclaration * vd = NULL; + if (e1->op == TOKvar) + vd = ((VarExp *)e1)->var->isVarDeclaration(); + + if (vd && vd->needThis()) { - unsigned errors = global.startGagging(); - e1 = die->semantic(sc, 1); - if (global.endGagging(errors) || e1->op == TOKerror) - { - return resolveUFCSProperties(sc, die, e2); - } + error("need 'this' to access member %s", e1->toChars()); + return new ErrorExp(); } } -Le1: e1 = e1->semantic(sc); if (e1->op == TOKerror) return new ErrorExp(); @@ -10420,6 +10665,11 @@ Le1: ethis = NULL; L3: { + e2 = e2->semantic(sc); + if (e2->op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); + assert(td); Expressions a; a.push(e2); @@ -10447,6 +10697,11 @@ Le1: ethis = NULL; L4: { + e2 = e2->semantic(sc); + if (e2->op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); + assert(fd); FuncDeclaration *f = fd; Expressions a; @@ -10491,6 +10746,16 @@ Le1: } assert(e1->type); + Type *t1 = e1->type->toBasetype(); + + e2 = e2->inferType(t1); + if (!e2->rvalue()) + return new ErrorExp(); + + e2 = e2->semantic(sc); + if (e2->op == TOKerror) + return new ErrorExp(); + e2 = resolveProperties(sc, e2); /* Rewrite tuple assignment as a tuple of assignments. */ @@ -10565,6 +10830,8 @@ Ltupleassign: } } + int ctorinit = e1->checkCtorInit(sc); + // Determine if this is an initialization of a reference int refinit = 0; if (op == TOKconstruct && e1->op == TOKvar) @@ -10574,8 +10841,6 @@ Ltupleassign: refinit = 1; } - Type *t1 = e1->type->toBasetype(); - /* If it is an assignment from a 'foreign' type, * check for operator overloading. */ @@ -10617,8 +10882,15 @@ Ltupleassign: e = ae->op_overload(sc); e2 = new CommaExp(loc, new CommaExp(loc, de, e), ve); e2 = e2->semantic(sc); + + e1 = e1->optimize(WANTvalue); + e1 = e1->modifiableLvalue(sc, e1); + e2 = e2->implicitCastTo(sc, e1->type); + type = e1->type; + assert(type); + e = this; } - else if (e) + if (e) return e; } else if (op == TOKconstruct && !refinit) @@ -10675,19 +10947,6 @@ Ltupleassign: { Type *t2 = e2->type->toBasetype(); - if (t2->ty == Tsarray && !t2->implicitConvTo(t1->nextOf())) - { // static array assignment should check their lengths - TypeSArray *tsa1 = (TypeSArray *)t1; - TypeSArray *tsa2 = (TypeSArray *)t2; - uinteger_t dim1 = tsa1->dim->toInteger(); - uinteger_t dim2 = tsa2->dim->toInteger(); - if (dim1 != dim2) - { - error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2); - return new ErrorExp(); - } - } - if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) { @@ -10713,15 +10972,6 @@ Ltupleassign: } } - e2 = e2->inferType(t1, 2); - if (!e2->rvalue()) - return new ErrorExp(); - - if (e2->op == TOKarrayliteral) - { - e2 = callCpCtor(loc, sc, e2, 1); - } - if (e1->op == TOKarraylength) { // e1 is not an lvalue, but we let code generator handle it @@ -10732,7 +10982,7 @@ Ltupleassign: else if (e1->op == TOKslice) { Type *tn = e1->type->nextOf(); - if (op == TOKassign && tn && (!tn->isMutable() || !tn->isAssignable())) + if (op == TOKassign && !ctorinit && !tn->isMutable()) { error("slice %s is not mutable", e1->toChars()); return new ErrorExp(); } @@ -10786,15 +11036,49 @@ Ltupleassign: } //error("cannot assign to static array %s", e1->toChars()); } - else if (e1->op == TOKslice && t2->ty == Tarray && + // Check element-wise assignment. + else if (e1->op == TOKslice && + (t2->ty == Tarray || t2->ty == Tsarray) && t2->nextOf()->implicitConvTo(t1->nextOf())) { - e2 = e2->implicitCastTo(sc, e1->type->constOf()); + if (((SliceExp *)e1)->lwr == NULL) + { + Type *tx1 = ((SliceExp *)e1)->e1->type->toBasetype(); + Type *tx2 = t2; + if (e2->op == TOKslice && ((SliceExp *)e2)->lwr == NULL) + tx2 = ((SliceExp *)e2)->e1->type->toBasetype(); + if (tx1->ty == Tsarray && tx2->ty == Tsarray) + { // sa1[] = sa2[]; + // sa1[] = sa2; + TypeSArray *tsa1 = (TypeSArray *)tx1; + TypeSArray *tsa2 = (TypeSArray *)tx2; + uinteger_t dim1 = tsa1->dim->toInteger(); + uinteger_t dim2 = tsa2->dim->toInteger(); + if (dim1 != dim2) + { + error("mismatched array lengths, %d and %d", (int)dim1, (int)dim2); + return new ErrorExp(); + } + } + } + if (op != TOKblit && + (e2->op == TOKslice && ((UnaExp *)e2)->e1->isLvalue() || + e2->op == TOKcast && ((UnaExp *)e2)->e1->isLvalue() || + e2->op != TOKslice && e2->isLvalue())) + { + checkPostblit(e2->loc, t2->nextOf()); + } + if (op == TOKconstruct) + e2 = e2->castTo(sc, e1->type->constOf()); + else + e2 = e2->implicitCastTo(sc, e1->type->constOf()); } else { e2 = e2->implicitCastTo(sc, e1->type); } + if (e2->op == TOKerror) + return new ErrorExp(); /* Look for array operations */ @@ -10818,6 +11102,10 @@ Ltupleassign: { error("cannot rebind scope variables"); } + if (e1->op == TOKvar && ((VarExp*)e1)->var->ident == Id::ctfe) + { + error("cannot modify compiler-generated variable __ctfe"); + } type = e1->type; assert(type); @@ -10903,6 +11191,7 @@ Expression *CatAssignExp::semantic(Scope *sc) ) ) { // Append array + checkPostblit(e1->loc, tb1next); e2 = e2->castTo(sc, e1->type); type = e1->type; e = this; @@ -10911,6 +11200,7 @@ Expression *CatAssignExp::semantic(Scope *sc) e2->implicitConvTo(tb1next) ) { // Append element + checkPostblit(e2->loc, tb2); e2 = e2->castTo(sc, tb1next); type = e1->type; e = this; @@ -11282,9 +11572,23 @@ Expression *CatExp::semantic(Scope *sc) Type *tb1next = tb1->nextOf(); Type *tb2next = tb2->nextOf(); - if ((tb1->ty == Tsarray || tb1->ty == Tarray) && + if (tb1next && tb2next && + (tb1next->implicitConvTo(tb2next) >= MATCHconst || + tb2next->implicitConvTo(tb1next) >= MATCHconst) + ) + { + /* Here to avoid the case of: + * void*[] a = [cast(void*)1]; + * void*[] b = [cast(void*)2]; + * a ~ b; + * becoming: + * a ~ [cast(void*)b]; + */ + } + else if ((tb1->ty == Tsarray || tb1->ty == Tarray) && e2->implicitConvTo(tb1next) >= MATCHconvert) { + checkPostblit(e2->loc, tb2); e2 = e2->implicitCastTo(sc, tb1next); type = tb1next->arrayOf(); if (tb2->ty == Tarray) @@ -11297,6 +11601,7 @@ Expression *CatExp::semantic(Scope *sc) else if ((tb2->ty == Tsarray || tb2->ty == Tarray) && e1->implicitConvTo(tb2next) >= MATCHconvert) { + checkPostblit(e1->loc, tb1); e1 = e1->implicitCastTo(sc, tb2next); type = tb2next->arrayOf(); if (tb1->ty == Tarray) @@ -11336,6 +11641,10 @@ Expression *CatExp::semantic(Scope *sc) { type = type->nextOf()->toHeadMutable()->arrayOf(); } + if (tb->nextOf()) + { + checkPostblit(loc, tb->nextOf()); + } #if 0 e1->type->print(); e2->type->print(); @@ -11629,6 +11938,7 @@ Expression *PowExp::semantic(Scope *sc) } static int importMathChecked = 0; + static bool importMath = false; if (!importMathChecked) { importMathChecked = 1; @@ -11638,13 +11948,24 @@ Expression *PowExp::semantic(Scope *sc) if (mi->ident == Id::math && mi->parent->ident == Id::std && !mi->parent->parent) + { + importMath = true; goto L1; + } } error("must import std.math to use ^^ operator"); return new ErrorExp(); L1: ; } + else + { + if (!importMath) + { + error("must import std.math to use ^^ operator"); + return new ErrorExp(); + } + } e = new IdentifierExp(loc, Id::empty); e = new DotIdExp(loc, e, Id::std); @@ -12138,10 +12459,12 @@ Expression *CmpExp::semantic(Scope *sc) if ((t1->ty == Tarray || t1->ty == Tsarray || t1->ty == Tpointer) && (t2->ty == Tarray || t2->ty == Tsarray || t2->ty == Tpointer)) { - if (t1->nextOf()->implicitConvTo(t2->nextOf()) < MATCHconst && - t2->nextOf()->implicitConvTo(t1->nextOf()) < MATCHconst && - (t1->nextOf()->ty != Tvoid && t2->nextOf()->ty != Tvoid)) - error("array comparison type mismatch, %s vs %s", t1->nextOf()->toChars(), t2->nextOf()->toChars()); + Type *t1next = t1->nextOf(); + Type *t2next = t2->nextOf(); + if (t1next->implicitConvTo(t2next) < MATCHconst && + t2next->implicitConvTo(t1next) < MATCHconst && + (t1next->ty != Tvoid && t2next->ty != Tvoid)) + error("array comparison type mismatch, %s vs %s", t1next->toChars(), t2next->toChars()); e = this; } else if (t1->ty == Tstruct || t2->ty == Tstruct || @@ -12472,6 +12795,11 @@ Expression *CondExp::semantic(Scope *sc) return this; } +int CondExp::checkCtorInit(Scope *sc) +{ + return e1->checkCtorInit(sc) && e2->checkCtorInit(sc); +} + int CondExp::isLvalue() { @@ -12618,4 +12946,76 @@ Expression *LineInitExp::resolveLoc(Loc loc, Scope *sc) return e; } +/************************************** + * Runs semantic on ae->arguments. Declares temporary variables + * if '$' was used. + */ +ArrayExp *resolveOpDollar(Scope *sc, ArrayExp *ae) +{ + assert(!ae->lengthVar); + + for (size_t i = 0; i < ae->arguments->dim; i++) + { + // Create scope for '$' variable for this dimension + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, ae); + sym->loc = ae->loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + ae->lengthVar = NULL; // Create it only if required + ae->currentDimension = i; // Dimension for $, if required + + Expression *e = (*ae->arguments)[i]; + e = e->semantic(sc); + e = resolveProperties(sc, e); + if (!e->type) + ae->error("%s has no value", e->toChars()); + if (ae->lengthVar) + { // If $ was used, declare it now + Expression *de = new DeclarationExp(ae->loc, ae->lengthVar); + e = new CommaExp(0, de, e); + e = e->semantic(sc); + } + (*ae->arguments)[i] = e; + sc = sc->pop(); + } + return ae; +} + +/************************************** + * Runs semantic on se->lwr and se->upr. Declares a temporary variable + * if '$' was used. + */ + +SliceExp *resolveOpDollar(Scope *sc, SliceExp *se) +{ + assert(!se->lengthVar); + assert(!se->lwr || se->upr); + + if (!se->lwr) return se; + + // create scope for '$' + ArrayScopeSymbol *sym = new ArrayScopeSymbol(sc, se); + sym->loc = se->loc; + sym->parent = sc->scopesym; + sc = sc->push(sym); + + for (size_t i = 0; i < 2; ++i) + { + Expression *e = i == 0 ? se->lwr : se->upr; + e = e->semantic(sc); + e = resolveProperties(sc, e); + if (!e->type) + se->error("%s has no value", e->toChars()); + i == 0 ? se->lwr : se->upr = e; + } + + if (se->lengthVar) + { // If $ was used, declare it now + Expression *de = new DeclarationExp(se->loc, se->lengthVar); + se->lwr = new CommaExp(0, de, se->lwr); + se->lwr = se->lwr->semantic(sc); + } + sc = sc->pop(); + return se; +} diff --git a/dmd2/expression.h b/dmd2/expression.h index 1054d696..205d1cfe 100644 --- a/dmd2/expression.h +++ b/dmd2/expression.h @@ -91,16 +91,21 @@ void argExpTypesToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *h void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); void expandTuples(Expressions *exps); TupleDeclaration *isAliasThisTuple(Expression *e); -int expandAliasThisTuples(Expressions *exps, int starti = 0); +int expandAliasThisTuples(Expressions *exps, size_t starti = 0); FuncDeclaration *hasThis(Scope *sc); Expression *fromConstInitializer(int result, Expression *e); int arrayExpressionCanThrow(Expressions *exps, bool mustNotThrow); TemplateDeclaration *getFuncTemplateDecl(Dsymbol *s); void valueNoDtor(Expression *e); -void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1); +int modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1); #if DMDV2 Expression *resolveAliasThis(Scope *sc, Expression *e); +Expression *callCpCtor(Loc loc, Scope *sc, Expression *e, int noscope); +int checkPostblit(Loc loc, Type *t); #endif +struct ArrayExp *resolveOpDollar(Scope *sc, struct ArrayExp *ae); +struct SliceExp *resolveOpDollar(Scope *sc, struct SliceExp *se); +Expressions *arrayExpressionSemantic(Expressions *exps, Scope *sc); /* Interpreter: what form of return value expression is required? */ @@ -132,8 +137,9 @@ struct Expression : Object void print(); char *toChars(); virtual void dump(int indent); - void error(const char *format, ...) IS_PRINTF(2); - void warning(const char *format, ...) IS_PRINTF(2); + void error(const char *format, ...); + void warning(const char *format, ...); + void deprecation(const char *format, ...); virtual int rvalue(); static Expression *combine(Expression *e1, Expression *e2); @@ -166,6 +172,8 @@ struct Expression : Object void checkPurity(Scope *sc, FuncDeclaration *f); void checkPurity(Scope *sc, VarDeclaration *v, Expression *e1); void checkSafety(Scope *sc, FuncDeclaration *f); + void checkModifiable(Scope *sc); + virtual int checkCtorInit(Scope *sc); virtual Expression *checkToBoolean(Scope *sc); virtual Expression *addDtorHook(Scope *sc); Expression *checkToPointer(); @@ -176,7 +184,7 @@ struct Expression : Object Expression *toDelegate(Scope *sc, Type *t); - virtual Expression *optimize(int result); + virtual Expression *optimize(int result, bool keepLvalue = false); #define WANTflags 1 #define WANTvalue 2 // A compile-time result is required. Give an error if not possible @@ -376,6 +384,7 @@ struct ThisExp : Expression void toCBuffer(OutBuffer *buf, HdrGenState *hgs); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); + Expression *modifiableLvalue(Scope *sc, Expression *e); int inlineCost3(InlineCostState *ics); Expression *doInline(InlineDoState *ids); @@ -477,7 +486,7 @@ struct TupleExp : Expression Expression *semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void checkEscape(); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); Expression *castTo(Scope *sc, Type *t); #if IN_DMD @@ -507,7 +516,7 @@ struct ArrayLiteralExp : Expression StringExp *toString(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); MATCH implicitConvTo(Type *t); Expression *castTo(Scope *sc, Type *t); @@ -542,7 +551,7 @@ struct AssocArrayLiteralExp : Expression #endif void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); MATCH implicitConvTo(Type *t); Expression *castTo(Scope *sc, Type *t); @@ -571,6 +580,7 @@ struct StructLiteralExp : Expression size_t soffset; // offset from start of s int fillHoles; // fill alignment 'holes' with zero bool ownedByCtfe; // true = created in CTFE + int ctorinit; StructLiteralExp(Loc loc, StructDeclaration *sd, Expressions *elements, Type *stype = NULL); @@ -581,7 +591,7 @@ struct StructLiteralExp : Expression int getFieldIndex(Type *type, unsigned offset); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void toMangleBuffer(OutBuffer *buf); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); MATCH implicitConvTo(Type *t); @@ -614,7 +624,7 @@ struct TypeExp : Expression Expression *semantic(Scope *sc); int rvalue(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); #if IN_DMD elem *toElem(IRState *irs); #endif @@ -669,7 +679,8 @@ struct NewExp : Expression int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); + MATCH implicitConvTo(Type *t); #if IN_DMD elem *toElem(IRState *irs); #endif @@ -753,13 +764,14 @@ struct VarExp : SymbolExp VarExp(Loc loc, Declaration *var, int hasOverloads = 0); int equals(Object *o); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void dump(int indent); char *toChars(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void checkEscape(); void checkEscapeRef(); + int checkCtorInit(Scope *sc); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); @@ -917,7 +929,7 @@ struct UnaExp : Expression int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); void dump(int indent); Expression *interpretCommon(InterState *istate, CtfeGoal goal, Expression *(*fp)(Type *, Expression *)); @@ -943,15 +955,15 @@ struct BinExp : Expression void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Expression *scaleFactor(Scope *sc); Expression *typeCombine(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); int isunsigned(); Expression *incompatibleTypes(); void dump(int indent); Expression *interpretCommon(InterState *istate, CtfeGoal goal, Expression *(*fp)(Type *, Expression *, Expression *)); - Expression *interpretCommon2(InterState *istate, CtfeGoal goal, - Expression *(*fp)(Loc, TOK, Type *, Expression *, Expression *)); + Expression *interpretCompareCommon(InterState *istate, CtfeGoal goal, + int (*fp)(Loc, TOK, Expression *, Expression *)); Expression *interpretAssignCommon(InterState *istate, CtfeGoal goal, Expression *(*fp)(Type *, Expression *, Expression *), int post = 0); Expression *interpretFourPointerRelation(InterState *istate, CtfeGoal goal); @@ -1050,10 +1062,11 @@ struct DotVarExp : UnaExp DotVarExp(Loc loc, Expression *e, Declaration *var, int hasOverloads = 0); Expression *semantic(Scope *sc); + int checkCtorInit(Scope *sc); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void dump(int indent); @@ -1134,7 +1147,7 @@ struct CallExp : UnaExp int apply(apply_fp_t fp, void *param); Expression *resolveUFCS(Scope *sc); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); void dump(int indent); @@ -1168,7 +1181,7 @@ struct AddrExp : UnaExp #endif MATCH implicitConvTo(Type *t); Expression *castTo(Scope *sc, Type *t); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_LLVM DValue* toElem(IRState* irs); @@ -1181,15 +1194,16 @@ struct PtrExp : UnaExp PtrExp(Loc loc, Expression *e); PtrExp(Loc loc, Expression *e, Type *t); Expression *semantic(Scope *sc); - int isLvalue(); void checkEscapeRef(); + int checkCtorInit(Scope *sc); + int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); #if IN_DMD elem *toElem(IRState *irs); #endif - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); // For operator overloading @@ -1205,7 +1219,7 @@ struct NegExp : UnaExp { NegExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1236,7 +1250,7 @@ struct ComExp : UnaExp { ComExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1258,7 +1272,7 @@ struct NotExp : UnaExp { NotExp(Loc loc, Expression *e); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); int isBit(); #if IN_DMD @@ -1274,7 +1288,7 @@ struct BoolExp : UnaExp { BoolExp(Loc loc, Expression *e, Type *type); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); int isBit(); #if IN_DMD @@ -1313,7 +1327,7 @@ struct CastExp : UnaExp Expression *semantic(Scope *sc); MATCH implicitConvTo(Type *t); IntRange getIntRange(); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void checkEscape(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -1364,12 +1378,13 @@ struct SliceExp : UnaExp Expression *semantic(Scope *sc); void checkEscape(); void checkEscapeRef(); + int checkCtorInit(Scope *sc); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); int isBool(int result); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void dump(int indent); #if IN_DMD @@ -1391,7 +1406,7 @@ struct ArrayLengthExp : UnaExp { ArrayLengthExp(Loc loc, Expression *e1); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); #if IN_DMD @@ -1444,6 +1459,7 @@ struct CommaExp : BinExp Expression *semantic(Scope *sc); void checkEscape(); void checkEscapeRef(); + int checkCtorInit(Scope *sc); IntRange getIntRange(); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); @@ -1452,7 +1468,7 @@ struct CommaExp : BinExp MATCH implicitConvTo(Type *t); Expression *addDtorHook(Scope *sc); Expression *castTo(Scope *sc, Type *t); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_DMD elem *toElem(IRState *irs); @@ -1472,11 +1488,12 @@ struct IndexExp : BinExp IndexExp(Loc loc, Expression *e1, Expression *e2); Expression *syntaxCopy(); Expression *semantic(Scope *sc); + int checkCtorInit(Scope *sc); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); Expression *doInline(InlineDoState *ids); @@ -1604,7 +1621,7 @@ struct AddExp : BinExp { AddExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1629,7 +1646,7 @@ struct MinExp : BinExp { MinExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1653,7 +1670,7 @@ struct CatExp : BinExp { CatExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); // For operator overloading @@ -1673,7 +1690,7 @@ struct MulExp : BinExp { MulExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1697,7 +1714,7 @@ struct DivExp : BinExp { DivExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1720,7 +1737,7 @@ struct ModExp : BinExp { ModExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1744,7 +1761,7 @@ struct PowExp : BinExp { PowExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1767,7 +1784,7 @@ struct ShlExp : BinExp { ShlExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); IntRange getIntRange(); @@ -1788,7 +1805,7 @@ struct ShrExp : BinExp { ShrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); IntRange getIntRange(); @@ -1809,7 +1826,7 @@ struct UshrExp : BinExp { UshrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); IntRange getIntRange(); @@ -1830,7 +1847,7 @@ struct AndExp : BinExp { AndExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1854,7 +1871,7 @@ struct OrExp : BinExp { OrExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1879,7 +1896,7 @@ struct XorExp : BinExp { XorExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); @@ -1906,7 +1923,7 @@ struct OrOrExp : BinExp Expression *semantic(Scope *sc); Expression *checkToBoolean(Scope *sc); int isBit(); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_DMD elem *toElem(IRState *irs); @@ -1923,7 +1940,7 @@ struct AndAndExp : BinExp Expression *semantic(Scope *sc); Expression *checkToBoolean(Scope *sc); int isBit(); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_DMD elem *toElem(IRState *irs); @@ -1938,7 +1955,7 @@ struct CmpExp : BinExp { CmpExp(enum TOK op, Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); int isBit(); @@ -1996,7 +2013,7 @@ struct EqualExp : BinExp { EqualExp(enum TOK op, Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); int isBit(); @@ -2021,7 +2038,7 @@ struct IdentityExp : BinExp IdentityExp(enum TOK op, Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); int isBit(); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); #if IN_DMD elem *toElem(IRState *irs); @@ -2042,10 +2059,11 @@ struct CondExp : BinExp Expression *syntaxCopy(); int apply(apply_fp_t fp, void *param); Expression *semantic(Scope *sc); - Expression *optimize(int result); + Expression *optimize(int result, bool keepLvalue = false); Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void checkEscape(); void checkEscapeRef(); + int checkCtorInit(Scope *sc); int isLvalue(); Expression *toLvalue(Scope *sc, Expression *e); Expression *modifiableLvalue(Scope *sc, Expression *e); @@ -2157,9 +2175,9 @@ Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr); // Const-folding functions used by CTFE -void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, int firstIndex); -void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, int firstIndex); -void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, int firstIndex); +void sliceAssignArrayLiteralFromString(ArrayLiteralExp *existingAE, StringExp *newval, size_t firstIndex); +void sliceAssignStringFromArrayLiteral(StringExp *existingSE, ArrayLiteralExp *newae, size_t firstIndex); +void sliceAssignStringFromString(StringExp *existingSE, StringExp *newstr, size_t firstIndex); int sliceCmpStringWithString(StringExp *se1, StringExp *se2, size_t lo1, size_t lo2, size_t len); int sliceCmpStringWithArray(StringExp *se1, ArrayLiteralExp *ae2, size_t lo1, size_t lo2, size_t len); diff --git a/dmd2/func.c b/dmd2/func.c index 65b017d7..3c20e5b4 100644 --- a/dmd2/func.c +++ b/dmd2/func.c @@ -94,8 +94,11 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla #if DMDV2 builtin = BUILTINunknown; tookAddressOf = 0; + requiresClosure = false; flags = 0; #endif + returns = NULL; + #if IN_LLVM // LDC isArrayOp = false; @@ -228,10 +231,23 @@ void FuncDeclaration::semantic(Scope *sc) storage_class |= sc->stc & ~STCref; ad = isThis(); if (ad) + { storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized); + if (StructDeclaration *sd = ad->isStructDeclaration()) + sd->makeNested(); + } + //printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal()); + FuncLiteralDeclaration *fld = isFuncLiteralDeclaration(); + if (fld && fld->treq) + linkage = ((TypeFunction *)fld->treq->nextOf())->linkage; + else + linkage = sc->linkage; + protection = sc->protection; + userAttributes = sc->userAttributes; + if (!originalType) originalType = type; if (!type->deco) @@ -250,6 +266,8 @@ void FuncDeclaration::semantic(Scope *sc) if (isCtorDeclaration()) sc->flags |= SCOPEctor; + sc->linkage = linkage; + /* Apply const, immutable, wild and shared storage class * to the function type. Do this before type semantic. */ @@ -318,9 +336,6 @@ void FuncDeclaration::semantic(Scope *sc) f = (TypeFunction *)(type); size_t nparams = Parameter::dim(f->parameters); - linkage = sc->linkage; - protection = sc->protection; - /* Purity and safety can be inferred for some functions by examining * the function body. */ @@ -411,8 +426,8 @@ void FuncDeclaration::semantic(Scope *sc) #endif isDtorDeclaration() || isInvariantDeclaration() || - isUnitTestDeclaration() || isNewDeclaration() || isDelete()) - error("constructors, destructors, postblits, invariants, unittests, new and delete functions are not allowed in interface %s", id->toChars()); + isNewDeclaration() || isDelete()) + error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id->toChars()); if (fbody && isVirtual()) error("function body is not abstract in interface %s", id->toChars()); } @@ -424,7 +439,7 @@ void FuncDeclaration::semantic(Scope *sc) cd = parent->isClassDeclaration(); if (cd) - { int vi; + { size_t vi; CtorDeclaration *ctor; DtorDeclaration *dtor; InvariantDeclaration *inv; @@ -539,7 +554,7 @@ void FuncDeclaration::semantic(Scope *sc) doesoverride = TRUE; #if DMDV2 if (!isOverride()) - warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars()); + ::deprecation(loc, "overriding base class function without using override attribute is deprecated (%s overrides %s)", toPrettyChars(), fdv->toPrettyChars()); #endif FuncDeclaration *fdc = ((Dsymbol *)cd->vtbl.data[vi])->isFuncDeclaration(); @@ -601,7 +616,7 @@ void FuncDeclaration::semantic(Scope *sc) * If this function is covariant with any members of those interface * functions, set the tintro. */ - for (int i = 0; i < cd->interfaces_dim; i++) + for (size_t i = 0; i < cd->interfaces_dim; i++) { BaseClass *b = cd->interfaces[i]; vi = findVtblIndex((Dsymbols *)&b->base->vtbl, b->base->vtbl.dim); @@ -688,7 +703,7 @@ void FuncDeclaration::semantic(Scope *sc) /* Go through all the interface bases. * Disallow overriding any final functions in the interface(s). */ - for (int i = 0; i < cd->interfaces_dim; i++) + for (size_t i = 0; i < cd->interfaces_dim; i++) { BaseClass *b = cd->interfaces[i]; if (b->base) @@ -770,32 +785,6 @@ void FuncDeclaration::semantic(Scope *sc) } } - if (ident == Id::assign && (sd || cd)) - { // Disallow identity assignment operator. - - // opAssign(...) - if (nparams == 0) - { if (f->varargs == 1) - goto Lassignerr; - } - else - { - Parameter *arg0 = Parameter::getNth(f->parameters, 0); - Type *t0 = arg0->type->toBasetype(); - Type *tb = sd ? sd->type : cd->type; - if (arg0->type->implicitConvTo(tb) || - (sd && t0->ty == Tpointer && t0->nextOf()->implicitConvTo(tb)) - ) - { - if (nparams == 1) - goto Lassignerr; - Parameter *arg1 = Parameter::getNth(f->parameters, 1); - if (arg1->defaultArg) - goto Lassignerr; - } - } - } - if (isVirtual() && semanticRun != PASSsemanticdone) { /* Rewrite contracts as nested functions, then call them. @@ -905,14 +894,6 @@ Ldone: scope = new Scope(*sc); scope->setNoFree(); return; - -Lassignerr: - if (sd) - { - sd->hasIdentityAssign = 1; // don't need to generate it - goto Ldone; - } - error("identity assignment operator overload is illegal"); } void FuncDeclaration::semantic2(Scope *sc) @@ -940,7 +921,7 @@ void FuncDeclaration::semantic3(Scope *sc) //{ static int x; if (++x == 2) *(char*)0=0; } //printf("\tlinkage = %d\n", sc->linkage); - //printf(" sc->incontract = %d\n", sc->incontract); + //printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract)); if (semanticRun >= PASSsemantic3) return; semanticRun = PASSsemantic3; @@ -954,12 +935,14 @@ void FuncDeclaration::semantic3(Scope *sc) if (!type || type->ty != Tfunction) return; f = (TypeFunction *)(type); + if (!inferRetType && f->next->ty == Terror) + return; #if 0 // Check the 'throws' clause if (fthrows) { - for (int i = 0; i < fthrows->dim; i++) + for (size_t i = 0; i < fthrows->dim; i++) { Type *t = (*fthrows)[i]; @@ -978,7 +961,7 @@ void FuncDeclaration::semantic3(Scope *sc) if (frequire) { - for (int i = 0; i < foverrides.dim; i++) + for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; @@ -1019,14 +1002,16 @@ void FuncDeclaration::semantic3(Scope *sc) sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; - sc2->incontract = 0; -#if !IN_LLVM - sc2->tf = NULL; -#else + sc2->flags = sc->flags & ~SCOPEcontract; +#if IN_LLVM sc2->enclosingFinally = NULL; sc2->enclosingScopeExit = NULL; +#else + sc2->tf = NULL; #endif sc2->noctor = 0; + sc2->speculative = sc->speculative || isSpeculative() != NULL; + sc2->userAttributes = NULL; // Declare 'this' AggregateDeclaration *ad = isThis(); @@ -1051,7 +1036,7 @@ void FuncDeclaration::semantic3(Scope *sc) Type *t; #if !IN_GCC && !IN_LLVM - if (global.params.is64bit) + if (global.params.is64bit && !global.params.isWindows) { // Declare save area for varargs registers Type *t = new TypeIdentifier(loc, Id::va_argsave_t); t = t->semantic(loc, sc); @@ -1115,8 +1100,8 @@ void FuncDeclaration::semantic3(Scope *sc) // This is important if it turned into a tuple. // In particular, the empty tuple should be handled or the // next parameter will be skipped. - // FIXME: Maybe we only need to do this for tuples, - // and can add tuple.length after decrement? + // LDC_FIXME: Maybe we only need to do this for tuples, + // and can add tuple.length after decrement? i--; } } @@ -1336,7 +1321,7 @@ void FuncDeclaration::semantic3(Scope *sc) sym->parent = sc2->scopesym; sc2 = sc2->push(sym); - AggregateDeclaration *ad = isAggregateMember(); + AggregateDeclaration *ad = isAggregateMember2(); /* If this is a class constructor */ @@ -1363,7 +1348,19 @@ void FuncDeclaration::semantic3(Scope *sc) ((TypeFunction *)type)->next = Type::tvoid; //type = type->semantic(loc, sc); // Removed with 6902 } - f = (TypeFunction *)type; + else if (returns && f->next->ty != Tvoid) + { + for (size_t i = 0; i < returns->dim; i++) + { Expression *exp = (*returns)[i]->exp; + if (!f->next->invariantOf()->equals(exp->type->invariantOf())) + { exp = exp->castTo(sc2, f->next); + exp = exp->optimize(WANTvalue); + (*returns)[i]->exp = exp; + } + //printf("[%d] %s %s\n", i, exp->type->toChars(), exp->toChars()); + } + } + assert(type == f); } if (isStaticCtorDeclaration()) @@ -1389,6 +1386,15 @@ void FuncDeclaration::semantic3(Scope *sc) if (isCtorDeclaration() && ad) { +#if DMDV2 + // Check for errors related to 'nothrow'. + int nothrowErrors = global.errors; + int blockexit = fbody->blockExit(f->isnothrow); + if (f->isnothrow && (global.errors != nothrowErrors) ) + error("'%s' is nothrow yet may throw", toChars()); + if (flags & FUNCFLAGnothrowInprocess) + f->isnothrow = !(blockexit & BEthrow); +#endif //printf("callSuper = x%x\n", sc2->callSuper); ClassDeclaration *cd = ad->isClassDeclaration(); @@ -1408,9 +1414,15 @@ void FuncDeclaration::semantic3(Scope *sc) * as delegating calls to other constructors */ if (v->isCtorinit() && !v->type->isMutable() && cd) - error("missing initializer for final field %s", v->toChars()); + { + OutBuffer buf; + MODtoBuffer(&buf, v->type->mod); + error("missing initializer for %s field %s", buf.toChars(), v->toChars()); + } else if (v->storage_class & STCnodefaultctor) error("field %s must be initialized in constructor", v->toChars()); + else if (v->type->needsNested()) + error("field %s must be initialized in constructor, because it is nested struct", v->toChars()); } } } @@ -1427,7 +1439,10 @@ void FuncDeclaration::semantic3(Scope *sc) e = e->trySemantic(sc2); if (!e) - error("no match for implicit super() call in constructor"); + { + const char* impGen = ((CtorDeclaration*)this)->isImplicit ? "implicitly generated " : ""; + error("no match for implicit super() call in %sconstructor", impGen); + } else { Statement *s = new ExpStatement(0, e); @@ -1453,15 +1468,11 @@ void FuncDeclaration::semantic3(Scope *sc) #if DMDV2 // Check for errors related to 'nothrow'. int nothrowErrors = global.errors; - int blockexit = fbody ? fbody->blockExit(f->isnothrow) : BEfallthru; + int blockexit = fbody->blockExit(f->isnothrow); if (f->isnothrow && (global.errors != nothrowErrors) ) error("'%s' is nothrow yet may throw", toChars()); if (flags & FUNCFLAGnothrowInprocess) - { - flags &= ~FUNCFLAGnothrowInprocess; - if (!(blockexit & BEthrow)) - f->isnothrow = TRUE; - } + f->isnothrow = !(blockexit & BEthrow); int offend = blockexit & BEfallthru; #endif @@ -1510,18 +1521,14 @@ void FuncDeclaration::semantic3(Scope *sc) ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc2->scopesym; sc2 = sc2->push(sym); - sc2->incontract++; + sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPErequire; // BUG: need to error if accessing out parameters // BUG: need to treat parameters as const // BUG: need to disallow returns and throws // BUG: verify that all in and ref parameters are read - DsymbolTable *labtab_save = labtab; - labtab = NULL; // so in contract can't refer to out/body labels freq = freq->semantic(sc2); - labtab = labtab_save; - sc2->incontract--; sc2 = sc2->pop(); if (!global.params.useIn) @@ -1540,16 +1547,12 @@ void FuncDeclaration::semantic3(Scope *sc) buildResultVar(); sc2 = scout; //push - sc2->incontract++; + sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure; // BUG: need to treat parameters as const // BUG: need to disallow returns and throws - DsymbolTable *labtab_save = labtab; - labtab = NULL; // so out contract can't refer to in/body labels fens = fens->semantic(sc2); - labtab = labtab_save; - sc2->incontract--; sc2 = sc2->pop(); if (!global.params.useOut) @@ -1586,7 +1589,7 @@ void FuncDeclaration::semantic3(Scope *sc) v_argptr->init = new VoidInitializer(loc); #else Type *t = argptr->type; - if (global.params.is64bit) + if (global.params.is64bit && !global.params.isWindows) { // Initialize _argptr to point to v_argsave Expression *e1 = new VarExp(0, argptr); Expression *e = new SymOffExp(0, v_argsave, 6*8 + 8*16); @@ -1599,12 +1602,13 @@ void FuncDeclaration::semantic3(Scope *sc) { // Initialize _argptr to point past non-variadic arg VarDeclaration *p; unsigned offset = 0; + Expression *e; Expression *e1 = new VarExp(0, argptr); // Find the last non-ref parameter if (parameters && parameters->dim) { - int lastNonref = parameters->dim -1; + size_t lastNonref = parameters->dim -1; p = (*parameters)[lastNonref]; /* The trouble with out and ref parameters is that taking * the address of it doesn't work, because later processing @@ -1612,9 +1616,8 @@ void FuncDeclaration::semantic3(Scope *sc) */ while (p->storage_class & (STCout | STCref)) { - --lastNonref; offset += PTRSIZE; - if (lastNonref < 0) + if (lastNonref-- == 0) { p = v_arguments; break; @@ -1624,15 +1627,32 @@ void FuncDeclaration::semantic3(Scope *sc) } else p = v_arguments; // last parameter is _arguments[] + if (global.params.is64bit && global.params.isWindows) + { offset += PTRSIZE; if (p->storage_class & STClazy) + { + /* Necessary to offset the extra level of indirection the Win64 + * ABI demands + */ + e = new SymOffExp(0,p,0); + e->type = Type::tvoidptr; + e = new AddrExp(0, e); + e->type = Type::tvoidptr; + e = new AddExp(0, e, new IntegerExp(offset)); + e->type = Type::tvoidptr; + goto L1; + } + } + else if (p->storage_class & STClazy) // If the last parameter is lazy, it's the size of a delegate offset += PTRSIZE * 2; else offset += p->type->size(); offset = (offset + PTRSIZE - 1) & ~(PTRSIZE - 1); // assume stack aligns on pointer size - Expression *e = new SymOffExp(0, p, offset); + e = new SymOffExp(0, p, offset); e->type = Type::tvoidptr; //e = e->semantic(sc); + L1: e = new AssignExp(0, e1, e); e->type = t; a->push(new ExpStatement(0, e)); @@ -1669,7 +1689,6 @@ void FuncDeclaration::semantic3(Scope *sc) else if (fpreinv) freq = new CompoundStatement(0, freq, fpreinv); - freq->incontract = 1; a->push(freq); } @@ -1745,6 +1764,13 @@ void FuncDeclaration::semantic3(Scope *sc) if (e) { Statement *s = new ExpStatement(0, e); s = s->semantic(sc2); + int nothrowErrors = global.errors; + bool isnothrow = f->isnothrow & !(flags & FUNCFLAGnothrowInprocess); + int blockexit = s->blockExit(isnothrow); + if (f->isnothrow && (global.errors != nothrowErrors) ) + error("'%s' is nothrow yet may throw", toChars()); + if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow) + f->isnothrow = FALSE; if (fbody->blockExit(f->isnothrow) == BEfallthru) fbody = new CompoundStatement(0, fbody, s); else @@ -1752,6 +1778,8 @@ void FuncDeclaration::semantic3(Scope *sc) } } } + // from this point on all possible 'throwers' are checked + flags &= ~FUNCFLAGnothrowInprocess; #endif #if 1 @@ -1764,7 +1792,7 @@ void FuncDeclaration::semantic3(Scope *sc) if (cd) { #if TARGET_WINDOS - if (/*config.flags2 & CFG2seh &&*/ // always on for WINDOS + if (!global.params.is64bit && !isStatic() && !fbody->usesEH()) { /* The back end uses the "jmonitor" hack for syncing; @@ -1821,7 +1849,16 @@ void FuncDeclaration::semantic3(Scope *sc) } if (global.gag && global.errors != nerrors) + { + /* Errors happened when compiling this function. + */ semanticRun = PASSsemanticdone; // Ensure errors get reported again + /* Except that re-running semantic3() doesn't always produce errors a second + * time through. + * See Bugzilla 8348 + * Need a better way to deal with this than gagging. + */ + } else { semanticRun = PASSsemantic3done; @@ -1963,7 +2000,9 @@ void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writebyte('{'); buf->writenl(); + buf->level++; fbody->toCBuffer(buf, hgs); + buf->level--; buf->writebyte('}'); buf->writenl(); } @@ -2063,7 +2102,7 @@ Statement *FuncDeclaration::mergeFrequire(Statement *sf, Expressions *params) * handler block, so it is always at the same offset from EBP. */ #endif - for (int i = 0; i < foverrides.dim; i++) + for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; @@ -2121,7 +2160,7 @@ Statement *FuncDeclaration::mergeFensure(Statement *sf, Expressions *params) * list for the 'this' pointer, something that would need an unknown amount * of tweaking of various parts of the compiler that I'd rather leave alone. */ - for (int i = 0; i < foverrides.dim; i++) + for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; @@ -2142,7 +2181,42 @@ Statement *FuncDeclaration::mergeFensure(Statement *sf, Expressions *params) { //printf("fdv->fensure: %s\n", fdv->fensure->toChars()); // Make the call: __ensure(result) + Expression *eresult = NULL; + if (outId) + { +#if IN_LLVM + eresult = (*params)[0]; +#else + eresult = new IdentifierExp(loc, outId); +#endif + Type *t1 = fdv->type->nextOf()->toBasetype(); +#if IN_LLVM + // We actually check for matching types in CommaExp::toElem, + // 'testcontract' breaks without this. + t1 = t1->constOf(); +#endif + Type *t2 = this->type->nextOf()->toBasetype(); + int offset; + if (t1->isBaseOf(t2, &offset) && offset != 0) + { + /* Making temporary reference variable is necessary + * to match offset difference in covariant return. + * See bugzilla 5204. + */ + ExpInitializer *ei = new ExpInitializer(0, eresult); + VarDeclaration *v = new VarDeclaration(0, t1, Lexer::uniqueId("__covres"), ei); + DeclarationExp *de = new DeclarationExp(0, v); + VarExp *ve = new VarExp(0, v); + eresult = new CommaExp(0, de, ve); + } + } +#if IN_LLVM + if (eresult) + (*params)[0] = eresult; Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), params); +#else + Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), eresult); +#endif Statement *s2 = new ExpStatement(loc, e); if (sf) @@ -2462,6 +2536,8 @@ FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t, Module* from) /******************************************** * Decide which function matches the arguments best. + * flags 1: do not issue error message on no match, just return NULL + * 2: do not issue error on ambiguous matches and need explicit this */ struct Param2 @@ -2530,6 +2606,23 @@ int fp2(void *param, FuncDeclaration *f) if (c1 < c2) goto LlastIsBetter; } + + /* If the two functions are the same function, like: + * int foo(int); + * int foo(int x) { ... } + * then pick the one with the body. + */ + if (tf->equals(m->lastf->type) && + f->storage_class == m->lastf->storage_class && + f->parent == m->lastf->parent && + f->protection == m->lastf->protection && + f->linkage == m->lastf->linkage) + { + if (f->fbody && !m->lastf->fbody) + goto LfIsBetter; + else if (!f->fbody && m->lastf->fbody) + goto LlastIsBetter; + } #endif Lambiguous: m->nextf = f; @@ -2596,17 +2689,14 @@ if (arguments) OutBuffer buf; buf.writeByte('('); - if (arguments) + if (arguments && arguments->dim) { HdrGenState hgs; - argExpTypesToCBuffer(&buf, arguments, &hgs); - buf.writeByte(')'); - if (ethis) - ethis->type->modToBuffer(&buf); } - else - buf.writeByte(')'); + buf.writeByte(')'); + if (ethis) + ethis->type->modToBuffer(&buf); if (m.last == MATCHnomatch) { @@ -2627,14 +2717,16 @@ if (arguments) } else { + if ((flags & 2) && m.lastf->needThis() && !ethis) + return m.lastf; #if 1 TypeFunction *t1 = (TypeFunction *)m.lastf->type; TypeFunction *t2 = (TypeFunction *)m.nextf->type; - error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s%s\nand:\n\t%s%s", + error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s(%d): %s%s\nand:\n\t%s(%d): %s%s", buf.toChars(), - m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs), - m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs)); + m.lastf->loc.filename, m.lastf->loc.linnum, m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs), + m.nextf->loc.filename, m.nextf->loc.linnum, m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs)); #else error(loc, "overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), @@ -2693,7 +2785,7 @@ MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g) */ Expressions args; args.setDim(nfparams); - for (int u = 0; u < nfparams; u++) + for (size_t u = 0; u < nfparams; u++) { Parameter *p = Parameter::getNth(tf->parameters, u); Expression *e; @@ -2807,14 +2899,14 @@ AggregateDeclaration *FuncDeclaration::isMember2() //printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind()); ad = s->isMember(); if (ad) -{ + { break; -} + } if (!s->parent || (!s->parent->isTemplateInstance())) -{ + { break; -} + } } //printf("-FuncDeclaration::isMember2() %p\n", ad); return ad; @@ -3071,6 +3163,8 @@ enum PURE FuncDeclaration::isPureBypassingInference() { if (flags & FUNCFLAGpurityInprocess) return PUREfwdref; + else if (type->nextOf() == NULL) + return PUREfwdref; else return isPure(); } @@ -3281,25 +3375,46 @@ int FuncDeclaration::needsClosure() */ //printf("FuncDeclaration::needsClosure() %s\n", toChars()); - for (int i = 0; i < closureVars.dim; i++) + + if (requiresClosure) + goto Lyes; + + for (size_t i = 0; i < closureVars.dim; i++) { VarDeclaration *v = closureVars[i]; assert(v->isVarDeclaration()); //printf("\tv = %s\n", v->toChars()); - for (int j = 0; j < v->nestedrefs.dim; j++) + for (size_t j = 0; j < v->nestedrefs.dim; j++) { FuncDeclaration *f = v->nestedrefs[j]; assert(f != this); - //printf("\t\tf = %s, %d, %p, %d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf); - if (f->isThis() || f->tookAddressOf) - goto Lyes; // assume f escapes this function's scope + //printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf); - // Look to see if any parents of f that are below this escape - for (Dsymbol *s = f->parent; s && s != this; s = s->parent) + // Look to see if f or any parents of f that are below this escape + for (Dsymbol *s = f; s && s != this; s = s->parent) { - f = s->isFuncDeclaration(); - if (f && (f->isThis() || f->tookAddressOf)) + FuncDeclaration *fx = s->isFuncDeclaration(); + if (fx && (fx->isThis() || fx->tookAddressOf)) + { + //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf); + + /* Mark as needing closure any functions between this and f + */ + for (Dsymbol *sx = fx; sx != this; sx = sx->parent) + { + if (sx != f) + { FuncDeclaration *fy = sx->isFuncDeclaration(); + if (fy && fy->closureVars.dim) + { + /* fy needs a closure if it has closureVars[], + * because the frame pointer in the closure will be accessed. + */ + fy->requiresClosure = true; + } + } + } goto Lyes; + } } } } @@ -3312,12 +3427,17 @@ int FuncDeclaration::needsClosure() Type *tret = ((TypeFunction *)type)->next; assert(tret); tret = tret->toBasetype(); + //printf("\t\treturning %s\n", tret->toChars()); if (tret->ty == Tclass || tret->ty == Tstruct) { Dsymbol *st = tret->toDsymbol(NULL); + //printf("\t\treturning class/struct %s\n", tret->toChars()); for (Dsymbol *s = st->parent; s; s = s->parent) { + //printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars()); if (s == this) + { //printf("\t\treturning local %s\n", st->toChars()); goto Lyes; + } } } } @@ -3414,6 +3534,7 @@ FuncAliasDeclaration::FuncAliasDeclaration(FuncDeclaration *funcalias, int hasOv assert(!funcalias->isFuncAliasDeclaration()); this->hasOverloads = 0; } + userAttributes = funcalias->userAttributes; } const char *FuncAliasDeclaration::kind() @@ -3502,6 +3623,7 @@ CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *ty : FuncDeclaration(loc, endloc, Id::ctor, stc, type) { //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars()); + this->isImplicit = false; } Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) @@ -3550,17 +3672,14 @@ void CtorDeclaration::semantic(Scope *sc) tret = tret->addMod(type->mod); } tf->next = tret; + if (!originalType) + originalType = type->syntaxCopy(); type = type->semantic(loc, sc); #if STRUCTTHISREF if (ad && ad->isStructDeclaration()) - { if (!originalType) - originalType = type->syntaxCopy(); ((TypeFunction *)type)->isref = 1; - } #endif - if (!originalType) - originalType = type; // Append: // return this; @@ -3578,8 +3697,11 @@ void CtorDeclaration::semantic(Scope *sc) sc->pop(); - // See if it's the default constructor - if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0) + /* See if it's the default constructor + * But, template constructor should not become a default constructor. + */ + if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0 + && (!this->parent->isTemplateInstance() || this->parent->isTemplateMixin())) { StructDeclaration *sd = ad->isStructDeclaration(); if (sd) @@ -3592,7 +3714,9 @@ void CtorDeclaration::semantic(Scope *sc) sd->noDefaultCtor = TRUE; } else + { ad->defaultCtor = this; + } } } @@ -3625,20 +3749,15 @@ int CtorDeclaration::addPostInvariant() /********************************* PostBlitDeclaration ****************************/ #if DMDV2 -PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc) - : FuncDeclaration(loc, endloc, Id::_postblit, stc, NULL) -{ -} - -PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id) - : FuncDeclaration(loc, endloc, id, STCundefined, NULL) +PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id) + : FuncDeclaration(loc, endloc, id, stc, NULL) { } Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); - PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, ident); + PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, storage_class, ident); return FuncDeclaration::syntaxCopy(dd); } @@ -4103,7 +4222,7 @@ void InvariantDeclaration::semantic(Scope *sc) sc = sc->push(); sc->stc &= ~STCstatic; // not a static invariant sc->stc |= STCconst; // invariant() is always const - sc->incontract++; + sc->flags = (sc->flags & ~SCOPEcontract) | SCOPEinvariant; sc->linkage = LINKd; FuncDeclaration::semantic(sc); @@ -4142,13 +4261,21 @@ void InvariantDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) * instances per module. */ -static Identifier *unitTestId() +#if __DMC__ || _MSC_VER +#define snprintf _snprintf +#endif +static Identifier *unitTestId(Loc loc) { - return Lexer::uniqueId("__unittest"); + char name[24]; + snprintf(name, 24, "__unittestL%u_", loc.linnum); + return Lexer::uniqueId(name); } +#if __DMC__ || _MSC_VER +#undef snprintf +#endif UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc) - : FuncDeclaration(loc, endloc, unitTestId(), STCundefined, NULL) + : FuncDeclaration(loc, endloc, unitTestId(loc), STCundefined, NULL) { } diff --git a/dmd2/hdrgen.c b/dmd2/hdrgen.c index 87b8368f..6ba45e2d 100644 --- a/dmd2/hdrgen.c +++ b/dmd2/hdrgen.c @@ -51,6 +51,7 @@ void argsToCBuffer(OutBuffer *buf, Expressions *arguments, HdrGenState *hgs); void Module::genhdrfile() { OutBuffer hdrbufr; + hdrbufr.doindent = 1; hdrbufr.printf("// D import file generated from '%s'", srcfile->toChars()); hdrbufr.writenl(); diff --git a/dmd2/hdrgen.h b/dmd2/hdrgen.h index c60787ae..19900fed 100644 --- a/dmd2/hdrgen.h +++ b/dmd2/hdrgen.h @@ -28,6 +28,7 @@ struct HdrGenState int init; int decl; } FLinit; + Scope* scope; // Scope when generating ddoc HdrGenState() { memset(this, 0, sizeof(HdrGenState)); } }; diff --git a/dmd2/identifier.h b/dmd2/identifier.h index d8796000..b46c654f 100644 --- a/dmd2/identifier.h +++ b/dmd2/identifier.h @@ -28,7 +28,7 @@ struct Identifier : Object { int value; const char *string; - unsigned len; + size_t len; Identifier(const char *string, int value); int equals(Object *o); diff --git a/dmd2/idgen.c b/dmd2/idgen.c index 2fa2121a..edd66bf9 100644 --- a/dmd2/idgen.c +++ b/dmd2/idgen.c @@ -352,6 +352,7 @@ Msgtable msgtable[] = { "isArithmetic" }, { "isAssociativeArray" }, { "isFinalClass" }, + { "isPOD" }, { "isFloating" }, { "isIntegral" }, { "isScalar" }, @@ -367,6 +368,7 @@ Msgtable msgtable[] = { "isLazy" }, { "hasMember" }, { "identifier" }, + { "getProtection" }, { "parent" }, { "getMember" }, { "getOverloads" }, @@ -378,6 +380,7 @@ Msgtable msgtable[] = { "isSame" }, { "compiles" }, { "parameters" }, + { "getAttributes" }, }; diff --git a/dmd2/import.c b/dmd2/import.c index 07670f4f..0d8a7b80 100644 --- a/dmd2/import.c +++ b/dmd2/import.c @@ -169,15 +169,17 @@ void Import::importAll(Scope *sc) { if (!mod) { - load(sc); - mod->importAll(0); + load(sc); + if (mod) // if successfully loaded module + { mod->importAll(0); - if (!isstatic && !aliasId && !names.dim) - { - if (sc->explicitProtection) - protection = sc->protection; - sc->scopesym->importScope(mod, protection); - } + if (!isstatic && !aliasId && !names.dim) + { + if (sc->explicitProtection) + protection = sc->protection; + sc->scopesym->importScope(mod, protection); + } + } } } @@ -280,7 +282,9 @@ void Import::semantic(Scope *sc) escapePath(ob, sc->module->srcfile->toChars()); ob->writestring(") : "); - ProtDeclaration::protectionToCBuffer(ob, sc->protection); + // use protection instead of sc->protection because it couldn't be + // resolved yet, see the comment above + ProtDeclaration::protectionToCBuffer(ob, protection); if (isstatic) StorageClassDeclaration::stcToCBuffer(ob, STCstatic); ob->writestring(": "); @@ -333,10 +337,13 @@ void Import::semantic(Scope *sc) void Import::semantic2(Scope *sc) { //printf("Import::semantic2('%s')\n", toChars()); - mod->semantic2(); - if (mod->needmoduleinfo) - { //printf("module5 %s because of %s\n", sc->module->toChars(), mod->toChars()); - sc->module->needmoduleinfo = 1; + if (mod) + { + mod->semantic2(); + if (mod->needmoduleinfo) + { //printf("module5 %s because of %s\n", sc->module->toChars(), mod->toChars()); + sc->module->needmoduleinfo = 1; + } } } diff --git a/dmd2/init.c b/dmd2/init.c index 23758bda..53796359 100644 --- a/dmd2/init.c +++ b/dmd2/init.c @@ -22,6 +22,7 @@ #include "mtype.h" #include "hdrgen.h" #include "template.h" +#include "id.h" /********************************** Initializer *******************************/ @@ -152,9 +153,11 @@ Initializer *StructInitializer::semantic(Scope *sc, Type *t, NeedInterpret needI //printf("StructInitializer::semantic(t = %s) %s\n", t->toChars(), toChars()); vars.setDim(field.dim); t = t->toBasetype(); + if (t->ty == Tsarray && t->nextOf()->toBasetype()->ty == Tstruct) + t = t->nextOf()->toBasetype(); if (t->ty == Tstruct) { - unsigned fieldi = 0; + size_t fieldi = 0; TypeStruct *ts = (TypeStruct *)t; ad = ts->sym; @@ -292,7 +295,7 @@ Expression *StructInitializer::toExpression() { (*elements)[i] = NULL; } - unsigned fieldi = 0; + size_t fieldi = 0; for (size_t i = 0; i < value.dim; i++) { Identifier *id = field[i]; @@ -355,7 +358,20 @@ Expression *StructInitializer::toExpression() if (!(*elements)[i]) { // Default initialize if (vd->init) - (*elements)[i] = vd->init->toExpression(); + { + if (vd->scope) + { // Do deferred semantic analysis + Initializer *i2 = vd->init->syntaxCopy(); + i2 = i2->semantic(vd->scope, vd->type, INITinterpret); + (*elements)[i] = i2->toExpression(); + if (!global.gag) + { vd->scope = NULL; + vd->init = i2; // save result + } + } + else + (*elements)[i] = vd->init->toExpression(); + } else (*elements)[i] = vd->type->defaultInit(); } @@ -469,8 +485,8 @@ void ArrayInitializer::addInit(Expression *index, Initializer *value) } Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInterpret) -{ unsigned i; - unsigned length; +{ + size_t length; const unsigned amax = 0x80000000; //printf("ArrayInitializer::semantic(%s)\n", t->toChars()); @@ -478,6 +494,7 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn return this; sem = 1; type = t; + Initializer *aa = NULL; t = t->toBasetype(); switch (t->ty) { @@ -490,13 +507,18 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn t = ((TypeVector *)t)->basetype; break; + case Taarray: + // was actually an associative array literal + aa = new ExpInitializer(loc, toAssocArrayLiteral()); + return aa->semantic(sc, t, needInterpret); + default: error(loc, "cannot use array to initialize %s", type->toChars()); goto Lerr; } length = 0; - for (i = 0; i < index.dim; i++) + for (size_t i = 0; i < index.dim; i++) { Expression *idx = index[i]; if (idx) @@ -552,8 +574,8 @@ Initializer *ArrayInitializer::semantic(Scope *sc, Type *t, NeedInterpret needIn } } - if ((unsigned long) dim * t->nextOf()->size() >= amax) - { error(loc, "array dimension %u exceeds max of %u", dim, amax / t->nextOf()->size()); + if ((uinteger_t) dim * t->nextOf()->size() >= amax) + { error(loc, "array dimension %u exceeds max of %u", (unsigned) dim, (unsigned)(amax / t->nextOf()->size())); goto Lerr; } return this; @@ -602,7 +624,12 @@ Expression *ArrayInitializer::toExpression() for (size_t i = 0, j = 0; i < value.dim; i++, j++) { if (index[i]) - j = index[i]->toInteger(); + { + if (index[i]->op == TOKint64) + j = index[i]->toInteger(); + else + goto Lno; + } if (j >= edim) edim = j + 1; } @@ -881,6 +908,7 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte } Type *tb = t->toBasetype(); + Type *ti = exp->type->toBasetype(); if (exp->op == TOKtuple && expandTuples && @@ -893,7 +921,7 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte * Allow this by doing an explicit cast, which will lengthen the string * literal. */ - if (exp->op == TOKstring && tb->ty == Tsarray && exp->type->ty == Tsarray) + if (exp->op == TOKstring && tb->ty == Tsarray && ti->ty == Tsarray) { StringExp *se = (StringExp *)exp; if (!se->committed && se->type->ty == Tsarray && @@ -905,10 +933,30 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, NeedInterpret needInte } } + // Look for implicit constructor call + if (tb->ty == Tstruct && + !(ti->ty == Tstruct && tb->toDsymbol(sc) == ti->toDsymbol(sc)) && + !exp->implicitConvTo(t)) + { + StructDeclaration *sd = ((TypeStruct *)tb)->sym; + if (sd->ctor) + { // Rewrite as S().ctor(exp) + Expression *e; + e = new StructLiteralExp(loc, sd, NULL); + e = new DotIdExp(loc, e, Id::ctor); + e = new CallExp(loc, e, exp); + e = e->semantic(sc); + if (needInterpret) + exp = e->ctfeInterpret(); + else + exp = e->optimize(WANTvalue); + } + } + // Look for the case of statically initializing an array // with a single member. if (tb->ty == Tsarray && - !tb->nextOf()->equals(exp->type->toBasetype()->nextOf()) && + !tb->nextOf()->equals(ti->toBasetype()->nextOf()) && exp->implicitConvTo(tb->nextOf()) ) { diff --git a/dmd2/init.h b/dmd2/init.h index 260f35c8..ecc4c151 100644 --- a/dmd2/init.h +++ b/dmd2/init.h @@ -107,7 +107,7 @@ struct ArrayInitializer : Initializer { Expressions index; // indices Initializers value; // of Initializer *'s - unsigned dim; // length of array being initialized + size_t dim; // length of array being initialized Type *type; // type that array will be used to initialize int sem; // !=0 if semantic() is run diff --git a/dmd2/inline.c b/dmd2/inline.c index 8bf6fe8c..6198eb78 100644 --- a/dmd2/inline.c +++ b/dmd2/inline.c @@ -1134,62 +1134,6 @@ Statement *ReturnStatement::inlineScan(InlineScanState *iss) FuncDeclaration *func = iss->fd; TypeFunction *tf = (TypeFunction *)(func->type); - - /* Postblit call on return statement is processed in glue layer - * (Because NRVO may eliminate the copy), but inlining may remove - * ReturnStatement itself. To keep semantics we should insert - * temporary variable for postblit call. - * This is mostly the same as ReturnStatement::toIR. - */ - enum RET retmethod = tf->retStyle(); - if (retmethod == RETstack) - { - if (func->nrvo_can && func->nrvo_var) - ; - else - { - Type *tb = exp->type->toBasetype(); - if (exp->isLvalue() && tb->ty == Tstruct) - { StructDeclaration *sd = ((TypeStruct *)tb)->sym; - if (sd->postblit) - { FuncDeclaration *fd = sd->postblit; - if (fd->storage_class & STCdisable) - { - fd->toParent()->error(loc, "is not copyable because it is annotated with @disable"); - } - - /* Rewirte exp as: - * (__inlinectmp = exp), __inlinectmp.__postblit(), __inlinectmp - * And, __inlinectmp is marked as rvalue (See STCtemp comment) - */ - ExpInitializer *ei = new ExpInitializer(loc, exp); - - Identifier* tmp = Identifier::generateId("__inlinectmp"); - VarDeclaration *v = new VarDeclaration(loc, exp->type, tmp, ei); - v->storage_class = STCtemp; - v->linkage = LINKd; - v->parent = func; - - VarExp *ve = new VarExp(loc, v); - ve->type = exp->type; - - ei->exp = new ConstructExp(loc, ve, exp); - ei->exp->type = exp->type; - - DeclarationExp *de = new DeclarationExp(0, v); - de->type = Type::tvoid; - - Expression *e = new DotVarExp(ve->loc, ve, sd->postblit, 0); - e->type = sd->postblit->type; - e = new CallExp(ve->loc, e); - e->type = Type::tvoid; - - exp = Expression::combine(de, e); - exp = Expression::combine(exp, ve); - } - } - } - } } return this; } diff --git a/dmd2/interpret.c b/dmd2/interpret.c index b213d624..49e456e7 100644 --- a/dmd2/interpret.c +++ b/dmd2/interpret.c @@ -29,8 +29,7 @@ #include "template.h" #include "port.h" -int RealEquals(real_t x1, real_t x2); - +#include "ctfe.h" #define LOG 0 #define LOGASSIGN 0 @@ -39,7 +38,9 @@ int RealEquals(real_t x1, real_t x2); // Maximum allowable recursive function calls in CTFE #define CTFE_RECURSION_LIMIT 1000 -// The values of all CTFE variables. +/** + The values of all CTFE variables +*/ struct CtfeStack { private: @@ -65,103 +66,24 @@ private: size_t framepointer; // current frame pointer size_t maxStackPointer; // most stack we've ever used public: - CtfeStack() : framepointer(0), maxStackPointer(0) - { - } - size_t stackPointer() - { - return values.dim; - } + CtfeStack(); + + size_t stackPointer(); + // Largest number of stack positions we've used - size_t maxStackUsage() - { - return maxStackPointer; - } + size_t maxStackUsage(); // 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[v->ctfeAdrOnStack]; - } - assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); - return values[v->ctfeAdrOnStack]; - } - void setValue(VarDeclaration *v, Expression *e) - { - assert(!v->isDataseg() || v->isCTFE()); - assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); - values[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[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()); - assert(!(v->storage_class & (STCref | STCout))); - int oldid = v->ctfeAdrOnStack; - v->ctfeAdrOnStack = (size_t)(savedId[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[i]; - v->ctfeAdrOnStack = (size_t)(savedId[i]); - } - values.setDim(stackpointer); - vars.setDim(stackpointer); - savedId.setDim(stackpointer); - } - void saveGlobalConstant(VarDeclaration *v, Expression *e) - { -#if DMDV2 - assert( v->init && (v->isConst() || v->isImmutable()) && !v->isCTFE()); -#else - assert( v->init && v->isConst() && !v->isCTFE()); -#endif - v->ctfeAdrOnStack = globalValues.dim; - globalValues.push(e); - } + size_t startFrame(); + void endFrame(size_t oldframe); + bool isInCurrentFrame(VarDeclaration *v); + Expression *getValue(VarDeclaration *v); + void setValue(VarDeclaration *v, Expression *e); + void push(VarDeclaration *v); + void pop(VarDeclaration *v); + void popAll(size_t stackpointer); + void saveGlobalConstant(VarDeclaration *v, Expression *e); }; -CtfeStack ctfeStack; - - struct InterState { InterState *caller; // calling function's InterState @@ -178,22 +100,128 @@ struct InterState InterState(); }; +/************** CtfeStack ********************************************/ + +CtfeStack ctfeStack; + +CtfeStack::CtfeStack() : framepointer(0), maxStackPointer(0) +{ +} + +size_t CtfeStack::stackPointer() +{ + return values.dim; +} + +// Largest number of stack positions we've used +size_t CtfeStack::maxStackUsage() +{ + return maxStackPointer; +} + +// return the previous frame +size_t CtfeStack::startFrame() +{ + size_t oldframe = framepointer; + framepointer = stackPointer(); + return oldframe; +} + +void CtfeStack::endFrame(size_t oldframe) +{ + popAll(framepointer); + framepointer = oldframe; +} + +bool CtfeStack::isInCurrentFrame(VarDeclaration *v) +{ + if (v->isDataseg() && !v->isCTFE()) + return false; // It's a global + return v->ctfeAdrOnStack >= framepointer; +} + +Expression *CtfeStack::getValue(VarDeclaration *v) +{ + if ((v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE()) + { + assert(v->ctfeAdrOnStack >= 0 && + v->ctfeAdrOnStack < globalValues.dim); + return globalValues[v->ctfeAdrOnStack]; + } + assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); + return values[v->ctfeAdrOnStack]; +} + +void CtfeStack::setValue(VarDeclaration *v, Expression *e) +{ + assert(!v->isDataseg() || v->isCTFE()); + assert(v->ctfeAdrOnStack >= 0 && v->ctfeAdrOnStack < stackPointer()); + values[v->ctfeAdrOnStack] = e; +} + +void CtfeStack::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[v->ctfeAdrOnStack] = NULL; + return; + } + savedId.push((void *)(size_t)(v->ctfeAdrOnStack)); + v->ctfeAdrOnStack = values.dim; + vars.push(v); + values.push(NULL); +} + +void CtfeStack::pop(VarDeclaration *v) +{ + assert(!v->isDataseg() || v->isCTFE()); + assert(!(v->storage_class & (STCref | STCout))); + int oldid = v->ctfeAdrOnStack; + v->ctfeAdrOnStack = (int)(size_t)(savedId[oldid]); + if (v->ctfeAdrOnStack == values.dim - 1) + { + values.pop(); + vars.pop(); + savedId.pop(); + } +} + +void CtfeStack::popAll(size_t stackpointer) +{ + if (stackPointer() > maxStackPointer) + maxStackPointer = stackPointer(); + assert(values.dim >= stackpointer); + for (size_t i = stackpointer; i < values.dim; ++i) + { + VarDeclaration *v = vars[i]; + v->ctfeAdrOnStack = (size_t)(savedId[i]); + } + values.setDim(stackpointer); + vars.setDim(stackpointer); + savedId.setDim(stackpointer); +} + +void CtfeStack::saveGlobalConstant(VarDeclaration *v, Expression *e) +{ +#if DMDV2 + assert( v->init && (v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) && !v->isCTFE()); +#else + assert( v->init && v->isConst() && !v->isCTFE()); +#endif + v->ctfeAdrOnStack = globalValues.dim; + globalValues.push(e); +} + +/************** InterState ********************************************/ + InterState::InterState() { memset(this, 0, sizeof(InterState)); } -// Global status of the CTFE engine -struct CtfeStatus -{ - static int callDepth; // current number of recursive calls - 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 -}; +/************** CtfeStatus ********************************************/ int CtfeStatus::callDepth = 0; int CtfeStatus::stackTraceCallsToSuppress = 0; @@ -215,282 +243,9 @@ void printCtfePerformanceStats() Expression * resolveReferences(Expression *e, Expression *thisval); Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); VarDeclaration *findParentVar(Expression *e, Expression *thisval); -bool needToCopyLiteral(Expression *expr); -Expression *copyLiteral(Expression *e); -Expression *paintTypeOntoLiteral(Type *type, Expression *lit); -Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2); Expression *evaluateIfBuiltin(InterState *istate, Loc loc, FuncDeclaration *fd, Expressions *arguments, Expression *pthis); Expression *scrubReturnValue(Loc loc, Expression *e); -bool isAssocArray(Type *t); -bool isPointer(Type *t); -Expression *ctfeEqual(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2); - -// 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(); - } - VarDeclaration *getFieldAt(int index) - { - ClassDeclaration *cd = originalClass(); - size_t fieldsSoFar = 0; - while (index - fieldsSoFar >= cd->fields.dim) - { fieldsSoFar += cd->fields.dim; - cd = cd->baseClass; - } - return cd->fields[index - fieldsSoFar]; - } - // 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[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; - } - // Return index of the field, or -1 if not found - // Same as getFieldIndex, but checks for a direct match with the VarDeclaration - int findFieldIndexByName(VarDeclaration *v) - { - 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[j - fieldsSoFar]; - VarDeclaration *v2 = s->isVarDeclaration(); - if (v == v2) - { return value->elements->dim - fieldsSoFar - cd->fields.dim + (j-fieldsSoFar); - } - } - return -1; - } -}; - -struct VoidInitExp : Expression -{ - VarDeclaration *var; - - VoidInitExp(VarDeclaration *var, Type *type) - : Expression(var->loc, TOKvoid, sizeof(VoidInitExp)) - { - this->var = var; - this->type = var->type; - } - char *toChars() - { - return (char *)"void"; - } - Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue) - { - error("CTFE internal error: trying to read uninitialized variable"); - assert(0); - return EXP_CANT_INTERPRET; - } -}; - -// Return index of the field, or -1 if not found -// Same as getFieldIndex, but checks for a direct match with the VarDeclaration -int findFieldIndexByName(StructDeclaration *sd, VarDeclaration *v) -{ - for (int i = 0; i < sd->fields.dim; ++i) - { - if (sd->fields[i] == v) - return i; - } - 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 (char *)"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)) - errorSupplemental(loc, "thrown from here"); - } -}; - -// 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) -{ - for (int i = level; i>0; --i) printf(" "); - Expressions *elements = NULL; - // We need the struct definition to detect block assignment - StructDeclaration *sd = NULL; - 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); - } - else if (e->op == TOKarrayliteral) - { - elements = ((ArrayLiteralExp *)e)->elements; - printf("ARRAY LITERAL type=%s %p:\n", e->type->toChars(), - e); - } - else if (e->op == TOKassocarrayliteral) - { - printf("AA LITERAL type=%s %p:\n", e->type->toChars(), - e); - } - else if (e->op == TOKstring) - { - printf("STRING %s %p\n", e->toChars(), - ((StringExp *)e)->string); - } - else if (e->op == TOKslice) - { - printf("SLICE %p: %s\n", e, e->toChars()); - showCtfeExpr(((SliceExp *)e)->e1, level + 1); - } - else if (e->op == TOKvar) - { - printf("VAR %p %s\n", e, e->toChars()); - VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); - if (v && v->getValue()) - showCtfeExpr(v->getValue(), level + 1); - } - else if (isPointer(e->type)) - { - // This is potentially recursive. We mustn't try to print the thing we're pointing to. - if (e->op == TOKindex) - printf("POINTER %p into %p [%s]\n", e, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2->toChars()); - else if (e->op == TOKdotvar) - printf("POINTER %p to %p .%s\n", e, ((DotVarExp *)e)->e1, ((DotVarExp *)e)->var->toChars()); - else - printf("POINTER %p: %s\n", e, e->toChars()); - } - else - printf("VALUE %p: %s\n", e, e->toChars()); - - if (elements) - { - size_t fieldsSoFar = 0; - for (size_t i = 0; i < elements->dim; i++) - { Expression *z = NULL; - Dsymbol *s = NULL; - if (i > 15) { - printf("...(total %d elements)\n", elements->dim); - return; - } - if (sd) - { s = sd->fields[i]; - z = (*elements)[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[i - fieldsSoFar]; - size_t indx = (elements->dim - fieldsSoFar)- cd->fields.dim + i; - assert(indx >= 0); - assert(indx < elements->dim); - z = (*elements)[indx]; - } - if (!z) { - for (int j = level; j>0; --j) printf(" "); - printf(" void\n"); - continue; - } - - if (s) - { - VarDeclaration *v = s->isVarDeclaration(); - assert(v); - // If it is a void assignment, use the default initializer - if ((v->type->ty != z->type->ty) && v->type->ty == Tsarray) - { - for (int j = level; --j;) printf(" "); - printf(" field: block initalized static array\n"); - continue; - } - } - showCtfeExpr(z, level + 1); - } - } -} /************************************* * @@ -516,11 +271,13 @@ Expression *Expression::ctfeInterpret() Expression *FuncDeclaration::interpret(InterState *istate, Expressions *arguments, Expression *thisarg) { #if LOG - printf("\n********\nFuncDeclaration::interpret(istate = %p) %s\n", istate, toChars()); + printf("\n********\n%s FuncDeclaration::interpret(istate = %p) %s\n", loc.toChars(), istate, toChars()); #endif if (semanticRun == PASSsemantic3) + { + error("circular dependency. Functions cannot be interpreted while being compiled"); return EXP_CANT_INTERPRET; - + } if (semanticRun < PASSsemantic3 && scope) { /* Forward reference - we need to run semantic3 on this function. @@ -783,7 +540,7 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument Expression *Statement::interpret(InterState *istate) { #if LOG - printf("Statement::interpret()\n"); + printf("%s Statement::interpret()\n", loc.toChars()); #endif START() error("Statement %s cannot be interpreted at compile time", this->toChars()); @@ -793,7 +550,7 @@ Expression *Statement::interpret(InterState *istate) Expression *ExpStatement::interpret(InterState *istate) { #if LOG - printf("ExpStatement::interpret(%s)\n", exp ? exp->toChars() : ""); + printf("%s ExpStatement::interpret(%s)\n", loc.toChars(), exp ? exp->toChars() : ""); #endif START() if (exp) @@ -814,7 +571,7 @@ Expression *CompoundStatement::interpret(InterState *istate) { Expression *e = NULL; #if LOG - printf("CompoundStatement::interpret()\n"); + printf("%s CompoundStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -832,7 +589,7 @@ Expression *CompoundStatement::interpret(InterState *istate) } } #if LOG - printf("-CompoundStatement::interpret() %p\n", e); + printf("%s -CompoundStatement::interpret() %p\n", loc.toChars(), e); #endif return e; } @@ -841,7 +598,7 @@ Expression *UnrolledLoopStatement::interpret(InterState *istate) { Expression *e = NULL; #if LOG - printf("UnrolledLoopStatement::interpret()\n"); + printf("%s UnrolledLoopStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -877,17 +634,10 @@ Expression *UnrolledLoopStatement::interpret(InterState *istate) return e; } -// For CTFE only. Returns true if 'e' is TRUE or a non-null pointer. -int isTrueBool(Expression *e) -{ - return e->isBool(TRUE) || ((e->type->ty == Tpointer || e->type->ty == Tclass) - && e->op != TOKnull); -} - Expression *IfStatement::interpret(InterState *istate) { #if LOG - printf("IfStatement::interpret(%s)\n", condition->toChars()); + printf("%s IfStatement::interpret(%s)\n", loc.toChars(), condition->toChars()); #endif if (istate->start == this) @@ -924,118 +674,74 @@ Expression *IfStatement::interpret(InterState *istate) Expression *ScopeStatement::interpret(InterState *istate) { #if LOG - printf("ScopeStatement::interpret()\n"); + printf("%s ScopeStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; return statement ? statement->interpret(istate) : NULL; } -Expression *resolveSlice(Expression *e) -{ - if ( ((SliceExp *)e)->e1->op == TOKnull) - return ((SliceExp *)e)->e1; - return Slice(e->type, ((SliceExp *)e)->e1, - ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); -} -/* Determine the array length, without interpreting it. - * e must be an array literal, or a slice - * It's very wasteful to resolve the slice when we only - * need the length. - */ -uinteger_t resolveArrayLength(Expression *e) +/** + Given an expression e which is about to be returned from the current + function, generate an error if it contains pointers to local variables. + Return true if it is safe to return, false if an error was generated. + + Only checks expressions passed by value (pointers to local variables + may already be stored in members of classes, arrays, or AAs which + were passed as mutable function parameters). +*/ +bool stopPointersEscapingFromArray(Loc loc, Expressions *elems); + +bool stopPointersEscaping(Loc loc, Expression *e) { - if (e->op == TOKnull) - return 0; - if (e->op == TOKslice) - { uinteger_t ilo = ((SliceExp *)e)->lwr->toInteger(); - uinteger_t iup = ((SliceExp *)e)->upr->toInteger(); - return iup - ilo; + if (!e->type->hasPointers()) + return true; + if ( isPointer(e->type) ) + { + if (e->op == TOKvar && ((VarExp *)e)->var->isVarDeclaration() && + ctfeStack.isInCurrentFrame( ((VarExp *)e)->var->isVarDeclaration() ) ) + { error(loc, "returning a pointer to a local stack variable"); + return false; + } + // TODO: If it is a TOKdotvar or TOKindex, we should check that it is not + // pointing to a local struct or static array. } - if (e->op == TOKstring) - { return ((StringExp *)e)->len; + if (e->op == TOKstructliteral) + { + StructLiteralExp *se = (StructLiteralExp *)e; + return stopPointersEscapingFromArray(loc, se->elements); } if (e->op == TOKarrayliteral) - { ArrayLiteralExp *ale = (ArrayLiteralExp *)e; - return ale->elements ? ale->elements->dim : 0; + { + return stopPointersEscapingFromArray(loc, ((ArrayLiteralExp *)e)->elements); } if (e->op == TOKassocarrayliteral) - { AssocArrayLiteralExp *ale = (AssocArrayLiteralExp *)e; - return ale->keys->dim; + { + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; + if (!stopPointersEscapingFromArray(loc, aae->keys)) + return false; + return stopPointersEscapingFromArray(loc, aae->values); } - assert(0); - return 0; + return true; } -Expression *ctfeCat(Type *type, Expression *e1, Expression *e2) +// Check all members of an array for escaping local variables. Return false if error +bool stopPointersEscapingFromArray(Loc loc, Expressions *elems) { - Loc loc = e1->loc; - Type *t1 = e1->type->toBasetype(); - Type *t2 = e2->type->toBasetype(); - Expression *e; - if (e2->op == TOKstring && e1->op == TOKarrayliteral && - t1->nextOf()->isintegral()) + for (size_t i = 0; i < elems->dim; i++) { - // [chars] ~ string => string (only valid for CTFE) - StringExp *es1 = (StringExp *)e2; - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e1; - size_t len = es1->len + es2->elements->dim; - int sz = es1->sz; - - void *s = mem.malloc((len + 1) * sz); - memcpy((char *)s + sz * es2->elements->dim, es1->string, es1->len * sz); - for (size_t i = 0; i < es2->elements->dim; i++) - { Expression *es2e = es2->elements->tdata()[i]; - if (es2e->op != TOKint64) - return EXP_CANT_INTERPRET; - dinteger_t v = es2e->toInteger(); - memcpy((unsigned char *)s + i * sz, &v, sz); - } - - // Add terminating 0 - memset((unsigned char *)s + len * sz, 0, sz); - - StringExp *es = new StringExp(loc, s, len); - es->sz = sz; - es->committed = 0; - es->type = type; - e = es; - return e; + Expression *m = elems->tdata()[i]; + if (!m) + continue; + if (m) + if ( !stopPointersEscaping(loc, m) ) + return false; } - else if (e1->op == TOKstring && e2->op == TOKarrayliteral && - t2->nextOf()->isintegral()) - { - // string ~ [chars] => string (only valid for CTFE) - // Concatenate the strings - StringExp *es1 = (StringExp *)e1; - ArrayLiteralExp *es2 = (ArrayLiteralExp *)e2; - size_t len = es1->len + es2->elements->dim; - int sz = es1->sz; - - void *s = mem.malloc((len + 1) * sz); - memcpy(s, es1->string, es1->len * sz); - for (size_t i = 0; i < es2->elements->dim; i++) - { Expression *es2e = es2->elements->tdata()[i]; - if (es2e->op != TOKint64) - return EXP_CANT_INTERPRET; - dinteger_t v = es2e->toInteger(); - memcpy((unsigned char *)s + (es1->len + i) * sz, &v, sz); - } - - // Add terminating 0 - memset((unsigned char *)s + len * sz, 0, sz); - - StringExp *es = new StringExp(loc, s, len); - es->sz = sz; - es->committed = 0; //es1->committed; - es->type = type; - e = es; - return e; - } - return Cat(type, e1, e2); + return true; } + bool scrubArray(Loc loc, Expressions *elems, bool structlit = false); /* All results destined for use outside of CTFE need to have their CTFE-specific @@ -1110,7 +816,7 @@ bool scrubArray(Loc loc, Expressions *elems, bool structlit) Expression *ReturnStatement::interpret(InterState *istate) { #if LOG - printf("ReturnStatement::interpret(%s)\n", exp ? exp->toChars() : ""); + printf("%s ReturnStatement::interpret(%s)\n", loc.toChars(), exp ? exp->toChars() : ""); #endif START() if (!exp) @@ -1143,11 +849,24 @@ Expression *ReturnStatement::interpret(InterState *istate) // return a value OR a pointer Expression *e; if ( isPointer(exp->type) ) - e = exp->interpret(istate, ctfeNeedLvalue); + { e = exp->interpret(istate, ctfeNeedLvalue); + if (exceptionOrCantInterpret(e)) + return e; + // Disallow returning pointers to stack-allocated variables (bug 7876) + if (e->op == TOKvar && ((VarExp *)e)->var->isVarDeclaration() && + ctfeStack.isInCurrentFrame( ((VarExp *)e)->var->isVarDeclaration() ) ) + { error("returning a pointer to a local stack variable"); + return EXP_CANT_INTERPRET; + } + } else + { e = exp->interpret(istate); - if (exceptionOrCantInterpret(e)) - return e; + if (exceptionOrCantInterpret(e)) + return e; + if (!stopPointersEscaping(loc, e)) + return EXP_CANT_INTERPRET; + } if (needToCopyLiteral(e)) e = copyLiteral(e); #if LOGASSIGN @@ -1160,7 +879,7 @@ Expression *ReturnStatement::interpret(InterState *istate) Expression *BreakStatement::interpret(InterState *istate) { #if LOG - printf("BreakStatement::interpret()\n"); + printf("%s BreakStatement::interpret()\n", loc.toChars()); #endif START() if (ident) @@ -1184,7 +903,7 @@ Expression *BreakStatement::interpret(InterState *istate) Expression *ContinueStatement::interpret(InterState *istate) { #if LOG - printf("ContinueStatement::interpret()\n"); + printf("%s ContinueStatement::interpret()\n", loc.toChars()); #endif START() if (ident) @@ -1214,7 +933,7 @@ Expression *WhileStatement::interpret(InterState *istate) Expression *DoStatement::interpret(InterState *istate) { #if LOG - printf("DoStatement::interpret()\n"); + printf("%s DoStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -1267,7 +986,7 @@ Expression *DoStatement::interpret(InterState *istate) Expression *ForStatement::interpret(InterState *istate) { #if LOG - printf("ForStatement::interpret()\n"); + printf("%s ForStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -1348,7 +1067,7 @@ Expression *ForeachRangeStatement::interpret(InterState *istate) Expression *SwitchStatement::interpret(InterState *istate) { #if LOG - printf("SwitchStatement::interpret()\n"); + printf("%s SwitchStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -1376,8 +1095,6 @@ Expression *SwitchStatement::interpret(InterState *istate) Expression *econdition = condition->interpret(istate); if (exceptionOrCantInterpret(econdition)) return econdition; - if (econdition->op == TOKslice) - econdition = resolveSlice(econdition); Statement *s = NULL; if (cases) @@ -1388,10 +1105,8 @@ Expression *SwitchStatement::interpret(InterState *istate) Expression * caseExp = cs->exp->interpret(istate); if (exceptionOrCantInterpret(caseExp)) return caseExp; - e = ctfeEqual(caseExp->loc, TOKequal, Type::tint32, econdition, caseExp); - if (exceptionOrCantInterpret(e)) - return e; - if (e->isBool(TRUE)) + int eq = ctfeEqual(caseExp->loc, TOKequal, econdition, caseExp); + if (eq) { s = cs; break; } @@ -1425,7 +1140,7 @@ Expression *SwitchStatement::interpret(InterState *istate) Expression *CaseStatement::interpret(InterState *istate) { #if LOG - printf("CaseStatement::interpret(%s) this = %p\n", exp->toChars(), this); + printf("%s CaseStatement::interpret(%s) this = %p\n", loc.toChars(), exp->toChars(), this); #endif if (istate->start == this) istate->start = NULL; @@ -1438,7 +1153,7 @@ Expression *CaseStatement::interpret(InterState *istate) Expression *DefaultStatement::interpret(InterState *istate) { #if LOG - printf("DefaultStatement::interpret()\n"); + printf("%s DefaultStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -1451,7 +1166,7 @@ Expression *DefaultStatement::interpret(InterState *istate) Expression *GotoStatement::interpret(InterState *istate) { #if LOG - printf("GotoStatement::interpret()\n"); + printf("%s GotoStatement::interpret()\n", loc.toChars()); #endif START() assert(label && label->statement); @@ -1462,7 +1177,7 @@ Expression *GotoStatement::interpret(InterState *istate) Expression *GotoCaseStatement::interpret(InterState *istate) { #if LOG - printf("GotoCaseStatement::interpret()\n"); + printf("%s GotoCaseStatement::interpret()\n", loc.toChars()); #endif START() assert(cs); @@ -1473,7 +1188,7 @@ Expression *GotoCaseStatement::interpret(InterState *istate) Expression *GotoDefaultStatement::interpret(InterState *istate) { #if LOG - printf("GotoDefaultStatement::interpret()\n"); + printf("%s GotoDefaultStatement::interpret()\n", loc.toChars()); #endif START() assert(sw && sw->sdefault); @@ -1484,7 +1199,7 @@ Expression *GotoDefaultStatement::interpret(InterState *istate) Expression *LabelStatement::interpret(InterState *istate) { #if LOG - printf("LabelStatement::interpret()\n"); + printf("%s LabelStatement::interpret()\n", loc.toChars()); #endif if (istate->start == this) istate->start = NULL; @@ -1495,7 +1210,7 @@ Expression *LabelStatement::interpret(InterState *istate) Expression *TryCatchStatement::interpret(InterState *istate) { #if LOG - printf("TryCatchStatement::interpret()\n"); + printf("%s TryCatchStatement::interpret()\n", loc.toChars()); #endif START() Expression *e = body ? body->interpret(istate) : NULL; @@ -1567,7 +1282,7 @@ ThrownExceptionExp *chainExceptions(ThrownExceptionExp *oldest, ThrownExceptionE Expression *TryFinallyStatement::interpret(InterState *istate) { #if LOG - printf("TryFinallyStatement::interpret()\n"); + printf("%s TryFinallyStatement::interpret()\n", loc.toChars()); #endif START() Expression *e = body ? body->interpret(istate) : NULL; @@ -1589,7 +1304,7 @@ Expression *TryFinallyStatement::interpret(InterState *istate) Expression *ThrowStatement::interpret(InterState *istate) { #if LOG - printf("ThrowStatement::interpret()\n"); + printf("%s ThrowStatement::interpret()\n", loc.toChars()); #endif START() Expression *e = exp->interpret(istate); @@ -1608,7 +1323,7 @@ Expression *OnScopeStatement::interpret(InterState *istate) Expression *WithStatement::interpret(InterState *istate) { #if LOG - printf("WithStatement::interpret()\n"); + printf("%s WithStatement::interpret()\n", loc.toChars()); #endif START() Expression *e = exp->interpret(istate); @@ -1629,7 +1344,7 @@ Expression *WithStatement::interpret(InterState *istate) Expression *AsmStatement::interpret(InterState *istate) { #if LOG - printf("AsmStatement::interpret()\n"); + printf("%s AsmStatement::interpret()\n", loc.toChars()); #endif START() error("asm statements cannot be interpreted at compile time"); @@ -1652,7 +1367,7 @@ Expression *ImportStatement::interpret(InterState *istate) Expression *Expression::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("Expression::interpret() %s\n", toChars()); + printf("%s Expression::interpret() %s\n", loc.toChars(), toChars()); printf("type = %s\n", type->toChars()); dump(0); #endif @@ -1680,7 +1395,7 @@ Expression *NullExp::interpret(InterState *istate, CtfeGoal goal) Expression *IntegerExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("IntegerExp::interpret() %s\n", toChars()); + printf("%s IntegerExp::interpret() %s\n", loc.toChars(), toChars()); #endif return this; } @@ -1688,7 +1403,7 @@ Expression *IntegerExp::interpret(InterState *istate, CtfeGoal goal) Expression *RealExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("RealExp::interpret() %s\n", toChars()); + printf("%s RealExp::interpret() %s\n", loc.toChars(), toChars()); #endif return this; } @@ -1701,7 +1416,7 @@ Expression *ComplexExp::interpret(InterState *istate, CtfeGoal goal) Expression *StringExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("StringExp::interpret() %s\n", toChars()); + printf("%s StringExp::interpret() %s\n", loc.toChars(), toChars()); #endif /* In both D1 and D2, attempts to modify string literals are prevented * in BinExp::interpretAssignCommon. @@ -1724,36 +1439,15 @@ Expression *StringExp::interpret(InterState *istate, CtfeGoal goal) Expression *FuncExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("FuncExp::interpret() %s\n", toChars()); + printf("%s FuncExp::interpret() %s\n", loc.toChars(), toChars()); #endif 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 - printf("SymOffExp::interpret() %s\n", toChars()); + printf("%s SymOffExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (var->isFuncDeclaration() && offset == 0) { @@ -1829,7 +1523,7 @@ Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("AddrExp::interpret() %s\n", toChars()); + printf("%s AddrExp::interpret() %s\n", loc.toChars(), toChars()); #endif #if IN_LLVM if (e1->op == TOKvar) @@ -1899,7 +1593,7 @@ Expression *AddrExp::interpret(InterState *istate, CtfeGoal goal) Expression *DelegateExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("DelegateExp::interpret() %s\n", toChars()); + printf("%s DelegateExp::interpret() %s\n", loc.toChars(), toChars()); #endif return this; } @@ -1924,23 +1618,24 @@ Expression * resolveReferences(Expression *e, Expression *thisval) { VarExp *ve = (VarExp *)e; VarDeclaration *v = ve->var->isVarDeclaration(); - assert(v); + assert(v); 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->getValue() && (v->getValue()->op == TOKslice)) + Expression *val = v->getValue(); + if (val && (val->op == TOKslice)) { - SliceExp *se = (SliceExp *)v->getValue(); + SliceExp *se = (SliceExp *)val; if (se->e1->op == TOKarrayliteral || se->e1->op == TOKassocarrayliteral || se->e1->op == TOKstring) break; - e = v->getValue(); + e = val; continue; } - else if (v->getValue() && (v->getValue()->op==TOKindex || v->getValue()->op == TOKdotvar - || v->getValue()->op == TOKthis )) + else if (val && (val->op==TOKindex || val->op == TOKdotvar + || val->op == TOKthis || val->op == TOKvar)) { - e = v->getValue(); + e = val; continue; } } @@ -1965,9 +1660,10 @@ 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->hasValue()) + if ((v->isConst() || v->isImmutable() || v->storage_class & STCmanifest) + && v->init && !v->hasValue() && !v->isCTFE()) #else - if (v->isConst() && v->init) + if (v->isConst() && v->init && !v->isCTFE()) #endif { e = v->init->toExpression(); if (e && (e->op == TOKconstruct || e->op == TOKblit)) @@ -2017,7 +1713,7 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal else e = v->type->defaultInitLiteral(loc); } - else if (!v->isDataseg() && !v->isCTFE() && !istate) + else if (!(v->isDataseg() || v->storage_class & STCmanifest) && !v->isCTFE() && !istate) { error(loc, "variable %s cannot be read at compile time", v->toChars()); return EXP_CANT_INTERPRET; } @@ -2078,7 +1774,7 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal } #if !IN_LLVM else - error(loc, "cannot interpret symbol %s at compile time", v->toChars()); + error(loc, "cannot interpret symbol %s at compile time", s->toChars()); #endif } else @@ -2089,7 +1785,7 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("VarExp::interpret() %s\n", toChars()); + printf("%s VarExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (goal == ctfeNeedLvalueRef) { @@ -2098,9 +1794,17 @@ Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) { error("variable %s cannot be referenced at compile time", v->toChars()); return EXP_CANT_INTERPRET; } - 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; + else if (v && !v->hasValue()) + { + if (!v->isCTFE() && v->isDataseg()) + error("static variable %s cannot be referenced at compile time", v->toChars()); + else // CTFE initiated from inside a function + error("variable %s cannot be read at compile time", v->toChars()); + return EXP_CANT_INTERPRET; + } + else if (v && v->hasValue() && v->getValue()->op == TOKvar) + { // A ref of a reference, is the original reference + return v->getValue(); } return this; } @@ -2114,7 +1818,7 @@ Expression *VarExp::interpret(InterState *istate, CtfeGoal goal) Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("DeclarationExp::interpret() %s\n", toChars()); + printf("%s DeclarationExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression *e; VarDeclaration *v = declaration->isVarDeclaration(); @@ -2125,7 +1829,7 @@ Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) TupleDeclaration *td =v->toAlias()->isTupleDeclaration(); if (!td->objects) return NULL; - for(int i= 0; i < td->objects->dim; ++i) + for(size_t i= 0; i < td->objects->dim; ++i) { Object * o = td->objects->tdata()[i]; Expression *ex = isExpression(o); @@ -2143,7 +1847,7 @@ Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) { ExpInitializer *ie = v->init->isExpInitializer(); if (ie) - e = ie->exp->interpret(istate); + e = ie->exp->interpret(istate, goal); else if (v->init->isVoidInitializer()) { e = v->type->voidInitLiteral(v); @@ -2208,7 +1912,7 @@ Expression *DeclarationExp::interpret(InterState *istate, CtfeGoal goal) Expression *TupleExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("TupleExp::interpret() %s\n", toChars()); + printf("%s TupleExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expressions *expsx = NULL; @@ -2260,10 +1964,10 @@ Expression *ArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) { Expressions *expsx = NULL; #if LOG - printf("ArrayLiteralExp::interpret() %s\n", toChars()); + printf("%s ArrayLiteralExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (ownedByCtfe) // We've already interpreted all the elements - return copyLiteral(this); + return this; if (elements) { for (size_t i = 0; i < elements->dim; i++) @@ -2324,7 +2028,7 @@ Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) Expressions *valuesx = values; #if LOG - printf("AssocArrayLiteralExp::interpret() %s\n", toChars()); + printf("%s AssocArrayLiteralExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (ownedByCtfe) // We've already interpreted all the elements return copyLiteral(this); @@ -2375,14 +2079,10 @@ Expression *AssocArrayLiteralExp::interpret(InterState *istate, CtfeGoal goal) */ for (size_t i = 1; i < keysx->dim; i++) { Expression *ekey = keysx->tdata()[i - 1]; - if (ekey->op == TOKslice) - ekey = resolveSlice(ekey); for (size_t j = i; j < keysx->dim; j++) { Expression *ekey2 = keysx->tdata()[j]; - Expression *ex = ctfeEqual(loc, TOKequal, Type::tbool, ekey, ekey2); - if (ex == EXP_CANT_INTERPRET) - goto Lerr; - if (ex->isBool(TRUE)) // if a match + int eq = ctfeEqual(loc, TOKequal, ekey, ekey2); + if (eq) // if a match { // Remove ekey if (keysx == keys) @@ -2419,7 +2119,7 @@ Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) { Expressions *expsx = NULL; #if LOG - printf("StructLiteralExp::interpret() %s\n", toChars()); + printf("%s StructLiteralExp::interpret() %s\n", loc.toChars(), toChars()); #endif /* We don't know how to deal with overlapping fields */ @@ -2475,54 +2175,6 @@ Expression *StructLiteralExp::interpret(InterState *istate, CtfeGoal goal) return copyLiteral(this); } -/****************************** - * Helper for NewExp - * Create an array literal consisting of 'elem' duplicated 'dim' times. - */ -ArrayLiteralExp *createBlockDuplicatedArrayLiteral(Loc loc, Type *type, - Expression *elem, size_t dim) -{ - Expressions *elements = new Expressions(); - elements->setDim(dim); - bool mustCopy = needToCopyLiteral(elem); - for (size_t i = 0; i < dim; i++) - { if (mustCopy) - elem = copyLiteral(elem); - (*elements)[i] = elem; - } - ArrayLiteralExp *ae = new ArrayLiteralExp(loc, elements); - ae->type = type; - ae->ownedByCtfe = true; - return ae; -} - -/****************************** - * Helper for NewExp - * Create a string literal consisting of 'value' duplicated 'dim' times. - */ -StringExp *createBlockDuplicatedStringLiteral(Loc loc, Type *type, - unsigned value, size_t dim, int sz) -{ - unsigned char *s; - s = (unsigned char *)mem.calloc(dim + 1, sz); - for (size_t elemi=0; elemitype = type; - se->sz = sz; - se->committed = true; - se->ownedByCtfe = true; - return se; -} - // Create an array literal of type 'newtype' with dimensions given by // 'arguments'[argnum..$] Expression *recursivelyCreateArrayLiteral(Loc loc, Type *newtype, InterState *istate, @@ -2563,7 +2215,7 @@ Expression *recursivelyCreateArrayLiteral(Loc loc, Type *newtype, InterState *is Expression *NewExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("NewExp::interpret() %s\n", toChars()); + printf("%s NewExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (newtype->ty == Tarray && arguments) return recursivelyCreateArrayLiteral(loc, newtype, istate, arguments, 0); @@ -2645,7 +2297,7 @@ Expression *UnaExp::interpretCommon(InterState *istate, CtfeGoal goal, Expressi Expression *e1; #if LOG - printf("UnaExp::interpretCommon() %s\n", toChars()); + printf("%s UnaExp::interpretCommon() %s\n", loc.toChars(), toChars()); #endif e1 = this->e1->interpret(istate); if (exceptionOrCantInterpret(e1)) @@ -2665,135 +2317,6 @@ UNA_INTERPRET(Com) UNA_INTERPRET(Not) UNA_INTERPRET(Bool) -Expression *getAggregateFromPointer(Expression *e, dinteger_t *ofs) -{ - *ofs = 0; - if (e->op == TOKaddress) - e = ((AddrExp *)e)->e1; - if (e->op == TOKdotvar) - { - Expression *ex = ((DotVarExp *)e)->e1; - VarDeclaration *v = ((DotVarExp *)e)->var->isVarDeclaration(); - assert(v); - 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(e->type, v->offset); - else - i = se->getFieldIndex(e->type, v->offset); - assert(i != -1); - e = se->elements->tdata()[i]; - } - if (e->op == TOKindex) - { - IndexExp *ie = (IndexExp *)e; - // Note that each AA element is part of its own memory block - if ((ie->e1->type->ty == Tarray || ie->e1->type->ty == Tsarray - || ie->e1->op == TOKstring || ie->e1->op==TOKarrayliteral) && - ie->e2->op == TOKint64) - { - *ofs = ie->e2->toInteger(); - return ie->e1; - } - } - return e; -} - -// return e1 - e2 as an integer, or error if not possible -Expression *pointerDifference(Loc loc, Type *type, Expression *e1, Expression *e2) -{ - dinteger_t ofs1, ofs2; - Expression *agg1 = getAggregateFromPointer(e1, &ofs1); - Expression *agg2 = getAggregateFromPointer(e2, &ofs2); - if (agg1 == agg2) - { - Type *pointee = ((TypePointer *)agg1->type)->next; - dinteger_t sz = pointee->size(); - return new IntegerExp(loc, (ofs1-ofs2)*sz, type); - } - else if (agg1->op == TOKstring && agg2->op == TOKstring) - { - if (((StringExp *)agg1)->string == ((StringExp *)agg2)->string) - { - Type *pointee = ((TypePointer *)agg1->type)->next; - dinteger_t sz = pointee->size(); - return new IntegerExp(loc, (ofs1-ofs2)*sz, type); - } - } -#if LOGASSIGN - printf("FAILED POINTER DIFF\n"); - showCtfeExpr(agg1); - showCtfeExpr(agg2); -#endif - error(loc, "%s - %s cannot be interpreted at compile time: cannot subtract " - "pointers to two different memory blocks", - e1->toChars(), e2->toChars()); - return EXP_CANT_INTERPRET; -} - -// Return eptr op e2, where eptr is a pointer, e2 is an integer, -// and op is TOKadd or TOKmin -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; - Expression *agg1 = getAggregateFromPointer(eptr, &ofs1); - if (agg1->op != TOKstring && agg1->op != TOKarrayliteral) - { - error(loc, "cannot perform pointer arithmetic on non-arrays at compile time"); - return EXP_CANT_INTERPRET; - } - ofs2 = e2->toInteger(); - Type *pointee = ((TypePointer *)agg1->type)->next; - dinteger_t sz = pointee->size(); - Expression *dollar = ArrayLength(Type::tsize_t, agg1); - assert(dollar != EXP_CANT_INTERPRET); - dinteger_t len = dollar->toInteger(); - - Expression *val = agg1; - TypeArray *tar = (TypeArray *)val->type; - dinteger_t indx = ofs1; -#if IN_LLVM // LDC: llvm uses typesafe pointer arithmetic - if (op == TOKadd || op == TOKaddass || op == TOKplusplus) - indx += ofs2; - else if (op == TOKmin || op == TOKminass || op == TOKminusminus) - indx -= ofs2; -#else - if (op == TOKadd || op == TOKaddass || op == TOKplusplus) - indx = indx + ofs2/sz; - else if (op == TOKmin || op == TOKminass || op == TOKminusminus) - indx -= ofs2/sz; -#endif - else - { - error(loc, "CTFE Internal compiler error: bad pointer operation"); - return EXP_CANT_INTERPRET; - } - if (val->op != TOKarrayliteral && val->op != TOKstring) - { - error(loc, "CTFE Internal compiler error: pointer arithmetic %s", val->toChars()); - return EXP_CANT_INTERPRET; - } - if (indx < 0 || indx > len) - { - error(loc, "cannot assign pointer to index %lld inside memory block [0..%lld]", indx, len); - return EXP_CANT_INTERPRET; - } - - IntegerExp *ofs = new IntegerExp(loc, indx, Type::tsize_t); - IndexExp *ie = new IndexExp(loc, val, ofs); - ie->type = type; - return ie; -} - typedef Expression *(*fp_t)(Type *, Expression *, Expression *); Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) @@ -2802,7 +2325,7 @@ Expression *BinExp::interpretCommon(InterState *istate, CtfeGoal goal, fp_t fp) Expression *e2; #if LOG - printf("BinExp::interpretCommon() %s\n", toChars()); + printf("%s BinExp::interpretCommon() %s\n", loc.toChars(), toChars()); #endif if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer && op == TOKmin) { @@ -2882,312 +2405,16 @@ BIN_INTERPRET(Pow) #endif -typedef Expression *(*fp2_t)(Loc loc, enum TOK, Type *, Expression *, Expression *); +typedef int (*fp2_t)(Loc loc, enum TOK, Expression *, Expression *); -/** Return true if agg1 and agg2 are pointers to the same memory block -*/ -bool pointToSameMemoryBlock(Expression *agg1, Expression *agg2) + +Expression *BinExp::interpretCompareCommon(InterState *istate, CtfeGoal goal, fp2_t fp) { - // Note that type painting can occur with VarExp, so we - // must compare the variables being pointed to. - return agg1 == agg2 || - (agg1->op == TOKvar && agg2->op == TOKvar && - ((VarExp *)agg1)->var == ((VarExp *)agg2)->var); -} - -// Return 1 if true, 0 if false -// -1 if comparison is illegal because they point to non-comparable memory blocks -int comparePointers(Loc loc, enum TOK op, Type *type, Expression *agg1, dinteger_t ofs1, Expression *agg2, dinteger_t ofs2) -{ - if ( pointToSameMemoryBlock(agg1, agg2) ) - { - dinteger_t cm = ofs1 - ofs2; - dinteger_t n; - dinteger_t zero = 0; - switch(op) - { - case TOKlt: n = (ofs1 < ofs2); break; - case TOKle: n = (ofs1 <= ofs2); break; - case TOKgt: n = (ofs1 > ofs2); break; - case TOKge: n = (ofs1 >= ofs2); break; - case TOKidentity: - case TOKequal: n = (ofs1 == ofs2); break; - case TOKnotidentity: - case TOKnotequal: n = (ofs1 != ofs2); break; - default: - assert(0); - } - return n; - } - bool null1 = ( agg1->op == TOKnull ); - bool null2 = ( agg2->op == TOKnull ); - - int cmp; - if (null1 || null2) - { - switch (op) - { - case TOKlt: cmp = null1 && !null2; break; - case TOKgt: cmp = !null1 && null2; break; - case TOKle: cmp = null1; break; - case TOKge: cmp = null2; break; - case TOKidentity: - case TOKequal: - case TOKnotidentity: // 'cmp' gets inverted below - case TOKnotequal: - cmp = (null1 == null2); - break; - } - } - else - { - switch(op) - { - case TOKidentity: - case TOKequal: - case TOKnotidentity: // 'cmp' gets inverted below - case TOKnotequal: - cmp = 0; - break; - default: - return -1; // memory blocks are different - } - } - if (op == TOKnotidentity || op == TOKnotequal) - cmp ^= 1; - return cmp; -} - - -int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2); - -/* Conceptually the same as memcmp(e1, e2). - * e1 and e2 may be strings, arrayliterals, or slices. - * For string types, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. - * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. - */ -int ctfeCmpArrays(Loc loc, Expression *e1, Expression *e2, uinteger_t len) -{ - // Resolve slices, if necessary - uinteger_t lo1 = 0; - uinteger_t lo2 = 0; - - Expression *x = e1; - if (x->op == TOKslice) - { lo1 = ((SliceExp *)x)->lwr->toInteger(); - x = ((SliceExp*)x)->e1; - } - StringExp *se1 = (x->op == TOKstring) ? (StringExp *)x : 0; - ArrayLiteralExp *ae1 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0; - - x = e2; - if (x->op == TOKslice) - { lo2 = ((SliceExp *)x)->lwr->toInteger(); - x = ((SliceExp*)x)->e1; - } - StringExp *se2 = (x->op == TOKstring) ? (StringExp *)x : 0; - ArrayLiteralExp *ae2 = (x->op == TOKarrayliteral) ? (ArrayLiteralExp *)x : 0; - - // Now both must be either TOKarrayliteral or TOKstring - if (se1 && se2) - return sliceCmpStringWithString(se1, se2, lo1, lo2, len); - if (se1 && ae2) - return sliceCmpStringWithArray(se1, ae2, lo1, lo2, len); - if (se2 && ae1) - return -sliceCmpStringWithArray(se2, ae1, lo2, lo1, len); - - assert (ae1 && ae2); - // Comparing two array literals. This case is potentially recursive. - // If they aren't strings, we just need an equality check rather than - // a full cmp. - bool needCmp = ae1->type->nextOf()->isintegral(); - for (size_t i = 0; i < len; i++) - { Expression *ee1 = (*ae1->elements)[lo1 + i]; - Expression *ee2 = (*ae2->elements)[lo2 + i]; - if (needCmp) - { int c = ee1->toInteger() - ee2->toInteger(); - if (c) - return c; - } - else - { if (ctfeRawCmp(loc, ee1, ee2)) - return 1; - } - } - return 0; -} - -bool isArray(Expression *e) -{ - return e->op == TOKarrayliteral || e->op == TOKstring || - e->op == TOKslice || e->op == TOKnull; -} - -/* For strings, return <0 if e1 < e2, 0 if e1==e2, >0 if e1 > e2. - * For all other types, return 0 if e1 == e2, !=0 if e1 != e2. - */ -int ctfeRawCmp(Loc loc, Expression *e1, Expression *e2) -{ - if (e1->op == TOKclassreference || e2->op == TOKclassreference) - { if (e1->op == TOKclassreference && e2->op == TOKclassreference && - ((ClassReferenceExp *)e1)->value == ((ClassReferenceExp *)e2)->value) - return 0; - return 1; - } - if (e1->op == TOKnull && e2->op == TOKnull) - return 0; - - if (e1->type->ty == Tpointer && e2->type->ty == Tpointer) - { // Can only be an equality test. - if (e1->op == TOKnull && e2->op == TOKnull) - return 0; - dinteger_t ofs1, ofs2; - Expression *agg1 = getAggregateFromPointer(e1, &ofs1); - Expression *agg2 = getAggregateFromPointer(e2, &ofs2); - if ((agg1 == agg2) || (agg1->op == TOKvar && agg2->op == TOKvar && - ((VarExp *)agg1)->var == ((VarExp *)agg2)->var)) - { if (ofs1 == ofs2) - return 0; - } - return 1; - } - if (isArray(e1) && isArray(e2)) - { - uinteger_t len1 = resolveArrayLength(e1); - uinteger_t len2 = resolveArrayLength(e2); - if (len1 != len2) // only for equality - return len1 - len2; - if (len1 == 0 || len2 == 0) - return len1 - len2; // Equal - both are empty - return ctfeCmpArrays(loc, e1, e2, len1); - } - if (e1->type->isintegral()) - { - return e1->toInteger() - e2->toInteger(); - } - real_t r1; - real_t r2; - if (e1->type->isreal()) - { - r1 = e1->toReal(); - r2 = e2->toReal(); - goto L1; - } - else if (e1->type->isimaginary()) - { - r1 = e1->toImaginary(); - r2 = e2->toImaginary(); - L1: -#if __DMC__ - return (r1 != r2); -#else - if (Port::isNan(r1) || Port::isNan(r2)) // if unordered - { - return 1; - } - else - { - return (r1 != r2); - } -#endif - } - else if (e1->type->iscomplex()) - { - return e1->toComplex() != e2->toComplex(); - } - - if (e1->op == TOKstructliteral && e2->op == TOKstructliteral) - { StructLiteralExp *es1 = (StructLiteralExp *)e1; - StructLiteralExp *es2 = (StructLiteralExp *)e2; - // For structs, we only need to return 0 or 1 (< and > aren't legal). - - if (es1->sd != es2->sd) - return 1; - else if ((!es1->elements || !es1->elements->dim) && - (!es2->elements || !es2->elements->dim)) - return 0; // both arrays are empty - else if (!es1->elements || !es2->elements) - return 1; - else if (es1->elements->dim != es2->elements->dim) - return 1; - else - { - for (size_t i = 0; i < es1->elements->dim; i++) - { Expression *ee1 = (*es1->elements)[i]; - Expression *ee2 = (*es2->elements)[i]; - - if (ee1 == ee2) - continue; - if (!ee1 || !ee2) - return 1; - int cmp = ctfeRawCmp(loc, ee1, ee2); - if (cmp) - return 1; - } - return 0; // All elements are equal - } - } - error(loc, "CTFE internal error: bad compare"); - assert(0); - return 0; -} - -// As Equal, but resolves slices before comparing -Expression *ctfeEqual(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2) -{ - int cmp = !ctfeRawCmp(loc, e1, e2); - if (op == TOKnotequal) - cmp ^= 1; - return new IntegerExp(loc, cmp, type); -} - -Expression *ctfeIdentity(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2) -{ - int cmp; - if (e1->op == TOKnull) - { - cmp = (e2->op == TOKnull); - } - else if (e2->op == TOKnull) - { - cmp = 0; - } - else if (e1->op == TOKsymoff && e2->op == TOKsymoff) - { - SymOffExp *es1 = (SymOffExp *)e1; - SymOffExp *es2 = (SymOffExp *)e2; - cmp = (es1->var == es2->var && es1->offset == es2->offset); - } - else if (e1->type->isreal()) - cmp = RealEquals(e1->toReal(), e2->toReal()); - else if (e1->type->isimaginary()) - cmp = RealEquals(e1->toImaginary(), e2->toImaginary()); - else if (e1->type->iscomplex()) - { complex_t v1 = e1->toComplex(); - complex_t v2 = e2->toComplex(); - cmp = RealEquals(creall(v1), creall(v2)) && - RealEquals(cimagl(v1), cimagl(v1)); - } - else - cmp = !ctfeRawCmp(loc, e1, e2); - - if (op == TOKnotidentity || op == TOKnotequal) - cmp ^= 1; - return new IntegerExp(loc, cmp, type); -} - -Expression *ctfeCmp(Loc loc, enum TOK op, Type *type, Expression *e1, Expression *e2) -{ - return Cmp(op, type, e1, e2); -} - -Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp) -{ Expression *e; Expression *e1; Expression *e2; #if LOG - printf("BinExp::interpretCommon2() %s\n", toChars()); + printf("%s BinExp::interpretCommon2() %s\n", loc.toChars(), toChars()); #endif if (this->e1->type->ty == Tpointer && this->e2->type->ty == Tpointer) { @@ -3215,48 +2442,27 @@ Expression *BinExp::interpretCommon2(InterState *istate, CtfeGoal goal, fp2_t fp e1 = this->e1->interpret(istate); if (exceptionOrCantInterpret(e1)) return e1; - if (e1->op == TOKslice) - e1 = resolveSlice(e1); - - if (e1->isConst() != 1 && - e1->op != TOKnull && - e1->op != TOKstring && - e1->op != TOKarrayliteral && - e1->op != TOKstructliteral && - e1->op != TOKclassreference) + if (!isCtfeComparable(e1)) { error("cannot compare %s at compile time", e1->toChars()); - goto Lcant; + return EXP_CANT_INTERPRET; } - e2 = this->e2->interpret(istate); if (exceptionOrCantInterpret(e2)) return e2; - if (e2->op == TOKslice) - e2 = resolveSlice(e2); - if (e2->isConst() != 1 && - e2->op != TOKnull && - e2->op != TOKstring && - e2->op != TOKarrayliteral && - e2->op != TOKstructliteral && - e2->op != TOKclassreference) + if (!isCtfeComparable(e2)) { error("cannot compare %s at compile time", e2->toChars()); - goto Lcant; + return EXP_CANT_INTERPRET; } - e = (*fp)(loc, op, type, e1, e2); - if (e == EXP_CANT_INTERPRET) - error("%s cannot be interpreted at compile time", toChars()); - return e; - -Lcant: - return EXP_CANT_INTERPRET; + int cmp = (*fp)(loc, op, e1, e2); + return new IntegerExp(loc, cmp, type); } #define BIN_INTERPRET2(op, opfunc) \ Expression *op##Exp::interpret(InterState *istate, CtfeGoal goal) \ { \ - return interpretCommon2(istate, goal, &opfunc); \ + return interpretCompareCommon(istate, goal, &opfunc); \ } BIN_INTERPRET2(Equal, ctfeEqual) @@ -3266,70 +2472,6 @@ BIN_INTERPRET2(Cmp, ctfeCmp) /* Helper functions for BinExp::interpretAssignCommon */ -/*************************************** - * Duplicate the elements array, then set field 'indexToChange' = newelem. - */ -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++) - { - if (j == indexToChange) - (*expsx)[j] = newelem; - else - (*expsx)[j] = oldelems->tdata()[j]; - } - return expsx; -} - -// 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) -{ - int fieldi = se->getFieldIndex(newval->type, offset); - if (fieldi == -1) - return EXP_CANT_INTERPRET; - /* Create new struct literal reflecting updated fieldi - */ - Expressions *expsx = changeOneElement(se->elements, fieldi, newval); - StructLiteralExp * ee = new StructLiteralExp(se->loc, se->sd, expsx); - ee->type = se->type; - ee->ownedByCtfe = 1; - return ee; -} - -/******************************** - * Given an array literal arr (either arrayliteral, stringliteral, or assocArrayLiteral), - * set arr[index] = newval and return the new array. - * - */ -Expression *assignAssocArrayElement(Loc loc, AssocArrayLiteralExp *aae, Expression *index, Expression *newval) -{ - /* Create new associative array literal reflecting updated key/value - */ - Expressions *keysx = aae->keys; - Expressions *valuesx = aae->values; - int updated = 0; - for (size_t j = valuesx->dim; j; ) - { j--; - Expression *ekey = aae->keys->tdata()[j]; - Expression *ex = ctfeEqual(loc, TOKequal, Type::tbool, ekey, index); - if (exceptionOrCantInterpret(ex)) - return ex; - if (ex->isBool(TRUE)) - { valuesx->tdata()[j] = newval; - updated = 1; - } - } - if (!updated) - { // Append index/newval to keysx[]/valuesx[] - valuesx->push(newval); - keysx->push(index); - } - return newval; -} - // Return true if e is derived from UnaryExp. // Consider moving this function into Expression. UnaExp *isUnaExp(Expression *e) @@ -3376,358 +2518,11 @@ VarDeclaration * findParentVar(Expression *e, Expression *thisval) return v; } -// Given expr, which evaluates to an array/AA/string literal, -// return true if it needs to be copied -bool needToCopyLiteral(Expression *expr) -{ - for (;;) - { - switch (expr->op) - { - case TOKarrayliteral: - return !((ArrayLiteralExp *)expr)->ownedByCtfe; - case TOKassocarrayliteral: - return !((AssocArrayLiteralExp *)expr)->ownedByCtfe; - case TOKstructliteral: - return !((StructLiteralExp *)expr)->ownedByCtfe; - case TOKstring: - case TOKthis: - case TOKvar: - return false; - case TOKassign: - return false; - case TOKindex: - case TOKdotvar: - case TOKslice: - case TOKcast: - expr = ((UnaExp *)expr)->e1; - continue; - case TOKcat: - return needToCopyLiteral(((BinExp *)expr)->e1) || - needToCopyLiteral(((BinExp *)expr)->e2); - case TOKcatass: - expr = ((BinExp *)expr)->e2; - continue; - default: - return false; - } - } -} - -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++) - newelems->tdata()[i] = copyLiteral(oldelems->tdata()[i]); - return newelems; -} - - - -// Make a copy of the ArrayLiteral, AALiteral, String, or StructLiteral. -// This value will be used for in-place modification. -Expression *copyLiteral(Expression *e) -{ - if (e->op == TOKstring) // syntaxCopy doesn't make a copy for StringExp! - { - StringExp *se = (StringExp *)e; - unsigned char *s; - s = (unsigned char *)mem.calloc(se->len + 1, se->sz); - memcpy(s, se->string, se->len * se->sz); - StringExp *se2 = new StringExp(se->loc, s, se->len); - se2->committed = se->committed; - se2->postfix = se->postfix; - se2->type = se->type; - se2->sz = se->sz; - se2->ownedByCtfe = true; - return se2; - } - else if (e->op == TOKarrayliteral) - { - ArrayLiteralExp *ae = (ArrayLiteralExp *)e; - ArrayLiteralExp *r = new ArrayLiteralExp(e->loc, - copyLiteralArray(ae->elements)); - r->type = e->type; - r->ownedByCtfe = true; - return r; - } - else if (e->op == TOKassocarrayliteral) - { - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)e; - AssocArrayLiteralExp *r = new AssocArrayLiteralExp(e->loc, - copyLiteralArray(aae->keys), copyLiteralArray(aae->values)); - r->type = e->type; - r->ownedByCtfe = true; - return r; - } - /* syntaxCopy doesn't work for struct literals, because of a nasty special - * case: block assignment is permitted inside struct literals, eg, - * an int[4] array can be initialized with a single int. - */ - else if (e->op == TOKstructliteral) - { - StructLiteralExp *se = (StructLiteralExp *)e; - Expressions *oldelems = se->elements; - Expressions * newelems = new Expressions(); - newelems->setDim(oldelems->dim); - for (size_t i = 0; i < newelems->dim; i++) - { - Expression *m = oldelems->tdata()[i]; - // We need the struct definition to detect block assignment - AggregateDeclaration *sd = se->sd; - Dsymbol *s = sd->fields[i]; - VarDeclaration *v = s->isVarDeclaration(); - assert(v); - // If it is a void assignment, use the default initializer - if (!m) - m = v->type->voidInitLiteral(v); - if (m->op == TOKslice) - m = resolveSlice(m); - if ((v->type->ty != m->type->ty) && v->type->ty == Tsarray) - { - // Block assignment from inside struct literals - TypeSArray *tsa = (TypeSArray *)v->type; - uinteger_t length = tsa->dim->toInteger(); - m = createBlockDuplicatedArrayLiteral(e->loc, v->type, m, (size_t)length); - } - else if (v->type->ty != Tarray && v->type->ty!=Taarray) // NOTE: do not copy array references - m = copyLiteral(m); - newelems->tdata()[i] = m; - } -#if DMDV2 - StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems, se->stype); -#else - StructLiteralExp *r = new StructLiteralExp(e->loc, se->sd, newelems); -#endif - r->type = e->type; - r->ownedByCtfe = true; - return r; - } - else if (e->op == TOKfunction || e->op == TOKdelegate - || e->op == TOKsymoff || e->op == TOKnull - || e->op == TOKvar - || e->op == TOKint64 || e->op == TOKfloat64 - || e->op == TOKchar || e->op == TOKcomplex80 - || e->op == TOKvoid) - { // Simple value types - Expression *r = e->syntaxCopy(); - r->type = e->type; - return r; - } - else if ( isPointer(e->type) ) - { // For pointers, we only do a shallow copy. - Expression *r; - if (e->op == TOKaddress) - r = new AddrExp(e->loc, ((AddrExp *)e)->e1); - else if (e->op == TOKindex) - r = new IndexExp(e->loc, ((IndexExp *)e)->e1, ((IndexExp *)e)->e2); - else if (e->op == TOKdotvar) - r = new DotVarExp(e->loc, ((DotVarExp *)e)->e1, - ((DotVarExp *)e)->var -#if DMDV2 - , ((DotVarExp *)e)->hasOverloads -#endif - ); - else - assert(0); - r->type = e->type; - return r; - } - else if (e->op == TOKslice) - { // Array slices only do a shallow copy - Expression *r = new SliceExp(e->loc, ((SliceExp *)e)->e1, - ((SliceExp *)e)->lwr, ((SliceExp *)e)->upr); - 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()); - assert(0); - return e; - } -} - -/* Deal with type painting. - * Type painting is a major nuisance: we can't just set - * e->type = type, because that would change the original literal. - * But, we can't simply copy the literal either, because that would change - * the values of any pointers. - */ -Expression *paintTypeOntoLiteral(Type *type, Expression *lit) -{ - if (lit->type == type) - return lit; - Expression *e; - if (lit->op == TOKslice) - { - SliceExp *se = (SliceExp *)lit; - e = new SliceExp(lit->loc, se->e1, se->lwr, se->upr); - } - else if (lit->op == TOKindex) - { - IndexExp *ie = (IndexExp *)lit; - e = new IndexExp(lit->loc, ie->e1, ie->e2); - } - else if (lit->op == TOKarrayliteral) - { - e = new SliceExp(lit->loc, lit, - new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); - } - else if (lit->op == TOKstring) - { - // For strings, we need to introduce another level of indirection - e = new SliceExp(lit->loc, lit, - new IntegerExp(0, 0, Type::tsize_t), ArrayLength(Type::tsize_t, lit)); - } - else if (lit->op == TOKassocarrayliteral) - { - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)lit; - // TODO: we should be creating a reference to this AAExp, not - // just a ref to the keys and values. - bool wasOwned = aae->ownedByCtfe; - aae = new AssocArrayLiteralExp(lit->loc, aae->keys, aae->values); - aae->ownedByCtfe = wasOwned; - e = aae; - } - else - { // Can't type paint from struct to struct*; this needs another - // level of indirection - if (lit->op == TOKstructliteral && isPointer(type) ) - lit->error("CTFE internal error painting %s", type->toChars()); - e = copyLiteral(lit); - } - e->type = type; - 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 - return new NullExp(loc, to); - } - Expression *r = Cast(type, to, e); - if (r == EXP_CANT_INTERPRET) - error(loc, "cannot cast %s to %s at compile time", e->toChars(), to->toChars()); - if (e->op == TOKarrayliteral) - ((ArrayLiteralExp *)e)->ownedByCtfe = true; - if (e->op == TOKstring) - ((StringExp *)e)->ownedByCtfe = true; - return r; -} - -/* Set dest = src, where both dest and src are container value literals - * (ie, struct literals, or static arrays (can be an array literal or a string) - * Assignment is recursively in-place. - * Purpose: any reference to a member of 'dest' will remain valid after the - * assignment. - */ -void assignInPlace(Expression *dest, Expression *src) -{ - assert(dest->op == TOKstructliteral || dest->op == TOKarrayliteral || - dest->op == TOKstring); - Expressions *oldelems; - Expressions *newelems; - if (dest->op == TOKstructliteral) - { - assert(dest->op == src->op); - oldelems = ((StructLiteralExp *)dest)->elements; - newelems = ((StructLiteralExp *)src)->elements; - } - else if (dest->op == TOKarrayliteral && src->op==TOKarrayliteral) - { - oldelems = ((ArrayLiteralExp *)dest)->elements; - newelems = ((ArrayLiteralExp *)src)->elements; - } - else if (dest->op == TOKstring && src->op == TOKstring) - { - sliceAssignStringFromString((StringExp *)dest, (StringExp *)src, 0); - return; - } - else if (dest->op == TOKarrayliteral && src->op == TOKstring) - { - sliceAssignArrayLiteralFromString((ArrayLiteralExp *)dest, (StringExp *)src, 0); - return; - } - else if (src->op == TOKarrayliteral && dest->op == TOKstring) - { - sliceAssignStringFromArrayLiteral((StringExp *)dest, (ArrayLiteralExp *)src, 0); - return; - } - else assert(0); - - assert(oldelems->dim == newelems->dim); - - for (size_t i= 0; i < oldelems->dim; ++i) - { - Expression *e = newelems->tdata()[i]; - Expression *o = oldelems->tdata()[i]; - if (e->op == TOKstructliteral) - { - assert(o->op == e->op); - assignInPlace(o, e); - } - else if (e->type->ty == Tsarray && o->type->ty == Tsarray && e->op != TOKvoid) - { - assignInPlace(o, e); - } - else - { - oldelems->tdata()[i] = newelems->tdata()[i]; - } - } -} - -void recursiveBlockAssign(ArrayLiteralExp *ae, Expression *val, bool wantRef) -{ - assert( ae->type->ty == Tsarray || ae->type->ty == Tarray); -#if DMDV2 - Type *desttype = ((TypeArray *)ae->type)->next->castMod(0); - bool directblk = (val->type->toBasetype()->castMod(0)) == desttype; -#else - Type *desttype = ((TypeArray *)ae->type)->next; - bool directblk = (val->type->toBasetype()) == desttype; -#endif - - bool cow = !(val->op == TOKstructliteral || val->op == TOKarrayliteral - || val->op == TOKstring); - - for (size_t k = 0; k < ae->elements->dim; k++) - { - if (!directblk && ae->elements->tdata()[k]->op == TOKarrayliteral) - { - recursiveBlockAssign((ArrayLiteralExp *)ae->elements->tdata()[k], val, wantRef); - } - else - { - if (wantRef || cow) - ae->elements->tdata()[k] = val; - else - assignInPlace(ae->elements->tdata()[k], val); - } - } -} - Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_t fp, int post) { #if LOG - printf("BinExp::interpretAssignCommon() %s\n", toChars()); + printf("%s BinExp::interpretAssignCommon() %s\n", loc.toChars(), toChars()); #endif Expression *returnValue = EXP_CANT_INTERPRET; Expression *e1 = this->e1; @@ -3769,7 +2564,18 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } } } + // If it is a reference type (eg, an array), we need an lvalue. + // If it is a reference variable (such as happens in foreach), we + // need an lvalue reference. For example if x, y are int[], then + // y[0..4] = x[0..4] is an rvalue assignment (all copies in the + // slice are duplicated) + // y = x[0..4] is an lvalue assignment (if x[0] changes later, + // y[0] will also change) + // ref int [] z = x is an lvalueref assignment (if x itself changes, + // z will also change) bool wantRef = false; + bool wantLvalueRef = false; + if (!fp && this->e1->type->toBasetype() == this->e2->type->toBasetype() && (e1->type->toBasetype()->ty == Tarray || isAssocArray(e1->type) || e1->type->toBasetype()->ty == Tclass) @@ -3808,10 +2614,12 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ wantRef = true; } // If it is a construction of a ref variable, it is a ref assignment + // (in fact, it is an lvalue reference assignment). if (op == TOKconstruct && this->e1->op==TOKvar && ((VarExp*)this->e1)->var->storage_class & STCref) { wantRef = true; + wantLvalueRef = true; } if (fp) @@ -3970,66 +2778,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ oldval = oldval->interpret(istate); } } - if (oldval->op == TOKslice) - oldval = resolveSlice(oldval); Type *t = e1->type->toBasetype(); if (t->ty == Tarray) { - Type *elemType= NULL; - elemType = ((TypeArray *)t)->next; - assert(elemType); - Expression *defaultElem = elemType->defaultInitLiteral(loc); - - Expressions *elements = new Expressions(); - elements->setDim(newlen); - size_t copylen = oldlen < newlen ? oldlen : newlen; - if (oldval->op == TOKstring) - { - StringExp *oldse = (StringExp *)oldval; - unsigned char *s = (unsigned char *)mem.calloc(newlen + 1, oldse->sz); - memcpy(s, oldse->string, copylen * oldse->sz); - unsigned defaultValue = (unsigned)(defaultElem->toInteger()); - for (size_t elemi = copylen; elemi < newlen; ++elemi) - { - switch (oldse->sz) - { - case 1: s[elemi] = defaultValue; break; - case 2: ((unsigned short *)s)[elemi] = defaultValue; break; - case 4: ((unsigned *)s)[elemi] = defaultValue; break; - default: assert(0); - } - } - StringExp *se = new StringExp(loc, s, newlen); - se->type = t; - se->sz = oldse->sz; - se->committed = oldse->committed; - se->ownedByCtfe = true; - newval = se; - } - else - { - if (oldlen !=0) - assert(oldval->op == TOKarrayliteral); - ArrayLiteralExp *ae = (ArrayLiteralExp *)oldval; - for (size_t i = 0; i < copylen; i++) - (*elements)[i] = ae->elements->tdata()[i]; - if (elemType->ty == Tstruct || elemType->ty == Tsarray) - { /* If it is an aggregate literal representing a value type, - * we need to create a unique copy for each element - */ - for (size_t i = copylen; i < newlen; i++) - (*elements)[i] = copyLiteral(defaultElem); - } - else - { - for (size_t i = copylen; i < newlen; i++) - (*elements)[i] = defaultElem; - } - ArrayLiteralExp *aae = new ArrayLiteralExp(0, elements); - aae->type = t; - newval = aae; - aae->ownedByCtfe = true; - } + newval = changeArrayLiteralLength(loc, (TypeArray *)t, oldval, + oldlen, newlen); // We have changed it into a reference assignment // Note that returnValue is still the new length. wantRef = true; @@ -4094,7 +2847,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ // --------------------------------------- if (wantRef && !fp && this->e1->op != TOKarraylength) { - newval = this->e2->interpret(istate, ctfeNeedLvalue); + newval = this->e2->interpret(istate, + wantLvalueRef ? ctfeNeedLvalueRef : ctfeNeedLvalue); if (exceptionOrCantInterpret(newval)) return newval; // If it is an assignment from a array function parameter passed by @@ -4107,12 +2861,18 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } if (newval->op == TOKassocarrayliteral || newval->op == TOKstring || - newval->op==TOKarrayliteral) + newval->op == TOKarrayliteral) { if (needToCopyLiteral(newval)) newval = copyLiteral(newval); } - returnValue = newval; + + // Get the value to return. Note that 'newval' is an Lvalue, + // so if we need an Rvalue, we have to interpret again. + if (goal == ctfeNeedRvalue) + returnValue = newval->interpret(istate); + else + returnValue = newval; } // --------------------------------------- @@ -4204,7 +2964,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ keysx->push(index); AssocArrayLiteralExp *newaae = new AssocArrayLiteralExp(loc, keysx, valuesx); newaae->ownedByCtfe = true; - newaae->type = e1->type; + newaae->type = ((IndexExp *)e1)->e1->type; newval = newaae; e1 = ((IndexExp *)e1)->e1; } @@ -4649,7 +3409,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (aggregate->op == TOKslice) { // Slice of a slice --> change the bounds SliceExp *sexpold = (SliceExp *)aggregate; - dinteger_t hi = upperbound + sexpold->lwr->toInteger(); + sinteger_t hi = upperbound + sexpold->lwr->toInteger(); firstIndex = lowerbound + sexpold->lwr->toInteger(); if (hi > sexpold->upr->toInteger()) { @@ -4670,7 +3430,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ error("cannot slice null pointer %s", sexp->e1->toChars()); return EXP_CANT_INTERPRET; } - dinteger_t hi = upperbound + ofs; + sinteger_t hi = upperbound + ofs; firstIndex = lowerbound + ofs; if (firstIndex < 0 || hi > dim) { @@ -4851,7 +3611,7 @@ BIN_ASSIGN_INTERPRET(Pow) Expression *PostExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("PostExp::interpret() %s\n", toChars()); + printf("%s PostExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression *e; if (op == TOKplusplus) @@ -4975,7 +3735,7 @@ Expression *BinExp::interpretFourPointerRelation(InterState *istate, CtfeGoal go p4 = p4->interpret(istate); if (p4 == EXP_CANT_INTERPRET) return p4; - if (exceptionOrCantInterpret(p3)) + if (exceptionOrCantInterpret(p4)) except = p4; } if (except) @@ -5034,7 +3794,7 @@ Expression *BinExp::interpretFourPointerRelation(InterState *istate, CtfeGoal go Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("AndAndExp::interpret() %s\n", toChars()); + printf("%s AndAndExp::interpret() %s\n", loc.toChars(), toChars()); #endif // Check for an insidePointer expression, evaluate it if so @@ -5067,13 +3827,13 @@ Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) result = 1; else { - error("%s does not evaluate to a boolean", e->toChars()); + e->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->error("%s cannot be interpreted as a boolean", e->toChars()); e = EXP_CANT_INTERPRET; } } @@ -5085,7 +3845,7 @@ Expression *AndAndExp::interpret(InterState *istate, CtfeGoal goal) Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("OrOrExp::interpret() %s\n", toChars()); + printf("%s OrOrExp::interpret() %s\n", loc.toChars(), toChars()); #endif // Check for an insidePointer expression, evaluate it if so @@ -5121,14 +3881,14 @@ Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) result = 1; else { - error("%s cannot be interpreted as a boolean", e->toChars()); + e->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->error("%s cannot be interpreted as a boolean", e->toChars()); e = EXP_CANT_INTERPRET; } } @@ -5189,7 +3949,7 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG - printf("CallExp::interpret() %s\n", toChars()); + printf("%s CallExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression * pthis = NULL; @@ -5316,6 +4076,9 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) if (pthis->op == TOKvar) { assert(((VarExp*)thisval)->var->isVarDeclaration()); thisval = ((VarExp*)thisval)->var->isVarDeclaration()->getValue(); + // If it is a reference, resolve it + if (thisval->op != TOKnull && thisval->op != TOKclassreference) + thisval = pthis->interpret(istate); } // Get the function from the vtable of the original class ClassDeclaration *cd; @@ -5387,7 +4150,7 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("CommaExp::interpret() %s\n", toChars()); + printf("%s CommaExp::interpret() %s\n", loc.toChars(), toChars()); #endif CommaExp * firstComma = this; @@ -5456,7 +4219,7 @@ Expression *CommaExp::interpret(InterState *istate, CtfeGoal goal) Expression *CondExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("CondExp::interpret() %s\n", toChars()); + printf("%s CondExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression *e; if ( isPointer(econd->type) ) @@ -5489,7 +4252,7 @@ Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) Expression *e1; #if LOG - printf("ArrayLengthExp::interpret() %s\n", toChars()); + printf("%s ArrayLengthExp::interpret() %s\n", loc.toChars(), toChars()); #endif e1 = this->e1->interpret(istate); assert(e1); @@ -5508,56 +4271,6 @@ Expression *ArrayLengthExp::interpret(InterState *istate, CtfeGoal goal) return e; } -/* Given an AA literal 'ae', and a key 'e2': - * Return ae[e2] if present, or NULL if not found. - * Return EXP_CANT_INTERPRET on error. - */ -Expression *findKeyInAA(Loc loc, AssocArrayLiteralExp *ae, Expression *e2) -{ - /* Search the keys backwards, in case there are duplicate keys - */ - for (size_t i = ae->keys->dim; i;) - { - i--; - Expression *ekey = ae->keys->tdata()[i]; - Expression *ex = ctfeEqual(loc, TOKequal, Type::tbool, ekey, e2); - if (ex == EXP_CANT_INTERPRET) - return ex; - if (ex->isBool(TRUE)) - { - return ae->values->tdata()[i]; - } - } - return NULL; -} - -/* Same as for constfold.Index, except that it only works for static arrays, - * dynamic arrays, and strings. We know that e1 is an - * interpreted CTFE expression, so it cannot have side-effects. - */ -Expression *ctfeIndex(Loc loc, Type *type, Expression *e1, uinteger_t indx) -{ //printf("ctfeIndex(e1 = %s)\n", e1->toChars()); - assert(e1->type); - if (e1->op == TOKstring) - { StringExp *es1 = (StringExp *)e1; - if (indx >= es1->len) - { - error(loc, "string index %ju is out of bounds [0 .. %zu]", indx, es1->len); - return EXP_CANT_INTERPRET; - } - else - return new IntegerExp(loc, es1->charAt(indx), type); - } - assert(e1->op == TOKarrayliteral); - ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; - if (indx >= ale->elements->dim) - { - error(loc, "array index %ju is out of bounds %s[0 .. %u]", indx, e1->toChars(), ale->elements->dim); - return EXP_CANT_INTERPRET; - } - Expression *e = ale->elements->tdata()[indx]; - return paintTypeOntoLiteral(type, e); -} Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) { @@ -5565,7 +4278,7 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) Expression *e2; #if LOG - printf("IndexExp::interpret() %s\n", toChars()); + printf("%s IndexExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (this->e1->type->toBasetype()->ty == Tpointer) { @@ -5576,7 +4289,7 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) e2 = this->e2->interpret(istate); if (exceptionOrCantInterpret(e2)) return e2; - dinteger_t indx = e2->toInteger(); + sinteger_t indx = e2->toInteger(); dinteger_t ofs; Expression *agg = getAggregateFromPointer(e1, &ofs); @@ -5610,7 +4323,8 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) } } e1 = this->e1; - if (!(e1->op == TOKarrayliteral && ((ArrayLiteralExp *)e1)->ownedByCtfe)) + if (!(e1->op == TOKarrayliteral && ((ArrayLiteralExp *)e1)->ownedByCtfe) && + !(e1->op == TOKassocarrayliteral && ((AssocArrayLiteralExp *)e1)->ownedByCtfe)) e1 = e1->interpret(istate); if (exceptionOrCantInterpret(e1)) return e1; @@ -5708,7 +4422,7 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) Expression *upr; #if LOG - printf("SliceExp::interpret() %s\n", toChars()); + printf("%s SliceExp::interpret() %s\n", loc.toChars(), toChars()); #endif if (this->e1->type->toBasetype()->ty == Tpointer) @@ -5739,6 +4453,8 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) Expression *e; dinteger_t ofs; Expression *agg = getAggregateFromPointer(e1, &ofs); + ilwr += ofs; + iupr += ofs; if (agg->op == TOKnull) { if (iupr == ilwr) @@ -5759,12 +4475,16 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) assert(agg->op == TOKarrayliteral || agg->op == TOKstring); dinteger_t len = ArrayLength(Type::tsize_t, agg)->toInteger(); //Type *pointee = ((TypePointer *)agg->type)->next; - if ((ilwr + ofs) < 0 || (iupr+ofs) > (len + 1) || iupr < ilwr) + if (iupr > (len + 1) || iupr < ilwr) { error("pointer slice [%lld..%lld] exceeds allocated memory block [0..%lld]", - ilwr+ofs, iupr+ofs, len); + ilwr, iupr, len); return EXP_CANT_INTERPRET; } + if (ofs != 0) + { lwr = new IntegerExp(loc, ilwr, lwr->type); + upr = new IntegerExp(loc, iupr, upr->type); + } e = new SliceExp(loc, agg, lwr, upr); e->type = type; return e; @@ -5852,7 +4572,7 @@ Expression *SliceExp::interpret(InterState *istate, CtfeGoal goal) if (e1->op == TOKarrayliteral || e1->op == TOKstring) { - if (iupr < ilwr || ilwr < 0 || iupr > dollar) + if (iupr < ilwr || iupr > dollar) { error("slice [%lld..%lld] exceeds array bounds [0..%lld]", ilwr, iupr, dollar); @@ -5868,7 +4588,7 @@ Expression *InExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG - printf("InExp::interpret() %s\n", toChars()); + printf("%s InExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression *e1 = this->e1->interpret(istate); if (exceptionOrCantInterpret(e1)) @@ -5901,7 +4621,7 @@ Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) Expression *e2; #if LOG - printf("CatExp::interpret() %s\n", toChars()); + printf("%s CatExp::interpret() %s\n", loc.toChars(), toChars()); #endif e1 = this->e1->interpret(istate); if (exceptionOrCantInterpret(e1)) @@ -5929,58 +4649,12 @@ Expression *CatExp::interpret(InterState *istate, CtfeGoal goal) } -// Return true if t is a pointer (not a function pointer) -bool isPointer(Type *t) -{ - Type * tb = t->toBasetype(); - return tb->ty == Tpointer && tb->nextOf()->ty != Tfunction; -} - -// Return true if t is an AA, or AssociativeArray!(key, value) -bool isAssocArray(Type *t) -{ - t = t->toBasetype(); - if (t->ty == Taarray) - return true; -#if DMDV2 - if (t->ty != Tstruct) - return false; - StructDeclaration *sym = ((TypeStruct *)t)->sym; - if (sym->ident == Id::AssociativeArray && sym->parent && - sym->parent->parent && - sym->parent->parent->ident == Id::object) - { - return true; - } -#endif - return false; -} - -// Given a template AA type, extract the corresponding built-in AA type -TypeAArray *toBuiltinAAType(Type *t) -{ - t = t->toBasetype(); - if (t->ty == Taarray) - return (TypeAArray *)t; -#if DMDV2 - assert(t->ty == Tstruct); - StructDeclaration *sym = ((TypeStruct *)t)->sym; - assert(sym->ident == Id::AssociativeArray); - TemplateInstance *tinst = sym->parent->isTemplateInstance(); - assert(tinst); - return new TypeAArray((Type *)(tinst->tiargs->tdata()[1]), (Type *)(tinst->tiargs->tdata()[0])); -#else - assert(0); - return NULL; -#endif -} - Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; Expression *e1; #if LOG - printf("CastExp::interpret() %s\n", toChars()); + printf("%s CastExp::interpret() %s\n", loc.toChars(), toChars()); #endif e1 = this->e1->interpret(istate, goal); if (exceptionOrCantInterpret(e1)) @@ -6153,7 +4827,7 @@ Expression *AssertExp::interpret(InterState *istate, CtfeGoal goal) Expression *e1; #if LOG - printf("AssertExp::interpret() %s\n", toChars()); + printf("%s AssertExp::interpret() %s\n", loc.toChars(), toChars()); #endif #if DMDV2 e1 = this->e1->interpret(istate); @@ -6203,8 +4877,24 @@ Expression *PtrExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG - printf("PtrExp::interpret() %s\n", toChars()); + printf("%s PtrExp::interpret() %s\n", loc.toChars(), toChars()); #endif + + // Check for int<->float and long<->double casts. + + if ( e1->op == TOKsymoff && ((SymOffExp *)e1)->offset == 0 + && isFloatIntPaint(type, ((SymOffExp *)e1)->var->type) ) + { // *(cast(int*)&v, where v is a float variable + return paintFloatInt(getVarExp(loc, istate, ((SymOffExp *)e1)->var, ctfeNeedRvalue), + type); + } + else if (e1->op == TOKcast && ((CastExp *)e1)->e1->op == TOKaddress) + { // *(cast(int *))&x where x is a float expression + Expression *x = ((AddrExp *)(((CastExp *)e1)->e1))->e1; + if ( isFloatIntPaint(type, x->type) ) + return paintFloatInt(x->interpret(istate), type); + } + // Constant fold *(&structliteral + offset) if (e1->op == TOKadd) { AddExp *ae = (AddExp *)e1; @@ -6335,7 +5025,7 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; #if LOG - printf("DotVarExp::interpret() %s\n", toChars()); + printf("%s DotVarExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression *ex = e1->interpret(istate); @@ -6439,7 +5129,7 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) { #if LOG - printf("RemoveExp::interpret() %s\n", toChars()); + printf("%s RemoveExp::interpret() %s\n", loc.toChars(), toChars()); #endif Expression *agg = e1->interpret(istate); if (exceptionOrCantInterpret(agg)) @@ -6456,10 +5146,8 @@ Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) size_t removed = 0; for (size_t j = 0; j < valuesx->dim; ++j) { Expression *ekey = keysx->tdata()[j]; - Expression *ex = ctfeEqual(loc, TOKequal, Type::tbool, ekey, index); - if (exceptionOrCantInterpret(ex)) - return ex; - if (ex->isBool(TRUE)) + int eq = ctfeEqual(loc, TOKequal, ekey, index); + if (eq) ++removed; else if (removed != 0) { keysx->tdata()[j - removed] = ekey; @@ -6478,8 +5166,6 @@ Expression *interpret_length(InterState *istate, Expression *earg) { //printf("interpret_length()\n"); earg = earg->interpret(istate); - if (earg == EXP_CANT_INTERPRET) - return NULL; if (exceptionOrCantInterpret(earg)) return earg; dinteger_t len = 0; @@ -6490,47 +5176,43 @@ Expression *interpret_length(InterState *istate, Expression *earg) return e; } -Expression *interpret_keys(InterState *istate, Expression *earg, Type *elemType) +Expression *interpret_keys(InterState *istate, Expression *earg, Type *returnType) { #if LOG printf("interpret_keys()\n"); #endif 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); + return new NullExp(earg->loc, returnType); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) return NULL; assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, aae->keys); ae->ownedByCtfe = aae->ownedByCtfe; - ae->type = new TypeSArray(elemType, new IntegerExp(aae->keys->dim)); + ae->type = returnType; return copyLiteral(ae); } -Expression *interpret_values(InterState *istate, Expression *earg, Type *elemType) +Expression *interpret_values(InterState *istate, Expression *earg, Type *returnType) { #if LOG printf("interpret_values()\n"); #endif 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); + return new NullExp(earg->loc, returnType); if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) return NULL; assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; ArrayLiteralExp *ae = new ArrayLiteralExp(aae->loc, aae->values); ae->ownedByCtfe = aae->ownedByCtfe; - ae->type = new TypeSArray(elemType, new IntegerExp(aae->values->dim)); + ae->type = returnType; //printf("result is %s\n", e->toChars()); return copyLiteral(ae); } @@ -6561,6 +5243,10 @@ Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *de Type *valueType = fd->parameters->tdata()[numParams-1]->type; Type *keyType = numParams == 2 ? fd->parameters->tdata()[0]->type : Type::tsize_t; + + Parameter *valueArg = Parameter::getNth(((TypeFunction *)fd->type)->parameters, numParams - 1); + bool wantRefValue = 0 != (valueArg->storageClass & (STCout | STCref)); + Expressions args; args.setDim(numParams); @@ -6573,6 +5259,11 @@ Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *de { Expression *ekey = ae->keys->tdata()[i]; Expression *evalue = ae->values->tdata()[i]; + if (wantRefValue) + { Type *t = evalue->type; + evalue = new IndexExp(deleg->loc, ae, ekey); + evalue->type = t; + } args[numParams - 1] = evalue; if (numParams == 2) args[0] = ekey; @@ -6588,12 +5279,12 @@ Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *de } // Helper function: given a function of type A[] f(...), -// return A. -Type *returnedArrayElementType(FuncDeclaration *fd) +// return A[]. +Type *returnedArrayType(FuncDeclaration *fd) { assert(fd->type->ty == Tfunction); assert(fd->type->nextOf()->ty == Tarray); - return ((TypeFunction *)fd->type)->nextOf()->nextOf(); + return ((TypeFunction *)fd->type)->nextOf(); } /* Decoding UTF strings for foreach loops. Duplicates the functionality of @@ -6659,7 +5350,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del if (ale) { // If it is an array literal, copy the code points into the buffer - int buflen = 1; // #code points in the buffer + size_t buflen = 1; // #code points in the buffer size_t n = 1; // #code points in this char size_t sz = ale->type->nextOf()->size(); @@ -6681,7 +5372,7 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del } else buflen = (indx + 4 > len) ? len - indx : 4; - for (int i=0; i < buflen; ++i) + for (int i = 0; i < buflen; ++i) { Expression * r = ale->elements->tdata()[indx + i]; assert(r->op == TOKint64); @@ -6855,9 +5546,9 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, if (fd->ident == Id::length && nargs==0) return interpret_length(istate, pthis); else if (fd->ident == Id::keys && nargs==0) - return interpret_keys(istate, pthis, returnedArrayElementType(fd)); + return interpret_keys(istate, pthis, returnedArrayType(fd)); else if (fd->ident == Id::values && nargs==0) - return interpret_values(istate, pthis, returnedArrayElementType(fd)); + return interpret_values(istate, pthis, returnedArrayType(fd)); else if (fd->ident == Id::rehash && nargs==0) return pthis->interpret(istate, ctfeNeedLvalue); // rehash is a no-op } @@ -6880,38 +5571,14 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, e = EXP_CANT_INTERPRET; } } - /* Horrid hack to retrieve the builtin AA functions after they've been - * mashed by the inliner. - */ + if (!pthis) { Expression *firstarg = nargs > 0 ? (Expression *)(arguments->data[0]) : NULL; - // Check for the first parameter being a templatized AA. Hack: we assume that - // template AA.var is always the AA data itself. - Expression *firstdotvar = (firstarg && firstarg->op == TOKdotvar) - ? ((DotVarExp *)firstarg)->e1 : NULL; if (nargs==3 && isAssocArray(firstarg->type) && !strcmp(fd->ident->string, "_aaApply")) return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); if (nargs==3 && isAssocArray(firstarg->type) &&!strcmp(fd->ident->string, "_aaApply2")) return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); - if (firstdotvar && isAssocArray(firstdotvar->type)) - { if (fd->ident == Id::aaLen && nargs == 1) - return interpret_length(istate, firstdotvar->interpret(istate)); - else if (fd->ident == Id::aaKeys && nargs == 2) - { - Expression *trueAA = firstdotvar->interpret(istate); - return interpret_keys(istate, trueAA, toBuiltinAAType(trueAA->type)->index); - } - else if (fd->ident == Id::aaValues && nargs == 3) - { - Expression *trueAA = firstdotvar->interpret(istate); - return interpret_values(istate, trueAA, toBuiltinAAType(trueAA->type)->nextOf()); - } - else if (fd->ident == Id::aaRehash && nargs == 2) - { - return firstdotvar->interpret(istate, ctfeNeedLvalue); - } - } } #endif #if DMDV1 @@ -6924,9 +5591,9 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, if (fd->ident == Id::aaLen && nargs == 1) return interpret_length(istate, firstarg); else if (fd->ident == Id::aaKeys) - return interpret_keys(istate, firstarg, firstAAtype->index); + return interpret_keys(istate, firstarg, new DArray(firstAAtype->index)); else if (fd->ident == Id::aaValues) - return interpret_values(istate, firstarg, firstAAtype->nextOf()); + return interpret_values(istate, firstarg, new DArray(firstAAtype->nextOf())); else if (nargs==2 && fd->ident == Id::aaRehash) return firstarg->interpret(istate, ctfeNeedLvalue); //no-op else if (nargs==3 && !strcmp(fd->ident->string, "_aaApply")) @@ -6990,123 +5657,6 @@ Expression *evaluateIfBuiltin(InterState *istate, Loc loc, /* Setter functions for CTFE variable values. * These functions exist to check for compiler CTFE bugs. */ - -bool isCtfeValueValid(Expression *newval) -{ - if ( -#if DMDV2 - newval->type->ty == Tnull || -#endif - isPointer(newval->type) ) - { - if (newval->op == TOKaddress || newval->op == TOKnull || - newval->op == TOKstring) - return true; - if (newval->op == TOKindex) - { - Expression *g = ((IndexExp *)newval)->e1; - if (g->op == TOKarrayliteral || g->op == TOKstring || - g->op == TOKassocarrayliteral) - return true; - } - if (newval->op == TOKvar) - return true; - if (newval->type->nextOf()->ty == Tarray && newval->op == TOKslice) - return true; - if (newval->op == TOKint64) - return true; // Result of a cast, but cannot be dereferenced - // else it must be a reference - } - if (newval->op == TOKclassreference || (newval->op == TOKnull && newval->type->ty == Tclass)) - return true; - if (newval->op == TOKvar) - { - VarExp *ve = (VarExp *)newval; - VarDeclaration *vv = ve->var->isVarDeclaration(); - // Must not be a reference to a reference - if (!(vv && vv->getValue() && vv->getValue()->op == TOKvar)) - return true; - } - if (newval->op == TOKdotvar) - { - if (((DotVarExp *)newval)->e1->op == TOKstructliteral) - { - assert(((StructLiteralExp *)((DotVarExp *)newval)->e1)->ownedByCtfe); - return true; - } - } - if (newval->op == TOKindex) - { - IndexExp *ie = (IndexExp *)newval; - if (ie->e2->op == TOKint64) - { - if (ie->e1->op == TOKarrayliteral || ie->e1->op == TOKstring) - return true; - } - if (ie->e1->op == TOKassocarrayliteral) - return true; - // BUG: Happens ONLY in ref foreach. Should tighten this. - if (ie->e2->op == TOKvar) - return true; - } - if (newval->op == TOKfunction) return true; // function/delegate literal - if (newval->op == TOKdelegate) return true; - if (newval->op == TOKsymoff) // function pointer - { - if (((SymOffExp *)newval)->var->isFuncDeclaration()) - return true; - } -#if IN_LLVM - if (newval->op == TOKaddress) { // function pointer - AddrExp *ae = (AddrExp *)newval; - if (ae->e1->op == TOKvar) { - if (((VarExp *)ae->e1)->var->isFuncDeclaration()) - return true; - } - } -#endif - if (newval->op == TOKint64 || newval->op == TOKfloat64 || - newval->op == TOKchar || newval->op == TOKcomplex80) - return true; - - // References - - if (newval->op == TOKstructliteral) - assert(((StructLiteralExp *)newval)->ownedByCtfe); - if (newval->op == TOKarrayliteral) - assert(((ArrayLiteralExp *)newval)->ownedByCtfe); - if (newval->op == TOKassocarrayliteral) - assert(((AssocArrayLiteralExp *)newval)->ownedByCtfe); - - if ((newval->op ==TOKarrayliteral) || ( newval->op==TOKstructliteral) || - (newval->op==TOKstring) || (newval->op == TOKassocarrayliteral) || - (newval->op == TOKnull)) - { return true; - } - // 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) - if (newval->op == TOKdotvar || newval->op == TOKindex) - return true; // actually must be null - - if (newval->op == TOKslice) - { - SliceExp *se = (SliceExp *)newval; - assert(se->lwr && se->lwr != EXP_CANT_INTERPRET && se->lwr->op == TOKint64); - assert(se->upr && se->upr != EXP_CANT_INTERPRET && se->upr->op == TOKint64); - assert(se->e1->op == TOKarrayliteral || se->e1->op == TOKstring); - if (se->e1->op == TOKarrayliteral) - assert(((ArrayLiteralExp *)se->e1)->ownedByCtfe); - return true; - } - if (newval->op == TOKvoid) - { - return true; - } - newval->error("CTFE internal error: illegal value %s\n", newval->toChars()); - return false; -} - bool VarDeclaration::hasValue() { if (ctfeAdrOnStack == (size_t)-1) @@ -7136,28 +5686,3 @@ void VarDeclaration::setValue(Expression *newval) ctfeStack.setValue(this, newval); } - -Expression *Type::voidInitLiteral(VarDeclaration *var) -{ - return new VoidInitExp(var, this); -} - -Expression *TypeSArray::voidInitLiteral(VarDeclaration *var) -{ - return createBlockDuplicatedArrayLiteral(var->loc, this, next->voidInitLiteral(var), dim->toInteger()); -} - -Expression *TypeStruct::voidInitLiteral(VarDeclaration *var) -{ - Expressions *exps = new Expressions(); - exps->setDim(sym->fields.dim); - for (size_t i = 0; i < sym->fields.dim; i++) - { - //(*exps)[i] = new VoidInitExp(var, sym->fields[i]->type); - (*exps)[i] = sym->fields[i]->type->voidInitLiteral(var); - } - StructLiteralExp *se = new StructLiteralExp(var->loc, sym, exps); - se->type = this; - se->ownedByCtfe = true; - return se; -} diff --git a/dmd2/intrange.c b/dmd2/intrange.c index baf099c0..0cd7cc2a 100644 --- a/dmd2/intrange.c +++ b/dmd2/intrange.c @@ -214,6 +214,7 @@ SignExtendedNumber SignExtendedNumber::operator<<(const SignExtendedNumber& a) c // compute base-2 log of 'v' to determine the maximum allowed bits to shift. // Ref: http://graphics.stanford.edu/~seander/bithacks.html#IntegerLog + // Why is this a size_t? Looks like a bug. size_t r, s; r = (v > 0xFFFFFFFFULL) << 5; v >>= r; diff --git a/dmd2/intrange.h b/dmd2/intrange.h index 2904dab9..747e4ae7 100644 --- a/dmd2/intrange.h +++ b/dmd2/intrange.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars +// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by KennyTM // http://www.digitalmars.com @@ -44,6 +44,10 @@ struct SignExtendedNumber /// Get the minimum or maximum value of a sign-extended number. static SignExtendedNumber extreme(bool minimum); + + // These names probably shouldn't be used anyway, as they are common macros +#undef max +#undef min static SignExtendedNumber max(); static SignExtendedNumber min() { return SignExtendedNumber(0, true); } diff --git a/dmd2/irstate.c b/dmd2/irstate.c index 4fc35ece..0bb9cdce 100644 --- a/dmd2/irstate.c +++ b/dmd2/irstate.c @@ -11,6 +11,7 @@ #include "mtype.h" #include "declaration.h" #include "irstate.h" +#include "statement.h" IRState::IRState(IRState *irs, Statement *s) { @@ -108,16 +109,26 @@ IRState::IRState(Module *m, Dsymbol *s) block *IRState::getBreakBlock(Identifier *ident) { IRState *bc; - - for (bc = this; bc; bc = bc->prev) - { - if (ident) - { - if (bc->prev && bc->prev->ident == ident) + if (ident) { + Statement *related = NULL; + block *ret = NULL; + for (bc = this; bc; bc = bc->prev) { + // The label for a breakBlock may actually be some levels up (e.g. + // on a try/finally wrapping a loop). We'll see if this breakBlock + // is the one to return once we reach that outer statement (which + // in many cases will be this same statement). + if (bc->breakBlock) { + related = bc->statement->getRelatedLabeled(); + ret = bc->breakBlock; + } + if (bc->statement == related && bc->prev->ident == ident) + return ret; + } + } else { + for (bc = this; bc; bc = bc->prev) { + if (bc->breakBlock) return bc->breakBlock; } - else if (bc->breakBlock) - return bc->breakBlock; } return NULL; } diff --git a/dmd2/irstate.h b/dmd2/irstate.h index 75194b6f..7ddbbc1f 100644 --- a/dmd2/irstate.h +++ b/dmd2/irstate.h @@ -20,7 +20,12 @@ struct Identifier; struct Symbol; struct FuncDeclaration; struct Blockx; +#if IN_LLVM +struct DValue; +typedef DValue elem; +#else struct elem; +#endif #include "arraytypes.h" struct IRState diff --git a/dmd2/json.c b/dmd2/json.c index 26daa65e..fc4ca016 100644 --- a/dmd2/json.c +++ b/dmd2/json.c @@ -44,7 +44,6 @@ const char Ptype[] = "type"; const char Pcomment[] = "comment"; const char Pmembers[] = "members"; const char Pprotection[] = "protection"; -const char* Pprotectionnames[] = {NULL, "none", "private", "package", "protected", "public", "export"}; void JsonRemoveComma(OutBuffer *buf); @@ -74,7 +73,7 @@ void json_generate(Modules *modules) } else if (arg[0] == '-' && arg[1] == 0) { // Write to stdout; assume it succeeds - int n = fwrite(buf.data, 1, buf.offset, stdout); + size_t n = fwrite(buf.data, 1, buf.offset, stdout); assert(n == buf.offset); // keep gcc happy about return values return; } diff --git a/dmd2/lexer.c b/dmd2/lexer.c index 00a8365f..2e53a992 100644 --- a/dmd2/lexer.c +++ b/dmd2/lexer.c @@ -41,7 +41,7 @@ extern "C" char * __cdecl __locale_decpoint; #endif -extern int HtmlNamedEntity(unsigned char *p, int length); +extern int HtmlNamedEntity(unsigned char *p, size_t length); #define LS 0x2028 // UTF line separator #define PS 0x2029 // UTF paragraph separator @@ -127,11 +127,11 @@ const char *Token::toChars() break; case TOKint64v: - sprintf(buffer,"%lldL",(intmax_t)int64value); + sprintf(buffer,"%lldL",(longlong)int64value); break; case TOKuns64v: - sprintf(buffer,"%lluUL",(uintmax_t)uns64value); + sprintf(buffer,"%lluUL",(ulonglong)uns64value); break; #ifdef IN_GCC @@ -249,7 +249,7 @@ StringTable Lexer::stringtable; OutBuffer Lexer::stringbuffer; Lexer::Lexer(Module *mod, - unsigned char *base, unsigned begoffset, unsigned endoffset, + unsigned char *base, size_t begoffset, size_t endoffset, int doDocComment, int commentToken) : loc(mod, 1) { @@ -321,6 +321,14 @@ void Lexer::error(Loc loc, const char *format, ...) va_end(ap); } +void Lexer::deprecation(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::vdeprecation(tokenLoc(), format, ap); + va_end(ap); +} + TOK Lexer::nextToken() { Token *t; @@ -576,8 +584,7 @@ void Lexer::scan(Token *t) t->postfix = 0; t->value = TOKstring; #if DMDV2 - if (!global.params.useDeprecated) - error("Escape String literal %.*s is deprecated, use double quoted string literal \"%.*s\" instead", (int)(p - pstart), pstart, (int)(p - pstart), pstart); + error("Escape String literal %.*s is deprecated, use double quoted string literal \"%.*s\" instead", p - pstart, pstart, p - pstart, pstart); #endif return; } @@ -1294,7 +1301,7 @@ unsigned Lexer::escapeSequence() c = v; } else - error("undefined escape hex sequence \\%c\n",c); + error("undefined escape hex sequence \\%c",c); break; case '&': // named character entity @@ -1343,7 +1350,7 @@ unsigned Lexer::escapeSequence() error("0%03o is larger than a byte", c); } else - error("undefined escape sequence \\%c\n",c); + error("undefined escape sequence \\%c",c); break; } return c; @@ -1943,7 +1950,7 @@ TOK Lexer::number(Token *t) if (p[1] == '.') // .. is a separate token goto done; #if DMDV2 - if (isalpha(p[1]) || p[1] == '_') + if (isalpha(p[1]) || p[1] == '_' || (p[1] & 0x80)) goto done; #endif case 'i': @@ -1985,7 +1992,7 @@ TOK Lexer::number(Token *t) if (c == '.' && p[1] != '.') { #if DMDV2 - if (isalpha(p[1]) || p[1] == '_') + if (isalpha(p[1]) || p[1] == '_' || (p[1] & 0x80)) goto done; #endif goto real; @@ -2146,9 +2153,6 @@ done: f = FLAGS_unsigned; goto L1; - case 'l': - if (1 || !global.params.useDeprecated) - error("'l' suffix is deprecated, use 'L' instead"); case 'L': f = FLAGS_long; L1: @@ -2164,8 +2168,8 @@ done: } #if DMDV2 - if (state == STATE_octal && n >= 8 && !global.params.useDeprecated) - error("octal literals 0%llo%.*s are deprecated, use std.conv.octal!%llo%.*s instead", + if (state == STATE_octal && n >= 8) + deprecation("octal literals 0%llo%.*s are deprecated, use std.conv.octal!%llo%.*s instead", n, p - psuffix, psuffix, n, p - psuffix, psuffix); #endif @@ -2404,8 +2408,7 @@ done: break; case 'l': - if (!global.params.useDeprecated) - error("'l' suffix is deprecated, use 'L' instead"); + error("'l' suffix is deprecated, use 'L' instead"); case 'L': result = TOKfloat80v; p++; @@ -2413,7 +2416,7 @@ done: } if (*p == 'i' || *p == 'I') { - if (!global.params.useDeprecated && *p == 'I') + if (*p == 'I') error("'I' suffix is deprecated, use 'i' instead"); p++; switch (result) @@ -2427,6 +2430,7 @@ done: case TOKfloat80v: result = TOKimaginary80v; break; + default: break; } } #if _WIN32 && __DMC__ @@ -2839,8 +2843,8 @@ static Keyword keywords[] = { "uint", TOKuns32 }, { "long", TOKint64 }, { "ulong", TOKuns64 }, - { "cent", TOKcent, }, - { "ucent", TOKucent, }, + { "cent", TOKint128, }, + { "ucent", TOKuns128, }, { "float", TOKfloat32 }, { "double", TOKfloat64 }, { "real", TOKfloat80 }, @@ -2943,7 +2947,7 @@ static Keyword keywords[] = int Token::isKeyword() { - for (unsigned u = 0; u < sizeof(keywords) / sizeof(keywords[0]); u++) + for (size_t u = 0; u < sizeof(keywords) / sizeof(keywords[0]); u++) { if (keywords[u].value == value) return 1; @@ -2953,7 +2957,7 @@ int Token::isKeyword() void Lexer::initKeywords() { - unsigned nkeywords = sizeof(keywords) / sizeof(keywords[0]); + size_t nkeywords = sizeof(keywords) / sizeof(keywords[0]); stringtable.init(6151); @@ -2962,7 +2966,7 @@ void Lexer::initKeywords() cmtable_init(); - for (unsigned u = 0; u < nkeywords; u++) + for (size_t u = 0; u < nkeywords; u++) { //printf("keyword[%d] = '%s'\n",u, keywords[u].name); const char *s = keywords[u].name; diff --git a/dmd2/lexer.h b/dmd2/lexer.h index 293c9538..c3f3a826 100644 --- a/dmd2/lexer.h +++ b/dmd2/lexer.h @@ -120,11 +120,11 @@ enum TOK TOKint16, TOKuns16, TOKint32, TOKuns32, TOKint64, TOKuns64, + TOKint128, TOKuns128, TOKfloat32, TOKfloat64, TOKfloat80, TOKimaginary32, TOKimaginary64, TOKimaginary80, TOKcomplex32, TOKcomplex64, TOKcomplex80, TOKchar, TOKwchar, TOKdchar, TOKbit, TOKbool, - TOKcent, TOKucent, // 152 // Aggregates @@ -191,6 +191,7 @@ enum TOK case TOKint16: case TOKuns16: \ case TOKint32: case TOKuns32: \ case TOKint64: case TOKuns64: \ + case TOKint128: case TOKuns128: \ case TOKfloat32: case TOKfloat64: case TOKfloat80: \ case TOKimaginary32: case TOKimaginary64: case TOKimaginary80: \ case TOKcomplex32: case TOKcomplex64: case TOKcomplex80: \ @@ -206,6 +207,8 @@ enum TOK case TOKuns32: t = Type::tuns32; goto LabelX; \ case TOKint64: t = Type::tint64; goto LabelX; \ case TOKuns64: t = Type::tuns64; goto LabelX; \ + case TOKint128: t = Type::tint128; goto LabelX; \ + case TOKuns128: t = Type::tuns128; goto LabelX; \ case TOKfloat32: t = Type::tfloat32; goto LabelX; \ case TOKfloat64: t = Type::tfloat64; goto LabelX; \ case TOKfloat80: t = Type::tfloat80; goto LabelX; \ @@ -283,7 +286,7 @@ struct Lexer int commentToken; // !=0 means comments are TOKcomment's Lexer(Module *mod, - unsigned char *base, unsigned begoffset, unsigned endoffset, + unsigned char *base, size_t begoffset, size_t endoffset, int doDocComment, int commentToken); static void initKeywords(); @@ -310,8 +313,9 @@ struct Lexer unsigned wchar(unsigned u); TOK number(Token *t); TOK inreal(Token *t); - void error(const char *format, ...) IS_PRINTF(2); - void error(Loc loc, const char *format, ...) IS_PRINTF(3); + void error(const char *format, ...); + void error(Loc loc, const char *format, ...); + void deprecation(const char *format, ...); void poundLine(); unsigned decodeUTF(); void getDocComment(Token *t, unsigned lineComment); diff --git a/dmd2/lib.h b/dmd2/lib.h index d63e1ace..dc1e44ea 100644 --- a/dmd2/lib.h +++ b/dmd2/lib.h @@ -26,5 +26,7 @@ class Library virtual void write() = 0; }; +Library *LibMSCoff_factory(); + #endif /* DMD_LIB_H */ diff --git a/dmd2/macro.c b/dmd2/macro.c index 398ba77f..ae53002a 100644 --- a/dmd2/macro.c +++ b/dmd2/macro.c @@ -21,8 +21,9 @@ #include "macro.h" -#define isidstart(c) (isalpha(c) || (c) == '_') -#define isidchar(c) (isalnum(c) || (c) == '_') +int isIdStart(unsigned char *p); +int isIdTail(unsigned char *p); +int utfStride(unsigned char *p); unsigned char *memdup(unsigned char *p, size_t len) { @@ -97,7 +98,7 @@ Macro *Macro::define(Macro **ptable, unsigned char *name, size_t namelen, unsign * -1: get 2nd through end */ -unsigned extractArgN(unsigned char *p, unsigned end, unsigned char **pmarg, unsigned *pmarglen, int n) +size_t extractArgN(unsigned char *p, size_t end, unsigned char **pmarg, size_t *pmarglen, int n) { /* Scan forward for matching right parenthesis. * Nest parentheses. @@ -114,7 +115,7 @@ unsigned extractArgN(unsigned char *p, unsigned end, unsigned char **pmarg, unsi unsigned inexp = 0; unsigned argn = 0; - unsigned v = 0; + size_t v = 0; Largstart: #if 1 @@ -236,8 +237,8 @@ unsigned extractArgN(unsigned char *p, unsigned end, unsigned char **pmarg, unsi * Only look at the text in buf from start to end. */ -void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, - unsigned char *arg, unsigned arglen) +void Macro::expand(OutBuffer *buf, size_t start, size_t *pend, + unsigned char *arg, size_t arglen) { #if 0 printf("Macro::expand(buf[%d..%d], arg = '%.*s')\n", start, *pend, arglen, arg); @@ -249,14 +250,14 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, return; nest++; - unsigned end = *pend; + size_t end = *pend; assert(start <= end); assert(end <= buf->offset); /* First pass - replace $0 */ arg = memdup(arg, arglen); - for (unsigned u = start; u + 1 < end; ) + for (size_t u = start; u + 1 < end; ) { unsigned char *p = buf->data; // buf->data is not loop invariant @@ -276,7 +277,7 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, int n = (c == '+') ? -1 : c - '0'; unsigned char *marg; - unsigned marglen; + size_t marglen; extractArgN(arg, arglen, &marg, &marglen, n); if (marglen == 0) { // Just remove macro invocation @@ -293,7 +294,7 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, end += marglen - 2; // Scan replaced text for further expansion - unsigned mend = u + marglen; + size_t mend = u + marglen; expand(buf, u, &mend, NULL, 0); end += mend - (u + marglen); u = mend; @@ -309,7 +310,7 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, end += -2 + 2 + marglen + 2; // Scan replaced text for further expansion - unsigned mend = u + 2 + marglen; + size_t mend = u + 2 + marglen; expand(buf, u + 2, &mend, NULL, 0); end += mend - (u + 2 + marglen); u = mend; @@ -324,30 +325,30 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, /* Second pass - replace other macros */ - for (unsigned u = start; u + 4 < end; ) + for (size_t u = start; u + 4 < end; ) { unsigned char *p = buf->data; // buf->data is not loop invariant /* A valid start of macro expansion is $(c, where c is * an id start character, and not $$(c. */ - if (p[u] == '$' && p[u + 1] == '(' && isidstart(p[u + 2])) + if (p[u] == '$' && p[u + 1] == '(' && isIdStart(p+u+2)) { //printf("\tfound macro start '%c'\n", p[u + 2]); unsigned char *name = p + u + 2; - unsigned namelen = 0; + size_t namelen = 0; unsigned char *marg; - unsigned marglen; + size_t marglen; - unsigned v; + size_t v; /* Scan forward to find end of macro name and * beginning of macro argument (marg). */ - for (v = u + 2; v < end; v++) + for (v = u + 2; v < end; v+=utfStride(p+v)) { unsigned char c = p[v]; - if (!isidchar(c)) + if (!isIdTail(p+v)) { // We've gone past the end of the macro name. namelen = v - (u + 2); break; @@ -402,7 +403,7 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, // Scan replaced text for further expansion m->inuse++; - unsigned mend = v + 1 + 2+m->textlen+2; + size_t mend = v + 1 + 2+m->textlen+2; expand(buf, v + 1, &mend, marg, marglen); end += mend - (v + 1 + 2+m->textlen+2); m->inuse--; @@ -417,7 +418,7 @@ void Macro::expand(OutBuffer *buf, unsigned start, unsigned *pend, // Scan replaced text for further expansion m->inuse++; - unsigned mend = v + 1 + m->textlen; + size_t mend = v + 1 + m->textlen; expand(buf, v + 1, &mend, marg, marglen); end += mend - (v + 1 + m->textlen); m->inuse--; diff --git a/dmd2/macro.h b/dmd2/macro.h index 7c939621..d29a8cf0 100644 --- a/dmd2/macro.h +++ b/dmd2/macro.h @@ -38,8 +38,8 @@ struct Macro public: static Macro *define(Macro **ptable, unsigned char *name, size_t namelen, unsigned char *text, size_t textlen); - void expand(OutBuffer *buf, unsigned start, unsigned *pend, - unsigned char *arg, unsigned arglen); + void expand(OutBuffer *buf, size_t start, size_t *pend, + unsigned char *arg, size_t arglen); }; #endif diff --git a/dmd2/mangle.c b/dmd2/mangle.c index 75c75b53..e27dc43c 100644 --- a/dmd2/mangle.c +++ b/dmd2/mangle.c @@ -51,10 +51,10 @@ char *mangle(Declaration *sthis) else { id = s->ident->toChars(); - int len = strlen(id); + size_t len = strlen(id); char tmp[sizeof(len) * 3 + 1]; buf.prependstring(id); - sprintf(tmp, "%d", len); + sprintf(tmp, "%d", (int)len); buf.prependstring(tmp); } } diff --git a/dmd2/mars.c b/dmd2/mars.c index 67280066..146b8bb4 100644 --- a/dmd2/mars.c +++ b/dmd2/mars.c @@ -17,7 +17,7 @@ #include #include -#if POSIX +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include #endif @@ -45,9 +45,9 @@ long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep); #endif #if !IN_LLVM -int response_expand(int *pargc, char ***pargv); +int response_expand(size_t *pargc, char ***pargv); void browse(const char *url); -void getenv_setargv(const char *envvar, int *pargc, char** *pargv); +void getenv_setargv(const char *envvar, size_t *pargc, char** *pargv); void obj_start(char *srcfile); void obj_end(Library *library, File *objfile); @@ -55,6 +55,8 @@ void obj_end(Library *library, File *objfile); void printCtfePerformanceStats(); +static bool parse_arch(size_t argc, char** argv, bool is64bit); + Global global; Global::Global() @@ -99,7 +101,7 @@ Global::Global() "\nMSIL back-end (alpha release) by Cristian L. Vlasceanu and associates."; #endif ; - version = "v2.060"; + version = "v2.061"; #if IN_LLVM ldc_version = "trunk"; llvm_version = "LLVM "LDC_LLVM_VERSION_STRING; @@ -195,33 +197,51 @@ void errorSupplemental(Loc loc, const char *format, ...) va_end( ap ); } -void verror(Loc loc, const char *format, va_list ap, const char *p1, const char *p2) +void deprecation(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + vdeprecation(loc, format, ap); + + va_end( ap ); +} + +// Just print, doesn't care about gagging +void verrorPrint(Loc loc, const char *header, const char *format, va_list ap, + const char *p1, const char *p2) +{ + char *p = loc.toChars(); + + if (*p) + fprintf(stdmsg, "%s: ", p); + mem.free(p); + + fputs(header, stdmsg); + if (p1) + fprintf(stdmsg, "%s ", p1); + if (p2) + fprintf(stdmsg, "%s ", p2); +#if _MSC_VER + // MS doesn't recognize %zu format + OutBuffer tmp; + tmp.vprintf(format, ap); + fprintf(stdmsg, "%s", tmp.toChars()); +#else + vfprintf(stdmsg, format, ap); +#endif + fprintf(stdmsg, "\n"); + fflush(stdmsg); +} + +// header is "Error: " by default (see mars.h) +void verror(Loc loc, const char *format, va_list ap, + const char *p1, const char *p2, const char *header) { if (!global.gag) { - char *p = loc.toChars(); - - if (*p) - fprintf(stdmsg, "%s: ", p); - mem.free(p); - - fprintf(stdmsg, "Error: "); - if (p1) - fprintf(stdmsg, "%s ", p1); - if (p2) - fprintf(stdmsg, "%s ", p2); -#if _MSC_VER - // MS doesn't recognize %zu format - OutBuffer tmp; - tmp.vprintf(format, ap); - fprintf(stdmsg, "%s", tmp.toChars()); -#else - vfprintf(stdmsg, format, ap); -#endif - fprintf(stdmsg, "\n"); - fflush(stdmsg); + verrorPrint(loc, header, format, ap, p1, p2); if (global.errors >= 20) // moderate blizzard of cascading messages - fatal(); + fatal(); //halt(); } else @@ -235,48 +255,30 @@ void verror(Loc loc, const char *format, va_list ap, const char *p1, const char void verrorSupplemental(Loc loc, const char *format, va_list ap) { if (!global.gag) - { - fprintf(stdmsg, "%s: ", loc.toChars()); -#if _MSC_VER - // MS doesn't recognize %zu format - OutBuffer tmp; - tmp.vprintf(format, ap); - fprintf(stdmsg, "%s", tmp.toChars()); -#else - vfprintf(stdmsg, format, ap); -#endif - fprintf(stdmsg, "\n"); - fflush(stdmsg); - } + verrorPrint(loc, " ", format, ap); } void vwarning(Loc loc, const char *format, va_list ap) { if (global.params.warnings && !global.gag) { - char *p = loc.toChars(); - - if (*p) - fprintf(stdmsg, "%s: ", p); - mem.free(p); - - fprintf(stdmsg, "Warning: "); -#if _MSC_VER - // MS doesn't recognize %zu format - OutBuffer tmp; - tmp.vprintf(format, ap); - fprintf(stdmsg, "%s", tmp.toChars()); -#else - vfprintf(stdmsg, format, ap); -#endif - fprintf(stdmsg, "\n"); - fflush(stdmsg); + verrorPrint(loc, "Warning: ", format, ap); //halt(); if (global.params.warnings == 1) global.warnings++; // warnings don't count if gagged } } +void vdeprecation(Loc loc, const char *format, va_list ap, + const char *p1, const char *p2) +{ + static const char *header = "Deprecation: "; + if (global.params.useDeprecated == 0) + verror(loc, format, ap, p1, p2, header); + else if (global.params.useDeprecated == 2 && !global.gag) + verrorPrint(loc, header, format, ap, p1, p2); +} + /*************************************** * Call this after printing out fatal error messages to clean up and exit * the compiler. @@ -315,8 +317,8 @@ void usage() #else const char fpic[] = ""; #endif - printf("DMD%s D Compiler %s\n%s %s\n", - sizeof(size_t) == 4 ? "32" : "64", + printf("DMD%d D Compiler %s\n%s %s\n", + sizeof(size_t) * 8, global.version, global.copyright, global.written); printf("\ Documentation: http://www.dlang.org/index.html\n\ @@ -330,7 +332,9 @@ Usage:\n\ -D generate documentation\n\ -Dddocdir write documentation file to docdir directory\n\ -Dffilename write documentation file to filename\n\ - -d allow deprecated features\n\ + -d silently allow deprecated features\n\ + -dw show use of deprecated features as warnings (default)\n\ + -de show use of deprecated features as errors (halt compilation)\n\ -debug compile in debug code\n\ -debug=level compile in debug code <= level\n\ -debug=ident compile in debug code identified by ident\n\ @@ -357,7 +361,6 @@ Usage:\n\ " -man open web browser on manual page\n\ -map generate linker .map file\n\ -noboundscheck turns off array bounds checking for all functions\n\ - -nofloat do not emit reference to floating point\n\ -O optimize\n\ -o- do not write object file\n\ -odobjdir write object & library files to directory objdir\n\ @@ -393,7 +396,7 @@ extern "C" } #endif -int tryMain(int argc, char *argv[]) +int tryMain(size_t argc, char *argv[]) { mem.init(); // initialize storage allocator mem.setStackBottom(&argv); @@ -405,10 +408,10 @@ int tryMain(int argc, char *argv[]) Strings libmodules; char *p; Module *m; - int status = EXIT_SUCCESS; - int argcstart = argc; + size_t argcstart = argc; int setdebuglib = 0; char noboundscheck = 0; + int setdefaultlib = 0; const char *inifilename = NULL; #ifdef DEBUG @@ -448,6 +451,7 @@ int tryMain(int argc, char *argv[]) global.params.obj = 1; global.params.Dversion = 2; global.params.quiet = 1; + global.params.useDeprecated = 2; global.params.linkswitches = new Strings(); global.params.libfiles = new Strings(); @@ -512,12 +516,24 @@ int tryMain(int argc, char *argv[]) VersionCondition::addPredefinedGlobalIdent("all"); #if _WIN32 - inifilename = inifile(argv[0], "sc.ini"); -#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 - inifilename = inifile(argv[0], "dmd.conf"); + inifilename = inifile(argv[0], "sc.ini", "Environment"); +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun + inifilename = inifile(argv[0], "dmd.conf", "Environment"); #else #error "fix this" #endif + + size_t dflags_argc = 0; + char** dflags_argv = NULL; + getenv_setargv("DFLAGS", &dflags_argc, &dflags_argv); + + bool is64bit = global.params.is64bit; // use default + is64bit = parse_arch(argc, argv, is64bit); + is64bit = parse_arch(dflags_argc, dflags_argv, is64bit); + global.params.is64bit = is64bit; + + inifile(argv[0], inifilename, is64bit ? "Environment64" : "Environment32"); + getenv_setargv("DFLAGS", &argc, &argv); #if 0 @@ -532,8 +548,12 @@ int tryMain(int argc, char *argv[]) p = argv[i]; if (*p == '-') { - if (strcmp(p + 1, "d") == 0) + if (strcmp(p + 1, "de") == 0) + global.params.useDeprecated = 0; + else if (strcmp(p + 1, "d") == 0) global.params.useDeprecated = 1; + else if (strcmp(p + 1, "dw") == 0) + global.params.useDeprecated = 2; else if (strcmp(p + 1, "c") == 0) global.params.link = 0; else if (strcmp(p + 1, "cov") == 0) @@ -560,7 +580,7 @@ int tryMain(int argc, char *argv[]) else if (strcmp(p + 1, "gs") == 0) global.params.alwaysframe = 1; else if (strcmp(p + 1, "gt") == 0) - { error(0, "use -profile instead of -gt\n"); + { error(0, "use -profile instead of -gt"); global.params.trace = 1; } else if (strcmp(p + 1, "m32") == 0) @@ -700,6 +720,8 @@ int tryMain(int argc, char *argv[]) global.params.quiet = 1; else if (strcmp(p + 1, "release") == 0) global.params.release = 1; + else if (strcmp(p + 1, "betterC") == 0) + global.params.betterC = 1; #if DMDV2 else if (strcmp(p + 1, "noboundscheck") == 0) noboundscheck = 1; @@ -745,7 +767,7 @@ int tryMain(int argc, char *argv[]) else global.params.debuglevel = 1; } - else if (memcmp(p + 1, "version", 5) == 0) + else if (memcmp(p + 1, "version", 7) == 0) { // Parse: // -version=number @@ -791,6 +813,7 @@ int tryMain(int argc, char *argv[]) } else if (memcmp(p + 1, "defaultlib=", 11) == 0) { + setdefaultlib = 1; global.params.defaultlibname = p + 1 + 11; } else if (memcmp(p + 1, "debuglib=", 9) == 0) @@ -883,6 +906,11 @@ int tryMain(int argc, char *argv[]) files.push(p); } } + + if(global.params.is64bit != is64bit) + error(0, "the architecture must not be changed in the %s section of %s", + is64bit ? "Environment64" : "Environment32", inifilename); + if (global.errors) { fatal(); @@ -901,7 +929,7 @@ int tryMain(int argc, char *argv[]) #if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS if (global.params.lib && global.params.dll) - error(0, "cannot mix -lib and -shared\n"); + error(0, "cannot mix -lib and -shared"); #endif if (global.params.release) @@ -983,6 +1011,11 @@ int tryMain(int argc, char *argv[]) VersionCondition::addPredefinedGlobalIdent("D_SIMD"); #if TARGET_WINDOS VersionCondition::addPredefinedGlobalIdent("Win64"); + if (!setdefaultlib) + { global.params.defaultlibname = "phobos64"; + if (!setdebuglib) + global.params.debuglibname = global.params.defaultlibname; + } #endif } else @@ -1006,8 +1039,14 @@ int tryMain(int argc, char *argv[]) #if DMDV2 if (global.params.useUnitTests) VersionCondition::addPredefinedGlobalIdent("unittest"); + if (global.params.useAssert) + VersionCondition::addPredefinedGlobalIdent("assert"); + if (noboundscheck) + VersionCondition::addPredefinedGlobalIdent("D_NoBoundsChecks"); #endif + VersionCondition::addPredefinedGlobalIdent("D_HardFloat"); + // Initialization Type::init(); Id::initialize(); @@ -1156,7 +1195,7 @@ int tryMain(int argc, char *argv[]) } } else - { error(0, "unrecognized file extension %s\n", ext); + { error(0, "unrecognized file extension %s", ext); fatal(); } } @@ -1283,7 +1322,7 @@ int tryMain(int argc, char *argv[]) m->importAll(0); } if (global.errors) - fatal(); + fatal(); backend_init(); @@ -1445,6 +1484,7 @@ int tryMain(int argc, char *argv[]) if (global.errors) fatal(); + int status = EXIT_SUCCESS; if (!global.params.objfiles->dim) { if (global.params.link) @@ -1505,7 +1545,7 @@ int main(int argc, char *argv[]) * The string is separated into arguments, processing \ and ". */ -void getenv_setargv(const char *envvar, int *pargc, char** *pargv) +void getenv_setargv(const char *envvar, size_t *pargc, char** *pargv) { char *p; @@ -1519,7 +1559,7 @@ void getenv_setargv(const char *envvar, int *pargc, char** *pargv) env = mem.strdup(env); // create our own writable copy - int argc = *pargc; + size_t argc = *pargc; Strings *argv = new Strings(); argv->setDim(argc); @@ -1623,6 +1663,28 @@ Ldone: *pargv = argv->tdata(); } +/*********************************** + * Parse command line arguments for -m32 or -m64 + * to detect the desired architecture. + */ + +static bool parse_arch(size_t argc, char** argv, bool is64bit) +{ + for (size_t i = 0; i < argc; ++i) + { char* p = argv[i]; + if (p[0] == '-') + { + if (strcmp(p + 1, "m32") == 0) + is64bit = 0; + else if (strcmp(p + 1, "m64") == 0) + is64bit = 1; + else if (strcmp(p + 1, "run") == 0) + break; + } + } + return is64bit; +} + #if WINDOWS_SEH long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep) diff --git a/dmd2/mars.h b/dmd2/mars.h index baa7c437..42a5134a 100644 --- a/dmd2/mars.h +++ b/dmd2/mars.h @@ -39,7 +39,7 @@ Macros defined by the compiler, not the code: __APPLE__ Mac OSX __FreeBSD__ FreeBSD __OpenBSD__ OpenBSD - __sun&&__SVR4 Solaris, OpenSolaris (yes, both macros are necessary) + __sun Solaris, OpenSolaris, SunOS, OpenIndiana, etc For the target systems, there are the target operating system and the target object file format: @@ -203,25 +203,39 @@ struct Param ARCH cpu; // target CPU bool isLE; // generate little endian code bool is64bit; // generate 64 bit code -#if !IN_LLVM +#if IN_LLVM + OS os; +#else char isLinux; // generate code for linux char isOSX; // generate code for Mac OSX char isWindows; // generate code for Windows char isFreeBSD; // generate code for FreeBSD char isOPenBSD; // generate code for OpenBSD char isSolaris; // generate code for Solaris -#else - OS os; + char scheduler; // which scheduler to use #endif - bool useDeprecated; // allow use of deprecated features + ubyte useDeprecated; // 0: don't allow use of deprecated features + // 1: silently allow use of deprecated features + // 2: warn about the use of deprecated features bool useAssert; // generate runtime code for assert()'s bool useInvariants; // generate class invariant checks bool useIn; // generate precondition checks bool useOut; // generate postcondition checks - bool useArrayBounds;// generate array bounds checks - bool useSwitchError;// check for switches without a default +#if IN_LLVM + bool useArrayBounds; +#else + char useArrayBounds; // 0: no array bounds checks + // 1: array bounds checks for safe functions only + // 2: array bounds checks for all functions +#endif + bool noboundscheck; // no array bounds checking at all + bool useSwitchError; // check for switches without a default bool useUnitTests; // generate unittest code bool useInline; // inline expand functions +#if !IN_LLVM + char release; // build release version + char preservePaths; // !=0 means don't strip path from source file +#endif ubyte warnings; // 0: enable warnings // 1: warnings as errors // 2: informational warnings (no errors) @@ -230,9 +244,12 @@ struct Param char cov; // generate code coverage data char nofloat; // code should not pull in floating point support #endif - ubyte Dversion; // D version number + ubyte Dversion; // D version number bool ignoreUnsupportedPragmas; // rather than error on them bool enforcePropertySyntax; +#if !IN_LLVM + char betterC; // be a "better C" compiler; no dependency on D runtime +#endif char *argv0; // program name Strings *imppath; // array of char*'s of where to look for import modules @@ -493,6 +510,7 @@ enum DYNCAST DYNCAST_TYPE, DYNCAST_IDENTIFIER, DYNCAST_TUPLE, + DYNCAST_PARAMETER, }; enum MATCH @@ -508,12 +526,15 @@ enum MATCH typedef uint64_t StorageClass; -void warning(Loc loc, const char *format, ...) IS_PRINTF(2); -void error(Loc loc, const char *format, ...) IS_PRINTF(2); +void warning(Loc loc, const char *format, ...); +void deprecation(Loc loc, const char *format, ...); +void error(Loc loc, const char *format, ...); void errorSupplemental(Loc loc, const char *format, ...); -void verror(Loc loc, const char *format, va_list ap, const char *p1 = NULL, const char *p2 = NULL); +void verror(Loc loc, const char *format, va_list ap, const char *p1 = NULL, const char *p2 = NULL, const char *header = "Error: "); void vwarning(Loc loc, const char *format, va_list); -void verrorSupplemental(Loc loc, const char *format, va_list); +void verrorSupplemental(Loc loc, const char *format, va_list ap); +void verrorPrint(Loc loc, const char *header, const char *format, va_list ap, const char *p1 = NULL, const char *p2 = NULL); +void vdeprecation(Loc loc, const char *format, va_list ap, const char *p1 = NULL, const char *p2 = NULL); #if defined(__GNUC__) || defined(__clang__) __attribute__((noreturn)) @@ -527,7 +548,7 @@ void error(const char *format, ...) IS_PRINTF(1); int runLINK(); void deleteExeFile(); int runProgram(); -const char *inifile(const char *argv0, const char *inifile); +const char *inifile(const char *argv0, const char *inifile, const char* envsectionname); #endif void halt(); #if !IN_LLVM @@ -543,7 +564,7 @@ void util_progress(); #if !IN_LLVM struct Dsymbol; -struct Library; +class Library; struct File; void obj_start(char *srcfile); void obj_end(Library *library, File *objfile); diff --git a/dmd2/module.c b/dmd2/module.c index 135b8571..a0b5db62 100644 --- a/dmd2/module.c +++ b/dmd2/module.c @@ -12,7 +12,7 @@ #include #include -#if (defined (__SVR4) && defined (__sun)) +#if defined (__sun) #include #endif @@ -179,7 +179,7 @@ Module::Module(char *filename, Identifier *ident, int doDocComment, int doHdrGen #endif if (global.params.objname) - objfilename = new FileName(argobj, 0); + objfilename = new FileName(argobj); else objfilename = FileName::forceExt(argobj, global.obj_ext); @@ -281,7 +281,7 @@ void Module::setDocfile() argdoc = FileName::combine(global.params.docdir, argdoc); } if (global.params.docname) - docfilename = new FileName(argdoc, 0); + docfilename = new FileName(argdoc); else docfilename = FileName::forceExt(argdoc, global.doc_ext); @@ -309,7 +309,7 @@ void Module::setHdrfile() arghdr = FileName::combine(global.params.hdrdir, arghdr); } if (global.params.hdrname) - hdrfilename = new FileName(arghdr, 0); + hdrfilename = new FileName(arghdr); else hdrfilename = FileName::forceExt(arghdr, global.hdr_ext); @@ -515,7 +515,17 @@ bool Module::read(Loc loc) { //printf("Module::read('%s') file '%s'\n", toChars(), srcfile->toChars()); if (srcfile->read()) - { error(loc, "is in file '%s' which cannot be read", srcfile->toChars()); + { + if (!strcmp(srcfile->toChars(), "object.d")) + { + ::error(loc, "cannot find source code for runtime library file 'object.d'"); + errorSupplemental(loc, "dmd might not be correctly installed. Run 'dmd -man' for installation instructions."); + } + else + { + error(loc, "is in file '%s' which cannot be read", srcfile->toChars()); + } + if (!global.gag) { /* Print path */ @@ -582,7 +592,7 @@ void Module::parse() //printf("Module::parse(srcname = '%s')\n", srcname); unsigned char *buf = srcfile->buffer; - unsigned buflen = srcfile->len; + size_t buflen = srcfile->len; if (buflen >= 2) { @@ -1165,7 +1175,7 @@ void Module::runDeferredSemantic() static int nested; if (nested) return; - //if (deferred.dim) printf("+Module::runDeferredSemantic('%s'), len = %d\n", toChars(), deferred.dim); + //if (deferred.dim) printf("+Module::runDeferredSemantic(), len = %d\n", deferred.dim); nested++; size_t len; @@ -1177,6 +1187,7 @@ void Module::runDeferredSemantic() break; Dsymbol **todo; + Dsymbol **todoalloc = NULL; Dsymbol *tmp; if (len == 1) { @@ -1184,8 +1195,9 @@ void Module::runDeferredSemantic() } else { - todo = (Dsymbol **)alloca(len * sizeof(Dsymbol *)); + todo = (Dsymbol **)malloc(len * sizeof(Dsymbol *)); assert(todo); + todoalloc = todo; } memcpy(todo, deferred.tdata(), len * sizeof(Dsymbol *)); deferred.setDim(0); @@ -1198,9 +1210,11 @@ void Module::runDeferredSemantic() //printf("deferred: %s, parent = %s\n", s->toChars(), s->parent->toChars()); } //printf("\tdeferred.dim = %d, len = %d, dprogress = %d\n", deferred.dim, len, dprogress); + if (todoalloc) + free(todoalloc); } while (deferred.dim < len || dprogress); // while making progress nested--; - //printf("-Module::runDeferredSemantic('%s'), len = %d\n", toChars(), deferred.dim); + //printf("-Module::runDeferredSemantic(), len = %d\n", deferred.dim); } /************************************ diff --git a/dmd2/module.h b/dmd2/module.h index 71730021..dabd3a83 100644 --- a/dmd2/module.h +++ b/dmd2/module.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2008 by Digital Mars +// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -24,7 +24,7 @@ struct ModuleDeclaration; struct Macro; struct Escape; struct VarDeclaration; -struct Library; +class Library; // Back end #if IN_LLVM diff --git a/dmd2/mtype.c b/dmd2/mtype.c index defff32e..3f7dcbbf 100644 --- a/dmd2/mtype.c +++ b/dmd2/mtype.c @@ -12,7 +12,7 @@ #define __C99FEATURES__ 1 // Needed on Solaris for NaN and more #define __USE_ISOC99 1 // so signbit() gets defined -#if (defined (__SVR4) && defined (__sun)) +#if defined (__sun) #include #endif @@ -44,6 +44,7 @@ #include "scope.h" #include "init.h" #include "expression.h" +#include "statement.h" #include "attrib.h" #include "declaration.h" #include "template.h" @@ -87,7 +88,7 @@ int REALSIZE = 16; int REALPAD = 6; int REALALIGNSIZE = 16; #elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS -int REALSIZE = 12; // FIXME: We differ from DMD here, yet target defines are never set?! +int REALSIZE = 12; // LDC_FIXME: We differ from DMD here, yet target defines are never set?! int REALPAD = 2; int REALALIGNSIZE = 4; #elif defined(IN_GCC) @@ -185,8 +186,8 @@ int Type::equals(Object *o) t = (Type *)o; //printf("Type::equals(%s, %s)\n", toChars(), t->toChars()); if (this == o || - (t && deco == t->deco) && // deco strings are unique - deco != NULL) // and semantic() has been run + ((t && deco == t->deco) && // deco strings are unique + deco != NULL)) // and semantic() has been run { //printf("deco = '%s', t->deco = '%s'\n", deco, t->deco); return 1; @@ -281,6 +282,8 @@ void Type::init() mangleChar[Tslice] = '@'; mangleChar[Treturn] = '@'; mangleChar[Tvector] = '@'; + mangleChar[Tint128] = '@'; + mangleChar[Tuns128] = '@'; mangleChar[Tnull] = 'n'; // same as TypeNone @@ -293,6 +296,7 @@ void Type::init() // Set basic types static TY basetab[] = { Tvoid, Tint8, Tuns8, Tint16, Tuns16, Tint32, Tuns32, Tint64, Tuns64, + Tint128, Tuns128, Tfloat32, Tfloat64, Tfloat80, Timaginary32, Timaginary64, Timaginary80, Tcomplex32, Tcomplex64, Tcomplex80, @@ -334,9 +338,12 @@ void Type::init() #elif TARGET_LINUX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS REALSIZE = 12; REALPAD = 2; -#else +#elif TARGET_WINDOS REALSIZE = 10; REALPAD = 0; +#elif defined(IN_GCC) +#else + assert(0); #endif Tsize_t = Tuns32; Tptrdiff_t = Tint32; @@ -381,6 +388,12 @@ unsigned Type::alignsize() Type *Type::semantic(Loc loc, Scope *sc) { + if (ty == Tint128 || ty == Tuns128) + { + error(loc, "cent and ucent types not implemented"); + return terror; + } + return merge(); } @@ -1356,14 +1369,19 @@ Type *Type::aliasthisOf() { FuncDeclaration *fd = (FuncDeclaration *)d; Expression *ethis = this->defaultInit(0); - fd = fd->overloadResolve(0, ethis, NULL); + fd = fd->overloadResolve(0, ethis, NULL, 1); if (fd) { TypeFunction *tf = (TypeFunction *)fd->type; if (!tf->next && fd->inferRetType) { TemplateInstance *spec = fd->isSpeculative(); int olderrs = global.errors; + // If it isn't speculative, we need to show errors + unsigned oldgag = global.gag; + if (global.gag && !spec) + global.gag = 0; fd->semantic3(fd->scope); + global.gag = oldgag; // Update the template instantiation with the number // of errors which occured. if (spec && global.errors != olderrs) @@ -1835,7 +1853,7 @@ int Type::isString() * a = b; * ? */ -int Type::isAssignable() +int Type::isAssignable(int blit) { return TRUE; } @@ -1854,6 +1872,15 @@ int Type::needsDestruction() return FALSE; } +/********************************* + * + */ + +bool Type::needsNested() +{ + return false; +} + /********************************* * Check type to see if it is based on a deprecated symbol. */ @@ -2029,34 +2056,34 @@ Expression *Type::getProperty(Loc loc, Identifier *ident) { e = new IntegerExp(loc, size(loc), Type::tsize_t); } - else if (ident == Id::size) - { - error(loc, ".size property should be replaced with .sizeof"); - e = new ErrorExp(); - } else if (ident == Id::__xalignof) { e = new IntegerExp(loc, alignsize(), Type::tsize_t); } else if (ident == Id::typeinfo) { - if (!global.params.useDeprecated) - error(loc, ".typeinfo deprecated, use typeid(type)"); + error(loc, ".typeinfo deprecated, use typeid(type)"); e = getTypeInfo(NULL); } else if (ident == Id::init) { - if (ty == Tvoid) - error(loc, "void does not have an initializer"); - if (ty == Tfunction) - error(loc, "function does not have an initializer"); - if (toBasetype()->ty == Tstruct && - ((TypeStruct *)toBasetype())->sym->isNested()) + Type *tb = toBasetype(); +#if IN_LLVM + // LDC_FIXME: Port the below change to LDC. + if (tb->ty == Tstruct && tb->needsNested()) { e = defaultInit(loc); } else e = defaultInitLiteral(loc); +#else + e = defaultInitLiteral(loc); + if (tb->ty == Tstruct && tb->needsNested()) + { + StructLiteralExp *se = (StructLiteralExp *)e; + se->sinit = se->sd->toInitializer(); + } +#endif } else if (ident == Id::mangleof) { const char *s; @@ -2115,8 +2142,7 @@ Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident) { if (ident == Id::offset) { - if (!global.params.useDeprecated) - error(e->loc, ".offset deprecated, use .offsetof"); + error(e->loc, ".offset deprecated, use .offsetof"); goto Loffset; } else if (ident == Id::offsetof) @@ -2130,6 +2156,8 @@ Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident) } else if (ident == Id::init) { +#if IN_LLVM + // LDC_FIXME: Port the below (from 2.061). if (toBasetype()->ty == Tstruct && ((TypeStruct *)toBasetype())->sym->isNested()) { @@ -2137,13 +2165,21 @@ Expression *Type::dotExp(Scope *sc, Expression *e, Identifier *ident) } else e = defaultInitLiteral(e->loc); +#else + Type *tb = toBasetype(); + e = defaultInitLiteral(e->loc); + if (tb->ty == Tstruct && tb->needsNested()) + { + StructLiteralExp *se = (StructLiteralExp *)e; + se->sinit = se->sd->toInitializer(); + } +#endif goto Lreturn; } } if (ident == Id::typeinfo) { - if (!global.params.useDeprecated) - error(e->loc, ".typeinfo deprecated, use typeid(type)"); + error(e->loc, ".typeinfo deprecated, use typeid(type)"); e = getTypeInfo(sc); } else if (ident == Id::stringof) @@ -2260,7 +2296,7 @@ Identifier *Type::getTypeInfoIdent(int internal) OutBuffer buf; Identifier *id; char *name; - int len; + size_t len; if (internal) { buf.writeByte(mangleChar[ty]); @@ -2278,10 +2314,10 @@ Identifier *Type::getTypeInfoIdent(int internal) #else sprintf(name, "_D%dTypeInfo_%s6__initZ", 9 + len, buf.data); #endif -// LDC -// it is not clear where the underscore that's stripped here is added back in -// if (global.params.isWindows) -// name++; // C mangling will add it back in +#if !IN_LLVM + if (global.params.isWindows && !global.params.is64bit) + name++; // C mangling will add it back in +#endif //printf("name = %s\n", name); id = Lexer::idPool(name); return id; @@ -2721,6 +2757,14 @@ TypeBasic::TypeBasic(TY ty) flags |= TFLAGSintegral | TFLAGSunsigned | TFLAGSvector; break; + case Tint128: d = Token::toChars(TOKint128); + flags |= TFLAGSintegral; + break; + + case Tuns128: d = Token::toChars(TOKuns128); + flags |= TFLAGSintegral | TFLAGSunsigned; + break; + case Tfloat64: d = Token::toChars(TOKfloat64); flags |= TFLAGSfloating | TFLAGSreal | TFLAGSvector; break; @@ -2824,6 +2868,8 @@ d_uns64 TypeBasic::size(Loc loc) case Tcomplex32: size = 8; break; case Tcomplex64: + case Tint128: + case Tuns128: size = 16; break; case Tcomplex80: size = REALSIZE * 2; break; @@ -2953,8 +2999,8 @@ Expression *TypeBasic::getProperty(Loc loc, Identifier *ident) case Tcomplex80: case Timaginary80: case Tfloat80: - // For backwards compatibility - eventually, deprecate - goto Lmin_normal; + warning(loc, "min property is deprecated, use min_normal instead"); + goto Lmin_normal; } } else if (ident == Id::min_normal) @@ -3415,7 +3461,7 @@ MATCH TypeBasic::implicitConvTo(Type *to) if (tob->flags & TFLAGSintegral) return MATCHnomatch; - assert(tob->flags & TFLAGSfloating); + assert(tob->flags & TFLAGSfloating || to->ty == Tvector); // Disallow implicit conversion from complex to non-complex if (flags & TFLAGScomplex && !(tob->flags & TFLAGScomplex)) @@ -3619,7 +3665,7 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) if (!n->isMutable()) if (ident == Id::sort || ident == Id::reverse) - error(e->loc, "can only %s a mutable array\n", ident->toChars()); + error(e->loc, "can only %s a mutable array", ident->toChars()); if (ident == Id::reverse && (n->ty == Tchar || n->ty == Twchar)) { @@ -3906,14 +3952,23 @@ void TypeSArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol } if (o->dyncast() == DYNCAST_EXPRESSION) { - *ps = NULL; - *pe = (Expression *)o; + Expression *e = (Expression *)o; + if (e->op == TOKdsymbol) + { + *ps = ((DsymbolExp *)e)->s; + *pe = NULL; + } + else + { + *ps = NULL; + *pe = e; + } return; } if (o->dyncast() == DYNCAST_TYPE) { *ps = NULL; - *pt = (Type *)o; + *pt = ((Type *)o)->addMod(this->mod); return; } @@ -3963,7 +4018,7 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) { error(loc, "%s is not a type", toChars()); return Type::terror; } - t = (Type *)o; + t = ((Type *)o)->addMod(this->mod); return t; } @@ -4046,7 +4101,7 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) goto Lerror; } Parameter *arg = (*tt->arguments)[(size_t)d]; - return arg->type; + return arg->type->addMod(this->mod); } case Tstruct: { TypeStruct *ts = (TypeStruct *)tbn; @@ -4230,6 +4285,15 @@ int TypeSArray::needsDestruction() return next->needsDestruction(); } +/********************************* + * + */ + +bool TypeSArray::needsNested() +{ + return next->needsNested(); +} + Expression *TypeSArray::defaultInitLiteral(Loc loc) { #if LOGDEFAULTINIT @@ -4307,9 +4371,8 @@ unsigned TypeDArray::alignsize() } Type *TypeDArray::semantic(Loc loc, Scope *sc) -{ Type *tn = next; - - tn = next->semantic(loc,sc); +{ + Type *tn = next->semantic(loc,sc); Type *tbn = tn->toBasetype(); switch (tbn->ty) { @@ -4685,7 +4748,7 @@ void TypeAArray::resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol // Rewrite as a static array TypeSArray *tsa = new TypeSArray(next, e); - return tsa->resolve(loc, sc, pe, pt, ps); + return tsa->addMod(this->mod)->resolve(loc, sc, pe, pt, ps); } else if (t) index = t; @@ -5448,10 +5511,12 @@ void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) case TRUSTsafe: buf->writestring("Nf"); break; + default: break; } } - // LDC: if we're not producing a mangle string, add the this +#if IN_LLVM + // if we're not producing a mangle string, add the this // type to prevent merging different member function if (!mangle && funcdecl) { @@ -5489,6 +5554,7 @@ void TypeFunction::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) } }*/ } +#endif // Write argument types Parameter::argsToDecoBuffer(buf, parameters, mangle); @@ -5543,6 +5609,7 @@ void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, Hd case TRUSTsafe: buf->writestring("@safe "); break; + default: break; } if (hgs->ddoc != 1) @@ -5680,6 +5747,7 @@ void TypeFunction::attributesToCBuffer(OutBuffer *buf, int mod) case TRUSTsafe: buf->writestring(" @safe"); break; + default: break; } } @@ -5835,6 +5903,11 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) e = e->semantic(argsc); } e = e->implicitCastTo(argsc, fparam->type); + + // default arg must be an lvalue + if (fparam->storageClass & (STCout | STCref)) + e = e->toLvalue(argsc, e); + fparam->defaultArg = e; } @@ -5926,80 +5999,170 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) } +Type *getIndirection(Type *t) +{ + t = t->toBasetype(); + + if (t->ty == Tsarray) + { while (t->ty == Tsarray) + t = t->nextOf()->toBasetype(); + } + if (t->ty == Tarray || t->ty == Tpointer) + return t->nextOf()->toBasetype(); + if (t->ty == Taarray || t->ty == Tclass) + return t; + if (t->ty == Tstruct) + return t->hasPointers() ? t : NULL; // TODO + + // should consider TypeDelegate? + return NULL; +} + /******************************************** * Do this lazily, as the parameter types might be forward referenced. */ void TypeFunction::purityLevel() { + //printf("purityLevel(%s)\n", toChars()); + TypeFunction *tf = this; - if (tf->purity == PUREfwdref) + if (tf->purity == PUREfwdref && tf->next) { /* Evaluate what kind of purity based on the modifiers for the parameters */ - tf->purity = PUREstrong; // assume strong until something weakens it - if (tf->parameters) + enum PURE purity = PUREstrong; // assume strong until something weakens it + size_t dim = Parameter::dim(tf->parameters); + + if (dim) { - size_t dim = Parameter::dim(tf->parameters); + Type *tret = tf->next; + assert(tret); + Type *treti = tf->isref ? tret->toBasetype() : getIndirection(tret); + if (treti && (treti->mod & MODimmutable)) + treti = NULL; // indirection is immutable + //printf(" tret = %s, treti = %s\n", tret->toChars(), treti ? treti->toChars() : "NULL"); + for (size_t i = 0; i < dim; i++) { Parameter *fparam = Parameter::getNth(tf->parameters, i); if (fparam->storageClass & STClazy) { - tf->purity = PUREweak; + purity = PUREweak; break; } if (fparam->storageClass & STCout) { - tf->purity = PUREweak; + purity = PUREweak; break; } if (!fparam->type) continue; - if (fparam->storageClass & STCref) + + Type *tprm = fparam->type; + Type *tprmi = fparam->storageClass & STCref ? tprm->toBasetype() : getIndirection(tprm); + //printf(" [%d] tprm = %s, tprmi = %s\n", i, tprm->toChars(), tprmi ? tprmi->toChars() : "NULL"); + + if (!tprmi || (tprmi->mod & MODimmutable)) + continue; // there is no mutable indirection + if (tprmi->isMutable()) + { purity = PUREweak; // indirection is mutable + break; + } + if (!treti) + continue; // mutable indirection is never returned + + if (purity < PUREstrong) + continue; + + // Determine the parameter is really PUREconst or not + assert(tprmi->mod & (MODconst | MODwild)); + if (tprmi->constConv(treti)) // simple case + purity = PUREconst; + else if (tprmi->invariantOf()->equals(treti->invariantOf())) + continue; + else { - if (!(fparam->type->mod & (MODconst | MODimmutable | MODwild))) - { tf->purity = PUREweak; - break; - } - if (fparam->type->mod & MODconst) - { tf->purity = PUREconst; - continue; - } - } - Type *t = fparam->type->toBasetype(); - if (!t->hasPointers()) - continue; - if (t->mod & (MODimmutable | MODwild)) - continue; - /* The rest of this is too strict; fix later. - * For example, the only pointer members of a struct may be immutable, - * which would maintain strong purity. - */ - if (t->mod & MODconst) - { tf->purity = PUREconst; - continue; - } - Type *tn = t->nextOf(); - if (tn) - { tn = tn->toBasetype(); - if (tn->ty == Tpointer || tn->ty == Tarray) - { /* Accept immutable(T)* and immutable(T)[] as being strongly pure - */ - if (tn->mod & (MODimmutable | MODwild)) - continue; - if (tn->mod & MODconst) - { tf->purity = PUREconst; - continue; - } - } + /* The rest of this is little strict; fix later. + * For example: + * + * struct S { immutable* p; } + * pure S foo(const int* p); + * + * which would maintain strong purity. + */ + if (tprmi->hasPointers() || treti->hasPointers()) + purity = PUREconst; } + /* Should catch delegates and function pointers, and fold in their purity */ - tf->purity = PUREweak; // err on the side of too strict - break; } } + + //printf(" --> purity: %d\n", purity); + tf->purity = purity; } } +/******************************************** + * FIXME: This function is a workaround for fixing Bugzilla 9210. + * In 2.061, TypeFunction::purityLevel() improved to make more functions + * strong purity, but immutable conversion on return statemet had broken by that. + * Because, it is essentially unrelated to PUREstrong. This function is + * necessary to check the convertibility. + */ +bool TypeFunction::hasMutableIndirectionParams() +{ + TypeFunction *tf = this; + size_t dim = Parameter::dim(tf->parameters); + for (size_t i = 0; i < dim; i++) + { + Parameter *fparam = Parameter::getNth(tf->parameters, i); + if (fparam->storageClass & STClazy) + { + return true; + } + if (fparam->storageClass & STCout) + { + return true; + } + if (!fparam->type) + continue; + if (fparam->storageClass & STCref) + { + if (!(fparam->type->mod & (MODconst | MODimmutable | MODwild))) + return true; + if (fparam->type->mod & MODconst) + return true; + } + Type *t = fparam->type->toBasetype(); + if (!t->hasPointers()) + continue; + if (t->mod & (MODimmutable | MODwild)) + continue; + /* The rest of this is too strict; fix later. + * For example, the only pointer members of a struct may be immutable, + * which would maintain strong purity. + */ + if (t->mod & MODconst) + return true; + Type *tn = t->nextOf(); + if (tn) + { tn = tn->toBasetype(); + if (tn->ty == Tpointer || tn->ty == Tarray) + { /* Accept immutable(T)* and immutable(T)[] as being strongly pure + */ + if (tn->mod & (MODimmutable | MODwild)) + continue; + if (tn->mod & MODconst) + return true; + } + } + /* Should catch delegates and function pointers, and fold in their purity + */ + return true; + } + return false; +} + /******************************** * 'args' are being matched to function 'this' @@ -6010,7 +6173,7 @@ void TypeFunction::purityLevel() * MATCHxxxx */ -int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) +MATCH TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) { //printf("TypeFunction::callMatch() %s\n", toChars()); MATCH match = MATCHexact; // assume exact match @@ -6224,7 +6387,6 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) { Expression *arg = (*args)[u]; assert(arg); -#if 1 if (arg->op == TOKfunction) { arg = ((FuncExp *)arg)->inferType(tb->nextOf(), 1); @@ -6239,23 +6401,19 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) if (tret) { if (ta->next->equals(arg->type)) - { m = MATCHexact; - } + m = MATCHexact; + else if (tret->toBasetype()->ty == Tvoid) + m = MATCHconvert; else { m = arg->implicitConvTo(tret); if (m == MATCHnomatch) - { - if (tret->toBasetype()->ty == Tvoid) - m = MATCHconvert; - } + m = arg->implicitConvTo(ta->next); } } else m = arg->implicitConvTo(ta->next); -#else - m = arg->implicitConvTo(ta->next); -#endif + if (m == MATCHnomatch) goto Nomatch; if (m < match) @@ -6330,6 +6488,11 @@ bool TypeFunction::parameterEscapes(Parameter *p) if (p->storageClass & (STCscope | STClazy)) return FALSE; + /* If haven't inferred the return type yet, assume it escapes + */ + if (!nextOf()) + return TRUE; + if (purity) { /* With pure functions, we need only be concerned if p escapes * via any return statement. @@ -6810,13 +6973,11 @@ Type *TypeIdentifier::syntaxCopy() } void TypeIdentifier::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) -{ unsigned len; - char *name; - +{ Type::toDecoBuffer(buf, flag, mangle); - name = ident->toChars(); - len = strlen(name); - buf->printf("%d%s", len, name); + const char *name = ident->toChars(); + size_t len = strlen(name); + buf->printf("%u%s", (unsigned)len, name); } void TypeIdentifier::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) @@ -6915,7 +7076,7 @@ Type *TypeIdentifier::semantic(Loc loc, Scope *sc) if (t->ty == Ttypedef) { TypeTypedef *tt = (TypeTypedef *)t; - if (tt->sym->sem == 1) + if (tt->sym->sem == SemanticIn) error(loc, "circular reference of typedef %s", tt->toChars()); } t = t->addMod(mod); @@ -7318,11 +7479,15 @@ Dsymbol *TypeReturn::toDsymbol(Scope *sc) Type *TypeReturn::semantic(Loc loc, Scope *sc) { Type *t; - if (!sc->func) + FuncDeclaration *func = sc->func; + if (!func) { error(loc, "typeof(return) must be inside function"); goto Lerr; } - t = sc->func->type->nextOf(); + if (func->fes) + func = func->fes->func; + + t = func->type->nextOf(); if (!t) { error(loc, "cannot use typeof(return) inside function %s with inferred return type", sc->func->toChars()); @@ -7559,9 +7724,9 @@ int TypeEnum::isscalar() return sym->memtype->isscalar(); } -int TypeEnum::isAssignable() +int TypeEnum::isAssignable(int blit) { - return sym->memtype->isAssignable(); + return sym->memtype->isAssignable(blit); } int TypeEnum::checkBoolean() @@ -7574,6 +7739,11 @@ int TypeEnum::needsDestruction() return sym->memtype->needsDestruction(); } +bool TypeEnum::needsNested() +{ + return sym->memtype ? sym->memtype->needsNested() : false; +} + MATCH TypeEnum::implicitConvTo(Type *to) { MATCH m; @@ -7610,7 +7780,10 @@ Expression *TypeEnum::defaultInit(Loc loc) error(loc, "forward reference of %s.init", toChars()); return new ErrorExp(); } - return sym->defaultval; + Expression *e = sym->defaultval; + e = e->copy(); + e->type = this; + return e; } int TypeEnum::isZeroInit(Loc loc) @@ -7768,9 +7941,9 @@ int TypeTypedef::isscalar() return sym->basetype->isscalar(); } -int TypeTypedef::isAssignable() +int TypeTypedef::isAssignable(int blit) { - return sym->basetype->isAssignable(); + return sym->basetype->isAssignable(blit); } int TypeTypedef::checkBoolean() @@ -7783,6 +7956,11 @@ int TypeTypedef::needsDestruction() return sym->basetype->needsDestruction(); } +bool TypeTypedef::needsNested() +{ + return sym->basetype->needsNested(); +} + Type *TypeTypedef::toBasetype() { if (sym->inuse) @@ -8087,7 +8265,6 @@ L1: if (s->getType()) { - //return new DotTypeExp(e->loc, e, s); return new TypeExp(e->loc, s->getType()); } @@ -8168,6 +8345,7 @@ L1: e = e->semantic(sc); return e; } + accessCheck(e->loc, sc, e, d); VarExp *ve = new VarExp(e->loc, d, 1); if (d->isVarDeclaration() && d->needThis()) ve->type = d->type->addMod(e->type->mod); @@ -8251,6 +8429,8 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc) } else e = vd->type->defaultInitLiteral(loc); + if (e && vd->scope) + e = e->semantic(vd->scope); (*structelems)[j] = e; } StructLiteralExp *structinit = new StructLiteralExp(loc, (StructDeclaration *)sym, structelems); @@ -8259,13 +8439,11 @@ Expression *TypeStruct::defaultInitLiteral(Loc loc) /* Copy from the initializer symbol for larger symbols, * otherwise the literals expressed as code get excessively large. */ - if (size(loc) > PTRSIZE * 4 && !sym->isnested) + if (size(loc) > PTRSIZE * 4 && !needsNested()) structinit->sinit = sym->toInitializer(); #endif - // Why doesn't the StructLiteralExp constructor do this, when - // sym->type != NULL ? - structinit->type = sym->type; + structinit->type = this; return structinit; } @@ -8285,8 +8463,33 @@ int TypeStruct::needsDestruction() return sym->dtor != NULL; } -int TypeStruct::isAssignable() +bool TypeStruct::needsNested() { + if (sym->isnested) + return true; + + for (size_t i = 0; i < sym->fields.dim; i++) + { + Dsymbol *s = sym->fields[i]; + VarDeclaration *vd = s->isVarDeclaration(); + if (vd && !vd->isDataseg() && vd->type->needsNested()) + return true; + } + return false; +} + +int TypeStruct::isAssignable(int blit) +{ + if (!blit) + { + if (sym->hasIdentityAssign) + return TRUE; + + // has non-identity opAssign + if (search_function(sym, Id::assign)) + return FALSE; + } + int assignable = TRUE; unsigned offset; @@ -8312,7 +8515,7 @@ int TypeStruct::isAssignable() if (!assignable) return FALSE; } - assignable = v->type->isMutable() && v->type->isAssignable(); + assignable = v->type->isMutable() && v->type->isAssignable(blit); offset = v->offset; //printf(" -> assignable = %d\n", assignable); } @@ -8557,11 +8760,16 @@ Expression *TypeClass::dotExp(Scope *sc, Expression *e, Identifier *ident) L1: if (!s) { - // See if it's a base class - if (Dsymbol *cbase = sym->searchBase(e->loc, ident)) + // See if it's 'this' class or a base class + if (e->op != TOKtype) { - e = new DotTypeExp(0, e, cbase); - return e; + Dsymbol *cbase = sym->ident == ident ? + sym : sym->searchBase(e->loc, ident); + if (cbase) + { + e = new DotTypeExp(0, e, cbase); + return e; + } } if (ident == Id::classinfo) @@ -8681,8 +8889,7 @@ L1: if (ident == Id::typeinfo) { - if (!global.params.useDeprecated) - error(e->loc, ".typeinfo deprecated, use typeid(type)"); + error(e->loc, ".typeinfo deprecated, use typeid(type)"); return getTypeInfo(sc); } if (ident == Id::outer && sym->vthis) @@ -8710,9 +8917,7 @@ L1: if (s->getType()) { -// if (e->op == TOKtype) - return new TypeExp(e->loc, s->getType()); -// return new DotTypeExp(e->loc, e, s); + return new TypeExp(e->loc, s->getType()); } EnumMember *em = s->isEnumMember(); @@ -8788,38 +8993,89 @@ L1: e = e->semantic(sc); return e; } - else if (d->needThis() && (hasThis(sc) || !(sc->intypeof || d->isFuncDeclaration()))) + + #if 1 // Workaround for Bugzilla 9213 + FuncDeclaration *fd = sc->func; + if (d->needThis() && d->isVarDeclaration() && fd && fd->vthis) { - if (sc->func) - { - ClassDeclaration *thiscd; - thiscd = sc->func->toParent()->isClassDeclaration(); - - if (thiscd) - { - ClassDeclaration *cd = e->type->isClassHandle(); - - if (cd == thiscd) - { - e = new ThisExp(e->loc); - e = new DotTypeExp(e->loc, e, cd); - DotVarExp *de = new DotVarExp(e->loc, e, d); - e = de->semantic(sc); - return e; - } - else if ((!cd || !cd->isBaseOf(thiscd, NULL)) && - !d->isFuncDeclaration()) - e->error("'this' is required, but %s is not a base class of %s", e->type->toChars(), thiscd->toChars()); - } - } - - /* Rewrite as: - * this.d - */ - DotVarExp *de = new DotVarExp(e->loc, new ThisExp(e->loc), d); - e = de->semantic(sc); + e = new DotVarExp(e->loc, new ThisExp(e->loc), d); + e = e->semantic(sc); return e; } + #endif + + FuncDeclaration *fdthis = hasThis(sc); + if (d->needThis() && fdthis) + { + if (d->isFuncDeclaration()) + { + // This is almost same as getRightThis() in expression.c + Expression *e1 = new VarExp(e->loc, fdthis->vthis); + e1 = e1->semantic(sc); + L2: + Type *t = e1->type->toBasetype(); + ClassDeclaration *cd = e->type->isClassHandle(); + ClassDeclaration *tcd = t->isClassHandle(); + if (cd && tcd && (tcd == cd || cd->isBaseOf(tcd, NULL))) + { + e = new DotTypeExp(e1->loc, e1, cd); + e = new DotVarExp(e->loc, e, d); + e = e->semantic(sc); + return e; + } + if (tcd && tcd->isNested()) + { /* e1 is the 'this' pointer for an inner class: tcd. + * Rewrite it as the 'this' pointer for the outer class. + */ + + e1 = new DotVarExp(e->loc, e1, tcd->vthis); + e1->type = tcd->vthis->type; + e1->type = e1->type->addMod(t->mod); + // Do not call checkNestedRef() + //e1 = e1->semantic(sc); + + // Skip up over nested functions, and get the enclosing + // class type. + int n = 0; + Dsymbol *s; + for (s = tcd->toParent(); + s && s->isFuncDeclaration(); + s = s->toParent()) + { FuncDeclaration *f = s->isFuncDeclaration(); + if (f->vthis) + { + //printf("rewriting e1 to %s's this\n", f->toChars()); + n++; + e1 = new VarExp(e->loc, f->vthis); + } + else + { + e = new VarExp(e->loc, d, 1); + return e; + } + } + if (s && s->isClassDeclaration()) + { e1->type = s->isClassDeclaration()->type; + e1->type = e1->type->addMod(t->mod); + if (n > 1) + e1 = e1->semantic(sc); + } + else + e1 = e1->semantic(sc); + goto L2; + } + } + else + { + /* Rewrite as: + * this.d + */ + DotVarExp *de = new DotVarExp(e->loc, new ThisExp(e->loc), d); + e = de->semantic(sc); + return e; + } + } + accessCheck(e->loc, sc, e, d); VarExp *ve = new VarExp(e->loc, d, 1); if (d->isVarDeclaration() && d->needThis()) ve->type = d->type->addMod(e->type->mod); @@ -9127,7 +9383,7 @@ void TypeTuple::toDecoBuffer(OutBuffer *buf, int flag, bool mangle) Type::toDecoBuffer(buf, flag, mangle); OutBuffer buf2; Parameter::argsToDecoBuffer(&buf2, arguments, mangle); - unsigned len = buf2.offset; + int len = (int)buf2.offset; buf->printf("%d%.*s", len, len, (char *)buf2.extractData()); } @@ -9453,7 +9709,7 @@ void Parameter::argsToCBuffer(OutBuffer *buf, HdrGenState *hgs, Parameters *argu if (varargs) { if (arguments->dim && varargs == 1) - buf->writeByte(','); + buf->writestring(", "); buf->writestring("..."); } } diff --git a/dmd2/mtype.h b/dmd2/mtype.h index 3b1cd740..a60d70cc 100644 --- a/dmd2/mtype.h +++ b/dmd2/mtype.h @@ -108,6 +108,8 @@ enum ENUMTY Treturn, Tnull, Tvector, + Tint128, + Tuns128, TMAX }; typedef unsigned char TY; // ENUMTY @@ -163,6 +165,8 @@ struct Type : Object #define tuns32 basic[Tuns32] #define tint64 basic[Tint64] #define tuns64 basic[Tuns64] + #define tint128 basic[Tint128] + #define tuns128 basic[Tuns128] #define tfloat32 basic[Tfloat32] #define tfloat64 basic[Tfloat64] #define tfloat80 basic[Tfloat80] @@ -271,7 +275,7 @@ struct Type : Object virtual int isunsigned(); virtual int isscope(); virtual int isString(); - virtual int isAssignable(); + virtual int isAssignable(int blit = 0); virtual int checkBoolean(); // if can be converted to boolean value virtual void checkDeprecated(Loc loc, Scope *sc); int isConst() { return mod & MODconst; } @@ -342,6 +346,7 @@ struct Type : Object virtual Type *nextOf(); uinteger_t sizemask(); virtual int needsDestruction(); + virtual bool needsNested(); static void error(Loc loc, const char *format, ...) IS_PRINTF(2); @@ -466,6 +471,10 @@ struct TypeVector : Type int isZeroInit(Loc loc); TypeInfoDeclaration *getTypeInfoDeclaration(); TypeTuple *toArgTypes(); + +#if IN_DMD + type *toCtype(); +#endif }; struct TypeArray : TypeNext @@ -505,6 +514,7 @@ struct TypeSArray : TypeArray Expression *toExpression(); int hasPointers(); int needsDestruction(); + bool needsNested(); TypeTuple *toArgTypes(); #if CPP_MANGLE void toCppMangle(OutBuffer *buf, CppMangleState *cms); @@ -671,6 +681,7 @@ struct TypeFunction : TypeNext Type *syntaxCopy(); Type *semantic(Loc loc, Scope *sc); void purityLevel(); + bool hasMutableIndirectionParams(); void toDecoBuffer(OutBuffer *buf, int flag, bool mangle); void toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs); void toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td); @@ -686,7 +697,7 @@ struct TypeFunction : TypeNext bool parameterEscapes(Parameter *p); Type *addStorageClass(StorageClass stc); - int callMatch(Expression *ethis, Expressions *toargs, int flag = 0); + MATCH callMatch(Expression *ethis, Expressions *toargs, int flag = 0); #if IN_DMD type *toCtype(); #endif @@ -751,6 +762,7 @@ struct TypeQualified : Type struct TypeIdentifier : TypeQualified { Identifier *ident; + Dsymbol *originalSymbol; // The symbol representing this identifier, before alias resolution TypeIdentifier(Loc loc, Identifier *ident); Type *syntaxCopy(); @@ -824,9 +836,10 @@ struct TypeStruct : Type Expression *defaultInitLiteral(Loc loc); Expression *voidInitLiteral(VarDeclaration *var); int isZeroInit(Loc loc); - int isAssignable(); + int isAssignable(int blit = 0); int checkBoolean(); int needsDestruction(); + bool needsNested(); #if IN_DMD dt_t **toDt(dt_t **pdt); #endif @@ -875,8 +888,9 @@ struct TypeEnum : Type int isscalar(); int isunsigned(); int checkBoolean(); - int isAssignable(); + int isAssignable(int blit = 0); int needsDestruction(); + bool needsNested(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); Type *toBasetype(); @@ -919,8 +933,9 @@ struct TypeTypedef : Type int isscalar(); int isunsigned(); int checkBoolean(); - int isAssignable(); + int isAssignable(int blit = 0); int needsDestruction(); + bool needsNested(); Type *toBasetype(); MATCH implicitConvTo(Type *to); MATCH constConv(Type *to); @@ -1050,6 +1065,7 @@ struct Parameter : Object Parameter *syntaxCopy(); Type *isLazyArray(); void toDecoBuffer(OutBuffer *buf, bool mangle); + int dyncast() { return DYNCAST_PARAMETER; } // kludge for template.isType() static Parameters *arraySyntaxCopy(Parameters *args); static char *argsTypesToChars(Parameters *args, int varargs); static void argsCppMangle(OutBuffer *buf, CppMangleState *cms, Parameters *arguments, int varargs); @@ -1076,5 +1092,6 @@ void MODtoBuffer(OutBuffer *buf, unsigned char mod); int MODimplicitConv(unsigned char modfrom, unsigned char modto); int MODmethodConv(unsigned char modfrom, unsigned char modto); int MODmerge(unsigned char mod1, unsigned char mod2); +void identifierToDocBuffer(Identifier* ident, OutBuffer *buf, HdrGenState *hgs); #endif /* DMD_MTYPE_H */ diff --git a/dmd2/opover.c b/dmd2/opover.c index de57524a..5d731df2 100644 --- a/dmd2/opover.c +++ b/dmd2/opover.c @@ -237,6 +237,7 @@ Expression *UnaExp::op_overload(Scope *sc) Dsymbol *fd = search_function(ad, Id::opIndexUnary); if (fd) { + ae = resolveOpDollar(sc, ae); Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, targsi); e = new CallExp(loc, e, ae->arguments); @@ -274,12 +275,13 @@ Expression *UnaExp::op_overload(Scope *sc) Dsymbol *fd = search_function(ad, Id::opSliceUnary); if (fd) { + se = resolveOpDollar(sc, se); Expressions *a = new Expressions(); + assert(!se->lwr || se->upr); if (se->lwr) { a->push(se->lwr); a->push(se->upr); } - Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, se->e1, fd->ident, targsi); e = new CallExp(loc, e, a); @@ -376,35 +378,12 @@ 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)[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); - x = resolveProperties(sc, x); - 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)[i] = x; - sc = sc->pop(); - } - /* Rewrite op e1[arguments] as: * e1.opIndex(arguments) */ - Expression *e = new DotIdExp(loc, e1, fd->ident); - e = new CallExp(loc, e, arguments); + ArrayExp *ae = resolveOpDollar(sc, this); + Expression *e = new DotIdExp(loc, ae->e1, fd->ident); + e = new CallExp(loc, e, ae->arguments); e = e->semantic(sc); return e; } @@ -810,7 +789,7 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) } else { TemplateDeclaration *td = s->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args2); + templateResolve(&m, td, sc, loc, targsi, e1, &args2); } } @@ -826,7 +805,7 @@ Expression *BinExp::compare_overload(Scope *sc, Identifier *id) } else { TemplateDeclaration *td = s_r->isTemplateDeclaration(); - templateResolve(&m, td, sc, loc, targsi, NULL, &args1); + templateResolve(&m, td, sc, loc, targsi, e2, &args1); } } @@ -988,10 +967,9 @@ Expression *BinAssignExp::op_overload(Scope *sc) Dsymbol *fd = search_function(ad, Id::opIndexOpAssign); if (fd) { - Expressions *a = new Expressions(); - a->push(e2); - for (size_t i = 0; i < ae->arguments->dim; i++) - a->push((*ae->arguments)[i]); + ae = resolveOpDollar(sc, ae); + Expressions *a = (Expressions *)ae->arguments->copy(); + a->insert(0, e2); Objects *targsi = opToArg(sc, op); Expression *e = new DotTemplateInstanceExp(loc, ae->e1, fd->ident, targsi); @@ -1030,8 +1008,10 @@ Expression *BinAssignExp::op_overload(Scope *sc) Dsymbol *fd = search_function(ad, Id::opSliceOpAssign); if (fd) { + se = resolveOpDollar(sc, se); Expressions *a = new Expressions(); a->push(e2); + assert(!se->lwr || se->upr); if (se->lwr) { a->push(se->lwr); a->push(se->upr); @@ -1345,7 +1325,10 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) for (size_t u = 0; u < arguments->dim; u++) { Parameter *arg = (*arguments)[u]; if (arg->type) + { arg->type = arg->type->semantic(loc, sc); + arg->type = arg->type->addStorageClass(arg->storageClass); + } } Expression *ethis; @@ -1396,11 +1379,17 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) if (arguments->dim == 2) { if (!arg->type) + { arg->type = Type::tsize_t; // key type + arg->type = arg->type->addStorageClass(arg->storageClass); + } arg = (*arguments)[1]; } if (!arg->type && tab->ty != Ttuple) + { arg->type = tab->nextOf(); // value type + arg->type = arg->type->addStorageClass(arg->storageClass); + } break; case Taarray: @@ -1409,11 +1398,17 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) if (arguments->dim == 2) { if (!arg->type) + { arg->type = taa->index; // key type + arg->type = arg->type->addStorageClass(arg->storageClass); + } arg = (*arguments)[1]; } if (!arg->type) + { arg->type = taa->next; // value type + arg->type = arg->type->addStorageClass(arg->storageClass); + } break; } @@ -1440,7 +1435,10 @@ int ForeachStatement::inferApplyArgTypes(Scope *sc, Dsymbol *&sapply) // Resolve inout qualifier of front type arg->type = fd->type->nextOf(); if (arg->type) + { arg->type = arg->type->substWildTo(tab->mod); + arg->type = arg->type->addStorageClass(arg->storageClass); + } } else if (s && s->isTemplateDeclaration()) ; @@ -1560,7 +1558,10 @@ static int inferApplyArgTypesY(TypeFunction *tf, Parameters *arguments, int flag goto Lnomatch; } else if (!flags) + { arg->type = param->type; + arg->type = arg->type->addStorageClass(arg->storageClass); + } } Lmatch: return 1; @@ -1626,4 +1627,3 @@ static void templateResolve(Match *m, TemplateDeclaration *td, Scope *sc, Loc lo m->count = 1; } } - diff --git a/dmd2/optimize.c b/dmd2/optimize.c index 16e30130..0b0530b3 100644 --- a/dmd2/optimize.c +++ b/dmd2/optimize.c @@ -180,6 +180,7 @@ Expression *fromConstInitializer(int result, Expression *e1) !(v->storage_class & STCtemplateparameter)) { e1->error("variable %s cannot be read at compile time", v->toChars()); + e->type = Type::terror; } } } @@ -187,18 +188,18 @@ Expression *fromConstInitializer(int result, Expression *e1) } -Expression *Expression::optimize(int result) +Expression *Expression::optimize(int result, bool keepLvalue) { //printf("Expression::optimize(result = x%x) %s\n", result, toChars()); return this; } -Expression *VarExp::optimize(int result) +Expression *VarExp::optimize(int result, bool keepLvalue) { - return fromConstInitializer(result, this); + return keepLvalue ? this : fromConstInitializer(result, this); } -Expression *TupleExp::optimize(int result) +Expression *TupleExp::optimize(int result, bool keepLvalue) { for (size_t i = 0; i < exps->dim; i++) { Expression *e = (*exps)[i]; @@ -209,7 +210,7 @@ Expression *TupleExp::optimize(int result) return this; } -Expression *ArrayLiteralExp::optimize(int result) +Expression *ArrayLiteralExp::optimize(int result, bool keepLvalue) { if (elements) { @@ -223,7 +224,7 @@ Expression *ArrayLiteralExp::optimize(int result) return this; } -Expression *AssocArrayLiteralExp::optimize(int result) +Expression *AssocArrayLiteralExp::optimize(int result, bool keepLvalue) { assert(keys->dim == values->dim); for (size_t i = 0; i < keys->dim; i++) @@ -239,7 +240,7 @@ Expression *AssocArrayLiteralExp::optimize(int result) return this; } -Expression *StructLiteralExp::optimize(int result) +Expression *StructLiteralExp::optimize(int result, bool keepLvalue) { if (elements) { @@ -254,19 +255,19 @@ Expression *StructLiteralExp::optimize(int result) return this; } -Expression *TypeExp::optimize(int result) +Expression *TypeExp::optimize(int result, bool keepLvalue) { return this; } -Expression *UnaExp::optimize(int result) +Expression *UnaExp::optimize(int result, bool keepLvalue) { //printf("UnaExp::optimize() %s\n", toChars()); e1 = e1->optimize(result); return this; } -Expression *NegExp::optimize(int result) +Expression *NegExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -279,7 +280,7 @@ Expression *NegExp::optimize(int result) return e; } -Expression *ComExp::optimize(int result) +Expression *ComExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -292,7 +293,7 @@ Expression *ComExp::optimize(int result) return e; } -Expression *NotExp::optimize(int result) +Expression *NotExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -305,7 +306,7 @@ Expression *NotExp::optimize(int result) return e; } -Expression *BoolExp::optimize(int result) +Expression *BoolExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -318,7 +319,7 @@ Expression *BoolExp::optimize(int result) return e; } -Expression *AddrExp::optimize(int result) +Expression *AddrExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("AddrExp::optimize(result = %d) %s\n", result, toChars()); @@ -403,7 +404,7 @@ Expression *AddrExp::optimize(int result) && !ve->var->isImportedSymbol()) { TypeSArray *ts = (TypeSArray *)ve->type; - dinteger_t dim = ts->dim->toInteger(); + sinteger_t dim = ts->dim->toInteger(); if (index < 0 || index >= dim) error("array index %lld is out of bounds [0..%lld]", index, dim); e = new SymOffExp(loc, ve->var, index * ts->nextOf()->size()); @@ -416,7 +417,7 @@ Expression *AddrExp::optimize(int result) return this; } -Expression *PtrExp::optimize(int result) +Expression *PtrExp::optimize(int result, bool keepLvalue) { //printf("PtrExp::optimize(result = x%x) %s\n", result, toChars()); e1 = e1->optimize(result); @@ -435,6 +436,9 @@ Expression *PtrExp::optimize(int result) } return e; } + if (keepLvalue) + return this; + // Constant fold *(&structliteral + offset) if (e1->op == TOKadd) { @@ -458,10 +462,12 @@ Expression *PtrExp::optimize(int result) return this; } -Expression *DotVarExp::optimize(int result) +Expression *DotVarExp::optimize(int result, bool keepLvalue) { //printf("DotVarExp::optimize(result = x%x) %s\n", result, toChars()); e1 = e1->optimize(result); + if (keepLvalue) + return this; Expression *e = e1; @@ -485,7 +491,7 @@ Expression *DotVarExp::optimize(int result) return this; } -Expression *NewExp::optimize(int result) +Expression *NewExp::optimize(int result, bool keepLvalue) { if (thisexp) thisexp = thisexp->optimize(WANTvalue); @@ -517,23 +523,37 @@ Expression *NewExp::optimize(int result) return this; } -Expression *CallExp::optimize(int result) +Expression *CallExp::optimize(int result, bool keepLvalue) { //printf("CallExp::optimize(result = %d) %s\n", result, toChars()); Expression *e = this; - // Optimize parameters + // Optimize parameters with keeping lvalue-ness if (arguments) { + Type *t1 = e1->type->toBasetype(); + if (t1->ty == Tdelegate) t1 = t1->nextOf(); + assert(t1->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)t1; + size_t pdim = Parameter::dim(tf->parameters) - (tf->varargs == 2 ? 1 : 0); for (size_t i = 0; i < arguments->dim; i++) - { Expression *e = (*arguments)[i]; - - e = e->optimize(WANTvalue); + { + bool keepLvalue = false; + if (i < pdim) + { + Parameter *p = Parameter::getNth(tf->parameters, i); + keepLvalue = ((p->storageClass & (STCref | STCout)) != 0); + } + Expression *e = (*arguments)[i]; + e = e->optimize(WANTvalue, keepLvalue); (*arguments)[i] = e; } } e1 = e1->optimize(result); + if (keepLvalue) + return this; + #if 1 if (result & WANTinterpret) { @@ -585,7 +605,7 @@ Expression *CallExp::optimize(int result) } -Expression *CastExp::optimize(int result) +Expression *CastExp::optimize(int result, bool keepLvalue) { #if IN_LLVM if (disableOptimization) @@ -634,9 +654,8 @@ Expression *CastExp::optimize(int result) if (e1->op == TOKstructliteral && e1->type->implicitConvTo(type) >= MATCHconst) { - e1->type = type; if (X) printf(" returning2 %s\n", e1->toChars()); - return e1; + goto L1; } /* The first test here is to prevent infinite loops @@ -646,9 +665,8 @@ Expression *CastExp::optimize(int result) if (e1->op == TOKnull && (type->ty == Tpointer || type->ty == Tclass || type->ty == Tarray)) { - e1->type = type; if (X) printf(" returning3 %s\n", e1->toChars()); - return e1; + goto L1; } if (result & WANTflags && type->ty == Tclass && e1->type->ty == Tclass) @@ -662,18 +680,16 @@ Expression *CastExp::optimize(int result) cdto = type->isClassHandle(); if (cdto->isBaseOf(cdfrom, &offset) && offset == 0) { - e1->type = type; if (X) printf(" returning4 %s\n", e1->toChars()); - return e1; + goto L1; } } // We can convert 'head const' to mutable if (to->mutableOf()->constOf()->equals(e1->type->mutableOf()->constOf())) { - e1->type = type; if (X) printf(" returning5 %s\n", e1->toChars()); - return e1; + goto L1; } Expression *e; @@ -685,8 +701,7 @@ Expression *CastExp::optimize(int result) if (type->size() == e1->type->size() && type->toBasetype()->ty != Tsarray) { - e1->type = type; - return e1; + goto L1; } return this; } @@ -699,10 +714,14 @@ Expression *CastExp::optimize(int result) e = this; if (X) printf(" returning6 %s\n", e->toChars()); return e; +L1: // Returning e1 with changing its type + e = (e1old == e1 ? e1->copy() : e1); + e->type = type; + return e; #undef X } -Expression *BinExp::optimize(int result) +Expression *BinExp::optimize(int result, bool keepLvalue) { //printf("BinExp::optimize(result = %d) %s\n", result, toChars()); if (op != TOKconstruct && op != TOKblit) // don't replace const variable with its initializer @@ -712,7 +731,7 @@ Expression *BinExp::optimize(int result) { if (e2->isConst() == 1) { - dinteger_t i2 = e2->toInteger(); + sinteger_t i2 = e2->toInteger(); d_uns64 sz = e1->type->size() * 8; if (i2 < 0 || i2 >= sz) { error("shift assign by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); @@ -723,7 +742,7 @@ Expression *BinExp::optimize(int result) return this; } -Expression *AddExp::optimize(int result) +Expression *AddExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("AddExp::optimize(%s)\n", toChars()); @@ -740,7 +759,7 @@ Expression *AddExp::optimize(int result) return e; } -Expression *MinExp::optimize(int result) +Expression *MinExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -756,7 +775,7 @@ Expression *MinExp::optimize(int result) return e; } -Expression *MulExp::optimize(int result) +Expression *MulExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("MulExp::optimize(result = %d) %s\n", result, toChars()); @@ -771,7 +790,7 @@ Expression *MulExp::optimize(int result) return e; } -Expression *DivExp::optimize(int result) +Expression *DivExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("DivExp::optimize(%s)\n", toChars()); @@ -786,7 +805,7 @@ Expression *DivExp::optimize(int result) return e; } -Expression *ModExp::optimize(int result) +Expression *ModExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -807,7 +826,7 @@ Expression *shift_optimize(int result, BinExp *e, Expression *(*shift)(Type *, E e->e2 = e->e2->optimize(result); if (e->e2->isConst() == 1) { - dinteger_t i2 = e->e2->toInteger(); + sinteger_t i2 = e->e2->toInteger(); d_uns64 sz = e->e1->type->size() * 8; if (i2 < 0 || i2 >= sz) { e->error("shift by %lld is outside the range 0..%llu", i2, (ulonglong)sz - 1); @@ -819,25 +838,25 @@ Expression *shift_optimize(int result, BinExp *e, Expression *(*shift)(Type *, E return ex; } -Expression *ShlExp::optimize(int result) +Expression *ShlExp::optimize(int result, bool keepLvalue) { //printf("ShlExp::optimize(result = %d) %s\n", result, toChars()); return shift_optimize(result, this, Shl); } -Expression *ShrExp::optimize(int result) +Expression *ShrExp::optimize(int result, bool keepLvalue) { //printf("ShrExp::optimize(result = %d) %s\n", result, toChars()); return shift_optimize(result, this, Shr); } -Expression *UshrExp::optimize(int result) +Expression *UshrExp::optimize(int result, bool keepLvalue) { //printf("UshrExp::optimize(result = %d) %s\n", result, toChars()); return shift_optimize(result, this, Ushr); } -Expression *AndExp::optimize(int result) +Expression *AndExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -849,7 +868,7 @@ Expression *AndExp::optimize(int result) return e; } -Expression *OrExp::optimize(int result) +Expression *OrExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -861,7 +880,7 @@ Expression *OrExp::optimize(int result) return e; } -Expression *XorExp::optimize(int result) +Expression *XorExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -873,7 +892,7 @@ Expression *XorExp::optimize(int result) return e; } -Expression *PowExp::optimize(int result) +Expression *PowExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(result); @@ -938,7 +957,7 @@ Expression *PowExp::optimize(int result) return e; } -Expression *CommaExp::optimize(int result) +Expression *CommaExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("CommaExp::optimize(result = %d) %s\n", result, toChars()); @@ -956,7 +975,7 @@ Expression *CommaExp::optimize(int result) } e1 = e1->optimize(result & WANTinterpret); - e2 = e2->optimize(result); + e2 = e2->optimize(result, keepLvalue); if (!e1 || e1->op == TOKint64 || e1->op == TOKfloat64 || !e1->hasSideEffect()) { e = e2; @@ -969,7 +988,7 @@ Expression *CommaExp::optimize(int result) return e; } -Expression *ArrayLengthExp::optimize(int result) +Expression *ArrayLengthExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("ArrayLengthExp::optimize(result = %d) %s\n", result, toChars()); @@ -982,24 +1001,22 @@ Expression *ArrayLengthExp::optimize(int result) return e; } -Expression *EqualExp::optimize(int result) -{ Expression *e; - +Expression *EqualExp::optimize(int result, bool keepLvalue) +{ //printf("EqualExp::optimize(result = %x) %s\n", result, toChars()); e1 = e1->optimize(WANTvalue | (result & WANTinterpret)); e2 = e2->optimize(WANTvalue | (result & WANTinterpret)); - e = this; Expression *e1 = fromConstInitializer(result, this->e1); Expression *e2 = fromConstInitializer(result, this->e2); - e = Equal(op, type, e1, e2); + Expression *e = Equal(op, type, e1, e2); if (e == EXP_CANT_INTERPRET) e = this; return e; } -Expression *IdentityExp::optimize(int result) +Expression *IdentityExp::optimize(int result, bool keepLvalue) { //printf("IdentityExp::optimize(result = %d) %s\n", result, toChars()); e1 = e1->optimize(WANTvalue | (result & WANTinterpret)); @@ -1032,7 +1049,13 @@ void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr) else if (arr->op == TOKarrayliteral) len = ((ArrayLiteralExp *)arr)->elements->dim; else - return; // we don't know the length yet + { + Type *t = arr->type->toBasetype(); + if (t->ty == Tsarray) + len = ((TypeSArray *)t)->dim->toInteger(); + else + return; // we don't know the length yet + } Expression *dollar = new IntegerExp(0, len, Type::tsize_t); lengthVar->init = new ExpInitializer(0, dollar); @@ -1040,7 +1063,7 @@ void setLengthVarIfKnown(VarDeclaration *lengthVar, Expression *arr) } -Expression *IndexExp::optimize(int result) +Expression *IndexExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("IndexExp::optimize(result = %d) %s\n", result, toChars()); @@ -1060,6 +1083,8 @@ Expression *IndexExp::optimize(int result) // We might know $ now setLengthVarIfKnown(lengthVar, e1); e2 = e2->optimize(WANTvalue | (result & WANTinterpret)); + if (keepLvalue) + return this; e = Index(type, e1, e2); if (e == EXP_CANT_INTERPRET) e = this; @@ -1067,7 +1092,7 @@ Expression *IndexExp::optimize(int result) } -Expression *SliceExp::optimize(int result) +Expression *SliceExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("SliceExp::optimize(result = %d) %s\n", result, toChars()); @@ -1094,7 +1119,7 @@ Expression *SliceExp::optimize(int result) return e; } -Expression *AndAndExp::optimize(int result) +Expression *AndAndExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("AndAndExp::optimize(%d) %s\n", result, toChars()); @@ -1134,7 +1159,7 @@ Expression *AndAndExp::optimize(int result) return e; } -Expression *OrOrExp::optimize(int result) +Expression *OrOrExp::optimize(int result, bool keepLvalue) { Expression *e; e1 = e1->optimize(WANTflags | (result & WANTinterpret)); @@ -1170,7 +1195,7 @@ Expression *OrOrExp::optimize(int result) return e; } -Expression *CmpExp::optimize(int result) +Expression *CmpExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("CmpExp::optimize() %s\n", toChars()); @@ -1186,7 +1211,7 @@ Expression *CmpExp::optimize(int result) return e; } -Expression *CatExp::optimize(int result) +Expression *CatExp::optimize(int result, bool keepLvalue) { Expression *e; //printf("CatExp::optimize(%d) %s\n", result, toChars()); @@ -1199,17 +1224,17 @@ Expression *CatExp::optimize(int result) } -Expression *CondExp::optimize(int result) +Expression *CondExp::optimize(int result, bool keepLvalue) { Expression *e; econd = econd->optimize(WANTflags | (result & WANTinterpret)); if (econd->isBool(TRUE)) - e = e1->optimize(result); + e = e1->optimize(result, keepLvalue); else if (econd->isBool(FALSE)) - e = e2->optimize(result); + e = e2->optimize(result, keepLvalue); else - { e1 = e1->optimize(result); - e2 = e2->optimize(result); + { e1 = e1->optimize(result, keepLvalue); + e2 = e2->optimize(result, keepLvalue); e = this; } return e; diff --git a/dmd2/parse.c b/dmd2/parse.c index 113d2f78..de5a3e08 100644 --- a/dmd2/parse.c +++ b/dmd2/parse.c @@ -55,7 +55,7 @@ // Support D1 inout #define D1INOUT 0 -Parser::Parser(Module *module, unsigned char *base, unsigned length, int doDocComment) +Parser::Parser(Module *module, unsigned char *base, size_t length, int doDocComment) : Lexer(module, base, 0, length, doDocComment, 0) { //printf("Parser::Parser()\n"); @@ -183,13 +183,6 @@ Dsymbols *Parser::parseDeclDefs(int once) break; } - case TOKstruct: - case TOKunion: - case TOKclass: - case TOKinterface: - s = parseAggregate(); - break; - case TOKimport: s = parseImport(decldefs, 0); break; @@ -233,6 +226,10 @@ Dsymbols *Parser::parseDeclDefs(int once) case TOKtypeof: case TOKdot: case TOKvector: + case TOKstruct: + case TOKunion: + case TOKclass: + case TOKinterface: Ldeclaration: a = parseDeclarations(STCundefined, NULL); decldefs->append(a); @@ -245,11 +242,6 @@ Dsymbols *Parser::parseDeclDefs(int once) s = parseCtor(); break; -#if 0 // dead end, use this(this){} instead - case TOKassign: - s = parsePostBlit(); - break; -#endif case TOKtilde: s = parseDtor(); break; @@ -268,8 +260,7 @@ Dsymbols *Parser::parseDeclDefs(int once) } else { - if (!global.params.useDeprecated) - error("use of 'invariant' rather than 'immutable' is deprecated"); + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; goto Lstc; } @@ -290,6 +281,8 @@ Dsymbols *Parser::parseDeclDefs(int once) case TOKeof: case TOKrcurly: + if (once) + error("Declaration expected, not '%s'", token.toChars()); return decldefs; case TOKstatic: @@ -370,7 +363,6 @@ Dsymbols *Parser::parseDeclDefs(int once) case TOKoverride: stc = STCoverride; goto Lstc; case TOKabstract: stc = STCabstract; goto Lstc; case TOKsynchronized: stc = STCsynchronized; goto Lstc; - case TOKdeprecated: stc = STCdeprecated; goto Lstc; #if DMDV2 case TOKnothrow: stc = STCnothrow; goto Lstc; case TOKpure: stc = STCpure; goto Lstc; @@ -378,7 +370,16 @@ Dsymbols *Parser::parseDeclDefs(int once) case TOKtls: stc = STCtls; goto Lstc; case TOKgshared: stc = STCgshared; goto Lstc; //case TOKmanifest: stc = STCmanifest; goto Lstc; - case TOKat: stc = parseAttribute(); goto Lstc; + case TOKat: + { + Expressions *exps = NULL; + stc = parseAttribute(&exps); + if (stc) + goto Lstc; // it's a predefined attribute + a = parseBlock(); + s = new UserAttributeDeclaration(exps, a); + break; + } #endif Lstc: @@ -412,25 +413,36 @@ Dsymbols *Parser::parseDeclDefs(int once) stc = STCwild; else { - if (token.value == TOKinvariant && !global.params.useDeprecated) - error("use of 'invariant' rather than 'immutable' is deprecated"); + if (token.value == TOKinvariant) + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; } goto Lstc; + case TOKdeprecated: + if (peek(&token)->value == TOKlparen) + break; + stc = STCdeprecated; + goto Lstc; case TOKfinal: stc = STCfinal; goto Lstc; case TOKauto: stc = STCauto; goto Lstc; case TOKscope: stc = STCscope; goto Lstc; case TOKoverride: stc = STCoverride; goto Lstc; case TOKabstract: stc = STCabstract; goto Lstc; case TOKsynchronized: stc = STCsynchronized; goto Lstc; - case TOKdeprecated: stc = STCdeprecated; goto Lstc; case TOKnothrow: stc = STCnothrow; goto Lstc; case TOKpure: stc = STCpure; goto Lstc; case TOKref: stc = STCref; goto Lstc; case TOKtls: stc = STCtls; goto Lstc; case TOKgshared: stc = STCgshared; goto Lstc; //case TOKmanifest: stc = STCmanifest; goto Lstc; - case TOKat: stc = parseAttribute(); goto Lstc; + case TOKat: + { Expressions *udas = NULL; + stc = parseAttribute(&udas); + if (udas) + // BUG: Should fix this + error("user defined attributes must be first"); + goto Lstc; + } default: break; } @@ -469,6 +481,31 @@ Dsymbols *Parser::parseDeclDefs(int once) s = new StorageClassDeclaration(storageClass, a); break; + case TOKdeprecated: + if (peek(&token)->value != TOKlparen) + { + stc = STCdeprecated; + goto Lstc; + } + { + nextToken(); + check(TOKlparen); + Expression *e = parseAssignExp(); + check(TOKrparen); + a = parseBlock(); + s = new DeprecatedDeclaration(e, a); + break; + } + + case TOKlbracket: + { + warning(loc, "use @(attributes) instead of [attributes]"); + Expressions *exps = parseArguments(); + a = parseBlock(); + s = new UserAttributeDeclaration(exps, a); + break; + } + case TOKextern: if (peek(&token)->value != TOKlparen) { stc = STCextern; @@ -499,6 +536,7 @@ Dsymbols *Parser::parseDeclDefs(int once) case TOKexport: error("redundant protection attribute"); break; + default: break; } a = parseBlock(); s = new ProtDeclaration(prot, a); @@ -605,10 +643,15 @@ Dsymbols *Parser::parseDeclDefs(int once) Lcondition: { - Loc lookingForElseSave = lookingForElse; - lookingForElse = loc; - a = parseBlock(); - lookingForElse = lookingForElseSave; + if (token.value == TOKcolon) + a = parseBlock(); + else + { + Loc lookingForElseSave = lookingForElse; + lookingForElse = loc; + a = parseBlock(); + lookingForElse = lookingForElseSave; + } } aelse = NULL; if (token.value == TOKelse) @@ -666,30 +709,63 @@ void Parser::composeStorageClass(StorageClass stc) #endif /*********************************************** - * Parse storage class, lexer is on '@' + * Parse attribute, lexer is on '@'. + * Input: + * pudas array of UDAs to append to + * Returns: + * storage class if a predefined attribute; also scanner remains on identifier. + * 0 if not a predefined attribute + * *pudas set if user defined attribute, scanner is past UDA + * *pudas NULL if not a user defined attribute */ #if DMDV2 -StorageClass Parser::parseAttribute() +StorageClass Parser::parseAttribute(Expressions **pudas) { nextToken(); + Expressions *udas = NULL; StorageClass stc = 0; - if (token.value != TOKidentifier) + if (token.value == TOKidentifier) { - error("identifier expected after @, not %s", token.toChars()); + if (token.ident == Id::property) + stc = STCproperty; + else if (token.ident == Id::safe) + stc = STCsafe; + else if (token.ident == Id::trusted) + stc = STCtrusted; + else if (token.ident == Id::system) + stc = STCsystem; + else if (token.ident == Id::disable) + stc = STCdisable; + else + { // Allow identifier, template instantiation, or function call + Expression *exp = parsePrimaryExp(); + if (token.value == TOKlparen) + exp = new CallExp(loc, exp, parseArguments()); + + udas = new Expressions(); + udas->push(exp); + } + } + else if (token.value == TOKlparen) + { // @( ArgumentList ) + // Concatenate with existing + udas = parseArguments(); } - else if (token.ident == Id::property) - stc = STCproperty; - else if (token.ident == Id::safe) - stc = STCsafe; - else if (token.ident == Id::trusted) - stc = STCtrusted; - else if (token.ident == Id::system) - stc = STCsystem; - else if (token.ident == Id::disable) - stc = STCdisable; else - error("valid attribute identifiers are @property, @safe, @trusted, @system, @disable not @%s", token.toChars()); + { + error("@identifier or @(ArgumentList) expected, not @%s", token.toChars()); + } + + if (stc) + { + } + else if (udas) + { + *pudas = UserAttributeDeclaration::concat(*pudas, udas); + } + else + error("valid attributes are @property, @safe, @trusted, @system, @disable"); return stc; } #endif @@ -708,14 +784,44 @@ StorageClass Parser::parsePostfix() { case TOKconst: stc |= STCconst; break; case TOKinvariant: - if (!global.params.useDeprecated) - error("use of 'invariant' rather than 'immutable' is deprecated"); + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); case TOKimmutable: stc |= STCimmutable; break; case TOKshared: stc |= STCshared; break; case TOKwild: stc |= STCwild; break; case TOKnothrow: stc |= STCnothrow; break; case TOKpure: stc |= STCpure; break; - case TOKat: stc |= parseAttribute(); break; + case TOKat: + { Expressions *udas = NULL; + stc |= parseAttribute(&udas); + if (udas) + // BUG: Should fix this + error("user defined attributes cannot appear as postfixes"); + break; + } + + default: return stc; + } + composeStorageClass(stc); + nextToken(); + } +} + +StorageClass Parser::parseTypeCtor() +{ + StorageClass stc = 0; + + while (1) + { + if (peek(&token)->value == TOKlparen) + return stc; + switch (token.value) + { + case TOKconst: stc |= STCconst; break; + case TOKinvariant: + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); + case TOKimmutable: stc |= STCimmutable; break; + case TOKshared: stc |= STCshared; break; + case TOKwild: stc |= STCwild; break; default: return stc; } @@ -944,10 +1050,13 @@ Condition *Parser::parseVersionCondition() #if DMDV2 /* Allow: * version (unittest) - * even though unittest is a keyword + * version (assert) + * even though they are keywords */ else if (token.value == TOKunittest) id = Lexer::idPool(Token::toChars(TOKunittest)); + else if (token.value == TOKassert) + id = Lexer::idPool(Token::toChars(TOKassert)); #endif else error("identifier or integer expected, not %s", token.toChars()); @@ -1011,7 +1120,7 @@ Dsymbol *Parser::parseCtor() nextToken(); check(TOKrparen); StorageClass stc = parsePostfix(); - PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0, stc); + PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0, stc, Id::_postblit); parseContracts(f); return f; } @@ -1057,26 +1166,6 @@ Dsymbol *Parser::parseCtor() return f; } -/***************************************** - * Parse a postblit definition: - * =this() { body } - * Current token is '='. - */ - -PostBlitDeclaration *Parser::parsePostBlit() -{ - Loc loc = this->loc; - - nextToken(); - check(TOKthis); - check(TOKlparen); - check(TOKrparen); - - PostBlitDeclaration *f = new PostBlitDeclaration(loc, 0); - parseContracts(f); - return f; -} - /***************************************** * Parse a destructor definition: * ~this() { body } @@ -1308,8 +1397,8 @@ Parameters *Parser::parseParameters(int *pvarargs, TemplateParameters **tpl) 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"); + if (token.value == TOKinvariant) + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; goto L2; @@ -1703,21 +1792,14 @@ BaseClasses *Parser::parseBaseClasses() protection = PROTpublic; nextToken(); break; + default: break; } - if (prot && !global.params.useDeprecated) - error("use of base class protection is deprecated"); - if (token.value == TOKidentifier) - { - BaseClass *b = new BaseClass(parseBasicType(), protection); - baseclasses->push(b); - if (token.value != TOKcomma) - break; - } - else - { - error("base classes expected instead of %s", token.toChars()); - return NULL; - } + if (prot) + deprecation("use of base class protection is deprecated"); + BaseClass *b = new BaseClass(parseBasicType(), protection); + baseclasses->push(b); + if (token.value != TOKcomma) + break; } return baseclasses; } @@ -2081,11 +2163,7 @@ Objects *Parser::parseTemplateArgumentList2() else { // Template argument is an expression Expression *ea = parseAssignExp(); - - if (ea->op == TOKfunction && ((FuncExp *)ea)->td) - tiargs->push(((FuncExp *)ea)->td); - else - tiargs->push(ea); + tiargs->push(ea); } if (token.value != TOKcomma) break; @@ -2411,8 +2489,7 @@ Type *Parser::parseBasicType() break; case TOKinvariant: - if (!global.params.useDeprecated) - error("use of 'invariant' rather than 'immutable' is deprecated"); + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); case TOKimmutable: // invariant(type) nextToken(); @@ -2532,10 +2609,14 @@ Type *Parser::parseBasicType2(Type *t) arguments = parseParameters(&varargs); StorageClass stc = parsePostfix(); - if (stc & (STCconst | STCimmutable | STCshared | STCwild)) - error("const/immutable/shared/inout attributes are only valid for non-static member functions"); - TypeFunction *tf = new TypeFunction(arguments, t, varargs, linkage, stc); + if (stc & (STCconst | STCimmutable | STCshared | STCwild)) + { + if (save == TOKfunction) + error("const/immutable/shared/inout attributes are only valid for non-static member functions"); + else + tf = (TypeFunction *)tf->addSTC(stc); + } if (save == TOKdelegate) t = new TypeDelegate(tf); @@ -2581,10 +2662,7 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * * although the D style would be: * int[]*[3] ident */ - if (!global.params.useDeprecated) - { - error("C-style function pointer and pointer to array syntax is deprecated. Use 'function' to declare function pointers"); - } + deprecation("C-style function pointer and pointer to array syntax is deprecated. Use 'function' to declare function pointers"); nextToken(); ts = parseDeclarator(t, pident); check(TOKrparen); @@ -2699,6 +2777,7 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * *pt = tf; break; } + default: break; } break; } @@ -2725,6 +2804,9 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c Dsymbols *a; enum TOK tok = TOKreserved; enum LINK link = linkage; + unsigned structalign = 0; + Loc loc = this->loc; + Expressions *udas = NULL; //printf("parseDeclarations() %s\n", token.toChars()); if (!comment) @@ -2743,9 +2825,9 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c */ tok = token.value; nextToken(); - if (token.value == TOKidentifier && peek(&token)->value == TOKthis) + if (token.value == TOKidentifier && peekNext() == TOKthis) { - AliasThis *s = new AliasThis(this->loc, token.ident); + AliasThis *s = new AliasThis(loc, token.ident); nextToken(); check(TOKthis); check(TOKsemicolon); @@ -2754,13 +2836,69 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c addComment(s, comment); return a; } + /* Look for: + * alias this = identifier; + */ + if (token.value == TOKthis && peekNext() == TOKassign && peekNext2() == TOKidentifier) + { + check(TOKthis); + check(TOKassign); + AliasThis *s = new AliasThis(loc, token.ident); + nextToken(); + check(TOKsemicolon); + a = new Dsymbols(); + a->push(s); + addComment(s, comment); + return a; + } + /* Look for: + * alias identifier = type; + */ + if (token.value == TOKidentifier && peekNext() == TOKassign) + { + a = new Dsymbols(); + while (1) + { + ident = token.ident; + nextToken(); + check(TOKassign); + t = parseType(); + Declaration *v = new AliasDeclaration(loc, ident, t); + a->push(v); + switch (token.value) + { case TOKsemicolon: + nextToken(); + addComment(v, comment); + break; + case TOKcomma: + nextToken(); + addComment(v, comment); + if (token.value != TOKidentifier) + { error("Identifier expected following comma, not %s", token.toChars()); + break; + } + else if (peek(&token)->value != TOKassign) + { error("= expected following identifier"); + nextToken(); + break; + } + continue; + default: + error("semicolon expected to close %s declaration", Token::toChars(tok)); + break; + } + break; + } + return a; + } + break; case TOKtypedef: - if (!global.params.useDeprecated) - error("use of typedef is deprecated; use alias instead"); + deprecation("use of typedef is deprecated; use alias instead"); tok = token.value; nextToken(); break; + default: break; } storage_class = STCundefined; @@ -2778,8 +2916,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"); + if (token.value == TOKinvariant) + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); stc = STCimmutable; goto L1; @@ -2810,7 +2948,13 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c case TOKtls: stc = STCtls; goto L1; case TOKgshared: stc = STCgshared; goto L1; case TOKenum: stc = STCmanifest; goto L1; - case TOKat: stc = parseAttribute(); goto L1; + case TOKat: + { + stc = parseAttribute(&udas); + if (stc) + goto L1; + continue; + } #endif L1: if (storage_class & stc) @@ -2829,12 +2973,69 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c link = parseLinkage(); continue; + case TOKalign: + { + nextToken(); + if (token.value == TOKlparen) + { + nextToken(); + if (token.value == TOKint32v && token.uns64value > 0) + structalign = (unsigned)token.uns64value; + else + { error("positive integer expected, not %s", token.toChars()); + structalign = 1; + } + nextToken(); + check(TOKrparen); + } + else + structalign = global.structalign; // default + continue; + } default: break; } break; } + if (token.value == TOKstruct || + token.value == TOKunion || + token.value == TOKclass || + token.value == TOKinterface) + { + Dsymbol *s = parseAggregate(); + Dsymbols *a = new Dsymbols(); + a->push(s); + + if (storage_class) + { + s = new StorageClassDeclaration(storage_class, a); + a = new Dsymbols(); + a->push(s); + } + if (structalign != 0) + { + s = new AlignDeclaration(structalign, a); + a = new Dsymbols(); + a->push(s); + } + if (udas) + { + s = new UserAttributeDeclaration(udas, a); + a = new Dsymbols(); + a->push(s); + } + + addComment(s, comment); + return a; + } + + if (udas) + { + // Need to improve this +// error("user defined attributes not allowed for local declarations"); + } + /* Look for auto initializers: * storage_class identifier = initializer; */ @@ -2842,17 +3043,13 @@ Dsymbols *Parser::parseDeclarations(StorageClass storage_class, unsigned char *c token.value == TOKidentifier && peek(&token)->value == TOKassign) { - return parseAutoDeclarations(storage_class, comment); - } - if (token.value == TOKclass) - { - AggregateDeclaration *s = (AggregateDeclaration *)parseAggregate(); - s->storage_class |= storage_class; - Dsymbols *a = new Dsymbols(); - a->push(s); - addComment(s, comment); - return a; + if (udas) + { + // Need to improve this + error("user defined attributes not allowed for auto declarations"); + } + return parseAutoDeclarations(storage_class, comment); } /* Look for return type inference for template functions. @@ -2887,7 +3084,7 @@ L2: while (1) { - Loc loc = this->loc; + loc = this->loc; TemplateParameters *tpl = NULL; ident = NULL; @@ -2912,6 +3109,7 @@ L2: * The grammar has already been fixed to preclude them. */ + assert(!udas); if (token.value == TOKassign) { nextToken(); @@ -2919,8 +3117,7 @@ L2: } if (tok == TOKtypedef) { v = new TypedefDeclaration(loc, ident, t, init); - if (!global.params.useDeprecated) - error("use of typedef is deprecated; use alias instead"); + deprecation("use of typedef is deprecated; use alias instead"); } else { if (init) @@ -2974,17 +3171,7 @@ L2: constraint = parseConstraint(); parseContracts(f); addComment(f, NULL); - Dsymbol *s; - if (link == linkage) - { - s = f; - } - else - { - Dsymbols *ax = new Dsymbols(); - ax->push(f); - s = new LinkDeclaration(link, ax); - } + Dsymbol *s = f; /* A template parameter list means it's a function template */ if (tpl) @@ -2996,6 +3183,18 @@ L2: new TemplateDeclaration(loc, s->ident, tpl, constraint, decldefs, 0); s = tempdecl; } + if (link != linkage) + { + Dsymbols *ax = new Dsymbols(); + ax->push(s); + s = new LinkDeclaration(link, ax); + } + if (udas) + { + Dsymbols *ax = new Dsymbols(); + ax->push(s); + s = new UserAttributeDeclaration(udas, ax); + } addComment(s, comment); a->push(s); } @@ -3010,15 +3209,20 @@ L2: VarDeclaration *v = new VarDeclaration(loc, t, ident, init); v->storage_class = storage_class; - if (link == linkage) - a->push(v); - else + Dsymbol *s = v; + if (link != linkage) { Dsymbols *ax = new Dsymbols(); - ax->push(v); - Dsymbol *s = new LinkDeclaration(link, ax); - a->push(s); + ax->push(s); + s = new LinkDeclaration(link, ax); } + if (udas) + { + Dsymbols *ax = new Dsymbols(); + ax->push(s); + s = new UserAttributeDeclaration(udas, ax); + } + a->push(s); switch (token.value) { case TOKsemicolon: nextToken(); @@ -3454,7 +3658,6 @@ void Parser::checkDanglingElse(Loc elseloc) Statement *Parser::parseStatement(int flags) { Statement *s; - Token *t; Condition *condition; Statement *ifbody; Statement *elsebody; @@ -3469,10 +3672,10 @@ Statement *Parser::parseStatement(int flags) switch (token.value) { case TOKidentifier: - /* A leading identifier can be a declaration, label, or expression. + { /* A leading identifier can be a declaration, label, or expression. * The easiest case to check first is label: */ - t = peek(&token); + Token *t = peek(&token); if (t->value == TOKcolon) { // It's a label @@ -3484,6 +3687,7 @@ Statement *Parser::parseStatement(int flags) break; } // fallthrough to TOKdot + } case TOKdot: case TOKtypeof: case TOKvector: @@ -3558,21 +3762,13 @@ Statement *Parser::parseStatement(int flags) condition = parseStaticIfCondition(); goto Lcondition; } - if (t->value == TOKstruct || t->value == TOKunion || t->value == TOKclass) - { - nextToken(); - Dsymbols *a = parseBlock(); - Dsymbol *d = new StorageClassDeclaration(STCstatic, a); - s = new ExpStatement(loc, d); - if (flags & PSscope) - s = new ScopeStatement(loc, s); - break; - } if (t->value == TOKimport) { nextToken(); Dsymbols *imports = new Dsymbols(); parseImport(imports, 1); // static import ... s = new ImportStatement(loc, imports); + if (flags & PSscope) + s = new ScopeStatement(loc, s); break; } goto Ldeclaration; @@ -3595,7 +3791,9 @@ Statement *Parser::parseStatement(int flags) case TOKalias: case TOKconst: case TOKauto: + case TOKabstract: case TOKextern: + case TOKalign: case TOKinvariant: #if DMDV2 case TOKimmutable: @@ -3608,6 +3806,10 @@ Statement *Parser::parseStatement(int flags) case TOKgshared: case TOKat: #endif + case TOKstruct: + case TOKunion: + case TOKclass: + case TOKinterface: Ldeclaration: { Dsymbols *a; @@ -3636,17 +3838,6 @@ Statement *Parser::parseStatement(int flags) break; } - case TOKstruct: - case TOKunion: - case TOKclass: - case TOKinterface: - { Dsymbol *d; - - d = parseAggregate(); - s = new ExpStatement(loc, d); - break; - } - case TOKenum: { /* Determine if this is a manifest constant declaration, * or a conventional enum. @@ -3667,11 +3858,13 @@ Statement *Parser::parseStatement(int flags) goto Ldeclaration; } s = new ExpStatement(loc, d); + if (flags & PSscope) + s = new ScopeStatement(loc, s); break; } case TOKmixin: - { t = peek(&token); + { Token *t = peek(&token); if (t->value == TOKlparen) { // mixin(string) Expression *e = parseAssignExp(); @@ -3689,6 +3882,8 @@ Statement *Parser::parseStatement(int flags) } Dsymbol *d = parseMixin(); s = new ExpStatement(loc, d); + if (flags & PSscope) + s = new ScopeStatement(loc, s); break; } @@ -3731,9 +3926,7 @@ Statement *Parser::parseStatement(int flags) if (!(flags & PSsemi_ok)) { if (flags & PSsemi) - { if (global.params.warnings) - warning(loc, "use '{ }' for an empty statement, not a ';'"); - } + warning(loc, "use '{ }' for an empty statement, not a ';'"); else error("use '{ }' for an empty statement, not a ';'"); } @@ -3756,8 +3949,8 @@ Statement *Parser::parseStatement(int flags) check(TOKrparen); if (token.value == TOKsemicolon) nextToken(); - else if (!global.params.useDeprecated) - error("do-while statement requires terminating ;"); + else + deprecation("do-while statement without terminating ; is deprecated"); s = new DoStatement(loc, body, condition); break; } @@ -3823,13 +4016,54 @@ Statement *Parser::parseStatement(int flags) Type *at; StorageClass storageClass = 0; - if (token.value == TOKref + Lagain: + switch (token.value) + { + case TOKref: #if D1INOUT - || token.value == TOKinout + case TOKinout: #endif - ) - { storageClass = STCref; - nextToken(); + storageClass |= STCref; + nextToken(); + goto Lagain; + + case TOKconst: + if (peekNext() != TOKlparen) + { + storageClass |= STCconst; + nextToken(); + goto Lagain; + } + break; + case TOKinvariant: + case TOKimmutable: + if (peekNext() != TOKlparen) + { + storageClass |= STCimmutable; + if (token.value == TOKinvariant) + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); + nextToken(); + goto Lagain; + } + break; + case TOKshared: + if (peekNext() != TOKlparen) + { + storageClass |= STCshared; + nextToken(); + goto Lagain; + } + break; + case TOKwild: + if (peekNext() != TOKlparen) + { + storageClass |= STCwild; + nextToken(); + goto Lagain; + } + break; + default: + break; } if (token.value == TOKidentifier) { @@ -3925,8 +4159,7 @@ Statement *Parser::parseStatement(int flags) arg = new Parameter(0, NULL, token.ident, NULL); nextToken(); nextToken(); - if (!global.params.useDeprecated) - error("if (v%s e) is deprecated, use if (auto v = e)", t->toChars()); + error("if (v; e) is deprecated, use if (auto v = e)"); } } @@ -4008,6 +4241,8 @@ Statement *Parser::parseStatement(int flags) checkDanglingElse(elseloc); } s = new ConditionalStatement(loc, condition, ifbody, elsebody); + if (flags & PSscope) + s = new ScopeStatement(loc, s); break; case TOKpragma: @@ -4054,7 +4289,6 @@ Statement *Parser::parseStatement(int flags) case TOKcase: { Expression *exp; - Statements *statements; Expressions cases; // array of Expression's Expression *last = NULL; @@ -4082,15 +4316,20 @@ Statement *Parser::parseStatement(int flags) } #endif - statements = new Statements(); - while (token.value != TOKcase && - token.value != TOKdefault && - token.value != TOKeof && - token.value != TOKrcurly) + if (flags & PScurlyscope) { - statements->push(parseStatement(PSsemi | PScurlyscope)); + Statements *statements = new Statements(); + while (token.value != TOKcase && + token.value != TOKdefault && + token.value != TOKeof && + token.value != TOKrcurly) + { + statements->push(parseStatement(PSsemi | PScurlyscope)); + } + s = new CompoundStatement(loc, statements); } - s = new CompoundStatement(loc, statements); + else + s = parseStatement(PSsemi | PScurlyscope); s = new ScopeStatement(loc, s); #if DMDV2 @@ -4113,20 +4352,23 @@ Statement *Parser::parseStatement(int flags) case TOKdefault: { - Statements *statements; - nextToken(); check(TOKcolon); - statements = new Statements(); - while (token.value != TOKcase && - token.value != TOKdefault && - token.value != TOKeof && - token.value != TOKrcurly) + if (flags & PScurlyscope) { - statements->push(parseStatement(PSsemi | PScurlyscope)); + Statements *statements = new Statements(); + while (token.value != TOKcase && + token.value != TOKdefault && + token.value != TOKeof && + token.value != TOKrcurly) + { + statements->push(parseStatement(PSsemi | PScurlyscope)); + } + s = new CompoundStatement(loc, statements); } - s = new CompoundStatement(loc, statements); + else + s = parseStatement(PSsemi | PScurlyscope); s = new ScopeStatement(loc, s); s = new DefaultStatement(loc, s); break; @@ -4213,6 +4455,10 @@ Statement *Parser::parseStatement(int flags) { Expression *exp; Statement *body; + Token *t = peek(&token); + if (skipAttributes(t, &t) && t->value == TOKclass) + goto Ldeclaration; + nextToken(); if (token.value == TOKlparen) { @@ -4309,30 +4555,27 @@ Statement *Parser::parseStatement(int flags) nextToken(); s = parseStatement(PSsemi | PScurlyscope); #if DMDV2 - if (!global.params.useDeprecated) - error("volatile statements deprecated; use synchronized statements instead"); + deprecation("volatile statements deprecated; use synchronized statements instead"); #endif s = new VolatileStatement(loc, s); break; case TOKasm: - { Statements *statements; - Identifier *label; - Loc labelloc; - Token *toklist; - Token **ptoklist; - + { // Parse the asm block into a sequence of AsmStatements, // each AsmStatement is one instruction. // Separate out labels. // Defer parsing of AsmStatements until semantic processing. + Loc labelloc; + nextToken(); check(TOKlcurly); - toklist = NULL; - ptoklist = &toklist; - label = NULL; - statements = new Statements(); + Token *toklist = NULL; + Token **ptoklist = &toklist; + Identifier *label = NULL; + Statements *statements = new Statements(); + size_t nestlevel = 0; while (1) { switch (token.value) @@ -4341,7 +4584,7 @@ Statement *Parser::parseStatement(int flags) if (!toklist) { // Look ahead to see if it is a label - t = peek(&token); + Token *t = peek(&token); if (t->value == TOKcolon) { // It's a label label = token.ident; @@ -4353,7 +4596,17 @@ Statement *Parser::parseStatement(int flags) } goto Ldefault; + case TOKlcurly: + ++nestlevel; + goto Ldefault; + case TOKrcurly: + if (nestlevel > 0) + { + --nestlevel; + goto Ldefault; + } + if (toklist || label) { error("asm statements must end in ';'"); @@ -4361,6 +4614,9 @@ Statement *Parser::parseStatement(int flags) break; case TOKsemicolon: + if (nestlevel != 0) + error("mismatched number of curly brackets"); + s = NULL; if (toklist || label) { // Create AsmStatement from list of tokens we've saved @@ -4379,7 +4635,7 @@ Statement *Parser::parseStatement(int flags) case TOKeof: /* { */ error("matching '}' expected, not end of file"); - break; + goto Lerror; default: Ldefault: @@ -4402,9 +4658,15 @@ Statement *Parser::parseStatement(int flags) { Dsymbols *imports = new Dsymbols(); parseImport(imports, 0); s = new ImportStatement(loc, imports); + if (flags & PSscope) + s = new ScopeStatement(loc, s); break; } + case TOKtemplate: + error("template definitions aren't allowed inside functions"); + goto Lerror; + default: error("found '%s' instead of statement", token.toChars()); goto Lerror; @@ -4465,18 +4727,23 @@ int Parser::isDeclaration(Token *t, int needId, enum TOK endtok, Token **pt) int haveId = 0; #if DMDV2 - if ((t->value == TOKconst || - t->value == TOKinvariant || - t->value == TOKimmutable || - t->value == TOKwild || - t->value == TOKshared) && - peek(t)->value != TOKlparen) - { /* const type - * immutable type - * shared type - * wild type - */ - t = peek(t); + while (1) + { + if ((t->value == TOKconst || + t->value == TOKinvariant || + t->value == TOKimmutable || + t->value == TOKwild || + t->value == TOKshared) && + peek(t)->value != TOKlparen) + { /* const type + * immutable type + * shared type + * wild type + */ + t = peek(t); + continue; + } + break; } #endif @@ -4709,7 +4976,9 @@ int Parser::isDeclarator(Token **pt, int *haveId, enum TOK endtok) t = peek(t); if (!isParameters(&t)) return FALSE; + skipAttributes(t, &t); continue; + default: break; } break; } @@ -4965,74 +5234,6 @@ int Parser::isExpression(Token **pt) return TRUE; } -/********************************************** - * Skip over - * instance foo.bar(parameters...) - * Output: - * if (pt), *pt is set to the token following the closing ) - * Returns: - * 1 it's valid instance syntax - * 0 invalid instance syntax - */ - -int Parser::isTemplateInstance(Token *t, Token **pt) -{ - t = peek(t); - if (t->value != TOKdot) - { - if (t->value != TOKidentifier) - goto Lfalse; - t = peek(t); - } - while (t->value == TOKdot) - { - t = peek(t); - if (t->value != TOKidentifier) - goto Lfalse; - t = peek(t); - } - if (t->value != TOKlparen) - goto Lfalse; - - // Skip over the template arguments - while (1) - { - while (1) - { - t = peek(t); - switch (t->value) - { - case TOKlparen: - if (!skipParens(t, &t)) - goto Lfalse; - continue; - case TOKrparen: - break; - case TOKcomma: - break; - case TOKeof: - case TOKsemicolon: - goto Lfalse; - default: - continue; - } - break; - } - - if (t->value != TOKcomma) - break; - } - if (t->value != TOKrparen) - goto Lfalse; - t = peek(t); - if (pt) - *pt = t; - return 1; - -Lfalse: - return 0; -} - /******************************************* * Skip parens, brackets. * Input: @@ -5121,7 +5322,59 @@ int Parser::skipAttributes(Token *t, Token **pt) case TOKat: t = peek(t); if (t->value == TOKidentifier) + { /* @identifier + * @identifier!arg + * @identifier!(arglist) + * any of the above followed by (arglist) + * @predefined_attribute + */ + if (t->ident == Id::property || + t->ident == Id::safe || + t->ident == Id::trusted || + t->ident == Id::system || + t->ident == Id::disable) + break; + t = peek(t); + if (t->value == TOKnot) + { + t = peek(t); + if (t->value == TOKlparen) + { // @identifier!(arglist) + if (!skipParens(t, &t)) + goto Lerror; + // t is on closing parenthesis + t = peek(t); + } + else + { + // @identifier!arg + // Do low rent skipTemplateArgument + if (t->value == TOKvector) + { // identifier!__vector(type) + t = peek(t); + if (!skipParens(t, &t)) + goto Lerror; + } + t = peek(t); + } + } + if (t->value == TOKlparen) + { + if (!skipParens(t, &t)) + goto Lerror; + // t is on closing parenthesis + t = peek(t); + continue; + } + continue; + } + if (t->value == TOKlparen) + { // @( ArgumentList ) + if (!skipParens(t, &t)) + goto Lerror; + // t is on closing parenthesis break; + } goto Lerror; default: goto Ldone; @@ -5291,32 +5544,26 @@ Expression *Parser::parsePrimaryExp() break; case TOKstring: - { unsigned char *s; - unsigned len; - unsigned char postfix; - + { // cat adjacent strings - s = token.ustring; - len = token.len; - postfix = token.postfix; + unsigned char *s = token.ustring; + size_t len = token.len; + unsigned char postfix = token.postfix; while (1) { nextToken(); if (token.value == TOKstring) - { unsigned len1; - unsigned len2; - unsigned char *s2; - + { if (token.postfix) { if (token.postfix != postfix) error("mismatched string literal postfixes '%c' and '%c'", postfix, token.postfix); postfix = token.postfix; } - len1 = len; - len2 = token.len; + size_t len1 = len; + size_t len2 = token.len; len = len1 + len2; - s2 = (unsigned char *)mem.malloc((len + 1) * sizeof(unsigned char)); + unsigned char *s2 = (unsigned char *)mem.malloc((len + 1) * sizeof(unsigned char)); memcpy(s2, s, len1 * sizeof(unsigned char)); memcpy(s2 + len1, token.ustring, (len2 + 1) * sizeof(unsigned char)); s = s2; @@ -5435,8 +5682,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"); + if (token.value == TOKinvariant) + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); tok2 = token.value; nextToken(); } @@ -5868,8 +6115,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"); + if (token.value == TOKinvariant) + deprecation("use of 'invariant' rather than 'immutable' is deprecated"); m = MODimmutable; goto Lmod2; } @@ -5918,7 +6165,9 @@ Expression *Parser::parseUnaryExp() case TOKinvariant: case TOKimmutable: // immutable(type)(arguments) { + StorageClass stc = parseTypeCtor(); Type *t = parseBasicType(); + t = t->addSTC(stc); e = new TypeExp(loc, t); if (token.value != TOKlparen) { @@ -6528,8 +6777,10 @@ Expression *Parser::parseNewExp(Expression *thisexp) return e; } + StorageClass stc = parseTypeCtor(); t = parseBasicType(); t = parseBasicType2(t); + t = t->addSTC(stc); if (t->ty == Taarray) { TypeAArray *taa = (TypeAArray *)t; Type *index = taa->index; @@ -6738,4 +6989,3 @@ void initPrecedence() precedence[TOKdeclaration] = PREC_expr; } - diff --git a/dmd2/parse.h b/dmd2/parse.h index 716e775a..7cce6c08 100644 --- a/dmd2/parse.h +++ b/dmd2/parse.h @@ -68,15 +68,16 @@ struct Parser : Lexer int inBrackets; // inside [] of array index or slice Loc lookingForElse; // location of lonely if looking for an else - Parser(Module *module, unsigned char *base, unsigned length, int doDocComment); + Parser(Module *module, unsigned char *base, size_t length, int doDocComment); Dsymbols *parseModule(); Dsymbols *parseDeclDefs(int once); Dsymbols *parseAutoDeclarations(StorageClass storageClass, unsigned char *comment); Dsymbols *parseBlock(); void composeStorageClass(StorageClass stc); - StorageClass parseAttribute(); + StorageClass parseAttribute(Expressions **pexps); StorageClass parsePostfix(); + StorageClass parseTypeCtor(); Expression *parseConstraint(); TemplateDeclaration *parseTemplateDeclaration(int ismixin); TemplateParameters *parseTemplateParameterList(int flag = 0); @@ -92,7 +93,6 @@ struct Parser : Lexer Condition *parseVersionCondition(); Condition *parseStaticIfCondition(); Dsymbol *parseCtor(); - PostBlitDeclaration *parsePostBlit(); DtorDeclaration *parseDtor(); StaticCtorDeclaration *parseStaticCtor(); StaticDtorDeclaration *parseStaticDtor(); @@ -126,7 +126,6 @@ struct Parser : Lexer int isDeclarator(Token **pt, int *haveId, enum TOK endtok); int isParameters(Token **pt); int isExpression(Token **pt); - int isTemplateInstance(Token *t, Token **pt); int skipParens(Token *t, Token **pt); int skipAttributes(Token *t, Token **pt); diff --git a/dmd2/root/array.c b/dmd2/root/array.c index 3ef48322..94c2cebb 100644 --- a/dmd2/root/array.c +++ b/dmd2/root/array.c @@ -13,7 +13,7 @@ #include #include -#if (defined (__SVR4) && defined (__sun)) +#if defined (__sun) #include #endif @@ -59,16 +59,15 @@ Array::~Array() } void Array::mark() -{ unsigned u; - +{ mem.mark(data); - for (u = 0; u < dim; u++) + for (size_t u = 0; u < dim; u++) mem.mark(data[u]); // BUG: what if arrays of Object's? } -void Array::reserve(unsigned nentries) +void Array::reserve(size_t nentries) { - //printf("Array::reserve: dim = %d, allocdim = %d, nentries = %d\n", dim, allocdim, nentries); + //printf("Array::reserve: dim = %d, allocdim = %d, nentries = %d\n", (int)dim, (int)allocdim, (int)nentries); if (allocdim - dim < nentries) { if (allocdim == 0) @@ -95,7 +94,7 @@ void Array::reserve(unsigned nentries) } } -void Array::setDim(unsigned newdim) +void Array::setDim(size_t newdim) { if (dim < newdim) { @@ -141,7 +140,7 @@ void Array::shift(void *ptr) dim++; } -void Array::insert(unsigned index, void *ptr) +void Array::insert(size_t index, void *ptr) { reserve(1); memmove(data + index + 1, data + index, (dim - index) * sizeof(*data)); @@ -150,12 +149,11 @@ void Array::insert(unsigned index, void *ptr) } -void Array::insert(unsigned index, Array *a) +void Array::insert(size_t index, Array *a) { if (a) - { unsigned d; - - d = a->dim; + { + size_t d = a->dim; reserve(d); if (dim != index) memmove(data + index + d, data + index, (dim - index) * sizeof(*data)); @@ -174,7 +172,7 @@ void Array::append(Array *a) insert(dim, a); } -void Array::remove(unsigned i) +void Array::remove(size_t i) { if (dim - i - 1) memmove(data + i, data + i + 1, (dim - i - 1) * sizeof(data[0])); @@ -183,8 +181,8 @@ void Array::remove(unsigned i) char *Array::toChars() { - unsigned len; - unsigned u; + size_t len; + size_t u; char **buf; char *str; char *p; diff --git a/dmd2/root/dmgcmem.c b/dmd2/root/dmgcmem.c index 9a283890..02d1c0d1 100644 --- a/dmd2/root/dmgcmem.c +++ b/dmd2/root/dmgcmem.c @@ -13,7 +13,7 @@ #include #include -#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include #include #endif @@ -135,7 +135,7 @@ void Mem::check(void *p) void Mem::error() { -#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun assert(0); #endif printf("Error: out of memory\n"); @@ -288,7 +288,7 @@ void Mem::operator delete(void *p) /* ===================== linux ================================ */ -#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include diff --git a/dmd2/root/longdouble.c b/dmd2/root/longdouble.c index 8a0f00e7..8852a835 100644 --- a/dmd2/root/longdouble.c +++ b/dmd2/root/longdouble.c @@ -377,7 +377,7 @@ bool operator!=(longdouble x, longdouble y) int _isnan(longdouble ld) { - return (ld.exponent == 0x7fff && ld.mantissa != 0); + return (ld.exponent == 0x7fff && ld.mantissa != 0 && ld.mantissa != (1LL << 63)); // exclude pseudo-infinity and infinity, but not FP Indefinite } longdouble fabsl(longdouble ld) @@ -514,7 +514,7 @@ int ld_type(longdouble x) return LD_TYPE_SNAN; } -int ld_sprint(char* str, int fmt, longdouble x) +size_t ld_sprint(char* str, int fmt, longdouble x) { // fmt is 'a','A','f' or 'g' if(fmt != 'a' && fmt != 'A') @@ -537,7 +537,7 @@ int ld_sprint(char* str, int fmt, longdouble x) return sprintf(str, x.sign ? "-INF" : "INF"); } - int len = 0; + size_t len = 0; if(x.sign) str[len++] = '-'; len += sprintf(str + len, mantissa & (1LL << 63) ? "0x1." : "0x0."); diff --git a/dmd2/root/longdouble.h b/dmd2/root/longdouble.h index d25223a7..99f91f4d 100644 --- a/dmd2/root/longdouble.h +++ b/dmd2/root/longdouble.h @@ -18,7 +18,7 @@ typedef real_t longdouble; template longdouble ldouble(T x) { return (longdouble) x; } -inline int ld_sprint(char* str, int fmt, longdouble x) +inline size_t ld_sprint(char* str, int fmt, longdouble x) { if(fmt == 'a' || fmt == 'A') return x.formatHex(buffer, 46); // don't know the size here, but 46 is the max @@ -34,7 +34,7 @@ typedef volatile long double volatile_longdouble; // template longdouble ldouble(T x) { return (longdouble) x; } #define ldouble(x) ((longdouble)(x)) -inline int ld_sprint(char* str, int fmt, longdouble x) +inline size_t ld_sprint(char* str, int fmt, longdouble x) { char sfmt[4] = "%Lg"; sfmt[2] = fmt; @@ -247,7 +247,7 @@ public: //_STCONSDEF(numeric_limits, int, min_exponent) //_STCONSDEF(numeric_limits, int, min_exponent10) -int ld_sprint(char* str, int fmt, longdouble x); +size_t ld_sprint(char* str, int fmt, longdouble x); #endif // !_MSC_VER diff --git a/dmd2/root/man.c b/dmd2/root/man.c index 3770aa96..f49d60b4 100644 --- a/dmd2/root/man.c +++ b/dmd2/root/man.c @@ -26,7 +26,7 @@ void browse(const char *url) #endif -#if linux || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#if linux || __FreeBSD__ || __OpenBSD__ || __sun #include #include diff --git a/dmd2/root/port.c b/dmd2/root/port.c index 0beb250d..6f489903 100644 --- a/dmd2/root/port.c +++ b/dmd2/root/port.c @@ -1,10 +1,11 @@ -// Copyright (c) 1999-2011 by Digital Mars +// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com #include "port.h" + #if __DMC__ #include #include @@ -12,6 +13,7 @@ #include #include #include +#include double Port::nan = NAN; double Port::infinity = INFINITY; @@ -127,6 +129,7 @@ char *Port::strupr(char *s) #include #include #include +#include #include #include // for std::numeric_limits @@ -343,6 +346,7 @@ char *Port::strupr(char *s) #include #include #include +#include #include #include @@ -384,7 +388,7 @@ int Port::isNan(double r) #else return __inline_isnan(r); #endif -#elif defined __HAIKU__ || __OpenBSD__ +#elif __HAIKU__ || __OpenBSD__ return isnan(r); #else #undef isnan @@ -400,7 +404,7 @@ int Port::isNan(longdouble r) #else return __inline_isnan(r); #endif -#elif defined __HAIKU__ || __OpenBSD__ +#elif __HAIKU__ || __OpenBSD__ return isnan(r); #else #undef isnan @@ -522,7 +526,7 @@ char *Port::strupr(char *s) #endif -#if __sun&&__SVR4 +#if __sun #define __C99FEATURES__ 1 // Needed on Solaris for NaN and more #include @@ -532,6 +536,7 @@ char *Port::strupr(char *s) #include #include #include +#include #include #include @@ -614,6 +619,11 @@ double Port::pow(double x, double y) return ::pow(x, y); } +longdouble Port::fmodl(longdouble x, longdouble y) +{ + return ::fmodl(x, y); +} + unsigned long long Port::strtoull(const char *p, char **pend, int base) { return ::strtoull(p, pend, base); @@ -660,4 +670,3 @@ char *Port::strupr(char *s) } #endif - diff --git a/dmd2/root/port.h b/dmd2/root/port.h index dd1ad97d..cdb6d429 100644 --- a/dmd2/root/port.h +++ b/dmd2/root/port.h @@ -1,5 +1,5 @@ -// Copyright (c) 1999-2009 by Digital Mars +// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -12,26 +12,21 @@ #include "longdouble.h" -#ifndef TYPEDEFS -#define TYPEDEFS - -#include - #if _MSC_VER -typedef __int64 longlong; -typedef unsigned __int64 ulonglong; - +#include // for _isnan +#include // for alloca // According to VC 8.0 docs, long double is the same as double longdouble strtold(const char *p,char **endp); #define strtof strtod +#define isnan _isnan +typedef __int64 longlong; +typedef unsigned __int64 ulonglong; #else typedef long long longlong; typedef unsigned long long ulonglong; #endif -#endif - typedef double d_time; struct Port diff --git a/dmd2/root/response.c b/dmd2/root/response.c index 45f08662..94d4c39a 100644 --- a/dmd2/root/response.c +++ b/dmd2/root/response.c @@ -20,7 +20,7 @@ #include #endif -#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include #include #include @@ -66,8 +66,8 @@ struct Narg { - int argc; /* arg count */ - int argvmax; /* dimension of nargv[] */ + size_t argc; // arg count + size_t argvmax; // dimension of nargv[] char **argv; }; @@ -91,17 +91,16 @@ static int addargp(struct Narg *n, char *p) return 0; } -int response_expand(int *pargc, char ***pargv) +int response_expand(size_t *pargc, char ***pargv) { struct Narg n; - int i; char *cp; int recurse = 0; n.argc = 0; n.argvmax = 0; /* dimension of n.argv[] */ n.argv = NULL; - for(i=0; i<*pargc; ++i) + for (size_t i = 0; i < *pargc; ++i) { cp = (*pargv)[i]; if (*cp == '@') diff --git a/dmd2/root/rmem.c b/dmd2/root/rmem.c index fd202732..ce6b3e0d 100644 --- a/dmd2/root/rmem.c +++ b/dmd2/root/rmem.c @@ -11,7 +11,7 @@ #include #include -#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun #include "../root/rmem.h" #else #include "rmem.h" diff --git a/dmd2/root/root.c b/dmd2/root/root.c index d4697100..01fe0d89 100644 --- a/dmd2/root/root.c +++ b/dmd2/root/root.c @@ -8,7 +8,7 @@ // See the included readme.txt for details. #ifndef POSIX -#define POSIX (linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4) +#define POSIX (linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun) #endif #include @@ -20,7 +20,7 @@ #include #include -#if (defined (__SVR4) && defined (__sun)) +#if defined (__sun) #include #endif @@ -62,68 +62,6 @@ extern "C" void __cdecl _assert(void *e, void *f, unsigned line) #endif -/************************************* - * Convert wchar string to ascii string. - */ - -char *wchar2ascii(wchar_t *us) -{ - return wchar2ascii(us, wcslen(us)); -} - -char *wchar2ascii(wchar_t *us, unsigned len) -{ - unsigned i; - char *p; - - p = (char *)mem.malloc(len + 1); - for (i = 0; i <= len; i++) - p[i] = (char) us[i]; - return p; -} - -int wcharIsAscii(wchar_t *us) -{ - return wcharIsAscii(us, wcslen(us)); -} - -int wcharIsAscii(wchar_t *us, unsigned len) -{ - unsigned i; - - for (i = 0; i <= len; i++) - { - if (us[i] & ~0xFF) // if high bits set - return 0; // it's not ascii - } - return 1; -} - - -/*********************************** - * Compare length-prefixed strings (bstr). - */ - -int bstrcmp(unsigned char *b1, unsigned char *b2) -{ - return (*b1 == *b2 && memcmp(b1 + 1, b2 + 1, *b2) == 0) ? 0 : 1; -} - -/*************************************** - * Convert bstr into a malloc'd string. - */ - -char *bstr2str(unsigned char *b) -{ - char *s; - unsigned len; - - len = *b; - s = (char *) mem.malloc(len + 1); - s[len] = 0; - return (char *)memcpy(s,b + 1,len); -} - /************************************** * Print error message and exit. */ @@ -142,11 +80,6 @@ void error(const char *format, ...) exit(EXIT_FAILURE); } -void error_mem() -{ - error("out of memory"); -} - /************************************** * Print warning message. */ @@ -206,10 +139,9 @@ void Object::mark() /****************************** String ********************************/ -String::String(char *str, int ref) +String::String(char *str) + : str(mem.strdup(str)) { - this->str = ref ? str : mem.strdup(str); - this->ref = ref; } String::~String() @@ -269,7 +201,7 @@ hash_t String::hashCode() return calcHash(str, strlen(str)); } -unsigned String::len() +size_t String::len() { return strlen(str); } @@ -297,8 +229,8 @@ void String::print() /****************************** FileName ********************************/ -FileName::FileName(char *str, int ref) - : String(str,ref) +FileName::FileName(char *str) + : String(str) { } @@ -332,11 +264,6 @@ char *FileName::combine(const char *path, const char *name) return f; } -FileName::FileName(char *path, char *name) - : String(combine(path,name),1) -{ -} - // Split a path into an Array of paths Strings *FileName::splitPath(const char *path) { @@ -684,7 +611,7 @@ FileName *FileName::defaultExt(const char *name, const char *ext) e = FileName::ext(name); if (e) // if already has an extension - return new FileName((char *)name, 0); + return new FileName((char *)name); len = strlen(name); extlen = strlen(ext); @@ -692,7 +619,7 @@ FileName *FileName::defaultExt(const char *name, const char *ext) memcpy(s,name,len); s[len] = '.'; memcpy(s + len + 1, ext, extlen + 1); - return new FileName(s, 0); + return new FileName(s); } /*************************** @@ -714,7 +641,7 @@ FileName *FileName::forceExt(const char *name, const char *ext) s = (char *)alloca(len + extlen + 1); memcpy(s,name,len); memcpy(s + len, ext, extlen + 1); - return new FileName(s, 0); + return new FileName(s); } else return defaultExt(name, ext); // doesn't have one @@ -986,9 +913,20 @@ char *FileName::canonicalName(const char *name) #endif #elif _WIN32 /* Apparently, there is no good way to do this on Windows. - * GetFullPathName isn't it. + * GetFullPathName isn't it, but use it anyway. */ - assert(0); + DWORD result = GetFullPathName(name, 0, NULL, NULL); + if (result) + { + char *buf = (char *)malloc(result); + result = GetFullPathName(name, result, buf, NULL); + if (result == 0) + { + free(buf); + return NULL; + } + return buf; + } return NULL; #else assert(0); @@ -1014,7 +952,7 @@ File::File(char *n) buffer = NULL; len = 0; touchtime = NULL; - name = new FileName(n, 0); + name = new FileName(n); } File::~File() @@ -1354,7 +1292,7 @@ err: void File::readv() { if (read()) - error("Error reading file '%s'\n",name->toChars()); + error("Error reading file '%s'",name->toChars()); } /************************************** @@ -1369,13 +1307,13 @@ void File::mmreadv() void File::writev() { if (write()) - error("Error writing file '%s'\n",name->toChars()); + error("Error writing file '%s'",name->toChars()); } void File::appendv() { if (write()) - error("Error appending to file '%s'\n",name->toChars()); + error("Error appending to file '%s'",name->toChars()); } /******************************************* @@ -1424,7 +1362,7 @@ void File::remove() Files *File::match(char *n) { - return match(new FileName(n, 0)); + return match(new FileName(n)); } Files *File::match(FileName *n) @@ -1524,6 +1462,10 @@ OutBuffer::OutBuffer() data = NULL; offset = 0; size = 0; + + doindent = 0; + level = 0; + linehead = 1; } OutBuffer::~OutBuffer() @@ -1547,7 +1489,7 @@ void OutBuffer::mark() mem.mark(data); } -void OutBuffer::reserve(unsigned nbytes) +void OutBuffer::reserve(size_t nbytes) { //printf("OutBuffer::reserve: size = %d, offset = %d, nbytes = %d\n", size, offset, nbytes); if (size - offset < nbytes) @@ -1562,13 +1504,26 @@ void OutBuffer::reset() offset = 0; } -void OutBuffer::setsize(unsigned size) +void OutBuffer::setsize(size_t size) { offset = size; } -void OutBuffer::write(const void *data, unsigned nbytes) +void OutBuffer::write(const void *data, size_t nbytes) { + if (doindent && linehead) + { + if (level) + { + reserve(level); + for (size_t i=0; idata[offset] = '\t'; + offset++; + } + } + linehead = 0; + } reserve(nbytes); memcpy(this->data + offset, data, nbytes); offset += nbytes; @@ -1585,9 +1540,8 @@ void OutBuffer::writestring(const char *string) } void OutBuffer::prependstring(const char *string) -{ unsigned len; - - len = strlen(string); +{ + size_t len = strlen(string); reserve(len); memmove(data + len, data, offset); memcpy(data, string, len); @@ -1601,10 +1555,26 @@ void OutBuffer::writenl() #else writeByte('\n'); #endif + if (doindent) + linehead = 1; } void OutBuffer::writeByte(unsigned b) { + if (doindent && linehead + && b != '\n') + { + if (level) + { + reserve(level); + for (size_t i=0; idata[offset] = '\t'; + offset++; + } + } + linehead = 0; + } reserve(1); this->data[offset] = (unsigned char)b; offset++; @@ -1672,6 +1642,24 @@ void OutBuffer::prependbyte(unsigned b) void OutBuffer::writeword(unsigned w) { + if (doindent && linehead +#if _WIN32 + && w != 0x0A0D) +#else + && w != '\n') +#endif + { + if (level) + { + reserve(level); + for (size_t i=0; idata[offset] = '\t'; + offset++; + } + } + linehead = 0; + } reserve(2); *(unsigned short *)(this->data + offset) = (unsigned short)w; offset += 2; @@ -1697,6 +1685,24 @@ void OutBuffer::writeUTF16(unsigned w) void OutBuffer::write4(unsigned w) { + if (doindent && linehead +#if _WIN32 + && w != 0x000A000D) +#else + ) +#endif + { + if (level) + { + reserve(level); + for (size_t i=0; idata[offset] = '\t'; + offset++; + } + } + linehead = 0; + } reserve(4); *(unsigned *)(this->data + offset) = w; offset += 4; @@ -1719,17 +1725,16 @@ void OutBuffer::write(Object *obj) } } -void OutBuffer::fill0(unsigned nbytes) +void OutBuffer::fill0(size_t nbytes) { reserve(nbytes); memset(data + offset,0,nbytes); offset += nbytes; } -void OutBuffer::align(unsigned size) -{ unsigned nbytes; - - nbytes = ((offset + size - 1) & ~(size - 1)) - offset; +void OutBuffer::align(size_t size) +{ + size_t nbytes = ((offset + size - 1) & ~(size - 1)) - offset; fill0(nbytes); } @@ -1831,7 +1836,7 @@ void OutBuffer::bracket(char left, char right) * Return index just past right. */ -unsigned OutBuffer::bracket(unsigned i, const char *left, unsigned j, const char *right) +size_t OutBuffer::bracket(size_t i, const char *left, size_t j, const char *right) { size_t leftlen = strlen(left); size_t rightlen = strlen(right); @@ -1841,7 +1846,7 @@ unsigned OutBuffer::bracket(unsigned i, const char *left, unsigned j, const char return j + leftlen + rightlen; } -void OutBuffer::spread(unsigned offset, unsigned nbytes) +void OutBuffer::spread(size_t offset, size_t nbytes) { reserve(nbytes); memmove(data + offset + nbytes, data + offset, @@ -1853,14 +1858,14 @@ void OutBuffer::spread(unsigned offset, unsigned nbytes) * Returns: offset + nbytes */ -unsigned OutBuffer::insert(unsigned offset, const void *p, unsigned nbytes) +size_t OutBuffer::insert(size_t offset, const void *p, size_t nbytes) { spread(offset, nbytes); memmove(data + offset, p, nbytes); return offset + nbytes; } -void OutBuffer::remove(unsigned offset, unsigned nbytes) +void OutBuffer::remove(size_t offset, size_t nbytes) { memmove(data + offset, data + offset + nbytes, this->offset - (offset + nbytes)); this->offset -= nbytes; @@ -1872,6 +1877,7 @@ char *OutBuffer::toChars() return (char *)data; } +// TODO: Remove (only used by disabled GC) /********************************* Bits ****************************/ Bits::Bits() @@ -1964,18 +1970,3 @@ void Bits::sub(Bits *b) for (u = 0; u < allocdim; u++) data[u] &= ~b->data[u]; } - - - - - - - - - - - - - - - diff --git a/dmd2/root/root.h b/dmd2/root/root.h index 842222e3..6b7ba246 100644 --- a/dmd2/root/root.h +++ b/dmd2/root/root.h @@ -15,6 +15,7 @@ #ifdef DEBUG #include #endif +#include "port.h" #if __DMC__ #pragma once @@ -22,38 +23,6 @@ typedef size_t hash_t; -#include "longdouble.h" - -char *wchar2ascii(wchar_t *); -int wcharIsAscii(wchar_t *); -char *wchar2ascii(wchar_t *, unsigned len); -int wcharIsAscii(wchar_t *, unsigned len); - -int bstrcmp(unsigned char *s1, unsigned char *s2); -char *bstr2str(unsigned char *b); - -#ifndef TYPEDEFS -#define TYPEDEFS - -#if _MSC_VER -#include // for _isnan -#include // for alloca -// According to VC 8.0 docs, long double is the same as double -longdouble strtold(const char *p,char **endp); -#define strtof strtod -#define isnan _isnan - -typedef __int64 longlong; -typedef unsigned __int64 ulonglong; -#else -typedef long long longlong; -typedef unsigned long long ulonglong; -#endif - -#endif - -longlong randomx(); - /* * Root of our class library. */ @@ -107,17 +76,15 @@ struct Object struct String : Object { - int ref; // != 0 if this is a reference to someone else's string char *str; // the string itself - String(char *str, int ref = 1); - + String(char *str); ~String(); static hash_t calcHash(const char *str, size_t len); static hash_t calcHash(const char *str); hash_t hashCode(); - unsigned len(); + size_t len(); int equals(Object *obj); int compare(Object *obj); char *toChars(); @@ -127,8 +94,7 @@ struct String : Object struct FileName : String { - FileName(char *str, int ref); - FileName(char *path, char *name); + FileName(char *str); hash_t hashCode(); int equals(Object *obj); static int equals(const char *name1, const char *name2); @@ -161,7 +127,7 @@ struct File : Object { int ref; // != 0 if this is a reference to someone else's buffer unsigned char *buffer; // data for our file - unsigned len; // amount of data in buffer[] + size_t len; // amount of data in buffer[] void *touchtime; // system time to use for file FileName *name; // name of our file @@ -251,7 +217,7 @@ struct File : Object /* Set buffer */ - void setbuffer(void *buffer, unsigned len) + void setbuffer(void *buffer, size_t len) { this->buffer = (unsigned char *)buffer; this->len = len; @@ -265,18 +231,20 @@ struct File : Object struct OutBuffer : Object { unsigned char *data; - unsigned offset; - unsigned size; + size_t offset; + size_t size; + + int doindent, level, linehead; OutBuffer(); ~OutBuffer(); char *extractData(); void mark(); - void reserve(unsigned nbytes); - void setsize(unsigned size); + void reserve(size_t nbytes); + void setsize(size_t size); void reset(); - void write(const void *data, unsigned nbytes); + void write(const void *data, size_t nbytes); void writebstring(unsigned char *string); void writestring(const char *string); void prependstring(const char *string); @@ -290,26 +258,26 @@ struct OutBuffer : Object void write4(unsigned w); void write(OutBuffer *buf); void write(Object *obj); - void fill0(unsigned nbytes); - void align(unsigned size); + void fill0(size_t nbytes); + void align(size_t size); void vprintf(const char *format, va_list args); void printf(const char *format, ...); void bracket(char left, char right); - unsigned bracket(unsigned i, const char *left, unsigned j, const char *right); - void spread(unsigned offset, unsigned nbytes); - unsigned insert(unsigned offset, const void *data, unsigned nbytes); - void remove(unsigned offset, unsigned nbytes); + size_t bracket(size_t i, const char *left, size_t j, const char *right); + void spread(size_t offset, size_t nbytes); + size_t insert(size_t offset, const void *data, size_t nbytes); + void remove(size_t offset, size_t nbytes); char *toChars(); char *extractString(); }; struct Array : Object { - unsigned dim; + size_t dim; void **data; private: - unsigned allocdim; + size_t allocdim; #define SMALLARRAYCAP 1 void *smallarray[SMALLARRAYCAP]; // inline storage for small arrays @@ -320,16 +288,16 @@ struct Array : Object void mark(); char *toChars(); - void reserve(unsigned nentries); - void setDim(unsigned newdim); + void reserve(size_t nentries); + void setDim(size_t newdim); void fixDim(); void push(void *ptr); void *pop(); void shift(void *ptr); - void insert(unsigned index, void *ptr); - void insert(unsigned index, Array *a); + void insert(size_t index, void *ptr); + void insert(size_t index, Array *a); void append(Array *a); - void remove(unsigned i); + void remove(size_t i); void zero(); void *tos(); void sort(); @@ -376,8 +344,24 @@ struct ArrayBase : Array { return (ArrayBase *)Array::copy(); } + + typedef int (*ArrayBase_apply_ft_t)(TYPE *, void *); + int apply(ArrayBase_apply_ft_t fp, void *param) + { + for (size_t i = 0; i < dim; i++) + { TYPE *e = (*this)[i]; + + if (e) + { + if (e->apply(fp, param)) + return 1; + } + } + return 0; + } }; +// TODO: Remove (only used by disabled GC) struct Bits : Object { unsigned bitdim; diff --git a/dmd2/root/speller.c b/dmd2/root/speller.c index 207b49c0..7f7a0a7b 100644 --- a/dmd2/root/speller.c +++ b/dmd2/root/speller.c @@ -12,7 +12,7 @@ #include #include -#if __sun&&__SVR4 || _MSC_VER +#if __sun || _MSC_VER #include #endif diff --git a/dmd2/root/stringtable.c b/dmd2/root/stringtable.c index 58dcd0b6..f90a197e 100644 --- a/dmd2/root/stringtable.c +++ b/dmd2/root/stringtable.c @@ -17,6 +17,7 @@ #include "rmem.h" // mem #include "stringtable.h" +// TODO: Merge with root.String hash_t calcHash(const char *str, size_t len) { hash_t hash = 0; @@ -66,14 +67,14 @@ hash_t calcHash(const char *str, size_t len) } } -void StringValue::ctor(const char *p, unsigned length) +void StringValue::ctor(const char *p, size_t length) { this->length = length; this->lstring[length] = 0; memcpy(this->lstring, p, length * sizeof(char)); } -void StringTable::init(unsigned size) +void StringTable::init(size_t size) { table = (void **)mem.calloc(size, sizeof(void *)); tabledim = size; @@ -82,11 +83,9 @@ void StringTable::init(unsigned size) StringTable::~StringTable() { - unsigned i; - // Zero out dangling pointers to help garbage collector. // Should zero out StringEntry's too. - for (i = 0; i < count; i++) + for (size_t i = 0; i < count; i++) table[i] = NULL; mem.free(table); @@ -101,10 +100,10 @@ struct StringEntry StringValue value; - static StringEntry *alloc(const char *s, unsigned len); + static StringEntry *alloc(const char *s, size_t len); }; -StringEntry *StringEntry::alloc(const char *s, unsigned len) +StringEntry *StringEntry::alloc(const char *s, size_t len) { StringEntry *se; @@ -114,7 +113,7 @@ StringEntry *StringEntry::alloc(const char *s, unsigned len) return se; } -void **StringTable::search(const char *s, unsigned len) +void **StringTable::search(const char *s, size_t len) { hash_t hash; unsigned u; @@ -148,7 +147,7 @@ void **StringTable::search(const char *s, unsigned len) return (void **)se; } -StringValue *StringTable::lookup(const char *s, unsigned len) +StringValue *StringTable::lookup(const char *s, size_t len) { StringEntry *se; se = *(StringEntry **)search(s,len); @@ -158,7 +157,7 @@ StringValue *StringTable::lookup(const char *s, unsigned len) return NULL; } -StringValue *StringTable::update(const char *s, unsigned len) +StringValue *StringTable::update(const char *s, size_t len) { StringEntry **pse; StringEntry *se; @@ -172,7 +171,7 @@ StringValue *StringTable::update(const char *s, unsigned len) return &se->value; } -StringValue *StringTable::insert(const char *s, unsigned len) +StringValue *StringTable::insert(const char *s, size_t len) { StringEntry **pse; StringEntry *se; diff --git a/dmd2/root/stringtable.h b/dmd2/root/stringtable.h index 403a98b8..e4b13b3a 100644 --- a/dmd2/root/stringtable.h +++ b/dmd2/root/stringtable.h @@ -30,7 +30,7 @@ struct StringValue char *string; }; private: - unsigned length; + size_t length; #ifndef IN_GCC #if _MSC_VER @@ -41,33 +41,33 @@ private: char lstring[]; public: - unsigned len() const { return length; } + size_t len() const { return length; } const char *toDchars() const { return lstring; } private: friend struct StringEntry; StringValue(); // not constructible // This is more like a placement new c'tor - void ctor(const char *p, unsigned length); + void ctor(const char *p, size_t length); }; struct StringTable { private: void **table; - unsigned count; - unsigned tabledim; + size_t count; + size_t tabledim; public: - void init(unsigned size = 37); + void init(size_t size = 37); ~StringTable(); - StringValue *lookup(const char *s, unsigned len); - StringValue *insert(const char *s, unsigned len); - StringValue *update(const char *s, unsigned len); + StringValue *lookup(const char *s, size_t len); + StringValue *insert(const char *s, size_t len); + StringValue *update(const char *s, size_t len); private: - void **search(const char *s, unsigned len); + void **search(const char *s, size_t len); }; #endif diff --git a/dmd2/scope.c b/dmd2/scope.c index 28f6b965..a58ed613 100644 --- a/dmd2/scope.c +++ b/dmd2/scope.c @@ -68,9 +68,9 @@ Scope::Scope() this->protection = PROTpublic; this->explicitProtection = 0; this->stc = 0; + this->depmsg = NULL; this->offset = 0; this->inunion = 0; - this->incontract = 0; this->nofree = 0; this->noctor = 0; this->noaccesscheck = 0; @@ -84,6 +84,7 @@ Scope::Scope() this->lastdc = NULL; this->lastoffset = 0; this->docbuf = NULL; + this->userAttributes = NULL; } Scope::Scope(Scope *enclosing) @@ -117,10 +118,10 @@ Scope::Scope(Scope *enclosing) this->linkage = enclosing->linkage; this->protection = enclosing->protection; this->explicitProtection = enclosing->explicitProtection; + this->depmsg = enclosing->depmsg; this->stc = enclosing->stc; this->offset = 0; this->inunion = enclosing->inunion; - this->incontract = enclosing->incontract; this->nofree = 0; this->noctor = enclosing->noctor; this->noaccesscheck = enclosing->noaccesscheck; @@ -130,10 +131,11 @@ Scope::Scope(Scope *enclosing) this->parameterSpecialization = enclosing->parameterSpecialization; this->ignoreTemplates = enclosing->ignoreTemplates; this->callSuper = enclosing->callSuper; - this->flags = 0; + this->flags = (enclosing->flags & SCOPEcontract); this->lastdc = NULL; this->lastoffset = 0; this->docbuf = enclosing->docbuf; + this->userAttributes = enclosing->userAttributes; assert(this != enclosing); } @@ -200,25 +202,48 @@ void Scope::mergeCallSuper(Loc loc, unsigned cs) // The two paths are callSuper and cs; the result is merged into callSuper. if (cs != callSuper) - { int a; - int b; + { // Have ALL branches called a constructor? + int aAll = (cs & (CSXthis_ctor | CSXsuper_ctor)) != 0; + int bAll = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0; - callSuper |= cs & (CSXany_ctor | CSXlabel); - if (cs & CSXreturn) - { + // Have ANY branches called a constructor? + bool aAny = (cs & CSXany_ctor) != 0; + bool bAny = (callSuper & CSXany_ctor) != 0; + + // Have any branches returned? + bool aRet = (cs & CSXreturn) != 0; + bool bRet = (callSuper & CSXreturn) != 0; + + bool ok = true; + + // If one has returned without a constructor call, there must be never + // have been ctor calls in the other. + if ( (aRet && !aAny && bAny) || + (bRet && !bAny && aAny)) + { ok = false; } - else if (callSuper & CSXreturn) + // If one branch has called a ctor and then exited, anything the + // other branch has done is OK (except returning without a + // ctor call, but we already checked that). + else if (aRet && aAll) + { + callSuper |= cs & (CSXany_ctor | CSXlabel); + } + else if (bRet && bAll) { callSuper = cs | (callSuper & (CSXany_ctor | CSXlabel)); } else - { - a = (cs & (CSXthis_ctor | CSXsuper_ctor)) != 0; - b = (callSuper & (CSXthis_ctor | CSXsuper_ctor)) != 0; - if (a != b) - error(loc, "one path skips constructor"); - callSuper |= cs; + { // Both branches must have called ctors, or both not. + ok = (aAll == bAll); + // If one returned without a ctor, we must remember that + // (Don't bother if we've already found an error) + if (ok && aRet && !aAny) + callSuper |= CSXreturn; + callSuper |= cs & (CSXany_ctor | CSXlabel); } + if (!ok) + error(loc, "one path skips constructor"); } } @@ -257,8 +282,7 @@ Dsymbol *Scope::search(Loc loc, Identifier *ident, Dsymbol **pscopesym) s = sc->scopesym->search(loc, ident, 0); if (s) { - if ((global.params.warnings || - global.params.Dversion > 1) && + if (global.params.Dversion > 1 && ident == Id::length && sc->scopesym->isArrayScopeSymbol() && sc->enclosing && diff --git a/dmd2/scope.h b/dmd2/scope.h index 2e54ab7b..09f60b03 100644 --- a/dmd2/scope.h +++ b/dmd2/scope.h @@ -68,7 +68,6 @@ struct Scope // set in a pass after semantic() on all fields so they can be // semantic'd in any order. int inunion; // we're processing members of a union - int incontract; // we're inside contract code int nofree; // set if shouldn't free it int noctor; // set if constructor calls aren't allowed int intypeof; // in typeof(exp) @@ -94,6 +93,7 @@ struct Scope int explicitProtection; // set if in an explicit protection attribute StorageClass stc; // storage class + char *depmsg; // customized deprecation message unsigned flags; #define SCOPEctor 1 // constructor type @@ -102,9 +102,15 @@ struct Scope #define SCOPEstaticassert 8 // inside static assert #define SCOPEdebug 0x10 // inside debug conditional +#define SCOPEinvariant 0x20 // inside invariant code +#define SCOPErequire 0x40 // inside in contract code +#define SCOPEensure 0x60 // inside out contract code +#define SCOPEcontract 0x60 // [mask] we're inside contract code + #ifdef IN_GCC Expressions *attributes; // GCC decl/type attributes #endif + Expressions *userAttributes; // user defined attributes DocComment *lastdc; // documentation comment for last symbol at this scope unsigned lastoffset; // offset in docbuf of where to insert next dec diff --git a/dmd2/statement.c b/dmd2/statement.c index ce716f21..df7b319f 100644 --- a/dmd2/statement.c +++ b/dmd2/statement.c @@ -59,6 +59,28 @@ extern int os_critsecsize64(); #endif +Identifier *fixupLabelName(Scope *sc, Identifier *ident) +{ + unsigned flags = (sc->flags & SCOPEcontract); + if (flags && flags != SCOPEinvariant && + !(ident->string[0] == '_' && ident->string[1] == '_')) + { + /* CTFE requires FuncDeclaration::labtab for the interpretation. + * So fixing the label name inside in/out contracts is necessary + * for the uniqueness in labtab. + */ + const char *prefix = flags == SCOPErequire ? "__in_" : "__out_"; + OutBuffer buf; + buf.printf("%s%s", prefix, ident->toChars()); + buf.writeByte(0); + + const char *name = (const char *)buf.extractData(); + ident = Lexer::idPool(name); + } + return ident; +} + + /******************************** Statement ***************************/ Statement::Statement(Loc loc) @@ -66,7 +88,6 @@ Statement::Statement(Loc loc) { // If this is an in{} contract scope statement (skip for determining // inlineStatus of a function body for header content) - incontract = 0; } Statement *Statement::syntaxCopy() @@ -143,20 +164,28 @@ void Statement::warning(const char *format, ...) va_end( ap ); } -int Statement::hasBreak() +void Statement::deprecation(const char *format, ...) +{ + va_list ap; + va_start(ap, format); + ::vdeprecation(loc, format, ap); + va_end( ap ); +} + +bool Statement::hasBreak() { //printf("Statement::hasBreak()\n"); return FALSE; } -int Statement::hasContinue() +bool Statement::hasContinue() { return FALSE; } // TRUE if statement uses exception handling -int Statement::usesEH() +bool Statement::usesEH() { return FALSE; } @@ -561,7 +590,7 @@ Statement *CompoundStatement::semantic(Scope *sc) * As: * s; * try { s1; s2; } - * catch (Object __o) + * catch (Throwable __o) * { sexception; throw __o; } */ Statements *a = new Statements(); @@ -577,11 +606,13 @@ Statement *CompoundStatement::semantic(Scope *sc) Statement *handler = sexception; if (sexception->blockExit(FALSE) & BEfallthru) { handler = new ThrowStatement(0, new IdentifierExp(0, id)); + ((ThrowStatement *)handler)->internalThrow = true; handler = new CompoundStatement(0, sexception, handler); } Catches *catches = new Catches(); Catch *ctch = new Catch(0, NULL, id, handler); + ctch->internalCatch = true; catches->push(ctch); s = new TryCatchStatement(0, body, catches); @@ -676,7 +707,7 @@ void CompoundStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) } } -int CompoundStatement::usesEH() +bool CompoundStatement::usesEH() { for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; @@ -782,7 +813,7 @@ Statement *CompoundDeclarationStatement::syntaxCopy() void CompoundDeclarationStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - int nwritten = 0; + bool anywritten = false; for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; ExpStatement *ds; @@ -800,7 +831,7 @@ void CompoundDeclarationStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) * that does not print the type. * Should refactor this. */ - if (nwritten) + if (anywritten) { buf->writeByte(','); buf->writestring(v->ident->toChars()); @@ -827,7 +858,7 @@ void CompoundDeclarationStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) } else d->toCBuffer(buf, hgs); - nwritten++; + anywritten = true; } } buf->writeByte(';'); @@ -887,6 +918,7 @@ void UnrolledLoopStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("unrolled {"); buf->writenl(); + buf->level++; for (size_t i = 0; i < statements->dim; i++) { Statement *s; @@ -896,21 +928,22 @@ void UnrolledLoopStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) s->toCBuffer(buf, hgs); } + buf->level--; buf->writeByte('}'); buf->writenl(); } -int UnrolledLoopStatement::hasBreak() +bool UnrolledLoopStatement::hasBreak() { return TRUE; } -int UnrolledLoopStatement::hasContinue() +bool UnrolledLoopStatement::hasContinue() { return TRUE; } -int UnrolledLoopStatement::usesEH() +bool UnrolledLoopStatement::usesEH() { for (size_t i = 0; i < statements->dim; i++) { Statement *s = (*statements)[i]; @@ -1009,18 +1042,18 @@ Statement *ScopeStatement::semantic(Scope *sc) return this; } -int ScopeStatement::hasBreak() +bool ScopeStatement::hasBreak() { //printf("ScopeStatement::hasBreak() %s\n", toChars()); return statement ? statement->hasBreak() : FALSE; } -int ScopeStatement::hasContinue() +bool ScopeStatement::hasContinue() { return statement ? statement->hasContinue() : FALSE; } -int ScopeStatement::usesEH() +bool ScopeStatement::usesEH() { return statement ? statement->usesEH() : FALSE; } @@ -1048,10 +1081,12 @@ void ScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writeByte('{'); buf->writenl(); + buf->level++; if (statement) statement->toCBuffer(buf, hgs); + buf->level--; buf->writeByte('}'); buf->writenl(); } @@ -1082,17 +1117,17 @@ Statement *WhileStatement::semantic(Scope *sc) return s; } -int WhileStatement::hasBreak() +bool WhileStatement::hasBreak() { return TRUE; } -int WhileStatement::hasContinue() +bool WhileStatement::hasContinue() { return TRUE; } -int WhileStatement::usesEH() +bool WhileStatement::usesEH() { assert(0); return body ? body->usesEH() : 0; @@ -1178,17 +1213,17 @@ Statement *DoStatement::semantic(Scope *sc) return this; } -int DoStatement::hasBreak() +bool DoStatement::hasBreak() { return TRUE; } -int DoStatement::hasContinue() +bool DoStatement::hasContinue() { return TRUE; } -int DoStatement::usesEH() +bool DoStatement::usesEH() { return body ? body->usesEH() : 0; } @@ -1233,6 +1268,7 @@ void DoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writestring("while ("); condition->toCBuffer(buf, hgs); buf->writestring(");"); + buf->writenl(); } /******************************** ForStatement ***************************/ @@ -1245,6 +1281,7 @@ ForStatement::ForStatement(Loc loc, Statement *init, Expression *condition, Expr this->increment = increment; this->body = body; this->nest = 0; + this->relatedLabeled = NULL; } Statement *ForStatement::syntaxCopy() @@ -1321,6 +1358,7 @@ Statement *ForStatement::semanticInit(Scope *sc) Statement *handler = sexception; if (sexception->blockExit(FALSE) & BEfallthru) { handler = new ThrowStatement(0, new IdentifierExp(0, id)); + ((ThrowStatement *)handler)->internalThrow = true; handler = new CompoundStatement(0, sexception, handler); } Catches *catches = new Catches(); @@ -1333,6 +1371,7 @@ Statement *ForStatement::semanticInit(Scope *sc) //printf("ex {{{\n"); s = s->semantic(sc); //printf("}}}\n"); + this->relatedLabeled = s; statement = s; if (init) @@ -1359,6 +1398,7 @@ Statement *ForStatement::semanticInit(Scope *sc) //printf("fi {{{\n"); s = s->semantic(sc); //printf("}}} fi\n"); + this->relatedLabeled = s; statement = s; if (init) @@ -1430,18 +1470,18 @@ Statement *ForStatement::scopeCode(Scope *sc, Statement **sentry, Statement **se return this; } -int ForStatement::hasBreak() +bool ForStatement::hasBreak() { //printf("ForStatement::hasBreak()\n"); return TRUE; } -int ForStatement::hasContinue() +bool ForStatement::hasContinue() { return TRUE; } -int ForStatement::usesEH() +bool ForStatement::usesEH() { return (init && init->usesEH()) || body->usesEH(); } @@ -1511,7 +1551,9 @@ void ForStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writebyte('{'); buf->writenl(); + buf->level++; body->toCBuffer(buf, hgs); + buf->level--; buf->writebyte('}'); buf->writenl(); } @@ -1686,7 +1728,8 @@ Statement *ForeachStatement::semantic(Scope *sc) VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); if (arg->storageClass & STCref) v->storage_class |= STCref | STCforeach; - if (e->isConst() || e->op == TOKstring) + if (e->isConst() || e->op == TOKstring || + e->op == TOKstructliteral || e->op == TOKarrayliteral) { if (v->storage_class & STCref) error("constant value %s cannot be ref", ie->toChars()); else @@ -1748,6 +1791,7 @@ Lagain: int i = (dim == 1) ? 0 : 1; // index of value arg = (*arguments)[i]; arg->type = arg->type->semantic(loc, sc); + arg->type = arg->type->addStorageClass(arg->storageClass); tnv = arg->type->toBasetype(); if (tnv->ty != tn->ty && (tnv->ty == Tchar || tnv->ty == Twchar || tnv->ty == Tdchar)) @@ -1766,30 +1810,31 @@ Lagain: for (size_t i = 0; i < dim; i++) { // Declare args Parameter *arg = (*arguments)[i]; - Type *argtype = arg->type->semantic(loc, sc); + arg->type = arg->type->semantic(loc, sc); + arg->type = arg->type->addStorageClass(arg->storageClass); VarDeclaration *var; if (dim == 2 && i == 0) { -#if (BUG6652 == 1 || BUG6652 == 2) - var = new VarDeclaration(loc, arg->type, Lexer::uniqueId("__key"), NULL); - var->storage_class |= arg->storageClass & (STCin | STCout | STC_TYPECTOR); -#else - if (arg->storageClass & STCref) - var = new VarDeclaration(loc, argtype, arg->ident, NULL); - else - var = new VarDeclaration(loc, arg->type, Lexer::uniqueId("__key"), NULL); - var->storage_class |= arg->storageClass & (STCin | STCout | STC_TYPECTOR); -#endif + var = new VarDeclaration(loc, arg->type->mutableOf(), Lexer::uniqueId("__key"), NULL); var->storage_class |= STCforeach; if (var->storage_class & (STCref | STCout)) var->storage_class |= STCnodtor; key = var; + if (arg->storageClass & STCref) + { + if (!var->type->invariantOf()->equals(arg->type->invariantOf()) || + !MODimplicitConv(var->type->mod, arg->type->mod)) + { + error("key type mismatch, %s to ref %s", + var->type->toChars(), arg->type->toChars()); + } + } } else { - var = new VarDeclaration(loc, argtype, arg->ident, NULL); + var = new VarDeclaration(loc, arg->type, arg->ident, NULL); var->storage_class |= STCforeach; var->storage_class |= arg->storageClass & (STCin | STCout | STCref | STC_TYPECTOR); if (var->storage_class & (STCref | STCout)) @@ -1804,11 +1849,11 @@ Lagain: var->storage_class |= STCconst; Type *t = tab->nextOf(); - if (!t->invariantOf()->equals(argtype->invariantOf()) || - !MODimplicitConv(t->mod, argtype->mod)) + if (!t->invariantOf()->equals(arg->type->invariantOf()) || + !MODimplicitConv(t->mod, arg->type->mod)) { error("argument type mismatch, %s to ref %s", - t->toChars(), argtype->toChars()); + t->toChars(), arg->type->toChars()); } } } @@ -1869,8 +1914,7 @@ Lagain: if (dim == 2) { Parameter *arg = (*arguments)[0]; -#if (BUG6652 == 1 || BUG6652 == 2) - if ((*arguments)[0]->storageClass & STCref) + if ((arg->storageClass & STCref) && arg->type->equals(key->type)) { AliasDeclaration *v = new AliasDeclaration(loc, arg->ident, key); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); @@ -1878,18 +1922,14 @@ Lagain: else { ExpInitializer *ie = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); - VarDeclaration *v = new VarDeclaration(loc, NULL, arg->ident, ie); - v->storage_class |= STCforeach | STCref | STCbug6652; - body = new CompoundStatement(loc, new ExpStatement(loc, v), body); - } + VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); +#if (BUG6652 == 1 || BUG6652 == 2) + v->storage_class |= STCforeach | STCref | (arg->storageClass & STCref ? 0 : STCbug6652); #else - if (!(arg->storageClass & STCref)) - { - ExpInitializer *ie = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); - VarDeclaration *v = new VarDeclaration(loc, NULL, arg->ident, ie); + v->storage_class |= STCforeach | (arg->storageClass & STCref); +#endif body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } -#endif } body = new CompoundStatement(loc, ds, body); @@ -2142,6 +2182,7 @@ Lagain: Identifier *id; arg->type = arg->type->semantic(loc, sc); + arg->type = arg->type->addStorageClass(arg->storageClass); if (tfld) { Parameter *prm = Parameter::getNth(tfld->parameters, i); //printf("\tprm = %s%s\n", (prm->storageClass&STCref?"ref ":""), prm->ident->toChars()); @@ -2162,7 +2203,7 @@ Lagain: { // Make a copy of the ref argument so it isn't // a reference. LcopyArg: - id = Lexer::uniqueId("__applyArg", i); + id = Lexer::uniqueId("__applyArg", (int)i); Initializer *ie = new ExpInitializer(0, new IdentifierExp(0, id)); VarDeclaration *v = new VarDeclaration(0, arg->type, arg->ident, ie); @@ -2256,7 +2297,7 @@ Lagain: Expressions *exps = new Expressions(); exps->push(aggr); size_t keysize = taa->index->size(); - keysize = (keysize + (PTRSIZE-1)) & ~(PTRSIZE-1); + keysize = (keysize + ((size_t)PTRSIZE-1)) & ~((size_t)PTRSIZE-1); exps->push(new IntegerExp(0, keysize, Type::tsize_t)); #if IN_LLVM @@ -2443,17 +2484,17 @@ bool ForeachStatement::checkForArgTypes() return result; } -int ForeachStatement::hasBreak() +bool ForeachStatement::hasBreak() { return TRUE; } -int ForeachStatement::hasContinue() +bool ForeachStatement::hasContinue() { return TRUE; } -int ForeachStatement::usesEH() +bool ForeachStatement::usesEH() { return body->usesEH(); } @@ -2502,8 +2543,10 @@ void ForeachStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writebyte('{'); buf->writenl(); + buf->level++; if (body) body->toCBuffer(buf, hgs); + buf->level--; buf->writebyte('}'); buf->writenl(); } @@ -2561,6 +2604,7 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) if (arg->type) { arg->type = arg->type->semantic(loc, sc); + arg->type = arg->type->addStorageClass(arg->storageClass); lwr = lwr->implicitCastTo(sc, arg->type); upr = upr->implicitCastTo(sc, arg->type); } @@ -2573,13 +2617,15 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) { /* Just picking the first really isn't good enough. */ - arg->type = lwr->type->mutableOf(); + arg->type = lwr->type; + arg->type = arg->type->addStorageClass(arg->storageClass); } else { AddExp ea(loc, lwr, upr); Expression *e = ea.typeCombine(sc); - arg->type = ea.type->mutableOf(); + arg->type = ea.type; + arg->type = arg->type->addStorageClass(arg->storageClass); lwr = ea.e1; upr = ea.e2; } @@ -2594,14 +2640,7 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) */ ExpInitializer *ie = new ExpInitializer(loc, (op == TOKforeach) ? lwr : upr); -#if (BUG6652 == 1 || BUG6652 == 2) - key = new VarDeclaration(loc, arg->type, Lexer::uniqueId("__key"), ie); -#else - if (arg->storageClass & STCref) - key = new VarDeclaration(loc, arg->type, arg->ident, ie); - else - key = new VarDeclaration(loc, arg->type, Lexer::uniqueId("__key"), ie); -#endif + key = new VarDeclaration(loc, arg->type->mutableOf(), Lexer::uniqueId("__key"), ie); Identifier *id = Lexer::uniqueId("__limit"); ie = new ExpInitializer(loc, (op == TOKforeach) ? upr : lwr); @@ -2648,27 +2687,31 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) //increment = new AddAssignExp(loc, new VarExp(loc, key), new IntegerExp(1)); increment = new PreExp(TOKpreplusplus, loc, new VarExp(loc, key)); -#if (BUG6652 == 1 || BUG6652 == 2) - if (arg->storageClass & STCref) + if ((arg->storageClass & STCref) && arg->type->equals(key->type)) { AliasDeclaration *v = new AliasDeclaration(loc, arg->ident, key); body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } else { - ExpInitializer *ie = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); - VarDeclaration *v = new VarDeclaration(loc, NULL, arg->ident, ie); - v->storage_class |= STCforeach | STCref | STCbug6652; - body = new CompoundStatement(loc, new ExpStatement(loc, v), body); - } + ie = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); + VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); +#if (BUG6652 == 1 || BUG6652 == 2) + v->storage_class |= STCforeach | STCref | (arg->storageClass & STCref ? 0 : STCbug6652); #else - if (!(arg->storageClass & STCref)) - { - ExpInitializer *ie = new ExpInitializer(loc, new IdentifierExp(loc, key->ident)); - VarDeclaration *v = new VarDeclaration(loc, NULL, arg->ident, ie); + v->storage_class |= STCforeach | (arg->storageClass & STCref); +#endif body = new CompoundStatement(loc, new ExpStatement(loc, v), body); } -#endif + if (arg->storageClass & STCref) + { + if (!key->type->invariantOf()->equals(arg->type->invariantOf()) || + !MODimplicitConv(key->type->mod, arg->type->mod)) + { + error("argument type mismatch, %s to ref %s", + key->type->toChars(), arg->type->toChars()); + } + } ForStatement *fs = new ForStatement(loc, forinit, cond, increment, body); s = fs->semantic(sc); @@ -2700,17 +2743,17 @@ Statement *ForeachRangeStatement::semantic(Scope *sc) #endif } -int ForeachRangeStatement::hasBreak() +bool ForeachRangeStatement::hasBreak() { return TRUE; } -int ForeachRangeStatement::hasContinue() +bool ForeachRangeStatement::hasContinue() { return TRUE; } -int ForeachRangeStatement::usesEH() +bool ForeachRangeStatement::usesEH() { assert(0); return body->usesEH(); @@ -2760,8 +2803,10 @@ void ForeachRangeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writebyte('{'); buf->writenl(); + buf->level++; if (body) body->toCBuffer(buf, hgs); + buf->level--; buf->writebyte('}'); buf->writenl(); } @@ -2855,7 +2900,7 @@ Statement *IfStatement::semantic(Scope *sc) return this; } -int IfStatement::usesEH() +bool IfStatement::usesEH() { return (ifbody && ifbody->usesEH()) || (elsebody && elsebody->usesEH()); } @@ -2913,11 +2958,19 @@ void IfStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) condition->toCBuffer(buf, hgs); buf->writebyte(')'); buf->writenl(); + if (!ifbody->isScopeStatement()) + buf->level++; ifbody->toCBuffer(buf, hgs); + if (!ifbody->isScopeStatement()) + buf->level--; if (elsebody) { buf->writestring("else"); buf->writenl(); + if (!elsebody->isScopeStatement()) + buf->level++; elsebody->toCBuffer(buf, hgs); + if (!elsebody->isScopeStatement()) + buf->level--; } } @@ -2991,7 +3044,7 @@ Statements *ConditionalStatement::flatten(Scope *sc) return a; } -int ConditionalStatement::usesEH() +bool ConditionalStatement::usesEH() { return (ifbody && ifbody->usesEH()) || (elsebody && elsebody->usesEH()); } @@ -3010,8 +3063,10 @@ void ConditionalStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; if (ifbody) ifbody->toCBuffer(buf, hgs); + buf->level--; buf->writeByte('}'); buf->writenl(); if (elsebody) @@ -3019,8 +3074,10 @@ void ConditionalStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writestring("else"); buf->writenl(); buf->writeByte('{'); + buf->level++; buf->writenl(); elsebody->toCBuffer(buf, hgs); + buf->level--; buf->writeByte('}'); buf->writenl(); } @@ -3061,6 +3118,7 @@ Statement *PragmaStatement::semantic(Scope *sc) Expression *e = (*args)[i]; e = e->semantic(sc); + e = resolveProperties(sc, e); if (e->op != TOKerror && e->op != TOKtype) e = e->ctfeInterpret(); if (e->op == TOKerror) @@ -3092,6 +3150,7 @@ Statement *PragmaStatement::semantic(Scope *sc) Expression *e = (*args)[0]; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; StringExp *se = e->toString(); @@ -3124,6 +3183,7 @@ Statement *PragmaStatement::semantic(Scope *sc) { Expression *e = (*args)[0]; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->ctfeInterpret(); (*args)[0] = e; Dsymbol *sa = getDsymbol(e); @@ -3147,7 +3207,7 @@ Lerror: return body; } -int PragmaStatement::usesEH() +bool PragmaStatement::usesEH() { return body && body->usesEH(); } @@ -3180,9 +3240,11 @@ void PragmaStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; body->toCBuffer(buf, hgs); + buf->level--; buf->writeByte('}'); buf->writenl(); } @@ -3358,11 +3420,11 @@ Statement *SwitchStatement::semantic(Scope *sc) } #endif - if (!sc->sw->sdefault && (!isFinal || needswitcherror)) + if (!sc->sw->sdefault && (!isFinal || needswitcherror || global.params.useAssert)) { hasNoDefault = 1; - if (!global.params.useDeprecated && !isFinal) - error("non-final switch statement without a default is deprecated"); + if (!isFinal) + deprecation("non-final switch statement without a default is deprecated"); // Generate runtime error if the default is hit Statements *a = new Statements(); @@ -3388,12 +3450,12 @@ Statement *SwitchStatement::semantic(Scope *sc) return this; } -int SwitchStatement::hasBreak() +bool SwitchStatement::hasBreak() { return TRUE; } -int SwitchStatement::usesEH() +bool SwitchStatement::usesEH() { return body ? body->usesEH() : 0; } @@ -3428,7 +3490,9 @@ void SwitchStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) if (!body->isScopeStatement()) { buf->writebyte('{'); buf->writenl(); + buf->level++; body->toCBuffer(buf, hgs); + buf->level--; buf->writebyte('}'); buf->writenl(); } @@ -3466,6 +3530,7 @@ Statement *CaseStatement::semantic(Scope *sc) //printf("CaseStatement::semantic() %s\n", toChars()); exp = exp->semantic(sc); + exp = resolveProperties(sc, exp); if (sw) { #if IN_LLVM @@ -3547,7 +3612,7 @@ int CaseStatement::compare(Object *obj) return exp->compare(cs2->exp); } -int CaseStatement::usesEH() +bool CaseStatement::usesEH() { return statement->usesEH(); } @@ -3600,10 +3665,12 @@ Statement *CaseRangeStatement::semantic(Scope *sc) error("case ranges not allowed in final switch"); first = first->semantic(sc); + first = resolveProperties(sc, first); first = first->implicitCastTo(sc, sw->condition->type); first = first->ctfeInterpret(); last = last->semantic(sc); + last = resolveProperties(sc, last); last = last->implicitCastTo(sc, sw->condition->type); last = last->ctfeInterpret(); @@ -3711,7 +3778,7 @@ Statement *DefaultStatement::semantic(Scope *sc) return this; } -int DefaultStatement::usesEH() +bool DefaultStatement::usesEH() { return statement->usesEH(); } @@ -3729,7 +3796,8 @@ int DefaultStatement::comeFrom() void DefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - buf->writestring("default:\n"); + buf->writestring("default:"); + buf->writenl(); statement->toCBuffer(buf, hgs); } @@ -3763,7 +3831,8 @@ int GotoDefaultStatement::blockExit(bool mustNotThrow) void GotoDefaultStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - buf->writestring("goto default;\n"); + buf->writestring("goto default;"); + buf->writenl(); } /******************************** GotoCaseStatement ***************************/ @@ -3889,7 +3958,7 @@ Statement *ReturnStatement::semantic(Scope *sc) exp = new IntegerExp(0); } - if (sc->incontract || scx->incontract) + if ((sc->flags & SCOPEcontract) || (scx->flags & SCOPEcontract)) error("return statements cannot be in contracts"); #if !IN_LLVM if (sc->tf || scx->tf) @@ -3918,13 +3987,22 @@ Statement *ReturnStatement::semantic(Scope *sc) FuncLiteralDeclaration *fld = fd->isFuncLiteralDeclaration(); if (tret) exp = exp->inferType(tbret); - else if (fld && fld->treq && fld->treq->nextOf()) - exp = exp->inferType(fld->treq->nextOf()); + else if (fld && fld->treq) + exp = exp->inferType(fld->treq->nextOf()->nextOf()); exp = exp->semantic(sc); exp = resolveProperties(sc, exp); if (!((TypeFunction *)fd->type)->isref) exp = exp->optimize(WANTvalue); + if (exp->op == TOKcall) + valueNoDtor(exp); + else + { + Expression *e = exp->isTemp(); + if (e) + exp = e; // don't need temporary + } + if (fd->nrvo_can && exp->op == TOKvar) { VarExp *ve = (VarExp *)exp; VarDeclaration *v = ve->var->isVarDeclaration(); @@ -3934,9 +4012,6 @@ Statement *ReturnStatement::semantic(Scope *sc) fd->nrvo_can = 0; else if (!v || v->isOut() || v->isRef()) fd->nrvo_can = 0; -// else if (tbret->ty == Tstruct && ((TypeStruct *)tbret)->sym->dtor) -// // Struct being returned has destructors -// fd->nrvo_can = 0; else if (fd->nrvo_var == NULL) { if (!v->isDataseg() && !v->isParameter() && v->toParent2() == fd) { //printf("Setting nrvo to %s\n", v->toChars()); @@ -3951,6 +4026,12 @@ Statement *ReturnStatement::semantic(Scope *sc) else fd->nrvo_can = 0; + if (!fd->nrvo_can && + exp->isLvalue() && !((TypeFunction *)fd->type)->isref) + { + exp = callCpCtor(exp->loc, sc, exp, 1); + } + if (fd->inferRetType) { TypeFunction *tf = (TypeFunction *)fd->type; assert(tf->ty == Tfunction); @@ -4018,10 +4099,17 @@ Statement *ReturnStatement::semantic(Scope *sc) } if (fd->returnLabel) eorg = exp; + + if (!fd->returns) + fd->returns = new ReturnStatements(); + fd->returns->push(this); } else if (tbret->ty != Tvoid) { - if (fd->isPureBypassingInference() == PUREstrong && + assert(fd->type->ty == Tfunction); + TypeFunction *tf = (TypeFunction *)fd->type; + if (fd->isPureBypassingInference() != PUREimpure && + !tf->hasMutableIndirectionParams() && !exp->type->implicitConvTo(tret) && exp->type->invariantOf()->implicitConvTo(tret)) { @@ -4093,7 +4181,9 @@ Statement *ReturnStatement::semantic(Scope *sc) if (!fd->vresult) { // Declare vresult Scope *sco = fd->scout ? fd->scout : scx; - VarDeclaration *v = new VarDeclaration(loc, tret, Id::result, NULL); + if (!fd->outId) + fd->outId = Id::result; + VarDeclaration *v = new VarDeclaration(loc, tret, fd->outId, NULL); v->noscope = 1; v->storage_class |= STCresult; if (((TypeFunction *)fd->type)->isref) @@ -4122,11 +4212,7 @@ Statement *ReturnStatement::semantic(Scope *sc) { if (((TypeFunction *)fd->type)->isref && !fd->isCtorDeclaration()) { // Function returns a reference - if (tret->isMutable()) - exp = exp->modifiableLvalue(sc, exp); - else - exp = exp->toLvalue(sc, exp); - + exp = exp->toLvalue(sc, exp); exp->checkEscapeRef(); } else @@ -4147,13 +4233,7 @@ Statement *ReturnStatement::semantic(Scope *sc) } } - /* BUG: need to issue an error on: - * this - * { if (x) return; - * super(); - * } - */ - + // If any branches have called a ctor, but this branch hasn't, it's an error if (sc->callSuper & CSXany_ctor && !(sc->callSuper & (CSXthis_ctor | CSXsuper_ctor))) error("return without calling constructor"); @@ -4195,17 +4275,6 @@ Statement *ReturnStatement::semantic(Scope *sc) return new CompoundStatement(loc, s, this); } - if (exp) - { if (exp->op == TOKcall) - valueNoDtor(exp); - else - { - Expression *e = exp->isTemp(); - if (e) - exp = e; // don't need temporary - } - } - return this; } @@ -4248,6 +4317,8 @@ Statement *BreakStatement::semantic(Scope *sc) // break Identifier; if (ident) { + ident = fixupLabelName(sc, ident); + Scope *scx; FuncDeclaration *thisfunc = sc->func; @@ -4347,6 +4418,8 @@ Statement *ContinueStatement::semantic(Scope *sc) //printf("ContinueStatement::semantic() %p\n", this); if (ident) { + ident = fixupLabelName(sc, ident); + Scope *scx; FuncDeclaration *thisfunc = sc->func; @@ -4602,17 +4675,17 @@ Lbody: return this; } -int SynchronizedStatement::hasBreak() +bool SynchronizedStatement::hasBreak() { return FALSE; //TRUE; } -int SynchronizedStatement::hasContinue() +bool SynchronizedStatement::hasContinue() { return FALSE; //TRUE; } -int SynchronizedStatement::usesEH() +bool SynchronizedStatement::usesEH() { return TRUE; } @@ -4729,12 +4802,13 @@ void WithStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("with ("); exp->toCBuffer(buf, hgs); - buf->writestring(")\n"); + buf->writestring(")"); + buf->writenl(); if (body) body->toCBuffer(buf, hgs); } -int WithStatement::usesEH() +bool WithStatement::usesEH() { return body ? body->usesEH() : 0; } @@ -4804,12 +4878,12 @@ Statement *TryCatchStatement::semantic(Scope *sc) return this; } -int TryCatchStatement::hasBreak() +bool TryCatchStatement::hasBreak() { return FALSE; //TRUE; } -int TryCatchStatement::usesEH() +bool TryCatchStatement::usesEH() { return TRUE; } @@ -4956,8 +5030,10 @@ void Catch::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writebyte('{'); buf->writenl(); + buf->level++; if (handler) handler->toCBuffer(buf, hgs); + buf->level--; buf->writebyte('}'); buf->writenl(); } @@ -5012,34 +5088,55 @@ Statement *TryFinallyStatement::semantic(Scope *sc) void TryFinallyStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - buf->printf("try\n{\n"); + buf->writestring("try"); + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + buf->level++; body->toCBuffer(buf, hgs); - buf->printf("}\nfinally\n{\n"); + buf->level--; + buf->writebyte('}'); + buf->writenl(); + buf->writestring("finally"); + buf->writenl(); + buf->writebyte('{'); + buf->writenl(); + buf->level++; finalbody->toCBuffer(buf, hgs); + buf->level--; buf->writeByte('}'); buf->writenl(); } -int TryFinallyStatement::hasBreak() +bool TryFinallyStatement::hasBreak() { return FALSE; //TRUE; } -int TryFinallyStatement::hasContinue() +bool TryFinallyStatement::hasContinue() { return FALSE; //TRUE; } -int TryFinallyStatement::usesEH() +bool TryFinallyStatement::usesEH() { return TRUE; } int TryFinallyStatement::blockExit(bool mustNotThrow) { + int result = BEfallthru; if (body) - return body->blockExit(mustNotThrow); - return BEfallthru; + result = body->blockExit(mustNotThrow); + // check finally body as well, it may throw (bug #4082) + if (finalbody) + { + int finalresult = finalbody->blockExit(mustNotThrow); + if (!(finalresult & BEfallthru)) + result &= ~BEfallthru; + result |= finalresult & ~BEfallthru; + } + return result; } @@ -5077,7 +5174,7 @@ void OnScopeStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) statement->toCBuffer(buf, hgs); } -int OnScopeStatement::usesEH() +bool OnScopeStatement::usesEH() { return 1; } @@ -5135,11 +5232,13 @@ ThrowStatement::ThrowStatement(Loc loc, Expression *exp) : Statement(loc) { this->exp = exp; + this->internalThrow = false; } Statement *ThrowStatement::syntaxCopy() { ThrowStatement *s = new ThrowStatement(loc, exp->syntaxCopy()); + s->internalThrow = internalThrow; return s; } @@ -5168,9 +5267,6 @@ Statement *ThrowStatement::semantic(Scope *sc) int ThrowStatement::blockExit(bool mustNotThrow) { -#if IN_LLVM - // Back-port of DMD commit d77b7c2b to allow usering Phobos from Git master - // for its many Win64 fixes. To be dropped during the 2.061 merge. if (mustNotThrow) { ClassDeclaration *cd = exp->type->toBasetype()->isClassHandle(); @@ -5178,16 +5274,12 @@ int ThrowStatement::blockExit(bool mustNotThrow) // Bugzilla 8675 // Throwing Errors is allowed even if mustNotThrow - if (cd != ClassDeclaration::errorException && + if (!internalThrow && + cd != ClassDeclaration::errorException && !ClassDeclaration::errorException->isBaseOf(cd, NULL)) error("%s is thrown but not caught", exp->type->toChars()); } return BEthrow; -#else - if (mustNotThrow) - error("%s is thrown but not caught", exp->type->toChars()); - return BEthrow; // obviously -#endif } @@ -5342,6 +5434,8 @@ Statement *GotoStatement::semantic(Scope *sc) { FuncDeclaration *fd = sc->parent->isFuncDeclaration(); //printf("GotoStatement::semantic()\n"); + ident = fixupLabelName(sc, ident); + #if !IN_LLVM tf = sc->tf; #else @@ -5420,6 +5514,8 @@ Statement *LabelStatement::semantic(Scope *sc) FuncDeclaration *fd = sc->parent->isFuncDeclaration(); //printf("LabelStatement::semantic()\n"); + ident = fixupLabelName(sc, ident); + ls = fd->searchLabel(ident); if (ls->statement) error("Label '%s' already defined", ls->toChars()); @@ -5471,7 +5567,7 @@ Statements *LabelStatement::flatten(Scope *sc) } -int LabelStatement::usesEH() +bool LabelStatement::usesEH() { return statement ? statement->usesEH() : FALSE; } @@ -5616,6 +5712,7 @@ Statement *ImportStatement::semantic(Scope *sc) } s->semantic(sc); + s->semantic2(sc); sc->insert(s); for (size_t i = 0; i < s->aliasdecls.dim; i++) diff --git a/dmd2/statement.h b/dmd2/statement.h index a97d6cd1..e085a409 100644 --- a/dmd2/statement.h +++ b/dmd2/statement.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2011 by Digital Mars +// Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -114,18 +114,18 @@ struct Statement : Object void print(); char *toChars(); - void error(const char *format, ...) IS_PRINTF(2); - void warning(const char *format, ...) IS_PRINTF(2); + void error(const char *format, ...); + void warning(const char *format, ...); + void deprecation(const char *format, ...); virtual void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - virtual AsmBlockStatement *isAsmBlockStatement() { return NULL; } - int incontract; virtual ScopeStatement *isScopeStatement() { return NULL; } virtual Statement *semantic(Scope *sc); Statement *semanticScope(Scope *sc, Statement *sbreak, Statement *scontinue); Statement *semanticNoScope(Scope *sc); - virtual int hasBreak(); - virtual int hasContinue(); - virtual int usesEH(); + virtual Statement *getRelatedLabeled() { return this; } + virtual bool hasBreak(); + virtual bool hasContinue(); + virtual bool usesEH(); virtual int blockExit(bool mustNotThrow); virtual int comeFrom(); virtual int isEmpty(); @@ -152,6 +152,7 @@ struct Statement : Object virtual LabelStatement *isLabelStatement() { return NULL; } #if IN_LLVM + virtual AsmBlockStatement *isAsmBlockStatement() { return NULL; } virtual void toNakedIR(IRState *irs); virtual AsmBlockStatement* endsWithAsm(); #endif @@ -226,7 +227,7 @@ struct CompoundStatement : Statement Statement *syntaxCopy(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); int isEmpty(); @@ -267,9 +268,9 @@ struct UnrolledLoopStatement : Statement UnrolledLoopStatement(Loc loc, Statements *statements); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -292,9 +293,9 @@ struct ScopeStatement : Statement void toCBuffer(OutBuffer *buf, HdrGenState *hgs); ScopeStatement *isScopeStatement() { return this; } Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); int isEmpty(); @@ -316,9 +317,9 @@ struct WhileStatement : Statement WhileStatement(Loc loc, Expression *c, Statement *b); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -337,9 +338,9 @@ struct DoStatement : Statement DoStatement(Loc loc, Statement *b, Expression *c); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -358,14 +359,20 @@ struct ForStatement : Statement Statement *body; int nest; + // When wrapped in try/finally clauses, this points to the outermost one, + // which may have an associated label. Internal break/continue statements + // treat that label as referring to this loop. + Statement *relatedLabeled; + ForStatement(Loc loc, Statement *init, Expression *condition, Expression *increment, Statement *body); Statement *syntaxCopy(); Statement *semanticInit(Scope *sc); Statement *semantic(Scope *sc); Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); - int hasBreak(); - int hasContinue(); - int usesEH(); + Statement *getRelatedLabeled() { return relatedLabeled ? relatedLabeled : this; } + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -399,9 +406,9 @@ struct ForeachStatement : Statement bool checkForArgTypes(); int inferAggregate(Scope *sc, Dsymbol *&sapply); int inferApplyArgTypes(Scope *sc, Dsymbol *&sapply); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -427,9 +434,9 @@ struct ForeachRangeStatement : Statement Expression *lwr, Expression *upr, Statement *body); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -455,7 +462,7 @@ struct IfStatement : Statement Statement *semantic(Scope *sc); Expression *interpret(InterState *istate); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); IfStatement *isIfStatement() { return this; } @@ -477,7 +484,7 @@ struct ConditionalStatement : Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); Statements *flatten(Scope *sc); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -492,7 +499,7 @@ struct PragmaStatement : Statement PragmaStatement(Loc loc, Identifier *ident, Expressions *args, Statement *body); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -534,8 +541,8 @@ struct SwitchStatement : Statement SwitchStatement(Loc loc, Expression *c, Statement *b, bool isFinal); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int usesEH(); + bool hasBreak(); + bool usesEH(); int blockExit(bool mustNotThrow); Expression *interpret(InterState *istate); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -561,7 +568,7 @@ struct CaseStatement : Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); int compare(Object *obj); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -608,7 +615,7 @@ struct DefaultStatement : Statement DefaultStatement(Loc loc, Statement *s); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); @@ -731,9 +738,9 @@ struct SynchronizedStatement : Statement SynchronizedStatement(Loc loc, Expression *exp, Statement *body); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); @@ -758,7 +765,7 @@ struct WithStatement : Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); Expression *interpret(InterState *istate); @@ -775,8 +782,8 @@ struct TryCatchStatement : Statement TryCatchStatement(Loc loc, Statement *body, Catches *catches); Statement *syntaxCopy(); Statement *semantic(Scope *sc); - int hasBreak(); - int usesEH(); + bool hasBreak(); + bool usesEH(); int blockExit(bool mustNotThrow); Expression *interpret(InterState *istate); @@ -793,7 +800,8 @@ struct Catch : Object Identifier *ident; VarDeclaration *var; Statement *handler; - bool internalCatch; + bool internalCatch; // was generated by the compiler, + // wasn't present in source code Catch(Loc loc, Type *t, Identifier *id, Statement *handler); Catch *syntaxCopy(); @@ -811,9 +819,9 @@ struct TryFinallyStatement : Statement Statement *syntaxCopy(); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - int hasBreak(); - int hasContinue(); - int usesEH(); + bool hasBreak(); + bool hasContinue(); + bool usesEH(); int blockExit(bool mustNotThrow); Expression *interpret(InterState *istate); @@ -832,7 +840,7 @@ struct OnScopeStatement : Statement int blockExit(bool mustNotThrow); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); Statement *semantic(Scope *sc); - int usesEH(); + bool usesEH(); Statement *scopeCode(Scope *sc, Statement **sentry, Statement **sexit, Statement **sfinally); Expression *interpret(InterState *istate); @@ -842,6 +850,8 @@ struct OnScopeStatement : Statement struct ThrowStatement : Statement { Expression *exp; + bool internalThrow; // was generated by the compiler, + // wasn't present in source code ThrowStatement(Loc loc, Expression *exp); Statement *syntaxCopy(); @@ -921,7 +931,7 @@ struct LabelStatement : Statement Statement *syntaxCopy(); Statement *semantic(Scope *sc); Statements *flatten(Scope *sc); - int usesEH(); + bool usesEH(); int blockExit(bool mustNotThrow); int comeFrom(); Expression *interpret(InterState *istate); diff --git a/dmd2/staticassert.c b/dmd2/staticassert.c index 6fa9606c..662b74a1 100644 --- a/dmd2/staticassert.c +++ b/dmd2/staticassert.c @@ -58,6 +58,7 @@ void StaticAssert::semantic2(Scope *sc) sc->flags |= SCOPEstaticassert; ++sc->ignoreTemplates; Expression *e = exp->semantic(sc); + e = resolveProperties(sc, e); sc = sc->pop(); if (!e->type->checkBoolean()) { @@ -78,8 +79,14 @@ void StaticAssert::semantic2(Scope *sc) OutBuffer buf; msg = msg->semantic(sc); + msg = resolveProperties(sc, msg); msg = msg->ctfeInterpret(); hgs.console = 1; + StringExp * s = msg->toString(); + if (s) + { s->postfix = 0; // Don't display a trailing 'c' + msg = s; + } msg->toCBuffer(&buf, &hgs); error("%s", buf.toChars()); } diff --git a/dmd2/struct.c b/dmd2/struct.c index 27729baf..3bef2d59 100644 --- a/dmd2/struct.c +++ b/dmd2/struct.c @@ -49,7 +49,7 @@ AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id) stag = NULL; sinit = NULL; #endif - isnested = 0; + isnested = false; vthis = NULL; #if DMDV2 @@ -71,6 +71,13 @@ enum PROT AggregateDeclaration::prot() return protection; } +void AggregateDeclaration::setScope(Scope *sc) +{ + if (sizeok == SIZEOKdone) + return; + ScopeDsymbol::setScope(sc); +} + void AggregateDeclaration::semantic2(Scope *sc) { //printf("AggregateDeclaration::semantic2(%s)\n", toChars()); @@ -84,6 +91,7 @@ void AggregateDeclaration::semantic2(Scope *sc) for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; + //printf("\t[%d] %s\n", i, s->toChars()); s->semantic2(sc); } sc->pop(); @@ -157,6 +165,10 @@ unsigned AggregateDeclaration::size(Loc loc) */ struct SV { + /* Returns: + * 0 this member doesn't need further processing to determine struct size + * 1 this member does + */ static int func(Dsymbol *s, void *param) { SV *psv = (SV *)param; VarDeclaration *v = s->isVarDeclaration(); @@ -164,7 +176,7 @@ unsigned AggregateDeclaration::size(Loc loc) { if (v->scope) v->semantic(NULL); - if (v->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCconst | STCimmutable | STCmanifest | STCctfe | STCtemplateparameter)) + if (v->storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCctfe | STCtemplateparameter)) return 0; if (v->storage_class & STCfield && v->sem >= SemanticDone) return 0; @@ -272,8 +284,6 @@ unsigned AggregateDeclaration::placeField( ; else if (8 < memalignsize) memalignsize = 8; - else if (alignment < memalignsize) - memalignsize = alignment; } else { @@ -295,6 +305,7 @@ unsigned AggregateDeclaration::placeField( int AggregateDeclaration::isNested() { + assert((isnested & ~1) == 0); return isnested; } @@ -403,7 +414,9 @@ void StructDeclaration::semantic(Scope *sc) assert(type); if (!members) // if forward reference + { return; + } if (symtab) { if (sizeok == SIZEOKdone || !scope) @@ -452,50 +465,15 @@ void StructDeclaration::semantic(Scope *sc) assert(!isAnonymous()); if (sc->stc & STCabstract) error("structs, unions cannot be abstract"); + userAttributes = sc->userAttributes; if (sizeok == SIZEOKnone) // if not already done the addMember step { - int hasfunctions = 0; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; //printf("adding member '%s' to '%s'\n", s->toChars(), this->toChars()); s->addMember(sc, this, 1); - if (s->isFuncDeclaration()) - hasfunctions = 1; - } - - // If nested struct, add in hidden 'this' pointer to outer scope - if (hasfunctions && !(storage_class & STCstatic)) - { Dsymbol *s = toParent2(); - if (s) - { - AggregateDeclaration *ad = s->isAggregateDeclaration(); - FuncDeclaration *fd = s->isFuncDeclaration(); - - TemplateInstance *ti; - if (ad && (ti = ad->parent->isTemplateInstance()) != NULL && ti->isnested || fd) - { isnested = 1; - Type *t; - if (ad) - t = ad->handle; - else if (fd) - { AggregateDeclaration *ad = fd->isMember2(); - if (ad) - t = ad->handle; - else - t = Type::tvoidptr; - } - else - assert(0); - if (t->ty == Tstruct) - t = Type::tvoidptr; // t should not be a ref type - assert(!vthis); - vthis = new ThisDeclaration(loc, t); - //vthis->storage_class |= STCref; - members->push(vthis); - } - } } } @@ -508,13 +486,12 @@ void StructDeclaration::semantic(Scope *sc) sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; - - size_t members_dim = members->dim; + sc2->userAttributes = NULL; /* Set scope so if there are forward references, we still might be able to * resolve individual members like enums. */ - for (size_t i = 0; i < members_dim; i++) + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; /* There are problems doing this in the general case because * Scope keeps track of things like 'offset' @@ -526,7 +503,7 @@ void StructDeclaration::semantic(Scope *sc) } } - for (size_t i = 0; i < members_dim; i++) + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; @@ -535,7 +512,7 @@ void StructDeclaration::semantic(Scope *sc) * field was processed. The problem is the chicken-and-egg determination * of when that is. See Bugzilla 7426 for more info. */ - if (i + 1 == members_dim) + if (i + 1 == members->dim) { if (sizeok == SIZEOKnone && s->isAliasDeclaration()) finalizeSize(sc2); @@ -670,7 +647,7 @@ void StructDeclaration::semantic(Scope *sc) postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); - buildOpAssign(sc2); + hasIdentityAssign = (buildOpAssign(sc2) != NULL); hasIdentityEquals = (buildOpEquals(sc2) != NULL); xeq = buildXopEquals(sc2); @@ -764,6 +741,45 @@ void StructDeclaration::finalizeSize(Scope *sc) sizeok = SIZEOKdone; } +void StructDeclaration::makeNested() +{ + if (!isnested && sizeok != SIZEOKdone) + { + // If nested struct, add in hidden 'this' pointer to outer scope + if (!(storage_class & STCstatic)) + { Dsymbol *s = toParent2(); + if (s) + { + AggregateDeclaration *ad = s->isAggregateDeclaration(); + FuncDeclaration *fd = s->isFuncDeclaration(); + + TemplateInstance *ti; + if (ad && (ti = ad->parent->isTemplateInstance()) != NULL && ti->isnested || fd) + { isnested = true; + Type *t; + if (ad) + t = ad->handle; + else if (fd) + { AggregateDeclaration *ad = fd->isMember2(); + if (ad) + t = ad->handle; + else + t = Type::tvoidptr; + } + else + assert(0); + if (t->ty == Tstruct) + t = Type::tvoidptr; // t should not be a ref type + assert(!vthis); + vthis = new ThisDeclaration(loc, t); + //vthis->storage_class |= STCref; + members->push(vthis); + } + } + } + } +} + /*************************************** * Return true if struct is POD (Plain Old Data). * This is defined as: @@ -820,13 +836,13 @@ void StructDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writeByte('{'); buf->writenl(); + buf->level++; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; - - buf->writestring(" "); s->toCBuffer(buf, hgs); } + buf->level--; buf->writeByte('}'); buf->writenl(); } diff --git a/dmd2/template.c b/dmd2/template.c index 250d9043..bafbf7ae 100644 --- a/dmd2/template.c +++ b/dmd2/template.c @@ -17,8 +17,7 @@ #include "aav.h" #include "rmem.h" #include "stringtable.h" -#include "mars.h" -#include "identifier.h" + #include "mtype.h" #include "template.h" #include "init.h" @@ -28,6 +27,9 @@ #include "aggregate.h" #include "declaration.h" #include "dsymbol.h" +#include "mars.h" +#include "dsymbol.h" +#include "identifier.h" #include "hdrgen.h" #include "id.h" @@ -38,6 +40,10 @@ long __cdecl __ehfilter(LPEXCEPTION_POINTERS ep); #define LOG 0 +#define IDX_NOTFOUND (0x12345678) // index is not found + +size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters); + /******************************************** * These functions substitute for dynamic_cast. dynamic_cast does not work * on earlier versions of gcc. @@ -75,6 +81,14 @@ Tuple *isTuple(Object *o) return (Tuple *)o; } +Parameter *isParameter(Object *o) +{ + //return dynamic_cast(o); + if (!o || o->dyncast() != DYNCAST_PARAMETER) + return NULL; + return (Parameter *)o; +} + /************************************** * Is this Object an error? */ @@ -123,6 +137,9 @@ Type *getType(Object *o) Dsymbol *getDsymbol(Object *oarg) { + //printf("getDsymbol()\n"); + //printf("e %p s %p t %p v %p\n", isExpression(oarg), isDsymbol(oarg), isType(oarg), isTuple(oarg)); + Dsymbol *sa; Expression *ea = isExpression(oarg); if (ea) @@ -150,6 +167,38 @@ Dsymbol *getDsymbol(Object *oarg) return sa; } +/*********************** + * Try to get value from manifest constant + */ + +Expression *getValue(Expression *e) +{ + if (e && e->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)e)->var->isVarDeclaration(); + if (v && v->storage_class & STCmanifest) + { ExpInitializer *ei = v->init->isExpInitializer(); + if (ei) + e = ei->exp; + } + } + return e; +} +Expression *getValue(Dsymbol *&s) +{ + Expression *e = NULL; + if (s) + { + VarDeclaration *v = s->isVarDeclaration(); + if (v && v->storage_class & STCmanifest) + { ExpInitializer *ei = v->init->isExpInitializer(); + if (ei) + e = ei->exp, s = NULL; + } + } + return e; +} + /****************************** * If o1 matches o2, return 1. * Else, return 0. @@ -159,10 +208,10 @@ int match(Object *o1, Object *o2, TemplateDeclaration *tempdecl, Scope *sc) { Type *t1 = isType(o1); Type *t2 = isType(o2); - Expression *e1 = isExpression(o1); - Expression *e2 = isExpression(o2); Dsymbol *s1 = isDsymbol(o1); Dsymbol *s2 = isDsymbol(o2); + Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); + Expression *e2 = s2 ? getValue(s2) : getValue(isExpression(o2)); Tuple *u1 = isTuple(o1); Tuple *u2 = isTuple(o2); @@ -173,24 +222,9 @@ int match(Object *o1, Object *o2, TemplateDeclaration *tempdecl, Scope *sc) * we'll do that another day. */ - if (s1) - { - VarDeclaration *v1 = s1->isVarDeclaration(); - if (v1 && v1->storage_class & STCmanifest) - { ExpInitializer *ei1 = v1->init->isExpInitializer(); - if (ei1) - e1 = ei1->exp, s1 = NULL; - } - } - if (s2) - { - VarDeclaration *v2 = s2->isVarDeclaration(); - if (v2 && v2->storage_class & STCmanifest) - { ExpInitializer *ei2 = v2->init->isExpInitializer(); - if (ei2) - e2 = ei2->exp, s2 = NULL; - } - } + /* Manifest constants should be compared by their values, + * at least in template arguments. + */ if (t1) { @@ -224,10 +258,8 @@ int match(Object *o1, Object *o2, TemplateDeclaration *tempdecl, Scope *sc) if (e1 && e2) { printf("match %d\n", e1->equals(e2)); - e1->print(); - e2->print(); - e1->type->print(); - e2->type->print(); + printf("\te1 = %p %s %s %s\n", e1, e1->type->toChars(), Token::toChars(e1->op), e1->toChars()); + printf("\te2 = %p %s %s %s\n", e2, e2->type->toChars(), Token::toChars(e2->op), e2->toChars()); } #endif if (!e2) @@ -423,11 +455,10 @@ Dsymbol *TemplateDeclaration::syntaxCopy(Dsymbol *) e = constraint->syntaxCopy(); Dsymbols *d = Dsymbol::arraySyntaxCopy(members); td = new TemplateDeclaration(loc, ident, p, e, d, ismixin); - + td->literal = literal; #if IN_LLVM td->intrinsicName = intrinsicName; #endif - return td; } @@ -502,6 +533,8 @@ void TemplateDeclaration::semantic(Scope *sc) if (!parent) parent = sc->parent; + protection = sc->protection; + if (global.params.doDocComments) { origParameters = new TemplateParameters(); @@ -631,7 +664,7 @@ void TemplateDeclaration::makeParamNamesVisibleInConstraint(Scope *paramscope, E TypeFunction *tf = (TypeFunction *)fd->type->syntaxCopy(); // Shouldn't run semantic on default arguments and return type. - for (int i = 0; iparameters->dim; i++) + for (size_t i = 0; iparameters->dim; i++) (*tf->parameters)[i]->defaultArg = NULL; tf->next = NULL; @@ -958,8 +991,8 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objec size_t nfparams; size_t nfargs; size_t nargsi; // array size of targsi - int fptupindex = -1; - int tuple_dim = 0; + size_t fptupindex = IDX_NOTFOUND; + size_t tuple_dim = 0; MATCH match = MATCHexact; MATCH matchTargsi = MATCHexact; FuncDeclaration *fd = onemember->toAlias()->isFuncDeclaration(); @@ -1322,7 +1355,7 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objec declareParameter(paramscope, tp, t); goto L2; } - fptupindex = -1; + fptupindex = IDX_NOTFOUND; } } @@ -1411,7 +1444,7 @@ L2: * arg[fputupindex+dim.. ] == param[fptupindex+1.. ] */ size_t i = parami; - if (fptupindex >= 0 && parami > fptupindex) + if (fptupindex != IDX_NOTFOUND && parami > fptupindex) i += tuple_dim - 1; Parameter *fparam = Parameter::getNth(fparameters, parami); @@ -1449,7 +1482,7 @@ Lretry: if (!inferparams || !prmtype->reliesOnTident(inferparams)) { // should copy prmtype to avoid affecting semantic result - prmtype = prmtype->syntaxCopy()->semantic(loc, paramscope); + prmtype = prmtype->syntaxCopy()->semantic(fd->loc, paramscope); } #if DMDV2 @@ -1596,12 +1629,57 @@ Lretry: Type *tb = prmtype->toBasetype(); switch (tb->ty) { - // Perhaps we can do better with this, see TypeFunction::callMatch() + // 6764 fix - TypeAArray may be TypeSArray have not yet run semantic(). case Tsarray: - { TypeSArray *tsa = (TypeSArray *)tb; - dinteger_t sz = tsa->dim->toInteger(); - if (sz != nfargs - i) - goto Lnomatch; + case Taarray: + { // Perhaps we can do better with this, see TypeFunction::callMatch() + if (tb->ty == Tsarray) + { TypeSArray *tsa = (TypeSArray *)tb; + dinteger_t sz = tsa->dim->toInteger(); + if (sz != nfargs - i) + goto Lnomatch; + } + else if (tb->ty == Taarray) + { TypeAArray *taa = (TypeAArray *)tb; + Expression *dim = new IntegerExp(loc, nfargs - i, Type::tsize_t); + + size_t i = templateParameterLookup(taa->index, parameters); + if (i == IDX_NOTFOUND) + { Expression *e; + Type *t; + Dsymbol *s; + taa->index->resolve(loc, sc, &e, &t, &s); + if (!e) + goto Lnomatch; + e = e->optimize(WANTvalue | WANTinterpret); + e = e->implicitCastTo(sc, Type::tsize_t); + e = e->optimize(WANTvalue); + if (!dim->equals(e)) + goto Lnomatch; + } + else + { // This code matches code in TypeInstance::deduceType() + TemplateParameter *tprm = parameters->tdata()[i]; + TemplateValueParameter *tvp = tprm->isTemplateValueParameter(); + if (!tvp) + goto Lnomatch; + Expression *e = (Expression *)dedtypes[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[i] = dim; + } + } + } + /* fall through */ } case Tarray: { TypeArray *ta = (TypeArray *)tb; @@ -1700,7 +1778,7 @@ Lmatch: if (!oded) { if (tp && // if tuple parameter and - fptupindex < 0 && // tuple parameter was not in function parameter list and + fptupindex == IDX_NOTFOUND && // tuple parameter was not in function parameter list and nargsi == dedargs->dim - 1) // we're one argument short (i.e. no tuple argument) { // make tuple argument an empty tuple oded = (Object *)new Tuple(); @@ -1709,7 +1787,7 @@ Lmatch: goto Lnomatch; } } - declareParameter(paramscope, tparam, oded); + oded = declareParameter(paramscope, tparam, oded); (*dedargs)[i] = oded; } } @@ -1815,7 +1893,7 @@ Lnomatch: * Declare template parameter tp with value o, and install it in the scope sc. */ -void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Object *o) +Object *TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Object *o) { //printf("TemplateDeclaration::declareParameter('%s', o = %p)\n", tp->ident->toChars(), o); @@ -1825,6 +1903,7 @@ void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Obj Tuple *va = isTuple(o); Dsymbol *s; + VarDeclaration *v = NULL; // See if tp->ident already exists with a matching definition Dsymbol *scopesym; @@ -1837,7 +1916,7 @@ void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Obj tup.objects = *td->objects; if (match(va, &tup, this, sc)) { - return; + return o; } } } @@ -1875,7 +1954,7 @@ void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Obj Type *t = tvp ? tvp->valType : NULL; - VarDeclaration *v = new VarDeclaration(loc, t, tp->ident, init); + v = new VarDeclaration(loc, t, tp->ident, init); v->storage_class = STCmanifest; s = v; } @@ -1894,6 +1973,11 @@ void TemplateDeclaration::declareParameter(Scope *sc, TemplateParameter *tp, Obj if (!sc->insert(s)) error("declaration %s is already defined", tp->ident->toChars()); s->semantic(sc); + /* So the caller's o gets updated with the result of semantic() being run on o + */ + if (v) + return (Object *)v->init->toExpression(); + return o; } /************************************** @@ -1974,8 +2058,48 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, } if (!td->onemember || !td->onemember->toAlias()->isFuncDeclaration()) { - error("is not a function template"); - goto Lerror; + if (!targsi) + targsi = new Objects(); + TemplateInstance *ti = new TemplateInstance(loc, td, targsi); + + Objects dedtypes; + dedtypes.setDim(td->parameters->dim); + assert(td->semanticRun); + MATCH m2 = td->matchWithInstance(ti, &dedtypes, fargs, 0); + //printf("matchWithInstance = %d\n", m2); + if (!m2 || m2 < m_best2) // no match or less match + continue; + + ti->semantic(sc, fargs); + if (!ti->inst) // if template failed to expand + continue; + + Dsymbol *s = ti->inst->toAlias(); + FuncDeclaration *fd = s->isFuncDeclaration(); + if (!fd) + { + td->error("is not a function template"); + goto Lerror; + } + fd = fd->overloadResolve(loc, ethis, fargs, flags); + if (!fd) + continue; + + TypeFunction *tf = (TypeFunction *)fd->type; + MATCH m = (MATCH) tf->callMatch(fd->needThis() && !fd->isCtorDeclaration() ? ethis : NULL, fargs); + if (m < m_best) + continue; + + // td is the new best match + td_ambig = NULL; + assert((size_t)td->scope > 0x10000); + td_best = td; + fd_best = fd; + m_best = m; + m_best2 = m2; + tdargs->setDim(dedtypes.dim); + memcpy(tdargs->tdata(), dedtypes.tdata(), tdargs->dim * sizeof(void *)); + continue; } MATCH m, m2; @@ -2072,8 +2196,28 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, if (!td_best) { if (!(flags & 1)) - ::error(loc, "%s %s.%s does not match any function template declaration", + { + ::error(loc, "%s %s.%s does not match any function template declaration. Candidates are:", kind(), parent->toPrettyChars(), ident->toChars()); + + // Display candidate template functions + int numToDisplay = 5; // sensible number to display + for (TemplateDeclaration *td = this; td; td = td->overnext) + { + ::errorSupplemental(td->loc, "%s", td->toPrettyChars()); + if (!global.params.verbose && --numToDisplay == 0) + { + // Too many overloads to sensibly display. + // Just show count of remaining overloads. + int remaining = 0; + for (; td; td = td->overnext) + ++remaining; + if (remaining > 0) + ::errorSupplemental(loc, "... (%d more, -v to show) ...", remaining); + break; + } + } + } goto Lerror; } if (td_ambig) @@ -2084,6 +2228,9 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, td_ambig->loc.filename, td_ambig->loc.linnum, td_ambig->toChars()); } + if (!td_best->onemember || !td_best->onemember->toAlias()->isFuncDeclaration()) + return fd_best; + /* The best match is td_best with arguments tdargs. * Now instantiate the template. */ @@ -2093,8 +2240,6 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, fd_best = ti->toAlias()->isFuncDeclaration(); if (!fd_best) goto Lerror; - if (!((TypeFunction*)fd_best->type)->callMatch(fd_best->needThis() && !fd_best->isCtorDeclaration() ? ethis : NULL, fargs)) - goto Lerror; if (FuncLiteralDeclaration *fld = fd_best->isFuncLiteralDeclaration()) { @@ -2114,6 +2259,16 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, if (tf->next) fd_best->type = tf->semantic(loc, sc); } + if (fd_best->scope) + { + TemplateInstance *spec = fd_best->isSpeculative(); + int olderrs = global.errors; + fd_best->semantic3(fd_best->scope); + // Update the template instantiation with the number + // of errors which occured. + if (spec && global.errors != olderrs) + spec->errors = global.errors - olderrs; + } return fd_best; @@ -2143,7 +2298,7 @@ FuncDeclaration *TemplateDeclaration::deduceFunctionTemplate(Scope *sc, Loc loc, kind(), parent->toPrettyChars(), ident->toChars(), bufa.toChars(), buf.toChars()); else - error("cannot deduce template function from argument types !(%s)(%s)", + error(loc, "cannot deduce template function from argument types !(%s)(%s)", bufa.toChars(), buf.toChars()); } return NULL; @@ -2245,7 +2400,7 @@ void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) if (hgs->ddoc) tp = (*origParameters)[i]; if (i) - buf->writeByte(','); + buf->writestring(", "); tp->toCBuffer(buf, hgs); } buf->writeByte(')'); @@ -2263,11 +2418,13 @@ void TemplateDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) buf->writenl(); buf->writebyte('{'); buf->writenl(); + buf->level++; for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; s->toCBuffer(buf, hgs); } + buf->level--; buf->writebyte('}'); buf->writenl(); hgs->tpltMember--; @@ -2286,10 +2443,22 @@ char *TemplateDeclaration::toChars() { TemplateParameter *tp = (*parameters)[i]; if (i) - buf.writeByte(','); + buf.writestring(", "); tp->toCBuffer(&buf, &hgs); } buf.writeByte(')'); + + if (onemember && onemember->toAlias()) + { + FuncDeclaration *fd = onemember->toAlias()->isFuncDeclaration(); + if (fd && fd->type) + { + TypeFunction *tf = (TypeFunction *)fd->type; + char const* args = Parameter::argsTypesToChars(tf->parameters, tf->varargs); + buf.writestring(args); + } + } + #if DMDV2 if (constraint) { buf.writestring(" if ("); @@ -2305,10 +2474,10 @@ char *TemplateDeclaration::toChars() /**** * Given an identifier, figure out which TemplateParameter it is. - * Return -1 if not found. + * Return IDX_NOTFOUND if not found. */ -int templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) +size_t templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) { for (size_t i = 0; i < parameters->dim; i++) { TemplateParameter *tp = (*parameters)[i]; @@ -2316,19 +2485,21 @@ int templateIdentifierLookup(Identifier *id, TemplateParameters *parameters) if (tp->ident->equals(id)) return i; } - return -1; + return IDX_NOTFOUND; } -int templateParameterLookup(Type *tparam, TemplateParameters *parameters) +size_t templateParameterLookup(Type *tparam, TemplateParameters *parameters) { - assert(tparam->ty == Tident); - TypeIdentifier *tident = (TypeIdentifier *)tparam; - //printf("\ttident = '%s'\n", tident->toChars()); - if (tident->idents.dim == 0) + if (tparam->ty == Tident) { - return templateIdentifierLookup(tident->ident, parameters); + TypeIdentifier *tident = (TypeIdentifier *)tparam; + //printf("\ttident = '%s'\n", tident->toChars()); + if (tident->idents.dim == 0) + { + return templateIdentifierLookup(tident->ident, parameters); + } } - return -1; + return IDX_NOTFOUND; } /* These form the heart of template argument deduction. @@ -2363,8 +2534,8 @@ MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, if (tparam->ty == Tident) { // Determine which parameter tparam is - int i = templateParameterLookup(tparam, parameters); - if (i == -1) + size_t i = templateParameterLookup(tparam, parameters); + if (i == IDX_NOTFOUND) { if (!sc) goto Lnomatch; @@ -2735,8 +2906,8 @@ MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame if (id) { // This code matches code in TypeInstance::deduceType() - int i = templateIdentifierLookup(id, parameters); - if (i == -1) + size_t i = templateIdentifierLookup(id, parameters); + if (i == IDX_NOTFOUND) goto Lnomatch; TemplateParameter *tprm = (*parameters)[i]; TemplateValueParameter *tvp = tprm->isTemplateValueParameter(); @@ -2932,8 +3103,8 @@ MATCH TypeInstance::deduceType(Scope *sc, /* Handle case of: * template Foo(T : sa!(T), alias sa) */ - int i = templateIdentifierLookup(tp->tempinst->name, parameters); - if (i == -1) + size_t i = templateIdentifierLookup(tp->tempinst->name, parameters); + if (i == IDX_NOTFOUND) { /* Didn't find it as a parameter identifier. Try looking * it up and seeing if is an alias. See Bugzilla 1454 */ @@ -3002,11 +3173,11 @@ MATCH TypeInstance::deduceType(Scope *sc, Object *o2 = (*tp->tempinst->tiargs)[i]; Type *t2 = isType(o2); - int j; + size_t j; if (t2 && t2->ty == Tident && i == tp->tempinst->tiargs->dim - 1 && - (j = templateParameterLookup(t2, parameters), j != -1) && + (j = templateParameterLookup(t2, parameters), j != IDX_NOTFOUND) && j == parameters->dim - 1 && (*parameters)[j]->isTemplateTupleParameter()) { @@ -3047,13 +3218,10 @@ MATCH TypeInstance::deduceType(Scope *sc, break; Type *t1 = isType(o1); - - Expression *e1 = isExpression(o1); - Expression *e2 = isExpression(o2); - Dsymbol *s1 = isDsymbol(o1); Dsymbol *s2 = isDsymbol(o2); - + Expression *e1 = s1 ? getValue(s1) : getValue(isExpression(o1)); + Expression *e2 = isExpression(o2); Tuple *v1 = isTuple(o1); Tuple *v2 = isTuple(o2); #if 0 @@ -3102,7 +3270,7 @@ MATCH TypeInstance::deduceType(Scope *sc, { j = templateParameterLookup(t2, parameters); L1: - if (j == -1) + if (j == IDX_NOTFOUND) { t2->resolve(loc, sc, &e2, &t2, &s2); if (e2) @@ -3112,21 +3280,34 @@ MATCH TypeInstance::deduceType(Scope *sc, TemplateParameter *tp = (*parameters)[j]; // BUG: use tp->matchArg() instead of the following TemplateValueParameter *tv = tp->isTemplateValueParameter(); - if (!tv) - goto Lnomatch; - Expression *e = (Expression *)(*dedtypes)[j]; - if (e) + TemplateAliasParameter *ta = tp->isTemplateAliasParameter(); + if (tv) { - if (!e1->equals(e)) - goto Lnomatch; + Expression *e = (Expression *)(*dedtypes)[j]; + if (e) + { + if (!e1->equals(e)) + goto Lnomatch; + } + else + { Type *vt = tv->valType->semantic(0, sc); + MATCH m = (MATCH)e1->implicitConvTo(vt); + if (!m) + goto Lnomatch; + (*dedtypes)[j] = e1; + } } - else - { Type *vt = tv->valType->semantic(0, sc); - MATCH m = (MATCH)e1->implicitConvTo(vt); - if (!m) - goto Lnomatch; + else if (ta) + { + if (ta->specType) + { + if (!e1->type->equals(ta->specType)) + goto Lnomatch; + } (*dedtypes)[j] = e1; } + else + goto Lnomatch; } else if (s1 && s2) { @@ -3137,7 +3318,7 @@ MATCH TypeInstance::deduceType(Scope *sc, else if (s1 && t2 && t2->ty == Tident) { j = templateParameterLookup(t2, parameters); - if (j == -1) + if (j == IDX_NOTFOUND) { t2->resolve(loc, sc, &e2, &t2, &s2); if (s2) @@ -4118,7 +4299,20 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, Type *vt; if (!ei && oarg) - goto Lnomatch; + { + Dsymbol *si = isDsymbol(oarg); + FuncDeclaration *f; + if (si && (f = si->isFuncDeclaration()) != NULL) + { + ei = new VarExp(loc, f); + ei = ei->semantic(sc); + if (!f->needThis()) + ei = resolveProperties(sc, ei); + ei = ei->ctfeInterpret(); + } + else + goto Lnomatch; + } if (ei && ei->op == TOKvar) { // Resolve const variables that we had skipped earlier @@ -4146,6 +4340,7 @@ MATCH TemplateValueParameter::matchArg(Scope *sc, Expression *e = specValue; e = e->semantic(sc); + e = resolveProperties(sc, e); e = e->implicitCastTo(sc, vt); e = e->ctfeInterpret(); @@ -4245,6 +4440,7 @@ Object *TemplateValueParameter::defaultArg(Loc loc, Scope *sc) { e = e->syntaxCopy(); e = e->semantic(sc); + e = resolveProperties(sc, e); #if DMDV2 e = e->resolveLoc(loc, sc); #endif @@ -4658,7 +4854,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) inst = this; //printf("error return %p, %d\n", tempdecl, global.errors); if (inst) - ++inst->errors; + inst->errors = true; return; // error recovery } @@ -4774,7 +4970,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) if (global.gag && sc->speculative) speculative = 1; - int tempdecl_instance_idx = tempdecl->instances.dim; + size_t tempdecl_instance_idx = tempdecl->instances.dim; tempdecl->instances.push(this); parent = tempdecl->parent; //printf("parent = '%s'\n", parent->kind()); @@ -4794,7 +4990,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) #if 1 int dosemantic3 = 0; Dsymbols *target_symbol_list = NULL; - int target_symbol_list_idx; + size_t target_symbol_list_idx; if (!sc->parameterSpecialization) { Dsymbols *a; @@ -4824,7 +5020,8 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) a = scx->scopesym->members; } else - { Module *m = sc->module->importedFrom; + { //Module *m = sc->module->importedFrom; + Module *m = tempdecl->scope->module->importedFrom; //printf("\t2: adding to module %s instead of module %s\n", m->toChars(), sc->module->toChars()); a = m->members; if (m->semanticRun >= 3) @@ -4832,7 +5029,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) dosemantic3 = 1; } } - for (int i = 0; 1; i++) + for (size_t i = 0; 1; i++) { if (i == a->dim) { @@ -4854,7 +5051,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) Scope *scope = tempdecl->scope; if (!tempdecl->semanticRun) { - error("template instantiation %s forward references template declaration %s\n", toChars(), tempdecl->toChars()); + error("template instantiation %s forward references template declaration %s", toChars(), tempdecl->toChars()); return; } @@ -4941,6 +5138,7 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) //printf("isnested = %d, sc->parent = %s\n", isnested, sc->parent->toChars()); sc2->parent = /*isnested ? sc->parent :*/ this; sc2->tinst = this; + sc2->speculative = speculative; tryExpandMembers(sc2); @@ -4950,18 +5148,25 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) * on them due to forward references, we cannot run semantic2() * or semantic3() yet. */ + bool found_deferred_ad = false; for (size_t i = 0; i < Module::deferred.dim; i++) { Dsymbol *sd = Module::deferred[i]; - if (sd->parent == this) + AggregateDeclaration *ad = sd->isAggregateDeclaration(); + if (ad && ad->parent && ad->parent->isTemplateInstance()) { - //printf("deferred %s %s\n", sd->parent->toChars(), sd->toChars()); - AggregateDeclaration *ad = sd->isAggregateDeclaration(); - if (ad) + //printf("deferred template aggregate: %s %s\n", + // sd->parent->toChars(), sd->toChars()); + found_deferred_ad = true; + if (ad->parent == this) + { ad->deferred = this; - goto Laftersemantic; + break; + } } } + if (found_deferred_ad) + goto Laftersemantic; /* ConditionalDeclaration may introduce eponymous declaration, * so we should find it once again after semantic. @@ -5051,6 +5256,7 @@ void TemplateInstance::semanticTiargs(Scope *sc) /********************************** * Input: * flags 1: replace const variables with their initializers + * 2: don't devolve Parameter to Type */ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int flags) @@ -5066,7 +5272,7 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); - //printf("1: (*tiargs)[%d] = %p, %p, %p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta); + //printf("1: (*tiargs)[%d] = %p, s=%p, v=%p, ea=%p, ta=%p\n", j, o, isDsymbol(o), isTuple(o), ea, ta); if (ta) { //printf("type %s\n", ta->toChars()); @@ -5075,6 +5281,7 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f if (ea) { ea = ea->semantic(sc); + //printf("-> ea = %s %s\n", Token::toChars(ea->op), ea->toChars()); /* This test is to skip substituting a const var with * its initializer. The problem is the initializer won't * match with an 'alias' parameter. Instead, do the @@ -5111,7 +5318,10 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f { tiargs->reserve(dim); for (size_t i = 0; i < dim; i++) { Parameter *arg = (*tt->arguments)[i]; - tiargs->insert(j + i, arg->type); + if (flags & 2 && arg->ident) + tiargs->insert(j + i, arg); + else + tiargs->insert(j + i, arg->type); } } j--; @@ -5127,11 +5337,7 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f } else if (ea) { - if (!ea) - { assert(global.errors); - ea = new ErrorExp(); - } - assert(ea); + //printf("+[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars()); ea = ea->semantic(sc); if (flags & 1) // only used by __traits, must not interpret the args ea = ea->optimize(WANTvalue); @@ -5139,7 +5345,13 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f ea->op != TOKimport && ea->op != TOKtype && ea->op != TOKfunction && ea->op != TOKerror && ea->op != TOKthis && ea->op != TOKsuper) + { + int olderrs = global.errors; ea = ea->ctfeInterpret(); + if (global.errors != olderrs) + ea = new ErrorExp(); + } + //printf("-[%d] ea = %s %s\n", j, Token::toChars(ea->op), ea->toChars()); (*tiargs)[j] = ea; if (ea->op == TOKtype) { ta = ea->type; @@ -5160,6 +5372,27 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f fe->fd->tok = TOKfunction; fe->fd->vthis = NULL; } + else if (fe->td) + { /* If template argument is a template lambda, + * get template declaration itself. */ + ea = NULL; + (*tiargs)[j] = sa = fe->td; + goto Lsa; + } + } + if (ea->op == TOKdotvar) + { // translate expression to dsymbol. + sa = ((DotVarExp *)ea)->var; + goto Ldsym; + } + if (ea->op == TOKtemplate) + { sa = ((TemplateExp *)ea)->td; + goto Ldsym; + } + if (ea->op == TOKdottd) + { // translate expression to dsymbol. + sa = ((DotTemplateExp *)ea)->td; + goto Ldsym; } if (ea->op == TOKtuple) { // Expand tuple @@ -5176,10 +5409,14 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f } else if (sa) { + Lsa: TemplateDeclaration *td = sa->isTemplateDeclaration(); if (td && !td->semanticRun && td->literal) td->semantic(sc); } + else if (isParameter(o)) + { + } else { assert(0); @@ -5345,12 +5582,18 @@ TemplateDeclaration *TemplateInstance::findBestMatch(Scope *sc, Expressions *far if (!td->semanticRun) { if (td->scope) - { // Try to fix forward reference + { // Try to fix forward reference. Ungag errors while doing so. + int oldgag = global.gag; + if (global.isSpeculativeGagging() && !td->isSpeculative()) + global.gag = 0; + td->semantic(td->scope); + + global.gag = oldgag; } if (!td->semanticRun) { - error("%s forward references template declaration %s\n", toChars(), td->toChars()); + error("%s forward references template declaration %s", toChars(), td->toChars()); return NULL; } } @@ -5465,6 +5708,7 @@ TemplateDeclaration *TemplateInstance::findBestMatch(Scope *sc, Expressions *far /***************************************** * Determines if a TemplateInstance will need a nested * generation of the TemplateDeclaration. + * Sets isnested property if so, and returns != 0; */ int TemplateInstance::hasNestedArgs(Objects *args) @@ -5479,6 +5723,24 @@ int TemplateInstance::hasNestedArgs(Objects *args) Expression *ea = isExpression(o); Dsymbol *sa = isDsymbol(o); Tuple *va = isTuple(o); +#define FIXBUG8863 0 +#if FIXBUG8863 + /* This does fix 8863, but it causes other complex + * failures in Phobos unittests and the test suite. + * Not sure why. + */ + Type *ta = isType(o); + if (ta && !sa) + { + Dsymbol *s = ta->toDsymbol(NULL); + if (s) + { + sa = s; + goto Lsa; + } + } + else +#endif if (ea) { if (ea->op == TOKvar) @@ -5504,8 +5766,12 @@ int TemplateInstance::hasNestedArgs(Objects *args) { Lsa: TemplateDeclaration *td = sa->isTemplateDeclaration(); + AggregateDeclaration *ad = sa->isAggregateDeclaration(); Declaration *d = sa->isDeclaration(); if ((td && td->literal) || +#if FIXBUG8863 + (ad && ad->isNested()) || +#endif (d && !d->isDataseg() && #if DMDV2 !(d->storage_class & STCmanifest) && @@ -5552,6 +5818,7 @@ int TemplateInstance::hasNestedArgs(Objects *args) nested |= hasNestedArgs(&va->objects); } } + //printf("-TemplateInstance::hasNestedArgs('%s') = %d\n", tempdecl->ident->toChars(), nested); return nested; } @@ -5642,8 +5909,15 @@ Identifier *TemplateInstance::genIdent(Objects *args) Lsa: buf.writeByte('S'); Declaration *d = sa->isDeclaration(); + Lsa2: if (d && (!d->type || !d->type->deco)) - { error("forward reference of %s", d->toChars()); + { + FuncAliasDeclaration *fad = d->isFuncAliasDeclaration(); + if (fad) + { d = fad->toAliasFunc(); + goto Lsa2; + } + error("forward reference of %s %s", d->kind(), d->toChars()); continue; } #if 0 @@ -5672,7 +5946,7 @@ Identifier *TemplateInstance::genIdent(Objects *args) { assert(i + 1 == args->dim); // must be last one args = &va->objects; - i = -1; + i = -(size_t)1; } else assert(0); @@ -5782,8 +6056,7 @@ int TemplateInstance::needsTypeInference(Scope *sc) } void TemplateInstance::semantic2(Scope *sc) -{ int i; - +{ if (semanticRun >= PASSsemantic2) return; semanticRun = PASSsemantic2; @@ -5797,7 +6070,7 @@ void TemplateInstance::semantic2(Scope *sc) sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; - for (i = 0; i < members->dim; i++) + for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; #if LOG @@ -5975,8 +6248,6 @@ void TemplateInstance::inlineScan() void TemplateInstance::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { - int i; - Identifier *id = name; buf->writestring(id->toChars()); buf->writestring("!("); @@ -5986,10 +6257,10 @@ void TemplateInstance::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { nest++; Objects *args = tiargs; - for (i = 0; i < args->dim; i++) + for (size_t i = 0; i < args->dim; i++) { if (i) - buf->writeByte(','); + buf->writestring(", "); Object *oarg = (*args)[i]; ObjectToCBuffer(buf, hgs, oarg); } @@ -6154,7 +6425,11 @@ void TemplateMixin::semantic(Scope *sc) // This for when a class/struct contains mixin members, and // is done over because of forward references if (parent && toParent()->isAggregateDeclaration()) + { + if (sc->parent != parent) + return; semanticRun = PASSsemantic; // do over + } else { #if LOG @@ -6241,25 +6516,30 @@ void TemplateMixin::semantic(Scope *sc) { if (!td->semanticRun) { - /* Cannot handle forward references if mixin is a struct member, - * because addField must happen during struct's semantic, not - * during the mixin semantic. - * runDeferred will re-run mixin's semantic outside of the struct's - * semantic. - */ - semanticRun = PASSinit; - AggregateDeclaration *ad = toParent()->isAggregateDeclaration(); - if (ad) - ad->sizeok = SIZEOKfwd; + if (td->scope) + td->semantic(td->scope); else { - // Forward reference - //printf("forward reference - deferring\n"); - scope = scx ? scx : new Scope(*sc); - scope->setNoFree(); - scope->module->addDeferredSemantic(this); + /* Cannot handle forward references if mixin is a struct member, + * because addField must happen during struct's semantic, not + * during the mixin semantic. + * runDeferred will re-run mixin's semantic outside of the struct's + * semantic. + */ + semanticRun = PASSinit; + AggregateDeclaration *ad = toParent()->isAggregateDeclaration(); + if (ad) + ad->sizeok = SIZEOKfwd; + else + { + // Forward reference + //printf("forward reference - deferring\n"); + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + } + return; } - return; } } @@ -6349,8 +6629,7 @@ void TemplateMixin::semantic(Scope *sc) #if LOG printf("\tcreate scope for template parameters '%s'\n", toChars()); #endif - Scope *scy = sc; - scy = sc->push(this); + Scope *scy = sc->push(this); scy->parent = this; argsym = new ScopeDsymbol(); @@ -6611,7 +6890,7 @@ void TemplateMixin::toCBuffer(OutBuffer *buf, HdrGenState *hgs) void TemplateMixin::toObjFile(int multiobj) { //printf("TemplateMixin::toObjFile('%s')\n", toChars()); - TemplateInstance::toObjFile(multiobj); + TemplateInstance::toObjFile(0); } #endif diff --git a/dmd2/template.h b/dmd2/template.h index 811bdcbf..bd0b872f 100644 --- a/dmd2/template.h +++ b/dmd2/template.h @@ -40,6 +40,7 @@ struct Expression; struct AliasDeclaration; struct FuncDeclaration; struct HdrGenState; +struct Parameter; enum MATCH; enum PASS; @@ -68,6 +69,7 @@ struct TemplateDeclaration : ScopeDsymbol int literal; // this template declaration is a literal int ismixin; // template declaration is only to be used as a mixin + enum PROT protection; struct Previous { Previous *prev; @@ -95,7 +97,7 @@ struct TemplateDeclaration : ScopeDsymbol MATCH deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *fargs, Objects *dedargs); FuncDeclaration *deduceFunctionTemplate(Scope *sc, Loc loc, Objects *targsi, Expression *ethis, Expressions *fargs, int flags = 0); - void declareParameter(Scope *sc, TemplateParameter *tp, Object *o); + Object *declareParameter(Scope *sc, TemplateParameter *tp, Object *o); FuncDeclaration *doHeaderInstantiation(Scope *sc, Objects *tdargs, Expressions *fargs); TemplateDeclaration *isTemplateDeclaration() { return this; } @@ -304,7 +306,6 @@ struct TemplateInstance : ScopeDsymbol int havetempdecl; // 1 if used second constructor Dsymbol *isnested; // if referencing local symbols, this is the context int speculative; // 1 if only instantiated with errors gagged - bool ignore; // true if the instance must be ignored when codegen'ing #ifdef IN_GCC /* On some targets, it is necessary to know whether a symbol will be emitted in the output or not before the symbol @@ -350,6 +351,7 @@ struct TemplateInstance : ScopeDsymbol AliasDeclaration *isAliasDeclaration(); #if IN_LLVM + bool ignore; // true if the instance must be ignored when codegen'ing Module* tmodule; // module from outermost enclosing template instantiation Module* emittedInModule; // which module this template instance has been emitted in @@ -392,6 +394,7 @@ Expression *isExpression(Object *o); Dsymbol *isDsymbol(Object *o); Type *isType(Object *o); Tuple *isTuple(Object *o); +Parameter *isParameter(Object *o); int arrayObjectIsError(Objects *args); int isError(Object *o); Type *getType(Object *o); diff --git a/dmd2/traits.c b/dmd2/traits.c index 7a678920..0b686777 100644 --- a/dmd2/traits.c +++ b/dmd2/traits.c @@ -61,11 +61,8 @@ static int fptraits(void *param, FuncDeclaration *f) return 0; Expression *e; - - if (p->e1->op == TOKdotvar) - { DotVarExp *dve = (DotVarExp *)p->e1; - e = new DotVarExp(0, dve->e1, new FuncAliasDeclaration(f, 0)); - } + if (p->e1) + e = new DotVarExp(0, p->e1, new FuncAliasDeclaration(f, 0)); else e = new DsymbolExp(0, new FuncAliasDeclaration(f, 0)); p->exps->push(e); @@ -149,6 +146,28 @@ Expression *TraitsExp::semantic(Scope *sc) { ISTYPE(t->toBasetype()->ty == Tclass && ((TypeClass *)t->toBasetype())->sym->storage_class & STCfinal) } + else if (ident == Id::isPOD) + { + if (dim != 1) + goto Ldimerror; + Object *o = (*args)[0]; + Type *t = isType(o); + StructDeclaration *sd; + if (!t) + { + error("type expected as second argument of __traits %s instead of %s", ident->toChars(), o->toChars()); + goto Lfalse; + } + if (t->toBasetype()->ty == Tstruct + && ((sd = (StructDeclaration *)(((TypeStruct *)t->toBasetype())->sym)) != NULL)) + { + if (sd->isPOD()) + goto Ltrue; + else + goto Lfalse; + } + goto Ltrue; + } else if (ident == Id::isAbstractFunction) { FuncDeclaration *f; @@ -190,20 +209,52 @@ Expression *TraitsExp::semantic(Scope *sc) else if (ident == Id::identifier) { // Get identifier for symbol as a string literal - // Specify 0 for the flags argument to semanticTiargs() so that - // a symbol should not be folded to a constant. - TemplateInstance::semanticTiargs(loc, sc, args, 0); + /* Specify 0 for bit 0 of the flags argument to semanticTiargs() so that + * a symbol should not be folded to a constant. + * Bit 1 means don't convert Parameter to Type if Parameter has an identifier + */ + TemplateInstance::semanticTiargs(loc, sc, args, 2); if (dim != 1) goto Ldimerror; Object *o = (*args)[0]; - Dsymbol *s = getDsymbol(o); - if (!s || !s->ident) + Parameter *po = isParameter(o); + Identifier *id; + if (po) + { id = po->ident; + assert(id); + } + else { - error("argument %s has no identifier", o->toChars()); + Dsymbol *s = getDsymbol(o); + if (!s || !s->ident) + { + error("argument %s has no identifier", o->toChars()); + goto Lfalse; + } + id = s->ident; + } + StringExp *se = new StringExp(loc, id->toChars()); + return se->semantic(sc); + } + else if (ident == Id::getProtection) + { + if (dim != 1) + goto Ldimerror; + Object *o = (*args)[0]; + Dsymbol *s = getDsymbol(o); + if (!s) + { + if (!isError(o)) + error("argument %s has no protection", o->toChars()); goto Lfalse; } - StringExp *se = new StringExp(loc, s->ident->toChars()); + + PROT protection = s->prot(); + + const char *protName = Pprotectionnames[protection]; + + StringExp *se = new StringExp(loc, (char *) protName); return se->semantic(sc); } else if (ident == Id::parent) @@ -213,7 +264,11 @@ Expression *TraitsExp::semantic(Scope *sc) Object *o = (*args)[0]; Dsymbol *s = getDsymbol(o); if (s) + { + if (FuncDeclaration *fd = s->isFuncDeclaration()) // Bugzilla 8943 + s = fd->toAliasFunc(); s = s->toParent(); + } if (!s) { error("argument %s has no parent", o->toChars()); @@ -221,7 +276,6 @@ Expression *TraitsExp::semantic(Scope *sc) } return (new DsymbolExp(loc, s))->semantic(sc); } - #endif else if (ident == Id::hasMember || ident == Id::getMember || @@ -313,10 +367,15 @@ Expression *TraitsExp::semantic(Scope *sc) if (e->op == TOKvar) { VarExp *ve = (VarExp *)e; f = ve->var->isFuncDeclaration(); + e = NULL; } else if (e->op == TOKdotvar) { DotVarExp *dve = (DotVarExp *)e; f = dve->var->isFuncDeclaration(); + if (dve->e1->op == TOKdottype || dve->e1->op == TOKthis) + e = NULL; + else + e = dve->e1; } else f = NULL; @@ -346,6 +405,23 @@ Expression *TraitsExp::semantic(Scope *sc) } return new IntegerExp(loc, cd->structsize, Type::tsize_t); } + else if (ident == Id::getAttributes) + { + if (dim != 1) + goto Ldimerror; + Object *o = (*args)[0]; + Dsymbol *s = getDsymbol(o); + if (!s) + { + error("first argument is not a symbol"); + goto Lfalse; + } + //printf("getAttributes %s, %p\n", s->toChars(), s->userAttributes); + if (!s->userAttributes) + s->userAttributes = new Expressions(); + TupleExp *tup = new TupleExp(loc, s->userAttributes); + return tup->semantic(sc); + } else if (ident == Id::allMembers || ident == Id::derivedMembers) { if (dim != 1) diff --git a/dmd2/utf.c b/dmd2/utf.c index c74f0770..52230b28 100644 --- a/dmd2/utf.c +++ b/dmd2/utf.c @@ -230,7 +230,7 @@ const char *utf_decodeChar(utf8_t const *s, size_t len, size_t *pidx, dchar_t *p //printf("utf_decodeChar(s = %02x, %02x, %02x len = %d)\n", u, s[1], s[2], len); // Get expected sequence length - unsigned n = UTF8_STRIDE[u]; + size_t n = UTF8_STRIDE[u]; switch (n) { case 1: // ASCII diff --git a/driver/cl_options.cpp b/driver/cl_options.cpp index 537eba64..bd3bd5c1 100644 --- a/driver/cl_options.cpp +++ b/driver/cl_options.cpp @@ -32,11 +32,16 @@ cl::list runargs("run", cl::Positional, cl::PositionalEatsArgs); - -static cl::opt useDeprecated("d", - cl::desc("Allow deprecated language features"), +static cl::opt useDeprecated( + cl::desc("Allow deprecated code/language features:"), cl::ZeroOrMore, - cl::location(global.params.useDeprecated)); + cl::values( + clEnumValN(0, "de", "Do not allow deprecated features"), + clEnumValN(1, "d", "Silently allow deprecated features"), + clEnumValN(2, "dw", "Warn about the use of deprecated features"), + clEnumValEnd), + cl::location(global.params.useDeprecated), + cl::init(2)); #if DMDV2 cl::opt enforcePropertySyntax("property", diff --git a/driver/ldmd.cpp b/driver/ldmd.cpp index ce70ed44..b97e978f 100644 --- a/driver/ldmd.cpp +++ b/driver/ldmd.cpp @@ -69,7 +69,7 @@ namespace ls = llvm::sys; // We reuse DMD's response file parsing routine for maximum compatibilty - it // handles quotes in a very peciuliar way. -int response_expand(int *pargc, char ***pargv); +int response_expand(size_t *pargc, char ***pargv); void browse(const char *url); /** @@ -395,10 +395,10 @@ struct Params * Parses the flags from the given command line and the DFLAGS environment * variable into a Params struct. */ -Params parseArgs(int originalArgc, char** originalArgv, ls::Path ldcPath) +Params parseArgs(size_t originalArgc, char** originalArgv, ls::Path ldcPath) { // Expand any response files present into the list of arguments. - int argc = originalArgc; + size_t argc = originalArgc; char** argv = originalArgv; if (response_expand(&argc, &argv)) { @@ -682,7 +682,7 @@ Params parseArgs(int originalArgc, char** originalArgv, ls::Path ldcPath) else if (strcmp(p + 1, "run") == 0) { result.run = true; - int runargCount = (((int)i >= originalArgc) ? argc : originalArgc) - i - 1; + int runargCount = ((i >= originalArgc) ? argc : originalArgc) - i - 1; if (runargCount) { result.files.push_back(argv[i + 1]); diff --git a/gen/functions.cpp b/gen/functions.cpp index f77bbaa6..cbabfba2 100644 --- a/gen/functions.cpp +++ b/gen/functions.cpp @@ -705,7 +705,14 @@ void DtoDeclareFunction(FuncDeclaration* fdecl) fdecl->ir.irFunc->func = func; // calling convention - if (!vafunc && fdecl->llvmInternal != LLVMintrinsic) + if (!vafunc && fdecl->llvmInternal != LLVMintrinsic +#if DMDV2 + // DMD treats _Dmain as having C calling convention and this has been + // hardcoded into druntime, even if the frontend type has D linkage. + // See Bugzilla issue 9028. + && !fdecl->isMain() +#endif + ) func->setCallingConv(DtoCallingConv(fdecl->loc, f->linkage)); else // fall back to C, it should be the right thing to do func->setCallingConv(llvm::CallingConv::C); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 2ce14705..c3bd93ec 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -189,6 +189,7 @@ static void LLVM_D_BuildRuntimeModule() LLType* objectTy = DtoType(ClassDeclaration::object->type); LLType* classInfoTy = DtoType(ClassDeclaration::classinfo->type); LLType* typeInfoTy = DtoType(Type::typeinfo->type); + LLType* aaTypeInfoTy = DtoType(Type::typeinfoassociativearray->type); LLType* aaTy = rt_ptr(LLStructType::get(gIR->context())); @@ -1013,7 +1014,7 @@ static void LLVM_D_BuildRuntimeModule() { llvm::StringRef fname("_d_assocarrayliteralTX"); std::vector types; - types.push_back(typeInfoTy); + types.push_back(aaTypeInfoTy); types.push_back(voidArrayTy); types.push_back(voidArrayTy); LLFunctionType* fty = llvm::FunctionType::get(voidPtrTy, types, false); diff --git a/gen/toir.cpp b/gen/toir.cpp index 79143c62..483621ad 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -3115,7 +3115,8 @@ DValue* AssocArrayLiteralExp::toElem(IRState* p) llvm::Function* func = LLVM_D_GetRuntimeFunction(gIR->module, "_d_assocarrayliteralTX"); LLFunctionType* funcTy = func->getFunctionType(); - LLValue* aaTypeInfo = DtoTypeInfoOf(stripModifiers(aatype)); + LLValue* aaTypeInfo = DtoBitCast(DtoTypeInfoOf(stripModifiers(aatype)), + DtoType(Type::typeinfoassociativearray->type)); LLConstant* idxs[2] = { DtoConstUint(0), DtoConstUint(0) }; diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 39cc92a9..12155111 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -86,7 +86,6 @@ file(GLOB_RECURSE DCRT_D ${RUNTIME_DC_DIR}/*.d) file(GLOB_RECURSE LDC_D ${RUNTIME_DIR}/src/ldc/*.d) list(REMOVE_ITEM DCRT_D ${RUNTIME_DC_DIR}/alloca.d - ${RUNTIME_DC_DIR}/critical_.d ${RUNTIME_DC_DIR}/deh.d ${RUNTIME_DC_DIR}/deh2.d ${RUNTIME_DC_DIR}/llmath.d @@ -128,6 +127,7 @@ if(PHOBOS2_DIR) endif() file(GLOB PHOBOS2_D ${PHOBOS2_DIR}/std/*.d) + file(GLOB PHOBOS2_D_DIGEST ${PHOBOS2_DIR}/std/digest/*.d) file(GLOB PHOBOS2_D_NET ${PHOBOS2_DIR}/std/net/*.d) file(GLOB_RECURSE PHOBOS2_D_INTERNAL ${PHOBOS2_DIR}/std/internal/*.d) file(GLOB PHOBOS2_D_C ${PHOBOS2_DIR}/std/c/*.d) @@ -151,6 +151,7 @@ if(PHOBOS2_DIR) file(GLOB PHOBOS2_D_WIN ${PHOBOS2_DIR}/std/windows/*.d) endif() list(APPEND PHOBOS2_D + ${PHOBOS2_D_DIGEST} ${PHOBOS2_D_NET} ${PHOBOS2_D_INTERNAL} ${PHOBOS2_D_WIN} diff --git a/runtime/druntime b/runtime/druntime index 5c6053c5..e2d17024 160000 --- a/runtime/druntime +++ b/runtime/druntime @@ -1 +1 @@ -Subproject commit 5c6053c546c2a67b78b705a7fdda85213fbd4511 +Subproject commit e2d17024e257830c02af1efbd3c384aea2b641c9 diff --git a/runtime/phobos b/runtime/phobos index c3574a7b..1a3eba6a 160000 --- a/runtime/phobos +++ b/runtime/phobos @@ -1 +1 @@ -Subproject commit c3574a7bc6048a6f0c20cdbf9306b5ee87fd6afc +Subproject commit 1a3eba6a183018f3158eeeb0c9508136c804847a diff --git a/tests/d2/dmd-testsuite b/tests/d2/dmd-testsuite index b6f7ffbf..0424040e 160000 --- a/tests/d2/dmd-testsuite +++ b/tests/d2/dmd-testsuite @@ -1 +1 @@ -Subproject commit b6f7ffbfa8b50fb376368ab1b759166db2aaa7d2 +Subproject commit 0424040e52c2eac41664cca593528ae34ff0825b