diff --git a/CMakeLists.txt b/CMakeLists.txt index 5a87da0d10..2bcde89ada 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -214,6 +214,12 @@ list(REMOVE_ITEM FE_SRC ${PROJECT_SOURCE_DIR}/${DMDFE_PATH}/id.c ${PROJECT_SOURCE_DIR}/${DMDFE_PATH}/impcnvtab.c ) +# exclude root/win32.c on non-windows systems +if(NOT WIN32) + list(REMOVE_ITEM FE_SRC ${PROJECT_SOURCE_DIR}/${DMDFE_PATH}/root/win32.c) +endif(NOT WIN32) +# disable dmd gc +list(REMOVE_ITEM FE_SRC ${PROJECT_SOURCE_DIR}/${DMDFE_PATH}/root/dmgcmem.c) set(LDC_SOURCE_FILES ${LDC_GENERATED} ${FE_SRC} diff --git a/dmd2/aggregate.h b/dmd2/aggregate.h index af85a260b7..5d0ca77031 100644 --- a/dmd2/aggregate.h +++ b/dmd2/aggregate.h @@ -150,11 +150,13 @@ struct StructDeclaration : AggregateDeclaration int zeroInit; // !=0 if initialize with 0 fill #if DMDV2 int hasIdentityAssign; // !=0 if has identity opAssign + int hasIdentityEquals; // !=0 if has identity opEquals FuncDeclaration *cpctor; // generated copy-constructor, if any - FuncDeclaration *eq; // bool opEquals(ref const T), if any - FuncDeclarations postblits; // Array of postblit functions FuncDeclaration *postblit; // aggregate postblit + + FuncDeclaration *xeq; // TypeInfo_Struct.xopEquals + static FuncDeclaration *xerreq; // object.xopEquals #endif StructDeclaration(Loc loc, Identifier *id); @@ -174,6 +176,8 @@ struct StructDeclaration : AggregateDeclaration FuncDeclaration *buildOpEquals(Scope *sc); FuncDeclaration *buildPostBlit(Scope *sc); FuncDeclaration *buildCpCtor(Scope *sc); + + FuncDeclaration *buildXopEquals(Scope *sc); #endif void toDocBuffer(OutBuffer *buf); diff --git a/dmd2/aliasthis.c b/dmd2/aliasthis.c index fab0dd2fb4..7696516234 100644 --- a/dmd2/aliasthis.c +++ b/dmd2/aliasthis.c @@ -52,7 +52,12 @@ void AliasThis::semantic(Scope *sc) assert(ad->members); Dsymbol *s = ad->search(loc, ident, 0); if (!s) - ::error(loc, "undefined identifier %s", ident->toChars()); + { s = sc->search(loc, ident, 0); + if (s) + ::error(loc, "%s is not a member of %s", s->toChars(), ad->toChars()); + else + ::error(loc, "undefined identifier %s", ident->toChars()); + } ad->aliasthis = s; } else diff --git a/dmd2/arrayop.c b/dmd2/arrayop.c index 36db8199b8..857c6a8e0f 100644 --- a/dmd2/arrayop.c +++ b/dmd2/arrayop.c @@ -1,5 +1,5 @@ -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -13,7 +13,7 @@ #include "rmem.h" -#include "stringtable.h" +#include "aav.h" #include "expression.h" #include "statement.h" @@ -31,7 +31,7 @@ extern int binary(const char *p , const char **tab, int high); * Hash table of array op functions already generated or known about. */ -StringTable arrayfuncs; +AA *arrayfuncs; #endif /********************************************** @@ -126,16 +126,17 @@ Expression *BinExp::arrayOp(Scope *sc) size_t namelen = buf.offset; buf.writeByte(0); - char *name = (char *)buf.extractData(); + char *name = buf.toChars(); + Identifier *ident = Lexer::idPool(name); /* Look up name in hash table */ #if IN_LLVM - StringValue *sv = sc->module->arrayfuncs.update(name, namelen); + FuncDeclaration **pfd = (FuncDeclaration **)_aaGet(&sc->module->arrayfuncs, ident); #else - StringValue *sv = arrayfuncs.update(name, namelen); + FuncDeclaration **pfd = (FuncDeclaration **)_aaGet(&arrayfuncs, ident); #endif - FuncDeclaration *fd = (FuncDeclaration *)sv->ptrvalue; + FuncDeclaration *fd = (FuncDeclaration *)*pfd; if (!fd) { #if IN_DMD @@ -326,7 +327,7 @@ Expression *BinExp::arrayOp(Scope *sc) Parameters *fparams = new Parameters(); Expression *loopbody = buildArrayLoop(fparams); - Parameter *p = fparams->tdata()[0 /*fparams->dim - 1*/]; + Parameter *p = (*fparams)[0 /*fparams->dim - 1*/]; #if DMDV1 // for (size_t i = 0; i < p.length; i++) Initializer *init = new ExpInitializer(0, new IntegerExp(0, 0, Type::tsize_t)); @@ -352,7 +353,7 @@ Expression *BinExp::arrayOp(Scope *sc) */ TypeFunction *ftype = new TypeFunction(fparams, type, 0, LINKc); //printf("ftype: %s\n", ftype->toChars()); - fd = new FuncDeclaration(loc, 0, Lexer::idPool(name), STCundefined, ftype); + fd = new FuncDeclaration(loc, 0, ident, STCundefined, ftype); fd->fbody = fbody; fd->protection = PROTpublic; fd->linkage = LINKd; @@ -373,10 +374,10 @@ Expression *BinExp::arrayOp(Scope *sc) else { /* In library, refer to it. */ - fd = FuncDeclaration::genCfunc(type, name); + fd = FuncDeclaration::genCfunc(type, ident); } #endif - sv->ptrvalue = fd; // cache symbol in hash table + *pfd = fd; // cache symbol in hash table } /* Call the function fd(arguments) @@ -537,7 +538,7 @@ Expression *AssignExp::buildArrayLoop(Parameters *fparams) ex2 = new CastExp(0, ex2, e1->type->nextOf()); #endif Expression *ex1 = e1->buildArrayLoop(fparams); - Parameter *param = fparams->tdata()[0]; + Parameter *param = (*fparams)[0]; param->storageClass = 0; Expression *e = new AssignExp(0, ex1, ex2); return e; @@ -550,7 +551,7 @@ Expression *Str##AssignExp::buildArrayLoop(Parameters *fparams) \ */ \ Expression *ex2 = e2->buildArrayLoop(fparams); \ Expression *ex1 = e1->buildArrayLoop(fparams); \ - Parameter *param = fparams->tdata()[0]; \ + Parameter *param = (*fparams)[0]; \ param->storageClass = 0; \ Expression *e = new Str##AssignExp(0, ex1, ex2); \ return e; \ diff --git a/dmd2/attrib.c b/dmd2/attrib.c index f3b807695b..01a09a1267 100644 --- a/dmd2/attrib.c +++ b/dmd2/attrib.c @@ -384,6 +384,35 @@ Dsymbol *StorageClassDeclaration::syntaxCopy(Dsymbol *s) return scd; } +int StorageClassDeclaration::oneMember(Dsymbol **ps) +{ + + int t = Dsymbol::oneMembers(decl, ps); + if (t && *ps) + { + /* This is to deal with the following case: + * struct Tick { + * template to(T) { const T to() { ... } } + * } + * For eponymous function templates, the 'const' needs to get attached to 'to' + * before the semantic analysis of 'to', so that template overloading based on the + * 'this' pointer can be successful. + */ + + FuncDeclaration *fd = (*ps)->isFuncDeclaration(); + if (fd) + { + /* Use storage_class2 instead of storage_class otherwise when we do .di generation + * we'll wind up with 'const const' rather than 'const'. + */ + /* Don't think we need to worry about mutually exclusive storage classes here + */ + fd->storage_class2 |= stc; + } + } + return t; +} + void StorageClassDeclaration::setScope(Scope *sc) { if (decl) diff --git a/dmd2/attrib.h b/dmd2/attrib.h index a0b44ef1dc..894e88dfcb 100644 --- a/dmd2/attrib.h +++ b/dmd2/attrib.h @@ -73,6 +73,7 @@ struct StorageClassDeclaration: AttribDeclaration Dsymbol *syntaxCopy(Dsymbol *s); void setScope(Scope *sc); void semantic(Scope *sc); + int oneMember(Dsymbol **ps); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); static void stcToCBuffer(OutBuffer *buf, StorageClass stc); diff --git a/dmd2/builtin.c b/dmd2/builtin.c index fbd42b3eed..4cfee71f00 100644 --- a/dmd2/builtin.c +++ b/dmd2/builtin.c @@ -43,6 +43,12 @@ enum BUILTIN FuncDeclaration::isBuiltin() { static const char FeZe [] = "FNaNbNfeZe"; // @safe pure nothrow real function(real) static const char FeZe2[] = "FNaNbNeeZe"; // @trusted pure nothrow real function(real) + static const char FuintZint[] = "FNaNbkZi"; // pure nothrow int function(uint) + static const char FuintZuint[] = "FNaNbkZk"; // pure nothrow uint function(uint) + static const char FulongZulong[] = "FNaNbkZk"; // pure nothrow int function(ulong) + static const char FulongZint[] = "FNaNbmZi"; // 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) //printf("FuncDeclaration::isBuiltin() %s, %d\n", toChars(), builtin); if (builtin == BUILTINunknown) @@ -68,6 +74,10 @@ enum BUILTIN FuncDeclaration::isBuiltin() builtin = BUILTINsqrt; else if (ident == Id::fabs) builtin = BUILTINfabs; + else if (ident == Id::expm1) + builtin = BUILTINexpm1; + else if (ident == Id::exp2) + builtin = BUILTINexp2; //printf("builtin = %d\n", builtin); } // if float or double versions @@ -77,19 +87,83 @@ enum BUILTIN FuncDeclaration::isBuiltin() if (ident == Id::_sqrt) builtin = BUILTINsqrt; } + else if (strcmp(type->deco, FrealrealZreal) == 0) + { + if (ident == Id::atan2) + builtin = BUILTINatan2; + else if (ident == Id::yl2x) + builtin = BUILTINyl2x; + else if (ident == Id::yl2xp1) + builtin = BUILTINyl2xp1; + } + else if (strcmp(type->deco, FrealZlong) == 0 && ident == Id::rndtol) + builtin = BUILTINrndtol; + } + if (parent->ident == Id::bitop && + parent->parent && parent->parent->ident == Id::core && + !parent->parent->parent) + { + //printf("deco = %s\n", type->deco); + if (strcmp(type->deco, FuintZint) == 0 || strcmp(type->deco, FulongZint) == 0) + { + if (ident == Id::bsf) + builtin = BUILTINbsf; + else if (ident == Id::bsr) + builtin = BUILTINbsr; + } + else if (strcmp(type->deco, FuintZuint) == 0) + { + if (ident == Id::bswap) + builtin = BUILTINbswap; + } } } } return builtin; } +int eval_bsf(uinteger_t n) +{ + n = (n ^ (n - 1)) >> 1; // convert trailing 0s to 1, and zero rest + int k = 0; + while( n ) + { ++k; + n >>=1; + } + return k; +} + +int eval_bsr(uinteger_t n) +{ int k= 0; + while(n>>=1) + { + ++k; + } + return k; +} + +uinteger_t eval_bswap(Expression *arg0) +{ uinteger_t n = arg0->toInteger(); + #define BYTEMASK 0x00FF00FF00FF00FFLL + #define SHORTMASK 0x0000FFFF0000FFFFLL + #define INTMASK 0x0000FFFF0000FFFFLL + // swap adjacent ubytes + n = ((n >> 8 ) & BYTEMASK) | ((n & BYTEMASK) << 8 ); + // swap adjacent ushorts + n = ((n >> 16) & SHORTMASK) | ((n & SHORTMASK) << 16); + TY ty = arg0->type->toBasetype()->ty; + // If 64 bits, we need to swap high and low uints + if (ty == Tint64 || ty == Tuns64) + n = ((n >> 32) & INTMASK) | ((n & INTMASK) << 32); + return n; +} /************************************** * Evaluate builtin function. * Return result; NULL if cannot evaluate it. */ -Expression *eval_builtin(enum BUILTIN builtin, Expressions *arguments) +Expression *eval_builtin(Loc loc, enum BUILTIN builtin, Expressions *arguments) { assert(arguments && arguments->dim); Expression *arg0 = arguments->tdata()[0]; @@ -120,6 +194,39 @@ Expression *eval_builtin(enum BUILTIN builtin, Expressions *arguments) if (arg0->op == TOKfloat64) e = new RealExp(0, fabsl(arg0->toReal()), arg0->type); break; + // These math intrinsics are not yet implemented + case BUILTINatan2: + break; + case BUILTINrndtol: + break; + case BUILTINexpm1: + break; + case BUILTINexp2: + break; + case BUILTINyl2x: + break; + case BUILTINyl2xp1: + break; + case BUILTINbsf: + if (arg0->op == TOKint64) + { if (arg0->toInteger()==0) + error(loc, "bsf(0) is undefined"); + else + e = new IntegerExp(loc, eval_bsf(arg0->toInteger()), Type::tint32); + } + break; + case BUILTINbsr: + if (arg0->op == TOKint64) + { if (arg0->toInteger()==0) + error(loc, "bsr(0) is undefined"); + else + e = new IntegerExp(loc, eval_bsr(arg0->toInteger()), Type::tint32); + } + break; + case BUILTINbswap: + if (arg0->op == TOKint64) + e = new IntegerExp(loc, eval_bswap(arg0), arg0->type); + break; } return e; } diff --git a/dmd2/cast.c b/dmd2/cast.c index 30af0fb5c6..6e242d7ca6 100644 --- a/dmd2/cast.c +++ b/dmd2/cast.c @@ -143,6 +143,8 @@ MATCH Expression::implicitConvTo(Type *t) toChars(), type->toChars(), t->toChars()); #endif //static int nest; if (++nest == 10) halt(); + if (t == Type::terror) + return MATCHnomatch; if (!type) { error("%s is not an expression", toChars()); type = Type::terror; @@ -543,6 +545,10 @@ MATCH ArrayLiteralExp::implicitConvTo(Type *t) if (result == MATCHnomatch) break; // no need to check for worse } + + if (!result) + result = type->implicitConvTo(t); + return result; } else @@ -590,7 +596,7 @@ MATCH CallExp::implicitConvTo(Type *t) /* Allow the result of strongly pure functions to * convert to immutable */ - if (f && f->isPure() == PUREstrong) + if (f && f->isPure() == PUREstrong && !f->type->hasWild()) return type->invariantOf()->implicitConvTo(t); return MATCHnomatch; @@ -1720,7 +1726,8 @@ Lagain: if (t1 == t2) { } - else if (t1->ty == Tpointer && t2->ty == Tpointer) + else if ((t1->ty == Tpointer && t2->ty == Tpointer) || + (t1->ty == Tdelegate && t2->ty == Tdelegate)) { // Bring pointers to compatible type Type *t1n = t1->nextOf(); @@ -1760,7 +1767,14 @@ Lagain: else d->trust = TRUSTtrusted; - Type *tx = d->pointerTo(); + Type *tx = NULL; + if (t1->ty == Tdelegate) + { + tx = new TypeDelegate(d); + tx = tx->merge(); + } + else + tx = d->pointerTo(); if (t1->implicitConvTo(tx) && t2->implicitConvTo(tx)) { diff --git a/dmd2/class.c b/dmd2/class.c index d662589407..bb17831785 100644 --- a/dmd2/class.c +++ b/dmd2/class.c @@ -970,7 +970,10 @@ int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) { FuncDeclaration *fdstart = s->isFuncDeclaration(); //printf("%s fdstart = %p\n", s->kind(), fdstart); - return !overloadApply(fdstart, &isf, fd); + if (overloadApply(fdstart, &isf, fd)) + return 0; + + return !fd->parent->isTemplateMixin(); } } #endif @@ -983,6 +986,8 @@ int ClassDeclaration::isFuncHidden(FuncDeclaration *fd) FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) { //printf("ClassDeclaration::findFunc(%s, %s) %s\n", ident->toChars(), tf->toChars(), toChars()); + FuncDeclaration *fdmatch = NULL; + FuncDeclaration *fdambig = NULL; ClassDeclaration *cd = this; Dsymbols *vtbl = &cd->vtbl; @@ -996,11 +1001,43 @@ FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) //printf("\t[%d] = %s\n", i, fd->toChars()); if (ident == fd->ident && - //tf->equals(fd->type) - fd->type->covariant(tf) == 1 - ) - { //printf("\t\tfound\n"); - return fd; + fd->type->covariant(tf) == 1) + { //printf("fd->parent->isClassDeclaration() = %p", fd->parent->isClassDeclaration()); + if (!fdmatch) + goto Lfd; + + { + // Function type matcing: exact > covariant + int m1 = tf->equals(fd ->type) ? MATCHexact : MATCHnomatch; + int m2 = tf->equals(fdmatch->type) ? MATCHexact : MATCHnomatch; + if (m1 > m2) + goto Lfd; + else if (m1 < m2) + goto Lfdmatch; + } + + { + // The way of definition: non-mixin > mixin + int m1 = fd ->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; + int m2 = fdmatch->parent->isClassDeclaration() ? MATCHexact : MATCHnomatch; + if (m1 > m2) + goto Lfd; + else if (m1 < m2) + goto Lfdmatch; + } + + Lambig: + fdambig = fd; + //printf("Lambig fdambig = %s %s [%s]\n", fdambig->toChars(), fdambig->type->toChars(), fdambig->loc.toChars()); + continue; + + Lfd: + fdmatch = fd, fdambig = NULL; + //printf("Lfd fdmatch = %s %s [%s]\n", fdmatch->toChars(), fdmatch->type->toChars(), fdmatch->loc.toChars()); + continue; + + Lfdmatch: + continue; } //else printf("\t\t%d\n", fd->type->covariant(tf)); } @@ -1010,7 +1047,9 @@ FuncDeclaration *ClassDeclaration::findFunc(Identifier *ident, TypeFunction *tf) cd = cd->baseClass; } - return NULL; + if (fdambig) + error("ambiguous virtual function %s", fdambig->toChars()); + return fdmatch; } void ClassDeclaration::interfaceSemantic(Scope *sc) @@ -1302,15 +1341,15 @@ void InterfaceDeclaration::semantic(Scope *sc) } sc = sc->push(this); - sc->stc &= ~(STCfinal | STCauto | STCscope | STCstatic | - STCabstract | STCdeprecated | STC_TYPECTOR | STCtls | STCgshared); - sc->stc |= storage_class & STC_TYPECTOR; + sc->stc &= STCsafe | STCtrusted | STCsystem; sc->parent = this; if (isCOMinterface()) sc->linkage = LINKwindows; else if (isCPPinterface()) sc->linkage = LINKcpp; sc->structalign = 8; + sc->protection = PROTpublic; + sc->explicitProtection = 0; structalign = sc->structalign; sc->offset = PTRSIZE * 2; inuse++; diff --git a/dmd2/clone.c b/dmd2/clone.c index 6b509025ba..24fb1e61b3 100644 --- a/dmd2/clone.c +++ b/dmd2/clone.c @@ -21,6 +21,7 @@ #include "expression.h" #include "statement.h" #include "init.h" +#include "template.h" /******************************************* @@ -71,48 +72,6 @@ Lneed: #undef X } -/******************************************* - * We need an opEquals for the struct if - * any fields has an opEquals. - * Generate one if a user-specified one does not exist. - */ - -int StructDeclaration::needOpEquals() -{ -#define X 0 - if (X) printf("StructDeclaration::needOpEquals() %s\n", toChars()); - - /* If any of the fields has an opEquals, then we - * need it too. - */ - for (size_t i = 0; i < fields.dim; i++) - { - Dsymbol *s = fields.tdata()[i]; - VarDeclaration *v = s->isVarDeclaration(); - assert(v && v->storage_class & STCfield); - if (v->storage_class & STCref) - continue; - Type *tv = v->type->toBasetype(); - while (tv->ty == Tsarray) - { TypeSArray *ta = (TypeSArray *)tv; - tv = tv->nextOf()->toBasetype(); - } - if (tv->ty == Tstruct) - { TypeStruct *ts = (TypeStruct *)tv; - StructDeclaration *sd = ts->sym; - if (sd->eq) - goto Lneed; - } - } - if (X) printf("\tdontneed\n"); - return 0; - -Lneed: - if (X) printf("\tneed\n"); - return 1; -#undef X -} - /****************************************** * Build opAssign for struct. * S* opAssign(S s) { ... } @@ -232,33 +191,99 @@ FuncDeclaration *StructDeclaration::buildOpAssign(Scope *sc) return fop; } +/******************************************* + * We need an opEquals for the struct if + * any fields has an opEquals. + * Generate one if a user-specified one does not exist. + */ + +int StructDeclaration::needOpEquals() +{ +#define X 0 + if (X) printf("StructDeclaration::needOpEquals() %s\n", toChars()); + + if (hasIdentityEquals) + goto Lneed; + + /* If any of the fields has an opEquals, then we + * need it too. + */ + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *v = s->isVarDeclaration(); + assert(v && v->storage_class & STCfield); + if (v->storage_class & STCref) + continue; + Type *tv = v->type->toBasetype(); + while (tv->ty == Tsarray) + { TypeSArray *ta = (TypeSArray *)tv; + tv = tv->nextOf()->toBasetype(); + } + if (tv->ty == Tstruct) + { TypeStruct *ts = (TypeStruct *)tv; + StructDeclaration *sd = ts->sym; + if (sd->needOpEquals()) + goto Lneed; + } + } + if (X) printf("\tdontneed\n"); + return 0; + +Lneed: + if (X) printf("\tneed\n"); + return 1; +#undef X +} + /****************************************** * Build opEquals for struct. - * const bool opEquals(const ref S s) { ... } + * const bool opEquals(const S s) { ... } */ FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) { + Dsymbol *eq = search_function(this, Id::eq); + if (eq) + { + for (size_t i = 0; i <= 1; i++) + { + Expression *e = + i == 0 ? new NullExp(loc, type->constOf()) // dummy rvalue + : type->constOf()->defaultInit(); // dummy lvalue + Expressions *arguments = new Expressions(); + arguments->push(e); + + // check identity opEquals exists + FuncDeclaration *fd = eq->isFuncDeclaration(); + if (fd) + { fd = fd->overloadResolve(loc, e, arguments, 1); + if (fd && !(fd->storage_class & STCdisable)) + return fd; + } + + TemplateDeclaration *td = eq->isTemplateDeclaration(); + if (td) + { fd = td->deduceFunctionTemplate(sc, loc, NULL, e, arguments, 1); + if (fd && !(fd->storage_class & STCdisable)) + return fd; + } + } + return NULL; + } + if (!needOpEquals()) return NULL; + //printf("StructDeclaration::buildOpEquals() %s\n", toChars()); - Loc loc = this->loc; Parameters *parameters = new Parameters; -#if STRUCTTHISREF - // bool opEquals(ref const T) const; - Parameter *param = new Parameter(STCref, type->constOf(), Id::p, NULL); -#else - // bool opEquals(const T*) const; - Parameter *param = new Parameter(STCin, type->pointerTo(), Id::p, NULL); -#endif + parameters->push(new Parameter(STCin, type, Id::p, NULL)); + TypeFunction *tf = new TypeFunction(parameters, Type::tbool, 0, LINKd); + tf->mod = MODconst; + tf = (TypeFunction *)tf->semantic(loc, sc); - parameters->push(param); - TypeFunction *ftype = new TypeFunction(parameters, Type::tbool, 0, LINKd); - ftype->mod = MODconst; - ftype = (TypeFunction *)ftype->semantic(loc, sc); - - FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::eq, STCundefined, ftype); + FuncDeclaration *fop = new FuncDeclaration(loc, 0, Id::eq, STCundefined, tf); Expression *e = NULL; /* Do memberwise compare @@ -300,16 +325,90 @@ FuncDeclaration *StructDeclaration::buildOpEquals(Scope *sc) return fop; } +/****************************************** + * Build __xopEquals for TypeInfo_Struct + * bool __xopEquals(in void* p, in void* q) { ... } + */ + +FuncDeclaration *StructDeclaration::buildXopEquals(Scope *sc) +{ + if (!search_function(this, Id::eq)) + return NULL; + + /* static bool__xopEquals(in void* p, in void* q) { + * return ( *cast(const S*)(p) ).opEquals( *cast(const S*)(q) ); + * } + */ + + Parameters *parameters = new Parameters; + 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); + + Identifier *id = Lexer::idPool("__xopEquals"); + FuncDeclaration *fop = new FuncDeclaration(loc, 0, id, STCstatic, tf); + + Expression *e = new CallExp(0, + new DotIdExp(0, + new PtrExp(0, new CastExp(0, + new IdentifierExp(0, Id::p), type->pointerTo()->constOf())), + Id::eq), + new PtrExp(0, new CastExp(0, + new IdentifierExp(0, Id::q), type->pointerTo()->constOf()))); + + fop->fbody = new ReturnStatement(loc, e); + + size_t index = members->dim; + members->push(fop); + + sc = sc->push(); + sc->stc = 0; + sc->linkage = LINKd; + + unsigned errors = global.startGagging(); + fop->semantic(sc); + if (errors == global.gaggedErrors) + { fop->semantic2(sc); + if (errors == global.gaggedErrors) + { fop->semantic3(sc); + if (errors == global.gaggedErrors) + fop->addMember(sc, this, 1); + } + } + if (global.endGagging(errors)) // if errors happened + { + members->remove(index); + + if (!xerreq) + { + Expression *e = new IdentifierExp(loc, Id::empty); + e = new DotIdExp(loc, e, Id::object); + e = new DotIdExp(loc, e, Lexer::idPool("_xopEquals")); + e = e->semantic(sc); + Dsymbol *s = getDsymbol(e); + FuncDeclaration *fd = s->isFuncDeclaration(); + + xerreq = fd; + } + fop = xerreq; + } + + sc->pop(); + + return fop; +} + /******************************************* * Build copy constructor for struct. * Copy constructors are compiler generated only, and are only * callable from the compiler. They are not user accessible. * A copy constructor is: - * void cpctpr(ref S s) + * void cpctpr(ref const S s) const * { - * *this = s; - * this.postBlit(); + * (*cast(S*)&this) = *cast(S*)s; + * (*cast(S*)&this).postBlit(); * } * This is done so: * - postBlit() never sees uninitialized data @@ -329,10 +428,11 @@ FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) { //printf("generating cpctor\n"); - Parameter *param = new Parameter(STCref, type, Id::p, NULL); + Parameter *param = new Parameter(STCref, type->constOf(), Id::p, NULL); Parameters *fparams = new Parameters; fparams->push(param); Type *ftype = new TypeFunction(fparams, Type::tvoid, FALSE, LINKd); + ftype->mod = MODconst; fcp = new FuncDeclaration(loc, 0, Id::cpctor, STCundefined, ftype); fcp->storage_class |= postblit->storage_class & STCdisable; @@ -344,12 +444,20 @@ FuncDeclaration *StructDeclaration::buildCpCtor(Scope *sc) #if !STRUCTTHISREF e = new PtrExp(0, e); #endif - AssignExp *ea = new AssignExp(0, e, new IdentifierExp(0, Id::p)); + AssignExp *ea = new AssignExp(0, + new PtrExp(0, new CastExp(0, new AddrExp(0, e), type->mutableOf()->pointerTo())), + new PtrExp(0, new CastExp(0, new AddrExp(0, new IdentifierExp(0, Id::p)), type->mutableOf()->pointerTo())) + ); ea->op = TOKblit; Statement *s = new ExpStatement(0, ea); // Build postBlit(); - e = new VarExp(0, postblit, 0); + e = new ThisExp(0); +#if !STRUCTTHISREF + e = new PtrExp(0, e); +#endif + e = new PtrExp(0, new CastExp(0, new AddrExp(0, e), type->mutableOf()->pointerTo())); + e = new DotVarExp(0, e, postblit, 0); e = new CallExp(0, e); s = new CompoundStatement(0, s, new ExpStatement(0, e)); diff --git a/dmd2/constfold.c b/dmd2/constfold.c index 65ee10b826..dd596a9c6b 100644 --- a/dmd2/constfold.c +++ b/dmd2/constfold.c @@ -558,6 +558,83 @@ Expression *Mod(Type *type, Expression *e1, Expression *e2) return e; } +Expression *Pow(Type *type, Expression *e1, Expression *e2) +{ Expression *e; + Loc loc = e1->loc; + + // Handle integer power operations. + if (e2->type->isintegral()) + { + Expression * r; + Expression * v; + dinteger_t n = e2->toInteger(); + bool neg; + + if (!e2->type->isunsigned() && (sinteger_t)n < 0) + { + if (e1->type->isintegral()) + return EXP_CANT_INTERPRET; + + // Don't worry about overflow, from now on n is unsigned. + neg = true; + n = -n; + } + else + neg = false; + + if (e1->type->isfloating()) + { + r = new RealExp(loc, e1->toReal(), e1->type); + v = new RealExp(loc, 1.0, e1->type); + } + else + { + r = new RealExp(loc, e1->toReal(), Type::tfloat64); + v = new RealExp(loc, 1.0, Type::tfloat64); + } + + while (n != 0) + { + if (n & 1) + v = Mul(v->type, v, r); + n >>= 1; + r = Mul(r->type, r, r); + } + + if (neg) + v = Div(v->type, new RealExp(loc, 1.0, v->type), v); + + if (type->isintegral()) + e = new IntegerExp(loc, v->toInteger(), type); + else + e = new RealExp(loc, v->toReal(), type); + } + else if (e2->type->isfloating()) + { + // x ^^ y for x < 0 and y not an integer is not defined + if (e1->toReal() < 0.0) + { + e = new RealExp(loc, Port::nan, type); + } + else if (e2->toReal() == 0.5) + { + // Special case: call sqrt directly. + Expressions args; + args.setDim(1); + args.tdata()[0] = e1; + e = eval_builtin(loc, BUILTINsqrt, &args); + if (!e) + e = EXP_CANT_INTERPRET; + } + else + e = EXP_CANT_INTERPRET; + } + else + e = EXP_CANT_INTERPRET; + + return e; +} + Expression *Shl(Type *type, Expression *e1, Expression *e2) { Expression *e; Loc loc = e1->loc; @@ -581,6 +658,7 @@ Expression *Shr(Type *type, Expression *e1, Expression *e2) break; case Tuns8: + case Tchar: value = (d_uns8)(value) >> count; break; @@ -589,6 +667,7 @@ Expression *Shr(Type *type, Expression *e1, Expression *e2) break; case Tuns16: + case Twchar: value = (d_uns16)(value) >> count; break; @@ -597,6 +676,7 @@ Expression *Shr(Type *type, Expression *e1, Expression *e2) break; case Tuns32: + case Tdchar: value = (d_uns32)(value) >> count; break; @@ -630,18 +710,21 @@ Expression *Ushr(Type *type, Expression *e1, Expression *e2) { case Tint8: case Tuns8: + case Tchar: // Possible only with >>>=. >>> always gets promoted to int. value = (value & 0xFF) >> count; break; case Tint16: case Tuns16: + case Twchar: // Possible only with >>>=. >>> always gets promoted to int. value = (value & 0xFFFF) >> count; break; case Tint32: case Tuns32: + case Tdchar: value = (value & 0xFFFFFFFF) >> count; break; @@ -1270,7 +1353,10 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) uinteger_t i = e2->toInteger(); if (i >= es1->len) + { e1->error("string index %ju is out of bounds [0 .. %zu]", i, es1->len); + e = new ErrorExp(); + } else { e = new IntegerExp(loc, es1->charAt(i), type); @@ -1282,7 +1368,9 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) uinteger_t i = e2->toInteger(); if (i >= length) - { e1->error("array index %ju is out of bounds %s[0 .. %ju]", i, e1->toChars(), length); + { + e1->error("array index %ju is out of bounds %s[0 .. %ju]", i, e1->toChars(), length); + e = new ErrorExp(); } else if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; @@ -1299,7 +1387,9 @@ Expression *Index(Type *type, Expression *e1, Expression *e2) if (e1->op == TOKarrayliteral) { ArrayLiteralExp *ale = (ArrayLiteralExp *)e1; if (i >= ale->elements->dim) - { e1->error("array index %ju is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); + { + e1->error("array index %ju is out of bounds %s[0 .. %u]", i, e1->toChars(), ale->elements->dim); + e = new ErrorExp(); } else { e = ale->elements->tdata()[i]; @@ -1353,7 +1443,10 @@ Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) uinteger_t iupr = upr->toInteger(); if (iupr > es1->len || ilwr > iupr) + { e1->error("string slice [%ju .. %ju] is out of bounds", ilwr, iupr); + e = new ErrorExp(); + } else { void *s; @@ -1380,7 +1473,10 @@ Expression *Slice(Type *type, Expression *e1, Expression *lwr, Expression *upr) uinteger_t iupr = upr->toInteger(); if (iupr > es1->elements->dim || ilwr > iupr) + { e1->error("array slice [%ju .. %ju] is out of bounds", ilwr, iupr); + e = new ErrorExp(); + } else { Expressions *elements = new Expressions(); diff --git a/dmd2/declaration.c b/dmd2/declaration.c index 2f04f8d292..4e47867a2e 100644 --- a/dmd2/declaration.c +++ b/dmd2/declaration.c @@ -107,6 +107,8 @@ void Declaration::checkModify(Loc loc, Scope *sc, Type *t) p = "const"; else if (isImmutable()) p = "immutable"; + else if (isWild()) + p = "inout"; else if (storage_class & STCmanifest) p = "enum"; else if (!t->isAssignable()) @@ -291,14 +293,31 @@ void TypedefDeclaration::semantic(Scope *sc) //printf("TypedefDeclaration::semantic(%s) sem = %d\n", toChars(), sem); if (sem == SemanticStart) { sem = SemanticIn; + parent = sc->parent; + int errors = global.errors; + Type *savedbasetype = basetype; basetype = basetype->semantic(loc, sc); + if (errors != global.errors) + { + basetype = savedbasetype; + sem = SemanticStart; + return; + } sem = SemanticDone; #if DMDV2 type = type->addStorageClass(storage_class); #endif + Type *savedtype = type; type = type->semantic(loc, sc); if (sc->parent->isFuncDeclaration() && init) semantic2(sc); + if (errors != global.errors) + { + basetype = savedbasetype; + type = savedtype; + sem = SemanticStart; + return; + } storage_class |= sc->stc & STCdeprecated; } else if (sem == SemanticIn) @@ -314,7 +333,14 @@ void TypedefDeclaration::semantic2(Scope *sc) { sem = Semantic2Done; if (init) { + Initializer *savedinit = init; + int errors = global.errors; init = init->semantic(sc, basetype, WANTinterpret); + if (errors != global.errors) + { + init = savedinit; + return; + } ExpInitializer *ie = init->isExpInitializer(); if (ie) @@ -440,6 +466,9 @@ void AliasDeclaration::semantic(Scope *sc) // type. If it is a symbol, then aliassym is set and type is NULL - // toAlias() will return aliasssym. + int errors = global.errors; + Type *savedtype = type; + Dsymbol *s; Type *t; Expression *e; @@ -486,12 +515,15 @@ void AliasDeclaration::semantic(Scope *sc) } else if (t) { - type = t; + type = t->semantic(loc, sc); //printf("\talias resolved to type %s\n", type->toChars()); } if (overnext) ScopeDsymbol::multiplyDefined(0, this, overnext); this->inSemantic = 0; + + if (errors != global.errors) + type = savedtype; return; L2: @@ -505,6 +537,7 @@ void AliasDeclaration::semantic(Scope *sc) } else { + Dsymbol *savedovernext = overnext; FuncDeclaration *f = s->toAlias()->isFuncDeclaration(); if (f) { @@ -528,6 +561,14 @@ void AliasDeclaration::semantic(Scope *sc) assert(global.errors); s = NULL; } + if (errors != global.errors) + { + type = savedtype; + overnext = savedovernext; + aliassym = NULL; + inSemantic = 0; + return; + } } //printf("setting aliassym %s to %s %s\n", toChars(), s->kind(), s->toChars()); aliassym = s; @@ -585,7 +626,7 @@ Dsymbol *AliasDeclaration::toAlias() //static int count; if (++count == 10) *(char*)0=0; if (inSemantic) { error("recursive alias declaration"); - aliassym = new TypedefDeclaration(loc, ident, Type::terror, NULL); + aliassym = new AliasDeclaration(loc, ident, Type::terror); type = Type::terror; } else if (!aliassym && scope) @@ -988,6 +1029,8 @@ Lnomatch: } VarDeclaration *v = new VarDeclaration(loc, arg->type, id, ti); + if (arg->storageClass & STCparameter) + v->storage_class |= arg->storageClass; //printf("declaring field %s of type %s\n", v->toChars(), v->type->toChars()); v->semantic(sc); @@ -1110,11 +1153,19 @@ Lnomatch: error("only parameters or foreach declarations can be ref"); } - if ((storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest) || - isDataseg()) && - type->hasWild()) + if (type->hasWild() && + !(type->ty == Tpointer && type->nextOf()->ty == Tfunction || type->ty == Tdelegate)) { - error("only fields, parameters or stack based variables can be inout"); + if (storage_class & (STCstatic | STCextern | STCtls | STCgshared | STCmanifest | STCfield) || + isDataseg() + ) + { + error("only parameters or stack based variables can be inout"); + } + if (sc->func && !sc->func->type->hasWild()) + { + error("inout variables can only be declared inside inout functions"); + } } if (!(storage_class & (STCctfe | STCref)) && tb->ty == Tstruct && @@ -1401,9 +1452,7 @@ Lnomatch: if (!global.errors && !inferred) { - unsigned errors = global.errors; - global.gag++; - //printf("+gag\n"); + unsigned errors = global.startGagging(); Expression *e; Initializer *i2 = init; inuse++; @@ -1462,12 +1511,8 @@ Lnomatch: i2 = i2->semantic(sc, type, WANTinterpret); } inuse--; - global.gag--; - //printf("-gag\n"); - if (errors != global.errors) // if errors happened + if (global.endGagging(errors)) // if errors happened { - if (global.gag == 0) - global.errors = errors; // act as if nothing happened #if DMDV2 /* Save scope for later use, to try again */ diff --git a/dmd2/declaration.h b/dmd2/declaration.h index c2d1434cb7..ddf0758a48 100644 --- a/dmd2/declaration.h +++ b/dmd2/declaration.h @@ -87,6 +87,7 @@ enum PURE; // but not typed as "shared" #define STCwild 0x80000000LL // for "wild" type constructor #define STC_TYPECTOR (STCconst | STCimmutable | STCshared | STCwild) +#define STC_FUNCATTR (STCref | STCnothrow | STCpure | STCproperty | STCsafe | STCtrusted | STCsystem) #define STCproperty 0x100000000LL #define STCsafe 0x200000000LL @@ -154,6 +155,7 @@ struct Declaration : Dsymbol int isAbstract() { return storage_class & STCabstract; } int isConst() { return storage_class & STCconst; } int isImmutable() { return storage_class & STCimmutable; } + int isWild() { return storage_class & STCwild; } int isAuto() { return storage_class & STCauto; } int isScope() { return storage_class & STCscope; } int isSynchronized() { return storage_class & STCsynchronized; } @@ -680,9 +682,18 @@ enum BUILTIN BUILTINtan, // std.math.tan BUILTINsqrt, // std.math.sqrt BUILTINfabs, // std.math.fabs + BUILTINatan2, // std.math.atan2 + BUILTINrndtol, // std.math.rndtol + BUILTINexpm1, // std.math.expm1 + BUILTINexp2, // std.math.exp2 + BUILTINyl2x, // std.math.yl2x + BUILTINyl2xp1, // std.math.yl2xp1 + BUILTINbsr, // core.bitop.bsr + BUILTINbsf, // core.bitop.bsf + BUILTINbswap, // core.bitop.bswap }; -Expression *eval_builtin(enum BUILTIN builtin, Expressions *arguments); +Expression *eval_builtin(Loc loc, enum BUILTIN builtin, Expressions *arguments); #else enum BUILTIN { }; @@ -729,6 +740,7 @@ struct FuncDeclaration : Declaration // of the 'introducing' function // this one is overriding int inferRetType; // !=0 if return type is to be inferred + StorageClass storage_class2; // storage class for template onemember's // Things that should really go into Scope int hasReturnExp; // 1 if there's a return exp; statement @@ -907,7 +919,6 @@ struct CtorDeclaration : FuncDeclaration CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type); Dsymbol *syntaxCopy(Dsymbol *); void semantic(Scope *sc); - void toCBuffer(OutBuffer *buf, HdrGenState *hgs); const char *kind(); char *toChars(); int isVirtual(); diff --git a/dmd2/delegatize.c b/dmd2/delegatize.c index 2f174159b3..b805ea56e4 100644 --- a/dmd2/delegatize.c +++ b/dmd2/delegatize.c @@ -44,7 +44,11 @@ Expression *Expression::toDelegate(Scope *sc, Type *t) #else e = this->syntaxCopy(); #endif - Statement *s = new ReturnStatement(loc, e); + Statement *s; + if (t->ty == Tvoid) + s = new ExpStatement(loc, e); + else + s = new ReturnStatement(loc, e); fld->fbody = s; e = new FuncExp(loc, fld); e = e->semantic(sc); diff --git a/dmd2/dsymbol.c b/dmd2/dsymbol.c index ef664030c5..8e147c17f1 100644 --- a/dmd2/dsymbol.c +++ b/dmd2/dsymbol.c @@ -553,35 +553,28 @@ int Dsymbol::addMember(Scope *sc, ScopeDsymbol *sd, int memnum) void Dsymbol::error(const char *format, ...) { //printf("Dsymbol::error()\n"); - if (!global.gag) + if (!loc.filename) // avoid bug 5861. { - char *p = locToChars(); + Module *m = getModule(); - if (*p) - fprintf(stdmsg, "%s: ", p); - mem.free(p); - - fprintf(stdmsg, "Error: "); - if (isAnonymous()) - fprintf(stdmsg, "%s ", kind()); - else - fprintf(stdmsg, "%s %s ", kind(), toPrettyChars()); - - va_list ap; - va_start(ap, format); - vfprintf(stdmsg, format, ap); - va_end(ap); - - fprintf(stdmsg, "\n"); - fflush(stdmsg); -//halt(); + if (m && m->srcfile) + loc.filename = m->srcfile->toChars(); } - global.errors++; - - //fatal(); + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); } void Dsymbol::error(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); +} + +void Dsymbol::verror(Loc loc, const char *format, va_list ap) { if (!global.gag) { @@ -596,15 +589,16 @@ void Dsymbol::error(Loc loc, const char *format, ...) fprintf(stdmsg, "Error: "); fprintf(stdmsg, "%s %s ", kind(), toPrettyChars()); - va_list ap; - va_start(ap, format); vfprintf(stdmsg, format, ap); - va_end(ap); fprintf(stdmsg, "\n"); fflush(stdmsg); halt(); } + else + { + global.gaggedErrors++; + } global.errors++; @@ -721,7 +715,7 @@ Dsymbols *Dsymbol::arraySyntaxCopy(Dsymbols *a) Dsymbols *b = NULL; if (a) { - b = (Dsymbols *)a->copy(); + b = a->copy(); for (size_t i = 0; i < b->dim; i++) { Dsymbol *s = (*b)[i]; @@ -1032,11 +1026,19 @@ size_t ScopeDsymbol::dim(Dsymbols *members) for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = (*members)[i]; AttribDeclaration *a = s->isAttribDeclaration(); + TemplateMixin *tm = s->isTemplateMixin(); + TemplateInstance *ti = s->isTemplateInstance(); if (a) { n += dim(a->decl); } + else if (tm) + { + n += dim(tm->members); + } + else if (ti) + ; else n++; } @@ -1262,7 +1264,9 @@ Dsymbol *ArrayScopeSymbol::search(Loc loc, Identifier *ident, int flags) * or a variable (in which case an expression is created in * toir.c). */ - v->init = new VoidInitializer(0); + VoidInitializer *e = new VoidInitializer(0); + e->type = Type::tsize_t; + v->init = e; } *pvar = v; } @@ -1279,6 +1283,7 @@ DsymbolTable::DsymbolTable() { #if STRINGTABLE tab = new StringTable; + tab->init(); #else tab = NULL; #endif diff --git a/dmd2/dsymbol.h b/dmd2/dsymbol.h index 930cae6db4..27cc2629b0 100644 --- a/dmd2/dsymbol.h +++ b/dmd2/dsymbol.h @@ -1,6 +1,6 @@ // Compiler implementation of the D programming language -// Copyright (c) 1999-2010 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -151,6 +151,7 @@ struct Dsymbol : Object int isAnonymous(); void error(Loc loc, const char *format, ...) IS_PRINTF(3); void error(const char *format, ...) IS_PRINTF(2); + void verror(Loc loc, const char *format, va_list ap); void checkDeprecated(Loc loc, Scope *sc); Module *getModule(); // module where declared Module *getCompilationModule(); // possibly different for templates @@ -360,11 +361,7 @@ struct OverloadSet : Dsymbol struct DsymbolTable : Object { -#if STRINGTABLE - StringTable *tab; -#else AA *tab; -#endif DsymbolTable(); ~DsymbolTable(); diff --git a/dmd2/expression.c b/dmd2/expression.c index 050923149b..656c7431aa 100644 --- a/dmd2/expression.c +++ b/dmd2/expression.c @@ -649,6 +649,23 @@ Expression *callCpCtor(Loc loc, Scope *sc, Expression *e, int noscope) } #endif +// Check if this function is a member of a template which has only been +// instantiated speculatively, eg from inside is(typeof()). +// Return the speculative template instance it is part of, +// or NULL if not speculative. +TemplateInstance *isSpeculativeFunction(FuncDeclaration *fd) +{ + Dsymbol * par = fd->parent; + while (par) + { + TemplateInstance *ti = par->isTemplateInstance(); + if (ti && ti->speculative) + return ti; + par = par->toParent(); + } + return NULL; +} + /**************************************** * Now that we know the exact type of the function we're calling, * the arguments[] need to be adjusted: @@ -662,7 +679,7 @@ Expression *callCpCtor(Loc loc, Scope *sc, Expression *e, int noscope) */ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, - Expressions *arguments, FuncDeclaration *fd) + Expression *ethis, Expressions *arguments, FuncDeclaration *fd) { //printf("functionParameters()\n"); assert(arguments); @@ -675,7 +692,15 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, // If inferring return type, and semantic3() needs to be run if not already run if (!tf->next && fd->inferRetType) + { + TemplateInstance *spec = isSpeculativeFunction(fd); + int olderrs = global.errors; fd->semantic3(fd->scope); + // 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) @@ -718,7 +743,9 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, //printf("\t\tvarargs == 2, p->type = '%s'\n", p->type->toChars()); if (arg->implicitConvTo(p->type)) { - if (nargs != nparams) + if (p->type->nextOf() && arg->implicitConvTo(p->type->nextOf())) + goto L2; + else if (nargs != nparams) { error(loc, "expected %zu function arguments, not %zu", nparams, nargs); return tf->next; } @@ -801,7 +828,16 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, L1: if (!(p->storageClass & STClazy && p->type->ty == Tvoid)) { - if (p->type != arg->type) + if (p->type->hasWild()) + { unsigned mod = p->type->wildMatch(arg->type); + if (mod) + { + wildmatch |= mod; + arg = arg->implicitCastTo(sc, p->type->substWildTo(mod)); + arg = arg->optimize(WANTvalue); + } + } + else if (p->type != arg->type) { //printf("arg->type = %s, p->type = %s\n", arg->type->toChars(), p->type->toChars()); if (arg->op == TOKtype) @@ -809,20 +845,6 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, arg = new ErrorExp(); goto L3; } - if (p->type->isWild() && tf->next->isWild()) - { Type *t = p->type; - MATCH m = arg->implicitConvTo(t); - if (m == MATCHnomatch) - { t = t->constOf(); - m = arg->implicitConvTo(t); - if (m == MATCHnomatch) - { t = t->sharedConstOf(); - m = arg->implicitConvTo(t); - } - wildmatch |= p->type->wildMatch(arg->type); - } - arg = arg->implicitCastTo(sc, t); - } else arg = arg->implicitCastTo(sc, p->type); arg = arg->optimize(WANTvalue); @@ -989,6 +1011,19 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, break; } + if (ethis && tf->isWild()) + { + Type *tthis = ethis->type; + if (tthis->isWild()) + wildmatch |= MODwild; + else if (tthis->isConst()) + wildmatch |= MODconst; + else if (tthis->isImmutable()) + wildmatch |= MODimmutable; + else + wildmatch |= MODmutable; + } + #if !IN_LLVM // If D linkage and variadic, add _arguments[] as first argument if (tf->linkage == LINKd && tf->varargs == 1) @@ -1003,15 +1038,16 @@ Type *functionParameters(Loc loc, Scope *sc, TypeFunction *tf, if (wildmatch) { /* Adjust function return type based on wildmatch */ - //printf("wildmatch = x%x\n", wildmatch); - assert(tret->isWild()); + //printf("wildmatch = x%x, tret = %s\n", wildmatch, tret->toChars()); if (wildmatch & MODconst || wildmatch & (wildmatch - 1)) - tret = tret->constOf(); + tret = tret->substWildTo(MODconst); else if (wildmatch & MODimmutable) - tret = tret->invariantOf(); + tret = tret->substWildTo(MODimmutable); + else if (wildmatch & MODwild) + ; else { assert(wildmatch & MODmutable); - tret = tret->mutableOf(); + tret = tret->substWildTo(MODmutable); } } return tret; @@ -1159,13 +1195,10 @@ Expression *Expression::semantic(Scope *sc) Expression *Expression::trySemantic(Scope *sc) { //printf("+trySemantic(%s)\n", toChars()); - unsigned errors = global.errors; - global.gag++; + unsigned errors = global.startGagging(); Expression *e = semantic(sc); - global.gag--; - if (errors != global.errors) + if (global.endGagging(errors)) { - global.errors = errors; e = NULL; } //printf("-trySemantic(%s)\n", toChars()); @@ -2542,7 +2575,7 @@ Expression *IdentifierExp::semantic(Scope *sc) if (hasThis(sc)) { AggregateDeclaration *ad = sc->getStructClassScope(); - if (ad->aliasthis) + if (ad && ad->aliasthis) { Expression *e; e = new IdentifierExp(loc, Id::This); @@ -2648,9 +2681,10 @@ Lagain: return this; if (!s->isFuncDeclaration()) // functions are checked after overloading checkDeprecated(sc, s); + Dsymbol *olds = s; s = s->toAlias(); //printf("s = '%s', s->kind = '%s', s->needThis() = %p\n", s->toChars(), s->kind(), s->needThis()); - if (!s->isFuncDeclaration()) + if (s != olds && !s->isFuncDeclaration()) checkDeprecated(sc, s); if (sc->func) @@ -2727,7 +2761,15 @@ Lagain: // if inferring return type, sematic3 needs to be run if (f->inferRetType && f->scope && f->type && !f->type->nextOf()) + { + TemplateInstance *spec = isSpeculativeFunction(f); + int olderrs = global.errors; f->semantic3(f->scope); + // Update the template instantiation with the number + // of errors which occured. + if (spec && global.errors != olderrs) + spec->errors = global.errors - olderrs; + } if (f->isUnitTestDeclaration()) { @@ -2785,7 +2827,8 @@ Lagain: t = s->getType(); if (t) { - return new TypeExp(loc, t); + TypeExp *te = new TypeExp(loc, t); + return te->semantic(sc); } TupleDeclaration *tup = s->isTupleDeclaration(); @@ -2859,10 +2902,7 @@ ThisExp::ThisExp(Loc loc) } Expression *ThisExp::semantic(Scope *sc) -{ FuncDeclaration *fd; - FuncDeclaration *fdthis; - int nested = 0; - +{ #if LOGSEMANTIC printf("ThisExp::semantic()\n"); #endif @@ -2874,13 +2914,15 @@ Expression *ThisExp::semantic(Scope *sc) return this; } + FuncDeclaration *fd = hasThis(sc); // fd is the uplevel function with the 'this' variable + /* Special case for typeof(this) and typeof(super) since both * should work even if they are not inside a non-static member function */ - if (sc->intypeof) + if (!fd && sc->intypeof) { // Find enclosing struct or class - for (Dsymbol *s = sc->parent; 1; s = s->parent) + for (Dsymbol *s = sc->getStructClassScope(); 1; s = s->parent) { if (!s) { @@ -2905,9 +2947,6 @@ Expression *ThisExp::semantic(Scope *sc) } } } - - fdthis = sc->parent->isFuncDeclaration(); - fd = hasThis(sc); // fd is the uplevel function with the 'this' variable if (!fd) goto Lerr; @@ -3826,7 +3865,10 @@ Expression *StructLiteralExp::semantic(Scope *sc) Initializer *i2 = v->init->syntaxCopy(); i2 = i2->semantic(v->scope, v->type, WANTinterpret); e = i2->toExpression(); - v->scope = NULL; + // remove v->scope (see bug 3426) + // but not if gagged, for we might be called again. + if (!global.gag) + v->scope = NULL; } } } @@ -4361,7 +4403,7 @@ Lagain: if (!arguments) arguments = new Expressions(); - functionParameters(loc, sc, tf, arguments, f); + functionParameters(loc, sc, tf, NULL, arguments, f); type = type->addMod(tf->nextOf()->mod); } @@ -4386,7 +4428,7 @@ Lagain: assert(allocator); TypeFunction *tf = (TypeFunction *)f->type; - functionParameters(loc, sc, tf, newargs, f); + functionParameters(loc, sc, tf, NULL, newargs, f); } else { @@ -4421,7 +4463,7 @@ Lagain: if (!arguments) arguments = new Expressions(); - functionParameters(loc, sc, tf, arguments, f); + functionParameters(loc, sc, tf, NULL, arguments, f); } else { @@ -4445,7 +4487,7 @@ Lagain: assert(allocator); tf = (TypeFunction *)f->type; - functionParameters(loc, sc, tf, newargs, f); + functionParameters(loc, sc, tf, NULL, newargs, f); #if 0 e = new VarExp(loc, f); e = new CallExp(loc, e, newargs); @@ -4965,10 +5007,6 @@ Expression *TupleExp::semantic(Scope *sc) } expandTuples(exps); - if (0 && exps->dim == 1) - { - return (*exps)[0]; - } type = new TypeTuple(exps); type = type->semantic(loc, sc); //printf("-TupleExp::semantic(%s)\n", toChars()); @@ -5030,27 +5068,28 @@ Expression *FuncExp::semantic(Scope *sc) #endif if (!type) { + unsigned olderrors = global.errors; fd->semantic(sc); //fd->parent = sc->parent; - if (global.errors) + if (olderrors != global.errors) { } else { fd->semantic2(sc); - if (!global.errors || + if ( (olderrors == global.errors) || // need to infer return type (fd->type && fd->type->ty == Tfunction && !fd->type->nextOf())) { fd->semantic3(sc); - if (!global.errors && global.params.useInline) + if ( (olderrors == global.errors) && global.params.useInline) fd->inlineScan(); } } // need to infer return type - if (global.errors && fd->type && fd->type->ty == Tfunction && !fd->type->nextOf()) + if ((olderrors != global.errors) && fd->type && fd->type->ty == Tfunction && !fd->type->nextOf()) ((TypeFunction *)fd->type)->next = Type::terror; // Type is a "delegate to" or "pointer to" the function literal @@ -5102,6 +5141,8 @@ Expression *DeclarationExp::semantic(Scope *sc) printf("DeclarationExp::semantic() %s\n", toChars()); #endif + unsigned olderrors = global.errors; + /* This is here to support extern(linkage) declaration, * where the extern(linkage) winds up being an AttribDeclaration * wrapper. @@ -5167,14 +5208,14 @@ Expression *DeclarationExp::semantic(Scope *sc) sc2->pop(); s->parent = sc->parent; } - if (!global.errors) + if (global.errors == olderrors) { declaration->semantic2(sc); - if (!global.errors) + if (global.errors == olderrors) { declaration->semantic3(sc); - if (!global.errors && global.params.useInline) + if ((global.errors == olderrors) && global.params.useInline) declaration->inlineScan(); } } @@ -6581,22 +6622,20 @@ Expression *DotIdExp::semantic(Scope *sc, int flag) return e->type->dotExp(sc, e, ident); } #if DMDV2 - else if (t1b->ty == Tarray || - t1b->ty == Tsarray || - t1b->ty == Taarray) + else if ((t1b->ty == Tarray || t1b->ty == Tsarray || + t1b->ty == Taarray) && + ident != Id::sort && ident != Id::reverse && + ident != Id::dup && ident != Id::idup) { /* If ident is not a valid property, rewrite: * e1.ident * as: * .ident(e1) */ - unsigned errors = global.errors; - global.gag++; + unsigned errors = global.startGagging(); Type *t1 = e1->type; e = e1->type->dotExp(sc, e1, ident); - global.gag--; - if (errors != global.errors) // if failed to find the property + if (global.endGagging(errors)) // if failed to find the property { - global.errors = errors; e1->type = t1; // kludge to restore type e = new DotIdExp(loc, new IdentifierExp(loc, Id::empty), ident); e = new CallExp(loc, e, e1); @@ -6776,7 +6815,7 @@ void modifyFieldVar(Loc loc, Scope *sc, VarDeclaration *var, Expression *e1) if (fd && ((fd->isCtorDeclaration() && var->storage_class & STCfield) || (fd->isStaticCtorDeclaration() && !(var->storage_class & STCfield))) && - fd->toParent2() == var->toParent() && + fd->toParent2() == var->toParent2() && (!e1 || e1->op == TOKthis) ) { @@ -6904,7 +6943,13 @@ L1: TemplateDeclaration *td = dte->td; eleft = dte->e1; ti->tempdecl = td; - ti->semantic(sc); + if (ti->needsTypeInference(sc)) + { + e = new CallExp(loc, this); + return e->semantic(sc); + } + else + ti->semantic(sc); if (!ti->inst) // if template failed to expand return new ErrorExp(); Dsymbol *s = ti->inst->toAlias(); @@ -7294,7 +7339,8 @@ Lagain: if (ve->var->storage_class & STClazy) { - TypeFunction *tf = new TypeFunction(NULL, ve->var->type, 0, LINKd); + // lazy paramaters can be called without violating purity and safety + TypeFunction *tf = new TypeFunction(NULL, ve->var->type, 0, LINKd, STCsafe | STCpure); TypeDelegate *t = new TypeDelegate(tf); ve->type = t->semantic(loc, sc); } @@ -7465,6 +7511,7 @@ Lagain: if (f->needThis()) { ue->e1 = getRightThis(loc, sc, ad, ue->e1, f); + ethis = ue->e1; } /* Cannot call public functions from inside invariant @@ -7703,10 +7750,7 @@ Lagain: tf = (TypeFunction *)(td->next); if (sc->func && !tf->purity && !(sc->flags & SCOPEdebug)) { - if (e1->op == TOKvar && ((VarExp *)e1)->var->storage_class & STClazy) - { // lazy paramaters can be called without violating purity - // since they are checked explicitly - } else if (sc->func->setImpure()) + if (sc->func->setImpure()) error("pure function '%s' cannot call impure delegate '%s'", sc->func->toChars(), e1->toChars()); } if (sc->func && tf->trust <= TRUSTsystem) @@ -7786,6 +7830,8 @@ Lagain: accessCheck(loc, sc, NULL, f); + ethis = NULL; + ve->var = f; // ve->hasOverloads = 0; ve->type = f->type; @@ -7799,7 +7845,7 @@ Lcheckargs: if (!arguments) arguments = new Expressions(); - type = functionParameters(loc, sc, tf, arguments, f); + type = functionParameters(loc, sc, tf, ethis, arguments, f); if (!type) { @@ -8331,7 +8377,7 @@ Expression *DeleteExp::semantic(Scope *sc) UnaExp::semantic(sc); e1 = resolveProperties(sc, e1); - e1 = e1->toLvalue(sc, NULL); + e1 = e1->modifiableLvalue(sc, NULL); if (e1->op == TOKerror) return e1; type = Type::tvoid; @@ -8542,7 +8588,9 @@ Expression *CastExp::semantic(Scope *sc) } // Struct casts are possible only when the sizes match - if (tob->ty == Tstruct || t1b->ty == Tstruct) + // Same with static array -> static array + if (tob->ty == Tstruct || t1b->ty == Tstruct || + (tob->ty == Tsarray && t1b->ty == Tsarray)) { size_t fromsize = t1b->size(loc); size_t tosize = tob->size(loc); @@ -8873,12 +8921,11 @@ Lagain: return e; } - if (t->ty == Tarray) - { + type = t->nextOf()->arrayOf(); + // Allow typedef[] -> typedef[] + if (type->equals(t)) type = e1->type; - } - else - type = t->nextOf()->arrayOf(); + return e; Lerror: @@ -9280,7 +9327,7 @@ Expression *IndexExp::semantic(Scope *sc) goto Lerr; } if (e2->type->ty == Ttuple && ((TupleExp *)e2)->exps->dim == 1) // bug 4444 fix - e2 = (Expression *)((TupleExp *)e2)->exps->data[0]; + e2 = ((TupleExp *)e2)->exps->tdata()[0]; e2 = resolveProperties(sc, e2); if (e2->type == Type::terror) goto Lerr; @@ -9900,6 +9947,9 @@ Ltupleassign: { /* Write as: * e1.cpctor(e2); */ + if (!e2->type->implicitConvTo(e1->type)) + error("conversion error from %s to %s", e2->type->toChars(), e1->type->toChars()); + Expression *e = new DotVarExp(loc, e1, sd->cpctor, 0); e = new CallExp(loc, e, e2); if (ec) @@ -9918,7 +9968,7 @@ Ltupleassign: } else if (t1->ty == Tclass) { // Disallow assignment operator overloads for same type - if (!e2->type->implicitConvTo(e1->type)) + if (!e2->implicitConvTo(e1->type)) { Expression *e = op_overload(sc); if (e) @@ -11163,6 +11213,8 @@ Expression *PowExp::semantic(Scope *sc) return e; assert(e1->type && e2->type); + typeCombine(sc); + if (e1->op == TOKslice) { // Check element types are arithmetic @@ -11186,64 +11238,14 @@ Expression *PowExp::semantic(Scope *sc) // TODO: backend support, especially for e1 ^^ 2. bool wantSqrt = false; - e1 = e1->optimize(0); - e2 = e2->optimize(0); - // Replace 1 ^^ x or 1.0^^x by (x, 1) - if ((e1->op == TOKint64 && e1->toInteger() == 1) || - (e1->op == TOKfloat64 && e1->toReal() == 1.0)) + // First, attempt to fold the expression. + e = optimize(WANTvalue); + if (e->op != TOKpow) { - typeCombine(sc); - e = new CommaExp(loc, e2, e1); e = e->semantic(sc); return e; } - // Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral - if (e2->type->isintegral() && e1->op == TOKint64 && (sinteger_t)e1->toInteger() == -1L) - { - typeCombine(sc); - Type* resultType = type; - e = new AndExp(loc, e2, new IntegerExp(loc, 1, e2->type)); - e = new CondExp(loc, e, new IntegerExp(loc, -1L, resultType), new IntegerExp(loc, 1L, resultType)); - e = e->semantic(sc); - return e; - } - // Replace x ^^ 0 or x^^0.0 by (x, 1) - if ((e2->op == TOKint64 && e2->toInteger() == 0) || - (e2->op == TOKfloat64 && e2->toReal() == 0.0)) - { - if (e1->type->isintegral()) - e = new IntegerExp(loc, 1, e1->type); - else - e = new RealExp(loc, 1.0, e1->type); - - typeCombine(sc); - e = new CommaExp(loc, e1, e); - e = e->semantic(sc); - return e; - } - // Replace x ^^ 1 or x^^1.0 by (x) - if ((e2->op == TOKint64 && e2->toInteger() == 1) || - (e2->op == TOKfloat64 && e2->toReal() == 1.0)) - { - typeCombine(sc); - return e1; - } - // Replace x ^^ -1.0 by (1.0 / x) - if ((e2->op == TOKfloat64 && e2->toReal() == -1.0)) - { - typeCombine(sc); - e = new DivExp(loc, new RealExp(loc, 1.0, e2->type), e1); - e = e->semantic(sc); - return e; - } - // All other negative integral powers are illegal - if ((e1->type->isintegral()) && (e2->op == TOKint64) && (sinteger_t)e2->toInteger() < 0) - { - error("cannot raise %s to a negative integer power. Did you mean (cast(real)%s)^^%s ?", - e1->type->toBasetype()->toChars(), e1->toChars(), e2->toChars()); - return new ErrorExp(); - } // Determine if we're raising to an integer power. sinteger_t intpow = 0; @@ -11255,7 +11257,6 @@ Expression *PowExp::semantic(Scope *sc) // Deal with x^^2, x^^3 immediately, since they are of practical importance. if (intpow == 2 || intpow == 3) { - typeCombine(sc); // Replace x^^2 with (tmp = x, tmp*tmp) // Replace x^^3 with (tmp = x, tmp*tmp*tmp) Identifier *idtmp = Lexer::uniqueId("__powtmp"); @@ -11296,27 +11297,14 @@ Expression *PowExp::semantic(Scope *sc) e = new DotIdExp(loc, e, Id::math); if (e2->op == TOKfloat64 && e2->toReal() == 0.5) { // Replace e1 ^^ 0.5 with .std.math.sqrt(x) - typeCombine(sc); e = new CallExp(loc, new DotIdExp(loc, e, Id::_sqrt), e1); } else { // Replace e1 ^^ e2 with .std.math.pow(e1, e2) - // We don't combine the types if raising to an integer power (because - // integer powers are treated specially by std.math.pow). - if (!e2->type->isintegral()) - typeCombine(sc); - // In fact, if it *could* have been an integer, make it one. - if (e2->op == TOKfloat64 && intpow != 0) - e2 = new IntegerExp(loc, intpow, Type::tint64); e = new CallExp(loc, new DotIdExp(loc, e, Id::_pow), e1, e2); } e = e->semantic(sc); - // Always constant fold integer powers of literals. This will run the interpreter - // on .std.math.pow - if ((e1->op == TOKfloat64 || e1->op == TOKint64) && (e2->op == TOKint64)) - e = e->optimize(WANTvalue | WANTinterpret); - return e; } incompatibleTypes(); @@ -11828,6 +11816,33 @@ EqualExp::EqualExp(enum TOK op, Loc loc, Expression *e1, Expression *e2) assert(op == TOKequal || op == TOKnotequal); } +int needDirectEq(Type *t1, Type *t2) +{ + assert(t1->ty == Tarray || t1->ty == Tsarray); + assert(t2->ty == Tarray || t2->ty == Tsarray); + + Type *t1n = t1->nextOf()->toBasetype(); + Type *t2n = t2->nextOf()->toBasetype(); + + if (((t1n->ty == Tchar || t1n->ty == Twchar || t1n->ty == Tdchar) && + (t2n->ty == Tchar || t2n->ty == Twchar || t2n->ty == Tdchar)) || + (t1n->ty == Tvoid || t2n->ty == Tvoid)) + { + return FALSE; + } + + if (t1n->constOf() != t2n->constOf()) + return TRUE; + + Type *t = t1n; + while (t->toBasetype()->nextOf()) + t = t->nextOf()->toBasetype(); + if (t->ty != Tstruct) + return FALSE; + + return ((TypeStruct *)t)->sym->xeq == StructDeclaration::xerreq; +} + Expression *EqualExp::semantic(Scope *sc) { Expression *e; @@ -11871,13 +11886,8 @@ Expression *EqualExp::semantic(Scope *sc) if ((t1->ty == Tarray || t1->ty == Tsarray) && (t2->ty == Tarray || t2->ty == Tsarray)) - { Type *t1n = t1->nextOf()->toBasetype(); - Type *t2n = t2->nextOf()->toBasetype(); - if (t1n->constOf() != t2n->constOf() && - !((t1n->ty == Tchar || t1n->ty == Twchar || t1n->ty == Tdchar) && - (t2n->ty == Tchar || t2n->ty == Twchar || t2n->ty == Tdchar)) && - !(t1n->ty == Tvoid || t2n->ty == Tvoid) - ) + { + if (needDirectEq(t1, t2)) { /* Rewrite as: * _ArrayEq(e1, e2) */ @@ -11888,7 +11898,11 @@ Expression *EqualExp::semantic(Scope *sc) e = new CallExp(loc, eq, args); if (op == TOKnotequal) e = new NotExp(loc, e); - e = e->semantic(sc); + e = e->trySemantic(sc); // for better error message + if (!e) + { error("cannot compare %s and %s", t1->toChars(), t2->toChars()); + return new ErrorExp(); + } return e; } } diff --git a/dmd2/expression.h b/dmd2/expression.h index 7f0508ad39..37c25e1da0 100644 --- a/dmd2/expression.h +++ b/dmd2/expression.h @@ -1564,6 +1564,9 @@ ASSIGNEXP(Mod) ASSIGNEXP(And) ASSIGNEXP(Or) ASSIGNEXP(Xor) +#if DMDV2 +ASSIGNEXP(Pow) +#endif #undef X #define X(a) @@ -1577,18 +1580,6 @@ ASSIGNEXP(Cat) #undef ASSIGNEXP #undef ASSIGNEXP_TOELEM -// Only a reduced subset of operations for now. -struct PowAssignExp : BinAssignExp -{ - PowAssignExp(Loc loc, Expression *e1, Expression *e2); - Expression *semantic(Scope *sc); - void buildArrayIdent(OutBuffer *buf, Expressions *arguments); - Expression *buildArrayLoop(Parameters *fparams); - - // For operator overloading - Identifier *opId(); -}; - struct AddExp : BinExp { AddExp(Loc loc, Expression *e1, Expression *e2); @@ -1733,12 +1724,22 @@ struct PowExp : BinExp { PowExp(Loc loc, Expression *e1, Expression *e2); Expression *semantic(Scope *sc); + Expression *optimize(int result); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void buildArrayIdent(OutBuffer *buf, Expressions *arguments); Expression *buildArrayLoop(Parameters *fparams); // For operator overloading Identifier *opId(); Identifier *opId_r(); + +#if IN_DMD + elem *toElem(IRState *irs); +#endif + +#if IN_LLVM + DValue* toElem(IRState* irs); +#endif }; #endif @@ -1960,6 +1961,7 @@ struct InExp : BinExp struct RemoveExp : BinExp { RemoveExp(Loc loc, Expression *e1, Expression *e2); + Expression *interpret(InterState *istate, CtfeGoal goal = ctfeNeedRvalue); void toCBuffer(OutBuffer *buf, HdrGenState *hgs); #if IN_DMD elem *toElem(IRState *irs); @@ -2121,6 +2123,7 @@ Expression *Min(Type *type, Expression *e1, Expression *e2); Expression *Mul(Type *type, Expression *e1, Expression *e2); Expression *Div(Type *type, Expression *e1, Expression *e2); Expression *Mod(Type *type, Expression *e1, Expression *e2); +Expression *Pow(Type *type, Expression *e1, Expression *e2); Expression *Shl(Type *type, Expression *e1, Expression *e2); Expression *Shr(Type *type, Expression *e1, Expression *e2); Expression *Ushr(Type *type, Expression *e1, Expression *e2); diff --git a/dmd2/func.c b/dmd2/func.c index 074115542b..fd768e3cea 100644 --- a/dmd2/func.c +++ b/dmd2/func.c @@ -38,6 +38,8 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla //printf("storage_class = x%x\n", storage_class); this->storage_class = storage_class; this->type = type; + if (type) + this->storage_class &= ~(STC_TYPECTOR | STC_FUNCATTR); this->loc = loc; this->endloc = endloc; fthrows = NULL; @@ -77,6 +79,7 @@ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageCla * NULL for the return type. */ inferRetType = (type && type->nextOf() == NULL); + storage_class2 = 0; hasReturnExp = 0; nrvo_can = 1; nrvo_var = NULL; @@ -199,8 +202,15 @@ void FuncDeclaration::semantic(Scope *sc) if (!type->deco) { sc = sc->push(); - sc->stc |= storage_class & (STCref | STCnothrow | STCpure | STCdisable - | STCsafe | STCtrusted | STCsystem | STCproperty); // forward to function type + sc->stc |= storage_class & STCdisable; // forward to function type + TypeFunction *tf = (TypeFunction *)type; + if (tf->isref) sc->stc |= STCref; + if (tf->isnothrow) sc->stc |= STCnothrow; + if (tf->isproperty) sc->stc |= STCproperty; + if (tf->purity == PUREfwdref) sc->stc |= STCpure; + if (tf->trust == TRUSTsafe) sc->stc |= STCsafe; + if (tf->trust == TRUSTsystem) sc->stc |= STCsystem; + if (tf->trust == TRUSTtrusted) sc->stc |= STCtrusted; if (isCtorDeclaration()) sc->flags |= SCOPEctor; @@ -287,12 +297,9 @@ void FuncDeclaration::semantic(Scope *sc) if (fbody && (isFuncLiteralDeclaration() || parent->isTemplateInstance())) { - if (f->purity == PUREimpure && // purity not specified - !f->hasLazyParameters() - ) - { + if (f->purity == PUREimpure) // purity not specified flags |= FUNCFLAGpurityInprocess; - } + if (f->trust == TRUSTdefault) flags |= FUNCFLAGsafetyInprocess; @@ -517,12 +524,13 @@ void FuncDeclaration::semantic(Scope *sc) warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars()); #endif - if (fdv->toParent() == parent) + FuncDeclaration *fdc = ((Dsymbol *)cd->vtbl.data[vi])->isFuncDeclaration(); + if (fdc->toParent() == parent) { // If both are mixins, then error. // If either is not, the one that is not overrides // the other. - if (fdv->parent->isClassDeclaration()) + if (fdc->parent->isClassDeclaration()) break; if (!this->parent->isClassDeclaration() #if !BREAKABI @@ -2935,7 +2943,7 @@ int FuncDeclaration::addPreInvariant() return (ad && //ad->isClassDeclaration() && global.params.useInvariants && - (protection == PROTpublic || protection == PROTexport) && + (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && !naked && ident != Id::cpctor); } @@ -2947,7 +2955,7 @@ int FuncDeclaration::addPostInvariant() ad->inv && //ad->isClassDeclaration() && global.params.useInvariants && - (protection == PROTpublic || protection == PROTexport) && + (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && !naked && ident != Id::cpctor); } @@ -3208,10 +3216,10 @@ void CtorDeclaration::semantic(Scope *sc) //printf("CtorDeclaration::semantic() %s\n", toChars()); TypeFunction *tf = (TypeFunction *)type; assert(tf && tf->ty == Tfunction); - Expressions *fargs = ((TypeFunction *)type)->fargs; // for auto ref sc = sc->push(); sc->stc &= ~STCstatic; // not a static constructor + sc->flags |= SCOPEctor; parent = sc->parent; Dsymbol *parent = toParent2(); @@ -3227,17 +3235,16 @@ void CtorDeclaration::semantic(Scope *sc) { tret = ad->handle; assert(tret); tret = tret->addStorageClass(storage_class | sc->stc); + tret = tret->addMod(type->mod); } - tf = new TypeFunction(tf->parameters, tret, tf->varargs, LINKd, storage_class | sc->stc); - tf->fargs = fargs; - type = tf; + tf->next = tret; + type = type->semantic(loc, sc); #if STRUCTTHISREF if (ad && ad->isStructDeclaration()) - { ((TypeFunction *)type)->isref = 1; - if (!originalType) - // Leave off the "ref" - originalType = new TypeFunction(tf->parameters, tret, tf->varargs, LINKd, storage_class | sc->stc); + { if (!originalType) + originalType = type->syntaxCopy(); + ((TypeFunction *)type)->isref = 1; } #endif if (!originalType) @@ -3303,18 +3310,6 @@ int CtorDeclaration::addPostInvariant() } -void CtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) -{ - TypeFunction *tf = (TypeFunction *)type; - assert(tf && tf->ty == Tfunction); - - if (originalType && originalType->ty == Tfunction) - ((TypeFunction *)originalType)->attributesToCBuffer(buf, 0); - buf->writestring("this"); - Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs); - bodyToCBuffer(buf, hgs); -} - /********************************* PostBlitDeclaration ****************************/ #if DMDV2 diff --git a/dmd2/idgen.c b/dmd2/idgen.c index 3729b3951c..78b502266a 100644 --- a/dmd2/idgen.c +++ b/dmd2/idgen.c @@ -73,6 +73,7 @@ Msgtable msgtable[] = { "line" }, { "empty", "" }, { "p" }, + { "q" }, { "coverage", "__coverage" }, { "__vptr" }, { "__monitor" }, @@ -315,7 +316,17 @@ Msgtable msgtable[] = { "tan" }, { "_sqrt", "sqrt" }, { "_pow", "pow" }, + { "atan2" }, + { "rndtol" }, + { "expm1" }, + { "exp2" }, + { "yl2x" }, + { "yl2xp1" }, { "fabs" }, + { "bitop" }, + { "bsf" }, + { "bsr" }, + { "bswap" }, // Traits { "isAbstractClass" }, diff --git a/dmd2/init.c b/dmd2/init.c index 6a0002208e..6bf2f07af9 100644 --- a/dmd2/init.c +++ b/dmd2/init.c @@ -813,6 +813,9 @@ Initializer *ExpInitializer::semantic(Scope *sc, Type *t, int needInterpret) if (!global.gag && olderrors != global.errors) return this; // Failed, suppress duplicate error messages + if (exp->op == TOKtype) + error("initializer must be an expression, not '%s'", exp->toChars()); + // Make sure all pointers are constants if (needInterpret && hasNonConstPointers(exp)) { diff --git a/dmd2/inline.c b/dmd2/inline.c index 63a33f224e..11b9c3c577 100644 --- a/dmd2/inline.c +++ b/dmd2/inline.c @@ -54,7 +54,7 @@ int CompoundStatement::inlineCost(InlineCostState *ics) { int cost = 0; for (size_t i = 0; i < statements->dim; i++) - { Statement *s = statements->tdata()[i]; + { Statement *s = (*statements)[i]; if (s) { cost += s->inlineCost(ics); @@ -69,7 +69,7 @@ int UnrolledLoopStatement::inlineCost(InlineCostState *ics) { int cost = 0; for (size_t i = 0; i < statements->dim; i++) - { Statement *s = statements->tdata()[i]; + { Statement *s = (*statements)[i]; if (s) { cost += s->inlineCost(ics); @@ -147,7 +147,7 @@ int arrayInlineCost(InlineCostState *ics, Expressions *arguments) if (arguments) { for (size_t i = 0; i < arguments->dim; i++) - { Expression *e = arguments->tdata()[i]; + { Expression *e = (*arguments)[i]; if (e) cost += e->inlineCost(ics); @@ -211,6 +211,8 @@ int AssocArrayLiteralExp::inlineCost(InlineCostState *ics) int StructLiteralExp::inlineCost(InlineCostState *ics) { + if (sd->isnested) + return COST_MAX; return 1 + arrayInlineCost(ics, elements); } @@ -246,7 +248,7 @@ int DeclarationExp::inlineCost(InlineCostState *ics) return COST_MAX; // finish DeclarationExp::doInline #else for (size_t i = 0; i < td->objects->dim; i++) - { Object *o = td->objects->tdata()[i]; + { Object *o = (*td->objects)[i]; if (o->dyncast() != DYNCAST_EXPRESSION) return COST_MAX; Expression *eo = (Expression *)o; @@ -379,7 +381,7 @@ Expression *CompoundStatement::doInline(InlineDoState *ids) //printf("CompoundStatement::doInline() %d\n", statements->dim); for (size_t i = 0; i < statements->dim; i++) - { Statement *s = statements->tdata()[i]; + { Statement *s = (*statements)[i]; if (s) { Expression *e2 = s->doInline(ids); @@ -411,7 +413,7 @@ Expression *UnrolledLoopStatement::doInline(InlineDoState *ids) //printf("UnrolledLoopStatement::doInline() %d\n", statements->dim); for (size_t i = 0; i < statements->dim; i++) - { Statement *s = statements->tdata()[i]; + { Statement *s = (*statements)[i]; if (s) { Expression *e2 = s->doInline(ids); @@ -843,9 +845,9 @@ Statement *ExpStatement::inlineScan(InlineScanState *iss) Statement *CompoundStatement::inlineScan(InlineScanState *iss) { for (size_t i = 0; i < statements->dim; i++) - { Statement *s = statements->tdata()[i]; + { Statement *s = (*statements)[i]; if (s) - statements->tdata()[i] = s->inlineScan(iss); + (*statements)[i] = s->inlineScan(iss); } return this; } @@ -853,9 +855,9 @@ Statement *CompoundStatement::inlineScan(InlineScanState *iss) Statement *UnrolledLoopStatement::inlineScan(InlineScanState *iss) { for (size_t i = 0; i < statements->dim; i++) - { Statement *s = statements->tdata()[i]; + { Statement *s = (*statements)[i]; if (s) - statements->tdata()[i] = s->inlineScan(iss); + (*statements)[i] = s->inlineScan(iss); } return this; } diff --git a/dmd2/interpret.c b/dmd2/interpret.c index 6d6b0d294f..d298ce14c7 100644 --- a/dmd2/interpret.c +++ b/dmd2/interpret.c @@ -25,9 +25,16 @@ #include "id.h" #include "utf.h" +#include "template.h" +TemplateInstance *isSpeculativeFunction(FuncDeclaration *fd); + + #define LOG 0 #define LOGASSIGN 0 +// Maximum allowable recursive function calls in CTFE +#define CTFE_RECURSION_LIMIT 1000 + struct InterState { InterState *caller; // calling function's InterState @@ -49,6 +56,18 @@ 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 + */ +}; + +int CtfeStatus::callDepth = 0; +int CtfeStatus::stackTraceCallsToSuppress = 0; + Expression * resolveReferences(Expression *e, Expression *thisval, bool *isReference = NULL); Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal); VarDeclaration *findParentVar(Expression *e, Expression *thisval); @@ -56,7 +75,8 @@ void addVarToInterstate(InterState *istate, VarDeclaration *v); bool needToCopyLiteral(Expression *expr); Expression *copyLiteral(Expression *e); Expression *paintTypeOntoLiteral(Type *type, Expression *lit); -bool evaluateIfBuiltin(Expression **result, InterState *istate, +Expression *findKeyInAA(AssocArrayLiteralExp *ae, Expression *e2); +Expression *evaluateIfBuiltin(InterState *istate, Loc loc, FuncDeclaration *fd, Expressions *arguments, Expression *pthis); @@ -150,9 +170,23 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument if (semanticRun < PASSsemantic3 && scope) { + /* Forward reference - we need to run semantic3 on this function. + * If errors are gagged, and it's not part of a speculative + * template instance, we need to temporarily ungag errors. + */ int olderrors = global.errors; + int oldgag = global.gag; + TemplateInstance *spec = isSpeculativeFunction(this); + if (global.gag && !spec) + global.gag = 0; semantic3(scope); - if (olderrors != global.errors) // if errors compiling this function + global.gag = oldgag; // regag errors + + // If it is a speculatively-instantiated template, and errors occur, + // we need to mark the template as having errors. + if (spec && global.errors != olderrors) + spec->errors = global.errors - olderrors; + if (olderrors != global.errors) // if errors compiling this function return NULL; } if (semanticRun < PASSsemantic3done) @@ -310,9 +344,19 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument } } + // Enter the function + ++CtfeStatus::callDepth; + Expression *e = NULL; while (1) { + if (CtfeStatus::callDepth > CTFE_RECURSION_LIMIT) + { // This is a compiler error. It must not be suppressed. + global.gag = 0; + error("CTFE recursion limit exceeded"); + e = NULL; + break; + } e = fbody->interpret(&istatex); if (e == EXP_CANT_INTERPRET) { @@ -338,6 +382,9 @@ Expression *FuncDeclaration::interpret(InterState *istate, Expressions *argument } assert(e != EXP_CONTINUE_INTERPRET && e != EXP_BREAK_INTERPRET); + // Leave the function + --CtfeStatus::callDepth; + /* Restore the parameter values */ for (size_t i = 0; i < dim; i++) @@ -1448,12 +1495,22 @@ Expression *SymOffExp::interpret(InterState *istate, CtfeGoal goal) { // Check for unsupported type painting operations Type *elemtype = ((TypeArray *)(val->type))->next; + + // It's OK to cast from fixed length to dynamic array, eg &int[3] to int[]* + if (val->type->ty == Tsarray && pointee->ty == Tarray + && elemtype->size() == pointee->nextOf()->size()) + { + Expression *e = new AddrExp(loc, val); + e->type = type; + return e; + } if ( #if DMDV2 elemtype->castMod(0) != pointee->castMod(0) #else elemtype != pointee #endif + // It's OK to cast from int[] to uint* && !(elemtype->isintegral() && pointee->isintegral() && elemtype->size() == pointee->size())) { @@ -1676,6 +1733,8 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal v->inuse++; e = e->interpret(istate, ctfeNeedAnyValue); v->inuse--; + if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) + fprintf(stdmsg, "%s: while evaluating %s.init\n", loc.toChars(), v->toChars()); if (e == EXP_CANT_INTERPRET) return e; e->type = v->type; @@ -1686,6 +1745,8 @@ Expression *getVarExp(Loc loc, InterState *istate, Declaration *d, CtfeGoal goal e->type = v->type; if (e) e = e->interpret(istate, ctfeNeedAnyValue); + if (e == EXP_CANT_INTERPRET && !global.gag && !CtfeStatus::stackTraceCallsToSuppress) + fprintf(stdmsg, "%s: while evaluating %s.init\n", loc.toChars(), v->toChars()); } if (e && e != EXP_CANT_INTERPRET) v->setValueWithoutChecking(e); @@ -2433,6 +2494,9 @@ BIN_INTERPRET(Ushr) BIN_INTERPRET(And) BIN_INTERPRET(Or) BIN_INTERPRET(Xor) +#if DMDV2 +BIN_INTERPRET(Pow) +#endif typedef Expression *(*fp2_t)(enum TOK, Type *, Expression *, Expression *); @@ -2809,7 +2873,7 @@ Expression *copyLiteral(Expression *e) uinteger_t length = tsa->dim->toInteger(); m = createBlockDuplicatedArrayLiteral(v->type, m, (size_t)length); } - else if (v->type->ty != Tarray) // NOTE: do not copy array references + else if (v->type->ty != Tarray && v->type->ty!=Taarray) // NOTE: do not copy array references m = copyLiteral(m); newelems->tdata()[i] = m; } @@ -3130,6 +3194,13 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if ((e1->op==TOKslice) && ((SliceExp *)e1)->e1->type->ty == Tsarray) wantRef = false; #endif + // If it is assignment from a ref parameter, it's not a ref assignment + if (this->e2->op == TOKvar) + { + VarDeclaration *v = ((VarExp *)this->e2)->var->isVarDeclaration(); + if (v && (v->storage_class & (STCref | STCout))) + wantRef = false; + } } if (isBlockAssignment && (e2->type->toBasetype()->ty == Tarray || e2->type->toBasetype()->ty == Tsarray)) { @@ -3238,7 +3309,7 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ // ~= can create new values (see bug 6052) if (op == TOKcatass) { - if (needToCopyLiteral(this->e2)) + if (needToCopyLiteral(newval)) newval = copyLiteral(newval); if (newval->op == TOKslice) newval = resolveSlice(newval); @@ -3351,6 +3422,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (type->toBasetype()->ty == Tstruct && newval->op == TOKint64) { newval = type->defaultInitLiteral(loc); + if (newval->op != TOKstructliteral) + { + error("nested structs with constructors are not yet supported in CTFE (Bug 6419)"); + return EXP_CANT_INTERPRET; + } } newval = Cast(type, type, newval); if (newval == EXP_CANT_INTERPRET) @@ -3425,6 +3501,103 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ returnValue = newval; } + // --------------------------------------- + // Deal with AA index assignment + // --------------------------------------- + /* This needs special treatment if the AA doesn't exist yet. + * There are two special cases: + * (1) If the AA is itself an index of another AA, we may need to create + * multiple nested AA literals before we can insert the new value. + * (2) If the ultimate AA is null, no insertion happens at all. Instead, we + * create nested AA literals, and change it into a assignment. + */ + if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + IndexExp *ie = (IndexExp *)e1; + int depth = 0; // how many nested AA indices are there? + while (ie->e1->op == TOKindex && ((IndexExp *)ie->e1)->e1->type->toBasetype()->ty == Taarray) + { + ie = (IndexExp *)ie->e1; + ++depth; + } + Expression *aggregate = resolveReferences(ie->e1, istate->localThis); + Expression *oldagg = aggregate; + // Get the AA to be modified. (We do an LvalueRef interpret, unless it + // is a simple ref parameter -- in which case, we just want the value) + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (aggregate == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + if (aggregate->op == TOKassocarrayliteral) + { // Normal case, ultimate parent AA already exists + // We need to walk from the deepest index up, checking that an AA literal + // already exists on each level. + Expression *index = ((IndexExp *)e1)->e2->interpret(istate); + if (index == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + if (index->op == TOKslice) // only happens with AA assignment + index = resolveSlice(index); + AssocArrayLiteralExp *existingAA = (AssocArrayLiteralExp *)aggregate; + while (depth > 0) + { // Walk the syntax tree to find the indexExp at this depth + IndexExp *xe = (IndexExp *)e1; + for (int d= 0; d < depth; ++d) + xe = (IndexExp *)xe->e1; + + Expression *indx = xe->e2->interpret(istate); + if (indx == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + if (indx->op == TOKslice) // only happens with AA assignment + indx = resolveSlice(indx); + + // Look up this index in it up in the existing AA, to get the next level of AA. + AssocArrayLiteralExp *newAA = (AssocArrayLiteralExp *)findKeyInAA(existingAA, indx); + if (newAA == EXP_CANT_INTERPRET) + return newAA; + if (!newAA) + { // Doesn't exist yet, create an empty AA... + Expressions *valuesx = new Expressions(); + Expressions *keysx = new Expressions(); + newAA = new AssocArrayLiteralExp(loc, keysx, valuesx); + newAA->type = xe->type; + //... and insert it into the existing AA. + existingAA->keys->push(indx); + existingAA->values->push(newAA); + } + existingAA = newAA; + --depth; + } + if (assignAssocArrayElement(loc, existingAA, index, newval) == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + return returnValue; + } + else + { /* The AA is currently null. 'aggregate' is actually a reference to + * whatever contains it. It could be anything: var, dotvarexp, ... + * We rewrite the assignment from: aggregate[i][j] = newval; + * into: aggregate = [i:[j: newval]]; + */ + while (e1->op == TOKindex && ((IndexExp *)e1)->e1->type->toBasetype()->ty == Taarray) + { + Expression *index = ((IndexExp *)e1)->e2->interpret(istate); + if (index == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + if (index->op == TOKslice) // only happens with AA assignment + index = resolveSlice(index); + Expressions *valuesx = new Expressions(); + Expressions *keysx = new Expressions(); + valuesx->push(newval); + keysx->push(index); + newval = new AssocArrayLiteralExp(loc, keysx, valuesx); + newval->type = e1->type; + e1 = ((IndexExp *)e1)->e1; + } + // We must return to the original aggregate, in case it was a reference + wantRef = true; + e1 = oldagg; + // fall through -- let the normal assignment logic take care of it + } + } + // --------------------------------------- // Deal with dotvar expressions // --------------------------------------- @@ -3537,12 +3710,14 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ { /* Assignment to array element of the form: * aggregate[i] = newval + * aggregate is not AA (AAs were dealt with already). */ IndexExp *ie = (IndexExp *)e1; - uinteger_t destarraylen = 0; // not for AAs + assert(ie->e1->type->toBasetype()->ty != Taarray); + uinteger_t destarraylen = 0; // Set the $ variable, and find the array literal to modify - if (ie->e1->type->toBasetype()->ty != Taarray && ie->e1->type->toBasetype()->ty != Tpointer) + if (ie->e1->type->toBasetype()->ty != Tpointer) { Expression *oldval = ie->e1->interpret(istate); if (oldval->op == TOKnull) @@ -3570,56 +3745,51 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ if (index == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; - if (index->op == TOKslice) // only happens with AA assignment - index = resolveSlice(index); + assert (index->op != TOKslice); // only happens with AA assignment ArrayLiteralExp *existingAE = NULL; StringExp *existingSE = NULL; - AssocArrayLiteralExp *existingAA = NULL; Expression *aggregate = resolveReferences(ie->e1, istate->localThis); - // Set the index to modify (for non-AAs), and check that it is in range - dinteger_t indexToModify = 0; - if (ie->e1->type->toBasetype()->ty != Taarray) + // Set the index to modify, and check that it is in range + dinteger_t indexToModify = index->toInteger(); + if (ie->e1->type->toBasetype()->ty == Tpointer) { - indexToModify = index->toInteger(); - if (ie->e1->type->toBasetype()->ty == Tpointer) + dinteger_t ofs; + aggregate = aggregate->interpret(istate, ctfeNeedLvalue); + if (aggregate == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + if (aggregate->op == TOKnull) { - dinteger_t ofs; - aggregate = aggregate->interpret(istate, ctfeNeedLvalue); - if (aggregate == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - if (aggregate->op == TOKnull) - { - error("cannot index through null pointer %s", ie->e1->toChars()); - return EXP_CANT_INTERPRET; - } - if (aggregate->op == TOKint64) - { - error("cannot index through invalid pointer %s of value %s", - ie->e1->toChars(), aggregate->toChars()); - return EXP_CANT_INTERPRET; - } - aggregate = getAggregateFromPointer(aggregate, &ofs); - indexToModify += ofs; - destarraylen = resolveArrayLength(aggregate); - } - if (indexToModify >= destarraylen) - { - error("array index %d is out of bounds [0..%d]", indexToModify, - destarraylen); + error("cannot index through null pointer %s", ie->e1->toChars()); return EXP_CANT_INTERPRET; } + if (aggregate->op == TOKint64) + { + error("cannot index through invalid pointer %s of value %s", + ie->e1->toChars(), aggregate->toChars()); + return EXP_CANT_INTERPRET; + } + aggregate = getAggregateFromPointer(aggregate, &ofs); + indexToModify += ofs; + destarraylen = resolveArrayLength(aggregate); + } + if (indexToModify >= destarraylen) + { + error("array index %d is out of bounds [0..%d]", indexToModify, + destarraylen); + return EXP_CANT_INTERPRET; } - /* The only possible indexable LValue aggregates are array literals, - * slices of array literals, and AA literals. + /* The only possible indexable LValue aggregates are array literals, and + * slices of array literals. */ if (aggregate->op == TOKindex || aggregate->op == TOKdotvar || aggregate->op == TOKslice || aggregate->op == TOKcall || aggregate->op == TOKstar) { + Expression *origagg = aggregate; aggregate = aggregate->interpret(istate, ctfeNeedLvalue); if (aggregate == EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; @@ -3637,21 +3807,6 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ aggregate = v->getValue(); if (aggregate->op == TOKnull) { - if (v->type->ty == Taarray) - { // Assign to empty associative array - Expressions *valuesx = new Expressions(); - Expressions *keysx = new Expressions(); - Expression *indx = ie->e2->interpret(istate); - if (indx == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - valuesx->push(newval); - keysx->push(indx); - Expression *aae2 = new AssocArrayLiteralExp(loc, keysx, valuesx); - aae2->type = v->type; - newval = aae2; - v->setRefValue(newval); - return returnValue; - } // This would be a runtime segfault error("cannot index null array %s", v->toChars()); return EXP_CANT_INTERPRET; @@ -3668,8 +3823,6 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ existingAE = (ArrayLiteralExp *)aggregate; else if (aggregate->op == TOKstring) existingSE = (StringExp *)aggregate; - else if (aggregate->op == TOKassocarrayliteral) - existingAA = (AssocArrayLiteralExp *)aggregate; else { error("CTFE internal compiler error %s", aggregate->toChars()); @@ -3684,6 +3837,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ assert(0); } } + if (wantRef && newval->op == TOKindex + && ((IndexExp *)newval)->e1 == aggregate) + { // It's a circular reference, resolve it now + newval = newval->interpret(istate); + } if (existingAE) { @@ -3708,12 +3866,6 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ } return returnValue; } - else if (existingAA) - { - if (assignAssocArrayElement(loc, existingAA, index, newval) == EXP_CANT_INTERPRET) - return EXP_CANT_INTERPRET; - return returnValue; - } else { error("Index assignment %s is not yet supported in CTFE ", toChars()); @@ -3849,6 +4001,11 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ assert(0); } } + if (wantRef && newval->op == TOKindex + && ((IndexExp *)newval)->e1 == aggregate) + { // It's a circular reference, resolve it now + newval = newval->interpret(istate); + } // For slice assignment, we check that the lengths match. size_t srclen = 0; @@ -3877,7 +4034,8 @@ Expression *BinExp::interpretAssignCommon(InterState *istate, CtfeGoal goal, fp_ sliceAssignStringFromString((StringExp *)existingSE, (StringExp *)newval, firstIndex); return newval; } - else if (newval->op == TOKstring && existingAE) + else if (newval->op == TOKstring && existingAE + && existingAE->type->isString()) { /* Mixed slice: it was initialized as an array literal of chars. * Now a slice of it is being set with a string. */ @@ -3991,6 +4149,9 @@ BIN_ASSIGN_INTERPRET(Ushr) BIN_ASSIGN_INTERPRET(And) BIN_ASSIGN_INTERPRET(Or) BIN_ASSIGN_INTERPRET(Xor) +#if DMDV2 +BIN_ASSIGN_INTERPRET(Pow) +#endif Expression *PostExp::interpret(InterState *istate, CtfeGoal goal) { @@ -4067,6 +4228,54 @@ Expression *OrOrExp::interpret(InterState *istate, CtfeGoal goal) return e; } +// Print a stack trace, starting from callingExp which called fd. +// To shorten the stack trace, try to detect recursion. +void showCtfeBackTrace(InterState *istate, CallExp * callingExp, FuncDeclaration *fd) +{ + if (CtfeStatus::stackTraceCallsToSuppress > 0) + { + --CtfeStatus::stackTraceCallsToSuppress; + return; + } + fprintf(stdmsg, "%s: called from here: %s\n", callingExp->loc.toChars(), callingExp->toChars()); + // Quit if it's not worth trying to compress the stack trace + if (CtfeStatus::callDepth < 6 || global.params.verbose) + return; + // Recursion happens if the current function already exists in the call stack. + int numToSuppress = 0; + int recurseCount = 0; + int depthSoFar = 0; + InterState *lastRecurse = istate; + for (InterState * cur = istate; cur; cur = cur->caller) + { + if (cur->fd == fd) + { ++recurseCount; + numToSuppress = depthSoFar; + lastRecurse = cur; + } + ++depthSoFar; + } + // We need at least three calls to the same function, to make compression worthwhile + if (recurseCount < 2) + return; + // We found a useful recursion. Print all the calls involved in the recursion + fprintf(stdmsg, "%s: %d recursive calls to function %s\n", fd->loc.toChars(), recurseCount, fd->toChars()); + for (InterState *cur = istate; cur->fd != fd; cur = cur->caller) + { + fprintf(stdmsg, "%s: recursively called from function %s\n", cur->fd->loc.toChars(), cur->fd->toChars()); + } + // We probably didn't enter the recursion in this function. + // Go deeper to find the real beginning. + InterState * cur = istate; + while (lastRecurse->caller && cur->fd == lastRecurse->caller->fd) + { + cur = cur->caller; + lastRecurse = lastRecurse->caller; + ++numToSuppress; + } + CtfeStatus::stackTraceCallsToSuppress = numToSuppress; +} + Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e = EXP_CANT_INTERPRET; @@ -4156,9 +4365,12 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) TypeFunction *tf = fd ? (TypeFunction *)(fd->type) : NULL; if (!tf) - { // DAC: I'm not sure if this ever happens + { // DAC: This should never happen, it's an internal compiler error. //printf("ecall=%s %d %d\n", ecall->toChars(), ecall->op, TOKcall); - error("cannot evaluate %s at compile time", toChars()); + if (ecall->op == TOKidentifier) + error("cannot evaluate %s at compile time. Circular reference?", toChars()); + else + error("CTFE internal error: cannot evaluate %s at compile time", toChars()); return EXP_CANT_INTERPRET; } if (!fd) @@ -4184,8 +4396,8 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) } } // Check for built-in functions - Expression *eresult; - if (evaluateIfBuiltin(&eresult, istate, fd, arguments, pthis)) + Expression *eresult = evaluateIfBuiltin(istate, loc, fd, arguments, pthis); + if (eresult) return eresult; // Inline .dup. Special case because it needs the return type. @@ -4214,7 +4426,10 @@ Expression *CallExp::interpret(InterState *istate, CtfeGoal goal) else if (fd->type->toBasetype()->nextOf()->ty == Tvoid && !global.errors) e = EXP_VOID_INTERPRET; else - error("cannot evaluate %s at compile time", toChars()); + { // Print a stack trace. + if (!global.gag) + showCtfeBackTrace(istate, this, fd); + } return e; } @@ -4397,6 +4612,8 @@ Expression *IndexExp::interpret(InterState *istate, CtfeGoal goal) if (e1->op == TOKnull) { + if (goal == ctfeNeedLvalue && e1->type->ty == Taarray) + return paintTypeOntoLiteral(type, e1); error("cannot index null array %s", this->e1->toChars()); return EXP_CANT_INTERPRET; } @@ -4691,6 +4908,35 @@ Lcant: return EXP_CANT_INTERPRET; } +#if DMDV2 +// 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 (t->ty != Tstruct) + return false; + StructDeclaration *sym = ((TypeStruct *)t)->sym; + if (sym->ident == Id::AssociativeArray) + return true; + 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; + 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])); +} +#endif Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) { Expression *e; @@ -4704,11 +4950,11 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) goto Lcant; if (to->ty == Tpointer && e1->op != TOKnull) { // Deal with casts from char[] to char * + Type *pointee = ((TypePointer *)type)->next; if (e1->type->ty == Tarray || e1->type->ty == Tsarray) { // Check for unsupported type painting operations Type *elemtype = ((TypeArray *)(e1->type))->next; - Type *pointee = ((TypePointer *)type)->next; if ( #if DMDV2 e1->type->nextOf()->castMod(0) != to->nextOf()->castMod(0) @@ -4734,16 +4980,12 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) e->type = type; return e; } - if (e1->op == TOKarrayliteral) + if (e1->op == TOKarrayliteral || e1->op == TOKstring) { e = new IndexExp(loc, e1, new IntegerExp(loc, 0, Type::tsize_t)); e->type = type; return e; } - if (e1->op == TOKstring) - { - return e1; - } if (e1->op == TOKindex && ((IndexExp *)e1)->e1->type != e1->type) { // type painting operation IndexExp *ie = (IndexExp *)e1; @@ -4755,6 +4997,13 @@ Expression *CastExp::interpret(InterState *istate, CtfeGoal goal) { // Happens with Windows HANDLEs, for example. return paintTypeOntoLiteral(to, e1); } +#if DMDV2 + if (pointee->ty == Taarray && e1->op == TOKaddress + && isAssocArray(((AddrExp*)e1)->e1->type)) + { // cast from template AA pointer to true AA pointer is OK. + return paintTypeOntoLiteral(to, e1); + } +#endif error("pointer cast from %s to %s is not supported at compile time", e1->type->toChars(), to->toChars()); return EXP_CANT_INTERPRET; @@ -4970,6 +5219,13 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) Expression *ex = e1->interpret(istate); if (ex != EXP_CANT_INTERPRET) { + #if DMDV2 + // Special case for template AAs: AA.var returns the AA itself. + // ie AA.p ----> AA. This is a hack, to get around the + // corresponding hack in the AA druntime implementation. + if (isAssocArray(ex->type)) + return ex; + #endif if (ex->op == TOKaddress) ex = ((AddrExp *)ex)->e1; if (ex->op == TOKstructliteral) @@ -5036,88 +5292,59 @@ Expression *DotVarExp::interpret(InterState *istate, CtfeGoal goal) return e; } +Expression *RemoveExp::interpret(InterState *istate, CtfeGoal goal) +{ +#if LOG + printf("RemoveExp::interpret() %s\n", toChars()); +#endif + Expression *agg = e1->interpret(istate); + if (agg == EXP_CANT_INTERPRET) + return agg; + Expression *index = e2->interpret(istate); + if (index == EXP_CANT_INTERPRET) + return index; + if (agg->op == TOKnull) + return EXP_VOID_INTERPRET; + assert(agg->op == TOKassocarrayliteral); + AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)agg; + Expressions *keysx = aae->keys; + Expressions *valuesx = aae->values; + size_t removed = 0; + for (size_t j = 0; j < valuesx->dim; ++j) + { Expression *ekey = keysx->tdata()[j]; + Expression *ex = ctfeEqual(TOKequal, Type::tbool, ekey, index); + if (ex == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + if (ex->isBool(TRUE)) + ++removed; + else if (removed != 0) + { keysx->tdata()[j - removed] = ekey; + valuesx->tdata()[j - removed] = valuesx->tdata()[j]; + } + } + valuesx->dim = valuesx->dim - removed; + keysx->dim = keysx->dim - removed; + return EXP_VOID_INTERPRET; +} + + /******************************* Special Functions ***************************/ -#if DMDV1 - -Expression *interpret_aaLen(InterState *istate, Expressions *arguments) -{ - if (!arguments || arguments->dim != 1) - return NULL; - Expression *earg = arguments->tdata()[0]; - earg = earg->interpret(istate); - if (earg == EXP_CANT_INTERPRET) - return NULL; - if (earg->op != TOKassocarrayliteral) - return NULL; - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; - Expression *e = new IntegerExp(aae->loc, aae->keys->dim, Type::tsize_t); - return e; -} - -Expression *interpret_aaKeys(InterState *istate, Expressions *arguments) -{ -#if LOG - printf("interpret_aaKeys()\n"); -#endif - if (!arguments || arguments->dim != 2) - return NULL; - Expression *earg = arguments->tdata()[0]; - earg = earg->interpret(istate); - if (earg == EXP_CANT_INTERPRET) - return NULL; - if (earg->op == TOKnull) - return new NullExp(earg->loc); - if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) - return NULL; - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; - Expression *e = new ArrayLiteralExp(aae->loc, aae->keys); - Type *elemType = ((TypeAArray *)aae->type)->index; - e->type = new TypeSArray(elemType, new IntegerExp(arguments ? arguments->dim : 0)); - return copyLiteral(e); -} - -Expression *interpret_aaValues(InterState *istate, Expressions *arguments) -{ -#if LOG - printf("interpret_aaValues()\n"); -#endif - if (!arguments || arguments->dim != 3) - return NULL; - Expression *earg = arguments->tdata()[0]; - earg = earg->interpret(istate); - if (earg == EXP_CANT_INTERPRET) - return NULL; - if (earg->op == TOKnull) - return new NullExp(earg->loc); - if (earg->op != TOKassocarrayliteral && earg->type->toBasetype()->ty != Taarray) - return NULL; - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; - Expression *e = new ArrayLiteralExp(aae->loc, aae->values); - Type *elemType = ((TypeAArray *)aae->type)->next; - e->type = new TypeSArray(elemType, new IntegerExp(arguments ? arguments->dim : 0)); - //printf("result is %s\n", e->toChars()); - return copyLiteral(e); -} - -#endif - -#if DMDV2 - Expression *interpret_length(InterState *istate, Expression *earg) { //printf("interpret_length()\n"); earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) return NULL; - if (earg->op != TOKassocarrayliteral) - return NULL; - AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; - Expression *e = new IntegerExp(aae->loc, aae->keys->dim, Type::tsize_t); + dinteger_t len = 0; + if (earg->op == TOKassocarrayliteral) + len = ((AssocArrayLiteralExp *)earg)->keys->dim; + else assert(earg->op == TOKnull); + Expression *e = new IntegerExp(earg->loc, len, Type::tsize_t); return e; } -Expression *interpret_keys(InterState *istate, Expression *earg, FuncDeclaration *fd) +Expression *interpret_keys(InterState *istate, Expression *earg, Type *elemType) { #if LOG printf("interpret_keys()\n"); @@ -5132,14 +5359,11 @@ Expression *interpret_keys(InterState *istate, Expression *earg, FuncDeclaration assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->keys); - assert(fd->type->ty == Tfunction); - assert(fd->type->nextOf()->ty == Tarray); - Type *elemType = ((TypeFunction *)fd->type)->nextOf()->nextOf(); e->type = new TypeSArray(elemType, new IntegerExp(aae->keys->dim)); return copyLiteral(e); } -Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclaration *fd) +Expression *interpret_values(InterState *istate, Expression *earg, Type *elemType) { #if LOG printf("interpret_values()\n"); @@ -5154,31 +5378,71 @@ Expression *interpret_values(InterState *istate, Expression *earg, FuncDeclarati assert(earg->op == TOKassocarrayliteral); AssocArrayLiteralExp *aae = (AssocArrayLiteralExp *)earg; Expression *e = new ArrayLiteralExp(aae->loc, aae->values); - assert(fd->type->ty == Tfunction); - assert(fd->type->nextOf()->ty == Tarray); - Type *elemType = ((TypeFunction *)fd->type)->nextOf()->nextOf(); e->type = new TypeSArray(elemType, new IntegerExp(aae->values->dim)); //printf("result is %s\n", e->toChars()); return copyLiteral(e); } -#endif +// signature is int delegate(ref Value) OR int delegate(ref Key, ref Value) +Expression *interpret_aaApply(InterState *istate, Expression *aa, Expression *deleg) +{ aa = aa->interpret(istate); + if (aa == EXP_CANT_INTERPRET) + return aa; + if (aa->op != TOKassocarrayliteral) + return new IntegerExp(deleg->loc, 0, Type::tsize_t); -#if DMDV2 -// 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 (t->ty != Tstruct) - return false; - StructDeclaration *sym = ((TypeStruct *)t)->sym; - if (sym->ident == Id::AssociativeArray) - return true; - return false; + FuncDeclaration *fd = NULL; + Expression *pthis = NULL; + if (deleg->op == TOKdelegate) + { + fd = ((DelegateExp *)deleg)->func; + pthis = ((DelegateExp *)deleg)->e1; + } + else if (deleg->op == TOKfunction) + fd = ((FuncExp*)deleg)->fd; + + assert(fd && fd->fbody); + assert(fd->parameters); + int numParams = fd->parameters->dim; + assert(numParams == 1 || numParams==2); + + Type *valueType = fd->parameters->tdata()[numParams-1]->type; + Type *keyType = numParams == 2 ? fd->parameters->tdata()[0]->type + : Type::tsize_t; + Expressions args; + args.setDim(numParams); + + AssocArrayLiteralExp *ae = (AssocArrayLiteralExp *)aa; + if (!ae->keys || ae->keys->dim == 0) + return new IntegerExp(deleg->loc, 0, Type::tsize_t); + Expression *eresult; + + for (size_t i = 0; i < ae->keys->dim; ++i) + { + Expression *ekey = ae->keys->tdata()[i]; + Expression *evalue = ae->values->tdata()[i]; + args.tdata()[numParams - 1] = evalue; + if (numParams == 2) args.tdata()[0] = ekey; + + eresult = fd->interpret(istate, &args, pthis); + if (eresult == EXP_CANT_INTERPRET) + return EXP_CANT_INTERPRET; + + assert(eresult->op == TOKint64); + if (((IntegerExp *)eresult)->value != 0) + return eresult; + } + return eresult; +} + +// Helper function: given a function of type A[] f(...), +// return A. +Type *returnedArrayElementType(FuncDeclaration *fd) +{ + assert(fd->type->ty == Tfunction); + assert(fd->type->nextOf()->ty == Tarray); + return ((TypeFunction *)fd->type)->nextOf()->nextOf(); } -#endif /* Decoding UTF strings for foreach loops. Duplicates the functionality of * the twelve _aApplyXXn functions in aApply.d in the runtime. @@ -5426,26 +5690,25 @@ Expression *foreachApplyUtf(InterState *istate, Expression *str, Expression *del return eresult; } -/* If this is a built-in function, set 'result' to the interpreted result, - * and return true. - * Otherwise, return false +/* If this is a built-in function, return the interpreted result, + * Otherwise, return NULL. */ -bool evaluateIfBuiltin(Expression **result, InterState *istate, +Expression *evaluateIfBuiltin(InterState *istate, Loc loc, FuncDeclaration *fd, Expressions *arguments, Expression *pthis) { Expression *e = NULL; int nargs = arguments ? arguments->dim : 0; #if DMDV2 - if (pthis && isAssocArray(pthis->type) && nargs==0) + if (pthis && isAssocArray(pthis->type)) { - if (fd->ident == Id::length) - e = interpret_length(istate, pthis); - else if (fd->ident == Id::keys) - e = interpret_keys(istate, pthis, fd); - else if (fd->ident == Id::values) - e = interpret_values(istate, pthis, fd); - else if (fd->ident == Id::rehash) - e = pthis; // rehash is a no-op + 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)); + else if (fd->ident == Id::values && nargs==0) + return interpret_values(istate, pthis, returnedArrayElementType(fd)); + else if (fd->ident == Id::rehash && nargs==0) + return pthis->interpret(istate, ctfeNeedLvalue); // rehash is a no-op } if (!pthis) { @@ -5458,31 +5721,67 @@ bool evaluateIfBuiltin(Expression **result, InterState *istate, Expression *earg = arguments->tdata()[i]; earg = earg->interpret(istate); if (earg == EXP_CANT_INTERPRET) - { - *result = EXP_CANT_INTERPRET; - return true; - } + return earg; args.tdata()[i] = earg; } - e = eval_builtin(b, &args); + e = eval_builtin(loc, b, &args); if (!e) 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 if (!pthis) { - if (fd->ident == Id::aaLen) - e = interpret_aaLen(istate, arguments); - else if (fd->ident == Id::aaKeys) - e = interpret_aaKeys(istate, arguments); - else if (fd->ident == Id::aaValues) - e = interpret_aaValues(istate, arguments); - else if (fd->ident == Id::aaRehash && nargs == 2) - { // rehash is a no-op - Expression *earg = (Expression *)(arguments->data[0]); - return earg->interpret(istate, ctfeNeedLvalue); + Expression *firstarg = nargs > 0 ? (Expression *)(arguments->data[0]) : NULL; + if (firstarg && firstarg->type->toBasetype()->ty == Taarray) + { + TypeAArray *firstAAtype = (TypeAArray *)firstarg->type; + 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); + else if (fd->ident == Id::aaValues) + return interpret_values(istate, firstarg, 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")) + return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); + else if (nargs==3 && !strcmp(fd->ident->string, "_aaApply2")) + return interpret_aaApply(istate, firstarg, (Expression *)(arguments->data[2])); } } #endif @@ -5503,19 +5802,12 @@ bool evaluateIfBuiltin(Expression **result, InterState *istate, { Expression *str = arguments->tdata()[0]; str = str->interpret(istate); if (str == EXP_CANT_INTERPRET) - { - *result = EXP_CANT_INTERPRET; - return true; - } - *result = foreachApplyUtf(istate, str, arguments->tdata()[1], rvs); - return true; + return str; + return foreachApplyUtf(istate, str, arguments->tdata()[1], rvs); } } } - if (!e) - return false; - *result = e; - return true; + return e; } /*************************** CTFE Sanity Checks ***************************/ diff --git a/dmd2/lexer.c b/dmd2/lexer.c index 08001dc5f7..9b4d4f46d8 100644 --- a/dmd2/lexer.c +++ b/dmd2/lexer.c @@ -102,7 +102,7 @@ void Token::print() const char *Token::toChars() { const char *p; - static char buffer[3 + 3 * sizeof(value) + 1]; + static char buffer[3 + 3 * sizeof(float80value) + 1]; p = buffer; switch (value) @@ -298,28 +298,21 @@ Lexer::Lexer(Module *mod, void Lexer::error(const char *format, ...) { - if (mod && !global.gag) - { - char *p = loc.toChars(); - if (*p) - fprintf(stdmsg, "%s: ", p); - mem.free(p); - - va_list ap; - va_start(ap, format); - vfprintf(stdmsg, format, ap); - va_end(ap); - - fprintf(stdmsg, "\n"); - fflush(stdmsg); - - if (global.errors >= 20) // moderate blizzard of cascading messages - fatal(); - } - global.errors++; + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); } void Lexer::error(Loc loc, const char *format, ...) +{ + va_list ap; + va_start(ap, format); + verror(loc, format, ap); + va_end(ap); +} + +void Lexer::verror(Loc loc, const char *format, va_list ap) { if (mod && !global.gag) { @@ -328,10 +321,7 @@ void Lexer::error(Loc loc, const char *format, ...) fprintf(stdmsg, "%s: ", p); mem.free(p); - va_list ap; - va_start(ap, format); vfprintf(stdmsg, format, ap); - va_end(ap); fprintf(stdmsg, "\n"); fflush(stdmsg); @@ -339,6 +329,10 @@ void Lexer::error(Loc loc, const char *format, ...) if (global.errors >= 20) // moderate blizzard of cascading messages fatal(); } + else + { + global.gaggedErrors++; + } global.errors++; } @@ -2286,9 +2280,11 @@ done: break; } +#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", n, p - psuffix, psuffix, n, p - psuffix, psuffix); +#endif switch (flags) { @@ -3041,6 +3037,8 @@ void Lexer::initKeywords() enum TOK v; unsigned nkeywords = sizeof(keywords) / sizeof(keywords[0]); + stringtable.init(); + if (global.params.Dversion == 1) nkeywords -= 2; diff --git a/dmd2/lexer.h b/dmd2/lexer.h index abe8df6d07..e7916d86ad 100644 --- a/dmd2/lexer.h +++ b/dmd2/lexer.h @@ -307,6 +307,7 @@ struct Lexer TOK inreal(Token *t); void error(const char *format, ...) IS_PRINTF(2); void error(Loc loc, const char *format, ...) IS_PRINTF(3); + void verror(Loc loc, const char *format, va_list ap); void pragma(); unsigned decodeUTF(); void getDocComment(Token *t, unsigned lineComment); diff --git a/dmd2/mars.c b/dmd2/mars.c index 233364b563..59ffa92e1d 100644 --- a/dmd2/mars.c +++ b/dmd2/mars.c @@ -100,7 +100,7 @@ Global::Global() "\nMSIL back-end (alpha release) by Cristian L. Vlasceanu and associates."; #endif ; - version = "v2.055"; + version = "v2.056"; #if IN_LLVM ldc_version = "LDC trunk"; llvm_version = "LLVM 2.9"; @@ -115,6 +115,24 @@ Global::Global() // may run before this one. } +unsigned Global::startGagging() +{ + ++gag; + return gaggedErrors; +} + +bool Global::endGagging(unsigned oldGagged) +{ + bool anyErrs = (gaggedErrors != oldGagged); + --gag; + // Restore the original state of gagged errors; set total errors + // to be original errors + new ungagged errors. + errors -= (gaggedErrors - oldGagged); + gaggedErrors = oldGagged; + return anyErrs; +} + + char *Loc::toChars() { OutBuffer buf; @@ -184,6 +202,10 @@ void verror(Loc loc, const char *format, va_list ap) fflush(stdmsg); //halt(); } + else + { + global.gaggedErrors++; + } global.errors++; } @@ -238,6 +260,1177 @@ void halt() #endif } +#if !IN_LLVM + +extern void backend_init(); +extern void backend_term(); + +void usage() +{ +#if TARGET_LINUX + const char fpic[] ="\ + -fPIC generate position independent code\n\ +"; +#else + const char fpic[] = ""; +#endif + printf("DMD%s D Compiler %s\n%s %s\n", + sizeof(size_t) == 4 ? "32" : "64", + global.version, global.copyright, global.written); + printf("\ +Documentation: http://www.digitalmars.com/d/2.0/index.html\n\ +Usage:\n\ + dmd files.d ... { -switch }\n\ +\n\ + files.d D source files\n\ + @cmdfile read arguments from cmdfile\n\ + -c do not link\n\ + -cov do code coverage analysis\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\ + -debug compile in debug code\n\ + -debug=level compile in debug code <= level\n\ + -debug=ident compile in debug code identified by ident\n\ + -debuglib=name set symbolic debug library to name\n\ + -defaultlib=name set default library to name\n\ + -deps=filename write module dependencies to filename\n%s" +#if TARGET_OSX +" -dylib generate dylib\n" +#endif +" -g add symbolic debug info\n\ + -gc add symbolic debug info, pretend to be C\n\ + -gs always emit stack frame\n\ + -H generate 'header' file\n\ + -Hddirectory write 'header' file to directory\n\ + -Hffilename write 'header' file to filename\n\ + --help print help\n\ + -Ipath where to look for imports\n\ + -ignore ignore unsupported pragmas\n\ + -inline do function inlining\n\ + -Jpath where to look for string imports\n\ + -Llinkerflag pass linkerflag to link\n\ + -lib generate library rather than object files\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\ + -offilename name output file to filename\n\ + -op do not strip paths from source file\n\ + -profile profile runtime performance of generated code\n\ + -property enforce property syntax\n\ + -quiet suppress unnecessary messages\n\ + -release compile release version\n\ + -run srcfile args... run resulting program, passing args\n\ + -unittest compile in unit tests\n\ + -v verbose\n\ + -version=level compile in version code >= level\n\ + -version=ident compile in version code identified by ident\n\ + -vtls list all variables going into thread local storage\n\ + -w enable warnings\n\ + -wi enable informational warnings\n\ + -X generate JSON file\n\ + -Xffilename write JSON file to filename\n\ +", fpic); +} + +extern signed char tyalignsize[]; + +#if _WIN32 +extern "C" +{ + extern int _xi_a; + extern int _end; +} +#endif + +int main(int argc, char *argv[]) +{ + mem.init(); // initialize storage allocator + mem.setStackBottom(&argv); +#if _WIN32 + mem.addroots((char *)&_xi_a, (char *)&_end); +#endif + + Strings files; + Strings libmodules; + char *p; + Module *m; + int status = EXIT_SUCCESS; + int argcstart = argc; + int setdebuglib = 0; + char noboundscheck = 0; + const char *inifilename = NULL; + +#ifdef DEBUG + printf("DMD %s DEBUG\n", global.version); +#endif + + unittests(); + + // Check for malformed input + if (argc < 1 || !argv) + { + Largs: + error("missing or null command line arguments"); + fatal(); + } + for (size_t i = 0; i < argc; i++) + { + if (!argv[i]) + goto Largs; + } + + if (response_expand(&argc,&argv)) // expand response files + error("can't open response file"); + + files.reserve(argc - 1); + + // Set default values + global.params.argv0 = argv[0]; + global.params.link = 1; + global.params.useAssert = 1; + global.params.useInvariants = 1; + global.params.useIn = 1; + global.params.useOut = 1; + global.params.useArrayBounds = 2; // default to all functions + global.params.useSwitchError = 1; + global.params.useInline = 0; + global.params.obj = 1; + global.params.Dversion = 2; + global.params.quiet = 1; + + global.params.linkswitches = new Strings(); + global.params.libfiles = new Strings(); + global.params.objfiles = new Strings(); + global.params.ddocfiles = new Strings(); + + // Default to -m32 for 32 bit dmd, -m64 for 64 bit dmd + global.params.is64bit = (sizeof(size_t) == 8); + +#if TARGET_WINDOS + global.params.defaultlibname = "phobos"; +#elif TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + global.params.defaultlibname = "phobos2"; +#elif TARGET_NET +#else +#error "fix this" +#endif + + // Predefine version identifiers + VersionCondition::addPredefinedGlobalIdent("DigitalMars"); + +#if TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("Windows"); + global.params.isWindows = 1; +#if TARGET_NET + // TARGET_NET macro is NOT mutually-exclusive with TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("D_NET"); +#endif +#elif TARGET_LINUX + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("linux"); + global.params.isLinux = 1; +#elif TARGET_OSX + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("OSX"); + global.params.isOSX = 1; + + // For legacy compatibility + VersionCondition::addPredefinedGlobalIdent("darwin"); +#elif TARGET_FREEBSD + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("FreeBSD"); + global.params.isFreeBSD = 1; +#elif TARGET_OPENBSD + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("OpenBSD"); + global.params.isFreeBSD = 1; +#elif TARGET_SOLARIS + VersionCondition::addPredefinedGlobalIdent("Posix"); + VersionCondition::addPredefinedGlobalIdent("Solaris"); + global.params.isSolaris = 1; +#else +#error "fix this" +#endif + + VersionCondition::addPredefinedGlobalIdent("LittleEndian"); + //VersionCondition::addPredefinedGlobalIdent("D_Bits"); +#if DMDV2 + VersionCondition::addPredefinedGlobalIdent("D_Version2"); +#endif + VersionCondition::addPredefinedGlobalIdent("all"); + +#if _WIN32 + inifilename = inifile(argv[0], "sc.ini"); +#elif linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ || __sun&&__SVR4 + inifilename = inifile(argv[0], "dmd.conf"); +#else +#error "fix this" +#endif + getenv_setargv("DFLAGS", &argc, &argv); + +#if 0 + for (size_t i = 0; i < argc; i++) + { + printf("argv[%d] = '%s'\n", i, argv[i]); + } +#endif + + for (size_t i = 1; i < argc; i++) + { + p = argv[i]; + if (*p == '-') + { + if (strcmp(p + 1, "d") == 0) + global.params.useDeprecated = 1; + else if (strcmp(p + 1, "c") == 0) + global.params.link = 0; + else if (strcmp(p + 1, "cov") == 0) + global.params.cov = 1; +#if TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_OPENBSD || TARGET_SOLARIS + else if (strcmp(p + 1, "fPIC") == 0) + global.params.pic = 1; +#endif +#if TARGET_OSX + else if (strcmp(p + 1, "dylib") == 0) + global.params.dll = 1; +#endif + else if (strcmp(p + 1, "map") == 0) + global.params.map = 1; + else if (strcmp(p + 1, "multiobj") == 0) + global.params.multiobj = 1; + else if (strcmp(p + 1, "g") == 0) + global.params.symdebug = 1; + else if (strcmp(p + 1, "gc") == 0) + global.params.symdebug = 2; + else if (strcmp(p + 1, "gs") == 0) + global.params.alwaysframe = 1; + else if (strcmp(p + 1, "gt") == 0) + { error("use -profile instead of -gt\n"); + global.params.trace = 1; + } + else if (strcmp(p + 1, "m32") == 0) + global.params.is64bit = 0; + else if (strcmp(p + 1, "m64") == 0) + global.params.is64bit = 1; + else if (strcmp(p + 1, "profile") == 0) + global.params.trace = 1; + else if (strcmp(p + 1, "v") == 0) + global.params.verbose = 1; +#if DMDV2 + else if (strcmp(p + 1, "vtls") == 0) + global.params.vtls = 1; +#endif + else if (strcmp(p + 1, "v1") == 0) + { +#if DMDV1 + global.params.Dversion = 1; +#else + error("use DMD 1.0 series compilers for -v1 switch"); + break; +#endif + } + else if (strcmp(p + 1, "w") == 0) + global.params.warnings = 1; + else if (strcmp(p + 1, "wi") == 0) + global.params.warnings = 2; + else if (strcmp(p + 1, "O") == 0) + global.params.optimize = 1; + else if (p[1] == 'o') + { + switch (p[2]) + { + case '-': + global.params.obj = 0; + break; + + case 'd': + if (!p[3]) + goto Lnoarg; + global.params.objdir = p + 3; + break; + + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.objname = p + 3; + break; + + case 'p': + if (p[3]) + goto Lerror; + global.params.preservePaths = 1; + break; + + case 0: + error("-o no longer supported, use -of or -od"); + break; + + default: + goto Lerror; + } + } + else if (p[1] == 'D') + { global.params.doDocComments = 1; + switch (p[2]) + { + case 'd': + if (!p[3]) + goto Lnoarg; + global.params.docdir = p + 3; + break; + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.docname = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } + else if (p[1] == 'H') + { global.params.doHdrGeneration = 1; + switch (p[2]) + { + case 'd': + if (!p[3]) + goto Lnoarg; + global.params.hdrdir = p + 3; + break; + + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.hdrname = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } + else if (p[1] == 'X') + { global.params.doXGeneration = 1; + switch (p[2]) + { + case 'f': + if (!p[3]) + goto Lnoarg; + global.params.xfilename = p + 3; + break; + + case 0: + break; + + default: + goto Lerror; + } + } + else if (strcmp(p + 1, "ignore") == 0) + global.params.ignoreUnsupportedPragmas = 1; + else if (strcmp(p + 1, "property") == 0) + global.params.enforcePropertySyntax = 1; + else if (strcmp(p + 1, "inline") == 0) + global.params.useInline = 1; + else if (strcmp(p + 1, "lib") == 0) + global.params.lib = 1; + else if (strcmp(p + 1, "nofloat") == 0) + global.params.nofloat = 1; + else if (strcmp(p + 1, "quiet") == 0) + global.params.quiet = 1; + else if (strcmp(p + 1, "release") == 0) + global.params.release = 1; +#if DMDV2 + else if (strcmp(p + 1, "noboundscheck") == 0) + noboundscheck = 1; +#endif + else if (strcmp(p + 1, "unittest") == 0) + global.params.useUnitTests = 1; + else if (p[1] == 'I') + { + if (!global.params.imppath) + global.params.imppath = new Strings(); + global.params.imppath->push(p + 2); + } + else if (p[1] == 'J') + { + if (!global.params.fileImppath) + global.params.fileImppath = new Strings(); + global.params.fileImppath->push(p + 2); + } + else if (memcmp(p + 1, "debug", 5) == 0 && p[6] != 'l') + { + // Parse: + // -debug + // -debug=number + // -debug=identifier + if (p[6] == '=') + { + if (isdigit((unsigned char)p[7])) + { long level; + + errno = 0; + level = strtol(p + 7, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + DebugCondition::setGlobalLevel((int)level); + } + else if (Lexer::isValidIdentifier(p + 7)) + DebugCondition::addGlobalIdent(p + 7); + else + goto Lerror; + } + else if (p[6]) + goto Lerror; + else + global.params.debuglevel = 1; + } + else if (memcmp(p + 1, "version", 5) == 0) + { + // Parse: + // -version=number + // -version=identifier + if (p[8] == '=') + { + if (isdigit((unsigned char)p[9])) + { long level; + + errno = 0; + level = strtol(p + 9, &p, 10); + if (*p || errno || level > INT_MAX) + goto Lerror; + VersionCondition::setGlobalLevel((int)level); + } + else if (Lexer::isValidIdentifier(p + 9)) + VersionCondition::addGlobalIdent(p + 9); + else + goto Lerror; + } + else + goto Lerror; + } + else if (strcmp(p + 1, "-b") == 0) + global.params.debugb = 1; + else if (strcmp(p + 1, "-c") == 0) + global.params.debugc = 1; + else if (strcmp(p + 1, "-f") == 0) + global.params.debugf = 1; + else if (strcmp(p + 1, "-help") == 0) + { usage(); + exit(EXIT_SUCCESS); + } + else if (strcmp(p + 1, "-r") == 0) + global.params.debugr = 1; + else if (strcmp(p + 1, "-x") == 0) + global.params.debugx = 1; + else if (strcmp(p + 1, "-y") == 0) + global.params.debugy = 1; + else if (p[1] == 'L') + { + global.params.linkswitches->push(p + 2); + } + else if (memcmp(p + 1, "defaultlib=", 11) == 0) + { + global.params.defaultlibname = p + 1 + 11; + } + else if (memcmp(p + 1, "debuglib=", 9) == 0) + { + setdebuglib = 1; + global.params.debuglibname = p + 1 + 9; + } + else if (memcmp(p + 1, "deps=", 5) == 0) + { + global.params.moduleDepsFile = p + 1 + 5; + if (!global.params.moduleDepsFile[0]) + goto Lnoarg; + global.params.moduleDeps = new OutBuffer; + } + else if (memcmp(p + 1, "man", 3) == 0) + { +#if _WIN32 +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-windows.html"); +#else + browse("http://www.digitalmars.com/d/2.0/dmd-windows.html"); +#endif +#endif +#if linux +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-linux.html"); +#else + browse("http://www.digitalmars.com/d/2.0/dmd-linux.html"); +#endif +#endif +#if __APPLE__ +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-osx.html"); +#else + browse("http://www.digitalmars.com/d/2.0/dmd-osx.html"); +#endif +#endif +#if __FreeBSD__ +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-freebsd.html"); +#else + browse("http://www.digitalmars.com/d/2.0/dmd-freebsd.html"); +#endif +#endif +#if __OpenBSD__ +#if DMDV1 + browse("http://www.digitalmars.com/d/1.0/dmd-openbsd.html"); +#else + browse("http://www.digitalmars.com/d/2.0/dmd-openbsd.html"); +#endif +#endif + exit(EXIT_SUCCESS); + } + else if (strcmp(p + 1, "run") == 0) + { global.params.run = 1; + global.params.runargs_length = ((i >= argcstart) ? argc : argcstart) - i - 1; + if (global.params.runargs_length) + { + files.push(argv[i + 1]); + global.params.runargs = &argv[i + 2]; + i += global.params.runargs_length; + global.params.runargs_length--; + } + else + { global.params.run = 0; + goto Lnoarg; + } + } + else + { + Lerror: + error("unrecognized switch '%s'", argv[i]); + continue; + + Lnoarg: + error("argument expected for switch '%s'", argv[i]); + continue; + } + } + else + { +#if TARGET_WINDOS + char *ext = FileName::ext(p); + if (ext && FileName::compare(ext, "exe") == 0) + { + global.params.objname = p; + continue; + } +#endif + files.push(p); + } + } + if (global.errors) + { + fatal(); + } + if (files.dim == 0) + { usage(); + return EXIT_FAILURE; + } + + if (!setdebuglib) + global.params.debuglibname = global.params.defaultlibname; + +#if TARGET_OSX + global.params.pic = 1; +#endif + + if (global.params.release) + { global.params.useInvariants = 0; + global.params.useIn = 0; + global.params.useOut = 0; + global.params.useAssert = 0; + global.params.useArrayBounds = 1; + global.params.useSwitchError = 0; + } + if (noboundscheck) + global.params.useArrayBounds = 0; + + if (global.params.run) + global.params.quiet = 1; + + if (global.params.useUnitTests) + global.params.useAssert = 1; + + if (!global.params.obj || global.params.lib) + global.params.link = 0; + + if (global.params.link) + { + global.params.exefile = global.params.objname; + global.params.oneobj = 1; + if (global.params.objname) + { + /* Use this to name the one object file with the same + * name as the exe file. + */ + global.params.objname = FileName::forceExt(global.params.objname, global.obj_ext)->toChars(); + + /* If output directory is given, use that path rather than + * the exe file path. + */ + if (global.params.objdir) + { char *name = FileName::name(global.params.objname); + global.params.objname = FileName::combine(global.params.objdir, name); + } + } + } + else if (global.params.lib) + { + global.params.libname = global.params.objname; + global.params.objname = NULL; + + // Haven't investigated handling these options with multiobj + if (!global.params.cov && !global.params.trace +#if 0 && TARGET_WINDOS + /* multiobj causes class/struct debug info to be attached to init-data, + * but this will not be linked into the executable, so this info is lost. + * Bugzilla 4014 + */ + && !global.params.symdebug +#endif + ) + global.params.multiobj = 1; + } + else if (global.params.run) + { + error("flags conflict with -run"); + fatal(); + } + else + { + if (global.params.objname && files.dim > 1) + { + global.params.oneobj = 1; + //error("multiple source files, but only one .obj name"); + //fatal(); + } + } + if (global.params.is64bit) + { + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86_64"); + VersionCondition::addPredefinedGlobalIdent("X86_64"); + VersionCondition::addPredefinedGlobalIdent("D_LP64"); +#if TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("Win64"); +#endif + } + else + { + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm"); + VersionCondition::addPredefinedGlobalIdent("D_InlineAsm_X86"); + VersionCondition::addPredefinedGlobalIdent("X86"); +#if TARGET_WINDOS + VersionCondition::addPredefinedGlobalIdent("Win32"); +#endif + } + if (global.params.doDocComments) + VersionCondition::addPredefinedGlobalIdent("D_Ddoc"); + if (global.params.cov) + VersionCondition::addPredefinedGlobalIdent("D_Coverage"); + if (global.params.pic) + VersionCondition::addPredefinedGlobalIdent("D_PIC"); +#if DMDV2 + if (global.params.useUnitTests) + VersionCondition::addPredefinedGlobalIdent("unittest"); +#endif + + // Initialization + Type::init(); + Id::initialize(); + Module::init(); + initPrecedence(); + + if (global.params.verbose) + { printf("binary %s\n", argv[0]); + printf("version %s\n", global.version); + printf("config %s\n", inifilename ? inifilename : "(none)"); + } + + //printf("%d source files\n",files.dim); + + // Build import search path + if (global.params.imppath) + { + for (size_t i = 0; i < global.params.imppath->dim; i++) + { + char *path = (*global.params.imppath)[i]; + Strings *a = FileName::splitPath(path); + + if (a) + { + if (!global.path) + global.path = new Strings(); + global.path->append(a); + } + } + } + + // Build string import search path + if (global.params.fileImppath) + { + for (size_t i = 0; i < global.params.fileImppath->dim; i++) + { + char *path = global.params.fileImppath->tdata()[i]; + Strings *a = FileName::splitPath(path); + + if (a) + { + if (!global.filePath) + global.filePath = new Strings(); + global.filePath->append(a); + } + } + } + + // Create Modules + Modules modules; + modules.reserve(files.dim); + int firstmodule = 1; + for (size_t i = 0; i < files.dim; i++) + { + char *ext; + char *name; + + p = files.tdata()[i]; + +#if _WIN32 + // Convert / to \ so linker will work + for (size_t i = 0; p[i]; i++) + { + if (p[i] == '/') + p[i] = '\\'; + } +#endif + + p = FileName::name(p); // strip path + ext = FileName::ext(p); + if (ext) + { /* Deduce what to do with a file based on its extension + */ + if (FileName::equals(ext, global.obj_ext)) + { + global.params.objfiles->push(files.tdata()[i]); + libmodules.push(files.tdata()[i]); + continue; + } + + if (FileName::equals(ext, global.lib_ext)) + { + global.params.libfiles->push(files.tdata()[i]); + libmodules.push(files.tdata()[i]); + continue; + } + + if (strcmp(ext, global.ddoc_ext) == 0) + { + global.params.ddocfiles->push(files.tdata()[i]); + continue; + } + + if (FileName::equals(ext, global.json_ext)) + { + global.params.doXGeneration = 1; + global.params.xfilename = files.tdata()[i]; + continue; + } + + if (FileName::equals(ext, global.map_ext)) + { + global.params.mapfile = files.tdata()[i]; + continue; + } + +#if TARGET_WINDOS + if (FileName::equals(ext, "res")) + { + global.params.resfile = files.tdata()[i]; + continue; + } + + if (FileName::equals(ext, "def")) + { + global.params.deffile = files.tdata()[i]; + continue; + } + + if (FileName::equals(ext, "exe")) + { + assert(0); // should have already been handled + } +#endif + + /* Examine extension to see if it is a valid + * D source file extension + */ + if (FileName::equals(ext, global.mars_ext) || + FileName::equals(ext, global.hdr_ext) || + FileName::equals(ext, "dd") || + FileName::equals(ext, "htm") || + FileName::equals(ext, "html") || + FileName::equals(ext, "xhtml")) + { + ext--; // skip onto '.' + assert(*ext == '.'); + name = (char *)mem.malloc((ext - p) + 1); + memcpy(name, p, ext - p); + name[ext - p] = 0; // strip extension + + if (name[0] == 0 || + strcmp(name, "..") == 0 || + strcmp(name, ".") == 0) + { + Linvalid: + error("invalid file name '%s'", files.tdata()[i]); + fatal(); + } + } + else + { error("unrecognized file extension %s\n", ext); + fatal(); + } + } + else + { name = p; + if (!*name) + goto Linvalid; + } + + /* At this point, name is the D source file name stripped of + * its path and extension. + */ + + Identifier *id = Lexer::idPool(name); + m = new Module(files[i], id, global.params.doDocComments, global.params.doHdrGeneration); + modules.push(m); + + if (firstmodule) + { global.params.objfiles->push(m->objfile->name->str); + firstmodule = 0; + } + } + +#if WINDOWS_SEH + __try + { +#endif + // Read files +#define ASYNCREAD 1 +#if ASYNCREAD + // Multi threaded + AsyncRead *aw = AsyncRead::create(modules.dim); + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + aw->addFile(m->srcfile); + } + aw->start(); +#else + // Single threaded + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + m->read(0); + } +#endif + + // Parse files + bool anydocfiles = false; + size_t filecount = modules.dim; + for (size_t filei = 0, modi = 0; filei < filecount; filei++, modi++) + { + m = modules[modi]; + if (global.params.verbose) + printf("parse %s\n", m->toChars()); + if (!Module::rootModule) + Module::rootModule = m; + m->importedFrom = m; + if (!global.params.oneobj || modi == 0 || m->isDocFile) + m->deleteObjFile(); +#if ASYNCREAD + if (aw->read(filei)) + { + error("cannot read file %s", m->srcfile->name->toChars()); + } +#endif + m->parse(); + if (m->isDocFile) + { + anydocfiles = true; + m->gendocfile(); + + // Remove m from list of modules + modules.remove(modi); + modi--; + + // Remove m's object file from list of object files + for (size_t j = 0; j < global.params.objfiles->dim; j++) + { + if (m->objfile->name->str == global.params.objfiles->tdata()[j]) + { + global.params.objfiles->remove(j); + break; + } + } + + if (global.params.objfiles->dim == 0) + global.params.link = 0; + } + } +#if ASYNCREAD + AsyncRead::dispose(aw); +#endif + + if (anydocfiles && modules.dim && + (global.params.oneobj || global.params.objname)) + { + error("conflicting Ddoc and obj generation options"); + fatal(); + } + if (global.errors) + fatal(); + if (global.params.doHdrGeneration) + { + /* Generate 'header' import files. + * Since 'header' import files must be independent of command + * line switches and what else is imported, they are generated + * before any semantic analysis. + */ + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("import %s\n", m->toChars()); + m->genhdrfile(); + } + } + if (global.errors) + fatal(); + + // load all unconditional imports for better symbol resolving + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("importall %s\n", m->toChars()); + m->importAll(0); + } + if (global.errors) + fatal(); + + backend_init(); + + // Do semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("semantic %s\n", m->toChars()); + m->semantic(); + } + if (global.errors) + fatal(); + + Module::dprogress = 1; + Module::runDeferredSemantic(); + + // Do pass 2 semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("semantic2 %s\n", m->toChars()); + m->semantic2(); + } + if (global.errors) + fatal(); + + // Do pass 3 semantic analysis + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("semantic3 %s\n", m->toChars()); + m->semantic3(); + } + if (global.errors) + fatal(); + + if (global.params.moduleDeps != NULL) + { + assert(global.params.moduleDepsFile != NULL); + + File deps(global.params.moduleDepsFile); + OutBuffer* ob = global.params.moduleDeps; + deps.setbuffer((void*)ob->data, ob->offset); + deps.writev(); + } + + + // Scan for functions to inline + if (global.params.useInline) + { + /* The problem with useArrayBounds and useAssert is that the + * module being linked to may not have generated them, so if + * we inline functions from those modules, the symbols for them will + * not be found at link time. + */ + if (!global.params.useArrayBounds && !global.params.useAssert) + { + // Do pass 3 semantic analysis on all imported modules, + // since otherwise functions in them cannot be inlined + for (size_t i = 0; i < Module::amodules.dim; i++) + { + m = Module::amodules[i]; + if (global.params.verbose) + printf("semantic3 %s\n", m->toChars()); + m->semantic3(); + } + if (global.errors) + fatal(); + } + + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("inline scan %s\n", m->toChars()); + m->inlineScan(); + } + } + + // Do not attempt to generate output files if errors or warnings occurred + if (global.errors || global.warnings) + fatal(); + + Library *library = NULL; + if (global.params.lib) + { + library = new Library(); + library->setFilename(global.params.objdir, global.params.libname); + + // Add input object and input library files to output library + for (size_t i = 0; i < libmodules.dim; i++) + { + char *p = libmodules[i]; + library->addObject(p, NULL, 0); + } + } + + // Generate output files + + if (global.params.doXGeneration) + json_generate(&modules); + + if (global.params.oneobj) + { + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("code %s\n", m->toChars()); + if (i == 0) + obj_start(m->srcfile->toChars()); + m->genobjfile(0); + if (!global.errors && global.params.doDocComments) + m->gendocfile(); + } + if (!global.errors && modules.dim) + { + obj_end(library, modules.tdata()[0]->objfile); + } + } + else + { + for (size_t i = 0; i < modules.dim; i++) + { + m = modules[i]; + if (global.params.verbose) + printf("code %s\n", m->toChars()); + if (global.params.obj) + { obj_start(m->srcfile->toChars()); + m->genobjfile(global.params.multiobj); + obj_end(library, m->objfile); + obj_write_deferred(library); + } + if (global.errors) + { + if (!global.params.lib) + m->deleteObjFile(); + } + else + { + if (global.params.doDocComments) + m->gendocfile(); + } + } + } + + if (global.params.lib && !global.errors) + library->write(); + +#if WINDOWS_SEH + } + __except (__ehfilter(GetExceptionInformation())) + { + printf("Stack overflow\n"); + fatal(); + } +#endif + backend_term(); + if (global.errors) + fatal(); + + if (!global.params.objfiles->dim) + { + if (global.params.link) + error("no object files to link"); + } + else + { + if (global.params.link) + status = runLINK(); + + if (global.params.run) + { + if (!status) + { + status = runProgram(); + + /* Delete .obj files and .exe file + */ + for (size_t i = 0; i < modules.dim; i++) + { + Module *m = modules[i]; + m->deleteObjFile(); + if (global.params.oneobj) + break; + } + deleteExeFile(); + } + } + } + + return status; +} + +#endif // !IN_LLVM + /*********************************** * Parse and append contents of environment variable envvar * to argc and argv[]. diff --git a/dmd2/mars.h b/dmd2/mars.h index 29ddcd1c32..0a01036e69 100644 --- a/dmd2/mars.h +++ b/dmd2/mars.h @@ -189,6 +189,7 @@ struct Param #endif ARCH cpu; // target CPU OS os; + bool alwaysframe; // always emit standard stack frame char map; // generate linker .map file bool isLE; // generate little endian code bool is64bit; // generate 64 bit code @@ -307,9 +308,18 @@ struct Global #endif Param params; - unsigned errors; // number of errors reported so far - unsigned warnings; // number of warnings reported so far - unsigned gag; // !=0 means gag reporting of errors & warnings + unsigned errors; // number of errors reported so far + unsigned warnings; // number of warnings reported so far + unsigned gag; // !=0 means gag reporting of errors & warnings + unsigned gaggedErrors; // number of errors reported while gagged + + // Start gagging. Return the current number of gagged errors + unsigned startGagging(); + + /* End gagging, restoring the old gagged state. + * Return true if errors occured while gagged. + */ + bool endGagging(unsigned oldGagged); Global(); }; diff --git a/dmd2/mem.c b/dmd2/mem.c index cdd0958c2e..360976922e 100644 --- a/dmd2/mem.c +++ b/dmd2/mem.c @@ -133,6 +133,10 @@ void Mem::mark(void *pointer) (void) pointer; // necessary for VC /W4 } +void Mem::setStackBottom(void */*bottom*/) +{ +} + /* =================================================== */ void * operator new(size_t m_size) @@ -262,4 +266,8 @@ void Mem::mark(void *pointer) { } +void Mem::setStackBottom(void */*stackbottom*/) +{ +} + #endif // USE_BOEHM_GC diff --git a/dmd2/module.c b/dmd2/module.c index 781308597a..2852cb1bb4 100644 --- a/dmd2/module.c +++ b/dmd2/module.c @@ -475,7 +475,7 @@ Module *Module::load(Loc loc, Identifiers *packages, Identifier *ident) { for (size_t i = 0; i < global.path->dim; i++) { - char *p = global.path->tdata()[i]; + char *p = (*global.path)[i]; char *n = FileName::combine(p, sdi); if (FileName::exists(n)) { result = n; @@ -795,6 +795,11 @@ void Module::parse() #endif p.nextToken(); members = p.parseModule(); + + ::free(srcfile->buffer); + srcfile->buffer = NULL; + srcfile->len = 0; + md = p.md; numlines = p.loc.linnum; @@ -855,7 +860,7 @@ void Module::importAll(Scope *prevsc) // Add import of "object" if this module isn't "object" if (ident != Id::object) { - if (members->dim == 0 || members->tdata()[0]->ident != Id::object) + if (members->dim == 0 || ((*members)[0])->ident != Id::object) { Import *im = new Import(0, NULL, Id::object, NULL, 0); members->shift(im); diff --git a/dmd2/module.h b/dmd2/module.h index 80bf87ab72..22c8de2e6e 100644 --- a/dmd2/module.h +++ b/dmd2/module.h @@ -211,7 +211,7 @@ struct Module : Package llvm::StructType* moduleInfoType; // array ops emitted in this module already - StringTable arrayfuncs; + AA *arrayfuncs; bool isRoot; #endif diff --git a/dmd2/mtype.c b/dmd2/mtype.c index f2c9fb845a..2034a7d63d 100644 --- a/dmd2/mtype.c +++ b/dmd2/mtype.c @@ -198,6 +198,10 @@ void Type::init(Ir* _sir) void Type::init() #endif { + stringtable.init(); +#if IN_LLVM + deco_stringtable.init(); +#endif Lexer::initKeywords(); for (size_t i = 0; i < TMAX; i++) @@ -362,13 +366,10 @@ Type *Type::semantic(Loc loc, Scope *sc) Type *Type::trySemantic(Loc loc, Scope *sc) { //printf("+trySemantic(%s) %d\n", toChars(), global.errors); - unsigned errors = global.errors; - global.gag++; // suppress printing of error messages + unsigned errors = global.startGagging(); Type *t = semantic(loc, sc); - global.gag--; - if (errors != global.errors) // if any errors happened + if (global.endGagging(errors)) // if any errors happened { - global.errors = errors; t = NULL; } //printf("-trySemantic(%s) %d\n", toChars(), global.errors); @@ -1098,6 +1099,37 @@ Type *Type::makeMutable() return t; } +/************************************* + * Apply STCxxxx bits to existing type. + * Use *before* semantic analysis is run. + */ + +Type *Type::addSTC(StorageClass stc) +{ Type *t = this; + + if (stc & STCconst) + { if (t->isShared()) + t = t->makeSharedConst(); + else + t = t->makeConst(); + } + if (stc & STCimmutable) + t = t->makeInvariant(); + if (stc & STCshared) + { if (t->isConst()) + t = t->makeSharedConst(); + else + t = t->makeShared(); + } + if (stc & STCwild) + { if (t->isShared()) + t = t->makeSharedWild(); + else + t = t->makeWild(); + } + return t; +} + /************************************ * Apply MODxxxx bits to existing type. */ @@ -1531,6 +1563,7 @@ void Type::modToBuffer(OutBuffer *buf) Type *Type::merge() { Type *t; + if (ty == Terror) return this; //printf("merge(%s)\n", toChars()); t = this; assert(t); @@ -2056,11 +2089,96 @@ int Type::hasWild() * Return MOD bits matching argument type (targ) to wild parameter type (this). */ +unsigned getWildModConv(Type *twild, Type *targ) +{ + assert(twild); + assert(targ); + + unsigned mod = 0; + + if (twild->nextOf()) + mod = getWildModConv(twild->nextOf(), targ->nextOf()); + + if (!mod) + { + if (twild->isWild()) + { + if (targ->isWild()) + mod = MODwild; + else if (targ->isConst()) + mod = MODconst; + else if (targ->isImmutable()) + mod = MODimmutable; + else if (targ->isMutable()) + mod = MODmutable; + else + assert(0); + } + } + + return mod; +} + unsigned Type::wildMatch(Type *targ) { + //printf("Type::wildMatch this = '%s', targ = '%s'\n", toChars(), targ->toChars()); + assert(hasWild()); + + Type *tc = substWildTo(MODconst); + if (targ->implicitConvTo(tc)) + return getWildModConv(this, targ); + return 0; } +Type *Type::substWildTo(unsigned mod) +{ + //printf("+Type::substWildTo this = %s, mod = x%x\n", toChars(), mod); + Type *t; + + if (nextOf()) + { + t = nextOf()->substWildTo(mod); + if (t == nextOf()) + t = this; + else + { + if (ty == Tpointer) + t = t->pointerTo(); + else if (ty == Tarray) + t = t->arrayOf(); + else if (ty == Tsarray) + t = new TypeSArray(t, ((TypeSArray *)this)->dim->syntaxCopy()); + else if (ty == Taarray) + { + t = new TypeAArray(t, ((TypeAArray *)this)->index->syntaxCopy()); + t = t->merge(); + } + else + assert(0); + + t = t->addMod(this->mod); + } + } + else + t = this; + + if (isWild()) + { + if (mod & MODconst) + t = t->constOf(); + else if (mod & MODimmutable) + t = t->invariantOf(); + else if (mod & MODwild) + t = t->wildOf(); + else + t = t->mutableOf(); + } + + //printf("-Type::substWildTo t = %s\n", t->toChars()); + return t; +} + /******************************** * We've mistakenly parsed this as a type. * Redo it as an Expression. @@ -2168,30 +2286,7 @@ Type *TypeNext::reliesOnTident() int TypeNext::hasWild() { - return mod == MODwild || next->hasWild(); -} - -/*************************************** - * Return MOD bits matching argument type (targ) to wild parameter type (this). - */ - -unsigned TypeNext::wildMatch(Type *targ) -{ unsigned mod; - - Type *tb = targ->nextOf(); - if (!tb) - return 0; - tb = tb->toBasetype(); - if (tb->isMutable()) - mod = MODmutable; - else if (tb->isConst() || tb->isWild()) - return MODconst; - else if (tb->isImmutable()) - mod = MODimmutable; - else - assert(0); - mod |= next->wildMatch(tb); - return mod; + return mod & MODwild || (next && next->hasWild()); } @@ -3152,6 +3247,11 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) #if LOGDOTEXP printf("TypeArray::dotExp(e = '%s', ident = '%s')\n", e->toChars(), ident->toChars()); #endif + + if (!n->isMutable()) + if (ident == Id::sort || ident == Id::reverse) + error(e->loc, "can only %s a mutable array\n", ident->toChars()); + if (ident == Id::reverse && (n->ty == Tchar || n->ty == Twchar)) { Expression *ec; @@ -3221,6 +3321,7 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) int size = next->size(e->loc); int dup; + Expression *olde = e; assert(size); dup = (ident == Id::dup || ident == Id::idup); //LDC: Build arguments. @@ -3263,9 +3364,18 @@ Expression *TypeArray::dotExp(Scope *sc, Expression *e, Identifier *ident) if (ident == Id::idup) { Type *einv = next->invariantOf(); if (next->implicitConvTo(einv) < MATCHconst) - error(e->loc, "cannot implicitly convert element type %s to immutable", next->toChars()); + error(e->loc, "cannot implicitly convert element type %s to immutable in %s.idup", + next->toChars(), olde->toChars()); e->type = einv->arrayOf(); } + else if (ident == Id::dup) + { + Type *emut = next->mutableOf(); + if (next->implicitConvTo(emut) < MATCHconst) + error(e->loc, "cannot implicitly convert element type %s to mutable in %s.dup", + next->toChars(), olde->toChars()); + e->type = emut->arrayOf(); + } else e->type = next->mutableOf()->arrayOf(); } @@ -3479,10 +3589,11 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) return t; } - next = next->semantic(loc,sc); - transitive(); + Type *tn = next->semantic(loc,sc); + if (tn->ty == Terror) + return terror; - Type *tbn = next->toBasetype(); + Type *tbn = tn->toBasetype(); if (dim) { dinteger_t n, n2; @@ -3571,7 +3682,9 @@ Type *TypeSArray::semantic(Loc loc, Scope *sc) /* Ensure things like const(immutable(T)[3]) become immutable(T[3]) * and const(T)[3] become const(T[3]) */ - t = addMod(next->mod); + next = tn; + transitive(); + t = addMod(tn->mod); return t->merge(); @@ -3860,6 +3973,8 @@ Expression *TypeDArray::dotExp(Scope *sc, Expression *e, Identifier *ident) return new IntegerExp(se->loc, se->len, Type::tindex); } + if (e->op == TOKnull) + return new IntegerExp(e->loc, 0, Type::tindex); e = new ArrayLengthExp(e->loc, e); e->type = Type::tsize_t; return e; @@ -4002,6 +4117,9 @@ d_uns64 TypeAArray::size(Loc loc) Type *TypeAArray::semantic(Loc loc, Scope *sc) { //printf("TypeAArray::semantic() %s index->ty = %d\n", toChars(), index->ty); + if (deco) + return this; + this->loc = loc; this->sc = sc; if (sc) @@ -4614,6 +4732,9 @@ TypeFunction::TypeFunction(Parameters *parameters, Type *treturn, int varargs, e if (stc & STCproperty) this->isproperty = true; + if (stc & STCref) + this->isref = true; + this->trust = TRUSTdefault; if (stc & STCsafe) this->trust = TRUSTsafe; @@ -4703,7 +4824,12 @@ int Type::covariant(Type *t) } } else if (t1->parameters != t2->parameters) - goto Ldistinct; + { + size_t dim1 = !t1->parameters ? 0 : t1->parameters->dim; + size_t dim2 = !t2->parameters ? 0 : t2->parameters->dim; + if (dim1 || dim2) + goto Ldistinct; + } // The argument lists match if (inoutmismatch) @@ -4879,8 +5005,6 @@ void TypeFunction::toCBuffer(OutBuffer *buf, Identifier *ident, HdrGenState *hgs void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td) { //printf("TypeFunction::toCBuffer() this = %p\n", this); - const char *p = NULL; - if (inuse) { inuse = 2; // flag error to caller return; @@ -4919,19 +5043,16 @@ void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, Hd break; } - if (next && (!ident || ident->toHChars2() == ident->toChars())) - next->toCBuffer2(buf, hgs, 0); - else if (hgs->ddoc && !next) - buf->writestring("auto"); if (hgs->ddoc != 1) { + const char *p = NULL; switch (attrs->linkage) { case LINKd: p = NULL; break; - case LINKc: p = "C "; break; - case LINKwindows: p = "Windows "; break; - case LINKpascal: p = "Pascal "; break; - case LINKcpp: p = "C++ "; break; + case LINKc: p = "C"; break; + case LINKwindows: p = "Windows"; break; + case LINKpascal: p = "Pascal"; break; + case LINKcpp: p = "C++"; break; // LDC case LINKintrinsic: p = "Intrinsic"; break; @@ -4939,14 +5060,28 @@ void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, Hd default: assert(0); } + if (!hgs->hdrgen && p) + { + buf->writestring("extern ("); + buf->writestring(p); + buf->writestring(") "); + } + } + + if (!ident || ident->toHChars2() == ident->toChars()) + { if (next) + next->toCBuffer2(buf, hgs, 0); + else if (hgs->ddoc) + buf->writestring("auto"); } - if (!hgs->hdrgen && p) - buf->writestring(p); if (ident) - { buf->writeByte(' '); + { + if (next || hgs->ddoc) + buf->writeByte(' '); buf->writestring(ident->toHChars2()); } + if (td) { buf->writeByte('('); for (size_t i = 0; i < td->origParameters->dim; i++) @@ -4965,24 +5100,21 @@ void TypeFunction::toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, Hd void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) { //printf("TypeFunction::toCBuffer2() this = %p, ref = %d\n", this, isref); - const char *p = NULL; - if (inuse) { inuse = 2; // flag error to caller return; } inuse++; - if (next) - next->toCBuffer2(buf, hgs, 0); if (hgs->ddoc != 1) { + const char *p = NULL; switch (linkage) { case LINKd: p = NULL; break; - case LINKc: p = " C"; break; - case LINKwindows: p = " Windows"; break; - case LINKpascal: p = " Pascal"; break; - case LINKcpp: p = " C++"; break; + case LINKc: p = "C"; break; + case LINKwindows: p = "Windows"; break; + case LINKpascal: p = "Pascal"; break; + case LINKcpp: p = "C++"; break; // LDC case LINKintrinsic: p = "Intrinsic"; break; @@ -4990,11 +5122,19 @@ void TypeFunction::toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod) default: assert(0); } + if (!hgs->hdrgen && p) + { + buf->writestring("extern ("); + buf->writestring(p); + buf->writestring(") "); + } } - - if (!hgs->hdrgen && p) - buf->writestring(p); - buf->writestring(" function"); + if (next) + { + next->toCBuffer2(buf, hgs, 0); + buf->writeByte(' '); + } + buf->writestring("function"); Parameter::argsToCBuffer(buf, hgs, parameters, varargs); attributesToCBuffer(buf, mod); inuse--; @@ -5091,7 +5231,10 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) bool wildreturn = FALSE; if (tf->next) { + sc = sc->push(); + sc->stc &= ~(STC_TYPECTOR | STC_FUNCATTR); tf->next = tf->next->semantic(loc,sc); + sc = sc->pop(); #if !SARRAYVALUE if (tf->next->toBasetype()->ty == Tsarray) { error(loc, "functions cannot return static array %s", tf->next->toChars()); @@ -5110,7 +5253,8 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) error(loc, "functions cannot return scope %s", tf->next->toChars()); if (tf->next->toBasetype()->ty == Tvoid) tf->isref = FALSE; // rewrite "ref void" as just "void" - if (tf->next->isWild()) + if (tf->next->hasWild() && + !(tf->next->ty == Tpointer && tf->next->nextOf()->ty == Tfunction || tf->next->ty == Tdelegate)) wildreturn = TRUE; } @@ -5152,7 +5296,8 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) if (!(fparam->storageClass & STClazy) && t->ty == Tvoid) error(loc, "cannot have parameter of type %s", fparam->type->toChars()); - if (t->isWild()) + if (t->hasWild() && + !(t->ty == Tpointer && t->nextOf()->ty == Tfunction || t->ty == Tdelegate)) { wildparams = TRUE; if (tf->next && !wildreturn) @@ -5211,6 +5356,9 @@ Type *TypeFunction::semantic(Loc loc, Scope *sc) } argsc->pop(); } + if (tf->isWild()) + wildparams = TRUE; + if (wildreturn && !wildparams) error(loc, "inout on return means inout must be on a parameter as well for %s", toChars()); if (wildsubparams && wildparams) @@ -5338,6 +5486,11 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) { if (MODimplicitConv(t->mod, mod)) match = MATCHconst; + else if ((mod & MODwild) + && MODimplicitConv(t->mod, (mod & ~MODwild) | MODconst)) + { + match = MATCHconst; + } else return MATCHnomatch; } @@ -5406,15 +5559,15 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) else m = arg->implicitConvTo(p->type); //printf("match %d\n", m); - if (p->type->isWild()) + if (p->type->hasWild()) { if (m == MATCHnomatch) { - m = arg->implicitConvTo(p->type->constOf()); - if (m == MATCHnomatch) - m = arg->implicitConvTo(p->type->sharedConstOf()); - if (m != MATCHnomatch) + if (p->type->wildMatch(arg->type)) + { wildmatch = TRUE; // mod matched to wild + m = MATCHconst; + } } else exactwildmatch = TRUE; // wild matched to wild @@ -5422,11 +5575,17 @@ int TypeFunction::callMatch(Expression *ethis, Expressions *args, int flag) /* If both are allowed, then there could be more than one * binding of mod to wild, leaving a gaping type hole. */ - if (wildmatch && exactwildmatch) - m = MATCHnomatch; + //if (wildmatch && exactwildmatch) + // m = MATCHnomatch; } } + /* prefer matching the element type rather than the array + * type when more arguments are present with T[]... + */ + if (varargs == 2 && u + 1 == nparams && nargs > nparams) + goto L1; + //printf("\tm = %d\n", m); if (m == MATCHnomatch) // if no match { @@ -5823,7 +5982,14 @@ void TypeQualified::resolveHelper(Loc loc, Scope *sc, t = s->getType(); if (!t && s->isDeclaration()) - t = s->isDeclaration()->type; + { t = s->isDeclaration()->type; + if (!t && s->isTupleDeclaration()) + { + e = new TupleExp(loc, s->isTupleDeclaration()); + e = e->semantic(sc); + t = e->type; + } + } if (t) { sm = t->toDsymbol(sc); @@ -6192,15 +6358,12 @@ Type *TypeInstance::semantic(Loc loc, Scope *sc) if (sc->parameterSpecialization) { - unsigned errors = global.errors; - global.gag++; + unsigned errors = global.startGagging(); resolve(loc, sc, &e, &t, &s); - global.gag--; - if (errors != global.errors) - { if (global.gag == 0) - global.errors = errors; + if (global.endGagging(errors)) + { return this; } } @@ -6225,17 +6388,12 @@ Dsymbol *TypeInstance::toDsymbol(Scope *sc) if (sc->parameterSpecialization) { - unsigned errors = global.errors; - global.gag++; + unsigned errors = global.startGagging(); resolve(loc, sc, &e, &t, &s); - global.gag--; - if (errors != global.errors) - { if (global.gag == 0) - global.errors = errors; + if (global.endGagging(errors)) return NULL; - } } else resolve(loc, sc, &e, &t, &s); @@ -6790,7 +6948,10 @@ char *TypeTypedef::toChars() Type *TypeTypedef::semantic(Loc loc, Scope *sc) { //printf("TypeTypedef::semantic(%s), sem = %d\n", toChars(), sym->sem); + int errors = global.errors; sym->semantic(sc); + if (errors != global.errors) + return terror; return merge(); } @@ -7024,6 +7185,7 @@ int TypeTypedef::hasPointers() int TypeTypedef::hasWild() { + assert(toBasetype()); return mod & MODwild || toBasetype()->hasWild(); } @@ -7459,13 +7621,10 @@ MATCH TypeStruct::implicitConvTo(Type *to) /* If there is an error instantiating AssociativeArray!(), it shouldn't * be reported -- it just means implicit conversion is impossible. */ - ++global.gag; - int errs = global.errors; + int errs = global.startGagging(); to = ((TypeAArray*)to)->getImpl()->type; - --global.gag; - if (errs != global.errors) + if (global.endGagging(errs)) { - global.errors = errs; return MATCHnomatch; } } diff --git a/dmd2/mtype.h b/dmd2/mtype.h index 74b179b29a..e1d5b26ef7 100644 --- a/dmd2/mtype.h +++ b/dmd2/mtype.h @@ -286,6 +286,7 @@ struct Type : Object Type *sharedWildOf(); void fixTo(Type *t); void check(); + Type *addSTC(StorageClass stc); Type *castMod(unsigned mod); Type *addMod(unsigned mod); Type *addStorageClass(StorageClass stc); @@ -317,7 +318,7 @@ struct Type : Object virtual dt_t **toDt(dt_t **pdt); #endif Identifier *getTypeInfoIdent(int internal); - virtual MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + virtual MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); virtual void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); Expression *getInternalTypeInfo(Scope *sc); Expression *getTypeInfo(Scope *sc); @@ -325,7 +326,8 @@ struct Type : Object virtual int builtinTypeInfo(); virtual Type *reliesOnTident(); virtual int hasWild(); - virtual unsigned wildMatch(Type *targ); + unsigned wildMatch(Type *targ); + Type *substWildTo(unsigned mod); virtual Expression *toExpression(); virtual int hasPointers(); virtual TypeTuple *toArgTypes(); @@ -376,7 +378,6 @@ struct TypeNext : Type void checkDeprecated(Loc loc, Scope *sc); Type *reliesOnTident(); int hasWild(); - unsigned wildMatch(Type *targ); Type *nextOf(); Type *makeConst(); Type *makeInvariant(); @@ -456,7 +457,7 @@ struct TypeSArray : TypeArray dt_t **toDt(dt_t **pdt); dt_t **toDtElem(dt_t **pdt, Expression *e); #endif - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); Expression *toExpression(); int hasPointers(); @@ -489,7 +490,7 @@ struct TypeDArray : TypeArray MATCH implicitConvTo(Type *to); Expression *defaultInit(Loc loc); int builtinTypeInfo(); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); TypeTuple *toArgTypes(); @@ -520,7 +521,7 @@ struct TypeAArray : TypeArray void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); Expression *dotExp(Scope *sc, Expression *e, Identifier *ident); Expression *defaultInit(Loc loc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); int isZeroInit(Loc loc); int checkBoolean(); TypeInfoDeclaration *getTypeInfoDeclaration(); @@ -630,7 +631,7 @@ struct TypeFunction : TypeNext void toCBufferWithAttributes(OutBuffer *buf, Identifier *ident, HdrGenState* hgs, TypeFunction *attrs, TemplateDeclaration *td); void toCBuffer2(OutBuffer *buf, HdrGenState *hgs, int mod); void attributesToCBuffer(OutBuffer *buf, int mod); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); Type *reliesOnTident(); bool hasLazyParameters(); @@ -713,7 +714,7 @@ struct TypeIdentifier : TypeQualified void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); Dsymbol *toDsymbol(Scope *sc); Type *semantic(Loc loc, Scope *sc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); Type *reliesOnTident(); Expression *toExpression(); }; @@ -732,7 +733,7 @@ struct TypeInstance : TypeQualified void resolve(Loc loc, Scope *sc, Expression **pe, Type **pt, Dsymbol **ps); Type *semantic(Loc loc, Scope *sc); Dsymbol *toDsymbol(Scope *sc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); }; struct TypeTypeof : TypeQualified @@ -781,7 +782,7 @@ struct TypeStruct : Type #if IN_DMD dt_t **toDt(dt_t **pdt); #endif - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); TypeTuple *toArgTypes(); @@ -832,7 +833,7 @@ struct TypeEnum : Type Type *toBasetype(); Expression *defaultInit(Loc loc); int isZeroInit(Loc loc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); TypeTuple *toArgTypes(); @@ -879,7 +880,7 @@ struct TypeTypedef : Type #if IN_DMD dt_t **toDt(dt_t **pdt); #endif - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); TypeInfoDeclaration *getTypeInfoDeclaration(); int hasPointers(); TypeTuple *toArgTypes(); @@ -913,7 +914,7 @@ struct TypeClass : Type MATCH implicitConvTo(Type *to); Expression *defaultInit(Loc loc); int isZeroInit(Loc loc); - MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes); + MATCH deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch = NULL); int isscope(); int checkBoolean(); TypeInfoDeclaration *getTypeInfoDeclaration(); diff --git a/dmd2/opover.c b/dmd2/opover.c index 7aaadf2276..9a9b40bedd 100644 --- a/dmd2/opover.c +++ b/dmd2/opover.c @@ -1275,7 +1275,10 @@ void inferApplyArgTypes(enum TOK op, Parameters *arguments, Expression *aggr, Mo break; goto Lapply; } + // Resolve inout qualifier of front type arg->type = fd->type->nextOf(); + if (arg->type) + arg->type = arg->type->substWildTo(tab->mod); } break; } diff --git a/dmd2/optimize.c b/dmd2/optimize.c index c7560f34f6..c540557bd4 100644 --- a/dmd2/optimize.c +++ b/dmd2/optimize.c @@ -874,6 +874,71 @@ Expression *XorExp::optimize(int result) return e; } +Expression *PowExp::optimize(int result) +{ Expression *e; + + e1 = e1->optimize(result); + e2 = e2->optimize(result); + + // Replace 1 ^^ x or 1.0^^x by (x, 1) + if ((e1->op == TOKint64 && e1->toInteger() == 1) || + (e1->op == TOKfloat64 && e1->toReal() == 1.0)) + { + e = new CommaExp(loc, e2, e1); + } + // Replace -1 ^^ x by (x&1) ? -1 : 1, where x is integral + else if (e2->type->isintegral() && e1->op == TOKint64 && (sinteger_t)e1->toInteger() == -1L) + { + Type* resultType = type; + e = new AndExp(loc, e2, new IntegerExp(loc, 1, e2->type)); + e = new CondExp(loc, e, new IntegerExp(loc, -1L, resultType), new IntegerExp(loc, 1L, resultType)); + } + // Replace x ^^ 0 or x^^0.0 by (x, 1) + else if ((e2->op == TOKint64 && e2->toInteger() == 0) || + (e2->op == TOKfloat64 && e2->toReal() == 0.0)) + { + if (e1->type->isintegral()) + e = new IntegerExp(loc, 1, e1->type); + else + e = new RealExp(loc, 1.0, e1->type); + + e = new CommaExp(loc, e1, e); + } + // Replace x ^^ 1 or x^^1.0 by (x) + else if ((e2->op == TOKint64 && e2->toInteger() == 1) || + (e2->op == TOKfloat64 && e2->toReal() == 1.0)) + { + e = e1; + } + // Replace x ^^ -1.0 by (1.0 / x) + else if ((e2->op == TOKfloat64 && e2->toReal() == -1.0)) + { + e = new DivExp(loc, new RealExp(loc, 1.0, e2->type), e1); + } + // All other negative integral powers are illegal + else if ((e1->type->isintegral()) && (e2->op == TOKint64) && (sinteger_t)e2->toInteger() < 0) + { + error("cannot raise %s to a negative integer power. Did you mean (cast(real)%s)^^%s ?", + e1->type->toBasetype()->toChars(), e1->toChars(), e2->toChars()); + e = new ErrorExp(); + } + else + { + // If e2 *could* have been an integer, make it one. + if (e2->op == TOKfloat64 && (e2->toReal() == (sinteger_t)(e2->toReal()))) + e2 = new IntegerExp(loc, e2->toInteger(), Type::tint64); + + if (e1->isConst() == 1 && e2->isConst() == 1) + { + e = Pow(type, e1, e2); + if (e != EXP_CANT_INTERPRET) + return e; + } + e = this; + } + return e; +} + Expression *CommaExp::optimize(int result) { Expression *e; diff --git a/dmd2/parse.c b/dmd2/parse.c index b0477ac0ed..b43dd7da5e 100644 --- a/dmd2/parse.c +++ b/dmd2/parse.c @@ -386,10 +386,16 @@ Dsymbols *Parser::parseDeclDefs(int once) storageClass |= stc; switch (token.value) { + case TOKshared: + // Look for "shared static this" or "shared static ~this" + if (peekNext() == TOKstatic) + { TOK next2 = peekNext2(); + if (next2 == TOKthis || next2 == TOKtilde) + break; + } case TOKconst: case TOKinvariant: case TOKimmutable: - case TOKshared: case TOKwild: // If followed by a (, it is not a storage class if (peek(&token)->value == TOKlparen) @@ -547,7 +553,7 @@ Dsymbols *Parser::parseDeclDefs(int once) nextToken(); if (token.value == TOKidentifier) s = new DebugSymbol(loc, token.ident); - else if (token.value == TOKint32v) + else if (token.value == TOKint32v || token.value == TOKint64v) s = new DebugSymbol(loc, (unsigned)token.uns64value); else { error("identifier or integer expected, not %s", token.toChars()); @@ -570,7 +576,7 @@ Dsymbols *Parser::parseDeclDefs(int once) nextToken(); if (token.value == TOKidentifier) s = new VersionSymbol(loc, token.ident); - else if (token.value == TOKint32v) + else if (token.value == TOKint32v || token.value == TOKint64v) s = new VersionSymbol(loc, (unsigned)token.uns64value); else { error("identifier or integer expected, not %s", token.toChars()); @@ -699,10 +705,9 @@ StorageClass Parser::parsePostfix() case TOKpure: stc |= STCpure; break; case TOKat: stc |= parseAttribute(); break; - default: - composeStorageClass(stc); - return stc; + default: return stc; } + composeStorageClass(stc); nextToken(); } } @@ -876,7 +881,7 @@ Condition *Parser::parseDebugCondition() if (token.value == TOKidentifier) id = token.ident; - else if (token.value == TOKint32v) + else if (token.value == TOKint32v || token.value == TOKint64v) level = (unsigned)token.uns64value; else error("identifier or integer expected, not %s", token.toChars()); @@ -905,7 +910,7 @@ Condition *Parser::parseVersionCondition() nextToken(); if (token.value == TOKidentifier) id = token.ident; - else if (token.value == TOKint32v) + else if (token.value == TOKint32v || token.value == TOKint64v) level = (unsigned)token.uns64value; #if DMDV2 /* Allow: @@ -995,7 +1000,9 @@ Dsymbol *Parser::parseCtor() Expression *constraint = tpl ? parseConstraint() : NULL; - Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + tf = tf->addSTC(stc); + CtorDeclaration *f = new CtorDeclaration(loc, 0, stc, tf); parseContracts(f); @@ -1012,7 +1019,9 @@ Dsymbol *Parser::parseCtor() int varargs; Parameters *parameters = parseParameters(&varargs); StorageClass stc = parsePostfix(); - Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + Type *tf = new TypeFunction(parameters, NULL, varargs, linkage, stc); // RetrunType -> auto + tf = tf->addSTC(stc); + CtorDeclaration *f = new CtorDeclaration(loc, 0, stc, tf); parseContracts(f); return f; @@ -2633,27 +2642,7 @@ Type *Parser::parseDeclarator(Type *t, Identifier **pident, TemplateParameters * StorageClass stc = parsePostfix(); stc |= storage_class; // merge prefix storage classes Type *tf = new TypeFunction(arguments, t, varargs, linkage, stc); - - if (stc & STCconst) - { if (tf->isShared()) - tf = tf->makeSharedConst(); - else - tf = tf->makeConst(); - } - if (stc & STCimmutable) - tf = tf->makeInvariant(); - if (stc & STCshared) - { if (tf->isConst()) - tf = tf->makeSharedConst(); - else - tf = tf->makeShared(); - } - if (stc & STCwild) - { if (tf->isShared()) - tf = tf->makeSharedWild(); - else - tf = tf->makeWild(); - } + tf = tf->addSTC(stc); /* Insert tf into * ts -> ... -> t @@ -3681,6 +3670,10 @@ Statement *Parser::parseStatement(int flags) check(TOKlparen); condition = parseExpression(); check(TOKrparen); + if (token.value == TOKsemicolon) + nextToken(); + else if (!global.params.useDeprecated) + error("do-while statement requires terminating ;"); s = new DoStatement(loc, body, condition); break; } @@ -5764,9 +5757,12 @@ Expression *Parser::parseUnaryExp() case TOKimmutable: // immutable(type)(arguments) { Type *t = parseBasicType(); - if (token.value != TOKlparen) - error("(arguments) expected following type"); e = new TypeExp(loc, t); + if (token.value != TOKlparen) + { + error("(arguments) expected following %s", t->toChars()); + return e; + } e = new CallExp(loc, e, parseArguments()); break; } diff --git a/dmd2/rmem.h b/dmd2/rmem.h deleted file mode 100644 index 5d84828d4a..0000000000 --- a/dmd2/rmem.h +++ /dev/null @@ -1,16 +0,0 @@ -#ifndef __RMEM_H__ -#define __RMEM_H__ - -// jam memory stuff here - -#include "mem.h" - -#if (defined (__SVR4) && defined (__sun)) -#include -#endif - -#ifdef __MINGW32__ -#include -#endif - -#endif // __RMEM_H__ diff --git a/dmd2/root/async.c b/dmd2/root/async.c index c6112f4f39..78f17c680c 100644 --- a/dmd2/root/async.c +++ b/dmd2/root/async.c @@ -12,7 +12,6 @@ #include #include "root.h" -#include "rmem.h" static unsigned __stdcall startthread(void *p); @@ -41,7 +40,7 @@ struct AsyncRead AsyncRead *AsyncRead::create(size_t nfiles) { - AsyncRead *aw = (AsyncRead *)mem.calloc(1, sizeof(AsyncRead) + + AsyncRead *aw = (AsyncRead *)calloc(1, sizeof(AsyncRead) + (nfiles - 1) * sizeof(FileData)); aw->filesmax = nfiles; return aw; @@ -92,7 +91,7 @@ int AsyncRead::read(size_t i) void AsyncRead::dispose(AsyncRead *aw) { - delete aw; + free(aw); } @@ -119,7 +118,6 @@ unsigned __stdcall startthread(void *p) #include #include "root.h" -#include "rmem.h" void *startthread(void *arg); @@ -155,7 +153,7 @@ struct AsyncRead AsyncRead *AsyncRead::create(size_t nfiles) { - AsyncRead *aw = (AsyncRead *)mem.calloc(1, sizeof(AsyncRead) + + AsyncRead *aw = (AsyncRead *)calloc(1, sizeof(AsyncRead) + (nfiles - 1) * sizeof(FileData)); aw->filesmax = nfiles; return aw; @@ -228,7 +226,7 @@ void AsyncRead::dispose(AsyncRead *aw) if (status != 0) err_abort(status, "mutex destroy"); } - delete aw; + free(aw); } @@ -265,7 +263,6 @@ void *startthread(void *p) #include #include "root.h" -#include "rmem.h" struct FileData { @@ -292,7 +289,7 @@ struct AsyncRead AsyncRead *AsyncRead::create(size_t nfiles) { - AsyncRead *aw = (AsyncRead *)mem.calloc(1, sizeof(AsyncRead) + + AsyncRead *aw = (AsyncRead *)calloc(1, sizeof(AsyncRead) + (nfiles - 1) * sizeof(FileData)); aw->filesmax = nfiles; return aw; @@ -322,7 +319,7 @@ int AsyncRead::read(size_t i) void AsyncRead::dispose(AsyncRead *aw) { - delete aw; + free(aw); } #endif diff --git a/dmd2/root/bits.c b/dmd2/root/bits.c new file mode 100644 index 0000000000..bbf3a32481 --- /dev/null +++ b/dmd2/root/bits.c @@ -0,0 +1,43 @@ +// Copyright (c) 2000-2011 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 "bits.h" + +GCBits::GCBits() +{ + data = NULL; + nwords = 0; + nbits = 0; +} + +GCBits::~GCBits() +{ + if (data) + ::free(data); + data = NULL; +} + +void GCBits::invariant() +{ + if (data) + { + assert(nwords * sizeof(*data) * 8 >= nbits); + } +} + +void GCBits::alloc(unsigned nbits) +{ + this->nbits = nbits; + nwords = (nbits + (BITS_PER_WORD - 1)) >> BITS_SHIFT; + data = (unsigned *)::calloc(nwords + 2, sizeof(unsigned)); + assert(data); +} diff --git a/dmd2/root/bits.h b/dmd2/root/bits.h new file mode 100644 index 0000000000..df41439260 --- /dev/null +++ b/dmd2/root/bits.h @@ -0,0 +1,83 @@ +// Copyright (c) 2000-2011 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 + +#if __DMC__ +// Inline bit operations +#include +#endif + +#ifdef linux +#include "gccbitops.h" +#endif + +#if _MSC_VER + // Disable useless warnings about unreferenced functions + #pragma warning (disable : 4514) +#endif // _MSC_VER + + +#define BITS_PER_WORD 32 +#define BITS_SHIFT 5 +#define BITS_MASK 31 + +struct Mem; + +struct GCBits +{ + unsigned *data; + unsigned nwords; // allocated words in data[] excluding sentinals + unsigned nbits; // number of bits in data[] excluding sentinals + + GCBits(); + ~GCBits(); + void invariant(); + + void alloc(unsigned nbits); + +#if __DMC__ + unsigned test(unsigned i) { return _inline_bt(data + 1, i); } + void set(unsigned i) { _inline_bts(data + 1, i); } + void clear(unsigned i) { _inline_btr(data + 1, i); } + unsigned testClear(unsigned i) { return _inline_btr(data + 1, i); } + unsigned testSet(unsigned i) { return _inline_bts(data + 1, i); } +#elif 0 //defined linux + // for unknown reasons, GCC does badly with this + unsigned test(unsigned i) { return _inline_bt(data + 1, i); } + void set(unsigned i) { _inline_bts(data + 1, i); } + void clear(unsigned i) { _inline_btr(data + 1, i); } + unsigned testClear(unsigned i) { return _inline_btr(data + 1, i); } + unsigned testSet(unsigned i) { return _inline_bts(data + 1, i); } +#else + unsigned test(unsigned i) { return data[1 + (i >> BITS_SHIFT)] & (1 << (i & BITS_MASK)); } + void set(unsigned i) { data[1 + (i >> BITS_SHIFT)] |= (1 << (i & BITS_MASK)); } + void clear(unsigned i) { data[1 + (i >> BITS_SHIFT)] &= ~(1 << (i & BITS_MASK)); } + unsigned testClear(unsigned i) + { + unsigned *p = &data[1 + (i >> BITS_SHIFT)]; + unsigned mask = (1 << (i & BITS_MASK)); + unsigned result = *p & mask; + *p &= ~mask; + return result; + } + unsigned testSet(unsigned i) + { + unsigned *p = &data[1 + (i >> BITS_SHIFT)]; + unsigned mask = (1 << (i & BITS_MASK)); + unsigned result = *p & mask; + *p |= mask; + return result; + } +#endif + + void zero() { memset(data + 1, 0, nwords * sizeof(unsigned)); } + void copy(GCBits *f) { memcpy(data + 1, f->data + 1, nwords * sizeof(unsigned)); } + + unsigned *base() { return data + 1; } +}; diff --git a/dmd2/root/dmgcmem.c b/dmd2/root/dmgcmem.c new file mode 100644 index 0000000000..9a283890d3 --- /dev/null +++ b/dmd2/root/dmgcmem.c @@ -0,0 +1,499 @@ + + +// Copyright (c) 2000-2011 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 + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ +#include +#include +#endif + +#include "rmem.h" +#include "gc/gc.h" +//#include "printf.h" + +/* This implementation of the storage allocator uses the Digital Mars gc. + */ + +Mem mem; + +//static int nuncollectable; + +extern "C" +{ + void gc_init(); + GC *gc_get(); +} + +void Mem::init() +{ + gc_init(); +} + +char *Mem::strdup(const char *s) +{ + return gc_get()->strdup(s); +} + +void *Mem::malloc(size_t size) +{ + if (gc) // if cached allocator + { +// PRINTF("Using cached gc for size %d, file = '%s', line = %d\n", size, GC::file, GC::line); +// GC::file = NULL; +// GC::line = 0; + return ((GC *)gc)->malloc(size); + } + if (this == &mem) // don't cache global mem + { +// PRINTF("Using global gc for size %d, file = '%s', line = %d\n", size, GC::file, GC::line); +// GC::file = NULL; +// GC::line = 0; + return gc_get()->malloc(size); + } +// PRINTF("Generating cached gc for size %d, file = '%s', line = %d\n", size, GC::file, GC::line); + gc = gc_get(); + return gc->malloc(size); +} + +void *Mem::malloc_uncollectable(size_t size) +{ void *p; + + p = ::malloc(size); + if (!p) + error(); + addroots((char *)p, (char *)p + size); + +#if 0 + ++nuncollectable; + WPRINTF(L"malloc_uncollectable(%u) = %x, n=%d\n", size, p, nuncollectable); +#endif + + return p; +} + +void *Mem::calloc(size_t size, size_t n) +{ + return gc_get()->calloc(size, n); +} + +void *Mem::realloc(void *p, size_t size) +{ + return gc_get()->realloc(p, size); +} + +void Mem::free(void *p) +{ + gc_get()->free(p); +} + +void Mem::free_uncollectable(void *p) +{ + if (p) + { removeroots((char *)p); + ::free(p); + +#if 0 + --nuncollectable; + WPRINTF(L"free_uncollectable(%x) n=%d\n", p, nuncollectable); +#endif + +#if 0 + gc_get()->fullcollect(); + + GCStats stats; + + getStats(&stats); + WPRINTF(L"poolsize = %x, usedsize = %x, freelistsize = %x\n", + stats.poolsize, stats.usedsize, stats.freelistsize); +#endif + } +} + +void *Mem::mallocdup(void *o, size_t size) +{ + return gc_get()->mallocdup(o, size); +} + +void Mem::check(void *p) +{ + if (gc) + gc->check(p); + else + gc_get()->check(p); +} + +void Mem::error() +{ +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ + assert(0); +#endif + printf("Error: out of memory\n"); + exit(EXIT_FAILURE); +} + +void Mem::fullcollect() +{ + gc_get()->fullcollect(); + +#if 0 + { + GCStats stats; + + gc_get()->getStats(&stats); + WPRINTF(L"Thread %x ", Thread::getId()); + WPRINTF(L"poolsize=x%x, usedsize=x%x, freelistsize=x%x, freeblocks=%d, pageblocks=%d\n", + stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks); + } +#endif +} + + +void Mem::fullcollectNoStack() +{ + gc_get()->fullcollectNoStack(); + +#if 0 + { + GCStats stats; + + gc_get()->getStats(&stats); + WPRINTF(L"Thread %x ", Thread::getId()); + WPRINTF(L"poolsize=x%x, usedsize=x%x, freelistsize=x%x, freeblocks=%d, pageblocks=%d\n", + stats.poolsize, stats.usedsize, stats.freelistsize, stats.freeblocks, stats.pageblocks); + } +#endif +} + + +void Mem::mark(void *pointer) +{ + (void) pointer; // for VC /W4 compatibility +} + + +void Mem::addroots(char* pStart, char* pEnd) +{ + gc_get()->addRange(pStart, pEnd); +} + + +void Mem::removeroots(char* pStart) +{ + gc_get()->removeRange(pStart); +} + + +void Mem::setFinalizer(void* pObj, FINALIZERPROC pFn, void* pClientData) +{ + (void)pClientData; + gc_get()->setFinalizer(pObj, pFn); +} + + +void Mem::setStackBottom(void *stackbottom) +{ + gc_get()->setStackBottom(stackbottom); +} + + +GC *Mem::getThreadGC() +{ + return gc_get(); +} + + +/* =================================================== */ + +#if 1 +void * operator new(size_t m_size) +{ + //PRINTF("Call to global operator new(%d), file = '%s', line = %d\n", m_size, GC::file ? GC::file : "(null)", GC::line); + GC::file = NULL; + GC::line = 0; + return mem.malloc(m_size); +} + +void operator delete(void *p) +{ + //WPRINTF(L"Call to global operator delete\n"); + mem.free(p); +} + +void* operator new[](size_t size) +{ + return operator new(size); +} + +void operator delete[](void *pv) +{ + operator delete(pv); +} +#endif + +void * Mem::operator new(size_t m_size) +{ void *p; + + p = gc_get()->malloc(m_size); + //printf("Mem::operator new(%d) = %p\n", m_size, p); + if (!p) + mem.error(); + return p; +} + +void * Mem::operator new(size_t m_size, Mem *mem) +{ void *p; + + p = mem->malloc(m_size); + //printf("Mem::operator new(%d) = %p\n", m_size, p); + if (!p) + ::mem.error(); + return p; +} + +void * Mem::operator new(size_t m_size, GC *gc) +{ void *p; + +// if (!gc) +// WPRINTF(L"gc is NULL\n"); + p = gc->malloc(m_size); + //printf("Mem::operator new(%d) = %p\n", m_size, p); + if (!p) + ::mem.error(); + return p; +} + +void Mem::operator delete(void *p) +{ +// printf("Mem::operator delete(%p)\n", p); + gc_get()->free(p); +} + +/* ============================================================ */ + +/* The following section of code exists to find the right + * garbage collector for this thread. There is one independent instance + * of the collector per thread. + */ + +/* ===================== linux ================================ */ + +#if linux || __APPLE__ || __FreeBSD__ || __OpenBSD__ + +#include + +#define LOG 0 // log thread creation / destruction + +extern "C" +{ + +// Key identifying the thread-specific data +static pthread_key_t gc_key; + +/* "Once" variable ensuring that the key for gc_alloc will be allocated + * exactly once. + */ +static pthread_once_t gc_alloc_key_once = PTHREAD_ONCE_INIT; + +/* Forward functions */ +static void gc_alloc_key(); +static void gc_alloc_destroy_gc(void * accu); + + +void gc_init() +{ +#if LOG + WPRINTF(L"Thread %lx: gc_init()\n", pthread_self()); +#endif + pthread_once(&gc_alloc_key_once, gc_alloc_key); +#if LOG + WPRINTF(L"Thread %lx: gc_init() return\n", pthread_self()); +#endif +} + +GC *gc_get() +{ + GC *gc; + + // Get the thread-specific data associated with the key + gc = (GC *) pthread_getspecific(gc_key); + + // It's initially NULL, meaning that we must allocate the buffer first. + if (gc == NULL) + { + GC_LOG(); + gc = new GC(); + gc->init(); + + // Store the buffer pointer in the thread-specific data. + pthread_setspecific(gc_key, (void *) gc); +#if LOG + WPRINTF(L"Thread %lx: allocating gc at %x\n", pthread_self(), gc); +#endif + } + return gc; +} + +// Function to allocate the key for gc_alloc thread-specific data. + +static void gc_alloc_key() +{ + pthread_key_create(&gc_key, gc_alloc_destroy_gc); +#if LOG + WPRINTF(L"Thread %lx: allocated gc key %d\n", pthread_self(), gc_key); +#endif +} + +// Function to free the buffer when the thread exits. +// Called only when the thread-specific data is not NULL. + +static void gc_alloc_destroy_gc(void *gc) +{ +#if LOG + WPRINTF(L"Thread %x: freeing gc at %x\n", pthread_self(), gc); +#endif + delete (GC *)gc; +} + +} + +#endif + +/* ===================== win32 ================================ */ + +#if !defined(linux) && defined(_WIN32) + +#if 1 // single threaded version + +extern "C" +{ + +static GC *gc; + +void gc_init() +{ + if (!gc) + { gc = (GC *)::malloc(sizeof(GC)); + gc->init(); + } +} + +GC *gc_get() +{ + return gc; +} + +} + +#else // multi threaded version + +#include "mutex.h" +#include "thread.h" + +/* This is the win32 version. It suffers from the bug that + * when the thread exits the data structure is not cleared, + * but the memory pool it points to is free'd. + * Thus, if a new thread comes along with the same thread id, + * the data will look initialized, but will point to garbage. + * + * What needs to happen is when a thread exits, the associated + * GC_context data struct is cleared. + */ + +struct GC_context +{ + ThreadId threadid; // identifier of current thread + GC *gc; +}; + +Mutex gc_mutex; + +static GC_context array[64]; + +// Array of pointers to GC_context objects, one per threadid +GC_context *gccontext = array; +unsigned gccontext_allocdim = 64; +unsigned gccontext_dim; + +ThreadId gc_cache_ti; +GC_context *gc_cache_cc; + +extern "C" void gc_init() +{ +} + + +extern "C" GC *gc_get() +{ + /* This works by creating an array of GC_context's, one + * for each thread. We match up by thread id. + */ + + ThreadId ti; + GC_context *cc; + + //PRINTF("gc_get()\n"); + + ti = Thread::getId(); + gc_mutex.acquire(); + + // Used cached version if we can + if (ti == gc_cache_ti) + { + cc = gc_cache_cc; + //exception(L"getGC_context(): cache x%x", ti); + } + else + { + // This does a linear search through gccontext[]. + // A hash table might be faster if there are more + // than a dozen threads. + GC_context *ccp; + GC_context *ccptop = &gccontext[gccontext_dim]; + for (ccp = gccontext; ccp < ccptop; ccp++) + { + cc = ccp; + if (cc->threadid == ti) + { + WPRINTF(L"getGC_context(): existing x%x", ti); + goto Lret; + } + } + + // Do not allocate with garbage collector, as this must reside + // global to all threads. + + assert(gccontext_dim < gccontext_allocdim); + cc = ccp; + memset(cc, 0, sizeof(*cc)); + cc->threadid = ti; + cc->gc = new GC(); + cc->gc->init(); + + gccontext_dim++; + WPRINTF(L"getGC_context(): new x%x\n", ti); + + Lret: + // Cache for next time + gc_cache_ti = ti; + gc_cache_cc = cc; + } + + gc_mutex.release(); + return cc->gc; +} + +#endif + + +#endif diff --git a/dmd2/root/gc.c b/dmd2/root/gc.c new file mode 100644 index 0000000000..7c412566b8 --- /dev/null +++ b/dmd2/root/gc.c @@ -0,0 +1,2223 @@ +// Copyright (c) 2000-2011 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. + + +/************** Debugging ***************************/ + +#define THREADINVARIANT 0 // check thread integrity +#define INVARIANT 0 // check class invariants +#define LOGGING 0 // log allocations / frees +#define MEMSTOMP 0 // stomp on memory +#define SENTINEL 0 // add underrun/overrrun protection +#define PTRCHECK 0 // 0: fast pointer checking + // 1: more pointer checking + // 2: thorough but slow pointer checking + +/*************** Configuration *********************/ + +#define USEROOT 0 // use root for printf +#define STACKGROWSDOWN 1 // 1: growing the stack means subtracting from the stack pointer + // (use 1 for Intel X86 CPUs) + // 0: growing the stack means adding to the stack pointer +#define ATOMIC 1 // mark some mallocs as "atomic" +#define LISTPREV 1 // use List prev pointers + +/***************************************************/ + + +#include +#include +#include +#include + +#include "gc.h" +#include "os.h" +#include "bits.h" + +#if CHECK_OUT_OF_MEM +#include +extern jmp_buf g_setjmp_buf; +#endif + +//#include "../root/perftimer.h" + +#if USEROOT +#if defined linux + +#include "../root/printf.h" + +#elif defined _WIN32 + +#include "..\root\printf.h" + +#endif +#else +#include +#define WPRINTF wprintf +#define PRINTF printf +#endif + +#ifdef linux +#include "gccbitops.h" +#endif + +#ifdef _MSC_VER +#include "mscbitops.h" +#endif + +#define PAGESIZE 4096 +#define COMMITSIZE (4096*16) +#define POOLSIZE (4096*256*2) // 2 megs + +//#define printf 1 || printf + +#undef assert +#define assert(e) if (!(e)) _gc_assert(__LINE__) +void _gc_assert(unsigned line); + +static int zero = 0; // used to avoid complaints about assert(0) by MSVC + +#if LOGGING + +struct Log; + +struct LogArray +{ + unsigned dim; + unsigned allocdim; + Log *data; + + LogArray(); + ~LogArray(); + + void reserve(unsigned nentries); + void push(Log foo); + void remove(unsigned i); + unsigned find(void *p); + void copy(LogArray *from); +}; + +#endif + +enum Bins +{ + B_16, + B_32, + B_64, + B_128, + B_256, + B_512, + B_1024, + B_2048, + B_PAGE, // start of large alloc + B_PAGEPLUS, // continuation of large alloc + B_FREE, // free page + B_UNCOMMITTED, // memory not committed for this page + B_MAX +}; + + +struct List +{ + List *next; + List *prev; +}; + +struct Range +{ + void *pbot; + void *ptop; +}; + +struct Pool +{ + char *baseAddr; + char *topAddr; + GCBits mark; + GCBits scan; + GCBits finals; + GCBits freebits; +#if ATOMIC + GCBits atomic; +#endif + + unsigned npages; + unsigned ncommitted; // ncommitted <= npages + unsigned char *pagetable; + + void init(unsigned npages); + ~Pool(); + void invariant(); + + unsigned allocPages(unsigned n); + void freePages(unsigned pagenum, unsigned npages); + int cmp(Pool *); +}; + +struct Gcx +{ +#if THREADINVARIANT + pthread_t self; +# define thread_invariant(gcx) assert(gcx->self == pthread_self()) +#else +# define thread_invariant(gcx) ((void)0) +#endif + + unsigned nroots; + unsigned rootdim; + void **roots; + + unsigned nranges; + unsigned rangedim; + Range *ranges; + + unsigned noStack; // !=0 means don't scan stack + unsigned log; + unsigned anychanges; + char *stackBottom; + + char *minAddr; // min(baseAddr) + char *maxAddr; // max(topAddr) + + unsigned npages; // total number of pages in all the pools + unsigned npools; + Pool **pooltable; + + List *bucket[B_MAX]; // free list for each size + + GC_FINALIZER finalizer; // finalizer function (one per GC) + + void init(); + ~Gcx(); + void invariant(); + + void addRoot(void *p); + void removeRoot(void *p); + + void addRange(void *pbot, void *ptop); // add range to scan for roots + void removeRange(void *pbot); // remove range + + Pool *findPool(void *p); + unsigned findSize(void *p); + static Bins findBin(unsigned size); + void *bigAlloc(unsigned size); + Pool *newPool(unsigned npages); + int allocPage(Bins bin); + void mark(void *pbot, void *ptop); + unsigned fullcollectshell(); + unsigned fullcollect(void *stackTop); + void doFinalize(void *p); + + + /***** Leak Detector ******/ +#if LOGGING + LogArray current; + LogArray prev; + + void log_init(); + void log_malloc(void *p, unsigned size); + void log_free(void *p); + void log_collect(); + void log_parent(void *p, void *parent); +#else + void log_init() { } + void log_malloc(void *p, unsigned size) { (void)p; (void)size; } + void log_free(void *p) { (void)p; } + void log_collect() { } + void log_parent(void *p, void *parent) { (void)p; (void)parent; } +#endif +}; + +void _gc_assert(unsigned line) +{ + (void)line; +#if USEROOT + WPRINTF(L"GC assert fail: gc.c(%d)\n", line); +#else + printf("GC assert fail: gc.c(%d)\n", line); +#endif + *(char *)0 = 0; + exit(0); +} + +const unsigned binsize[B_MAX] = { 16,32,64,128,256,512,1024,2048,4096 }; +const unsigned notbinsize[B_MAX] = { ~(16u-1),~(32u-1),~(64u-1),~(128u-1),~(256u-1), + ~(512u-1),~(1024u-1),~(2048u-1),~(4096u-1) }; + +unsigned binset[B_PAGE][PAGESIZE / (16 * 32)]; + +/******************************** + * Initialize binset[][]. + */ + +void binset_init() +{ + int bin; + + for (bin = 0; bin < B_PAGE; bin++) + { + unsigned bitstride = binsize[bin] / 16; + + for (unsigned bit = 0; bit < (PAGESIZE / 16); bit += bitstride) + { + unsigned u = bit / 32; + unsigned m = bit % 32; + binset[bin][u] |= 1 << m; + } + } +} + +/* ============================ SENTINEL =============================== */ + + +#if SENTINEL +# define SENTINEL_PRE 0xF4F4F4F4 // 32 bits +# define SENTINEL_POST 0xF5 // 8 bits +# define SENTINEL_EXTRA (2 * sizeof(unsigned) + 1) +# define sentinel_size(p) (((unsigned *)(p))[-2]) +# define sentinel_pre(p) (((unsigned *)(p))[-1]) +# define sentinel_post(p) (((unsigned char *)(p))[sentinel_size(p)]) + +void sentinel_init(void *p, unsigned size) +{ + sentinel_size(p) = size; + sentinel_pre(p) = SENTINEL_PRE; + sentinel_post(p) = SENTINEL_POST; +} + +void sentinel_invariant(void *p, int line) +{ + //WPRINTF(L"pre = %x, line = %d\n", sentinel_pre(p), line); + if (sentinel_pre(p) != SENTINEL_PRE) + WPRINTF(L"p = %x, pre = %x, size = %x line = %d\n", p, sentinel_pre(p), sentinel_size(p), line); + assert(sentinel_pre(p) == SENTINEL_PRE); + assert(sentinel_post(p) == SENTINEL_POST); +} +#define sentinel_invariant(p) sentinel_invariant(p, __LINE__) + +inline void *sentinel_add(void *p) +{ + //assert(((unsigned)p & 3) == 0); + return (void *)((char *)p + 2 * sizeof(unsigned)); +} + +inline void *sentinel_sub(void *p) +{ + return (void *)((char *)p - 2 * sizeof(unsigned)); +} + +#else + +# define SENTINEL_EXTRA 0 +# define sentinel_init(p,size) (void)(p) +# define sentinel_invariant(p) (void)(p) +# define sentinel_add(p) (void *)(p) +# define sentinel_sub(p) (void *)(p) + +#endif + +/* ============================ GC =============================== */ + +unsigned GC::line = 0; +char *GC::file = NULL; + +GC::~GC() +{ +#if defined linux + //WPRINTF(L"Thread %x ", pthread_self()); + //WPRINTF(L"GC::~GC()\n"); +#endif + if (gcx) + { + Gcx *g = (Gcx *)gcx; + delete g; + } +} + +void GC::init() +{ + //printf("GC::init()\n"); + if (!binset[0][0]) + binset_init(); + gcx = (Gcx *)::malloc(sizeof(Gcx)); + gcx->init(); +} + +void GC::setStackBottom(void *p) +{ + thread_invariant(gcx); +#if STACKGROWSDOWN + p = (void *)((unsigned *)p + 4); + if (p > gcx->stackBottom) +#else + p = (void *)((unsigned *)p - 4); + if (p < gcx->stackBottom) +#endif + { + //WPRINTF(L"setStackBottom(%x)\n", p); + gcx->stackBottom = (char *)p; + } +} + +char *GC::strdup(const char *s) +{ + unsigned len; + char *p = NULL; + + thread_invariant(gcx); + if (s) + { + len = strlen(s) + 1; + p = (char *)malloc(len); +#if CHECK_OUT_OF_MEM + if (p) +#endif + memcpy(p, s, len); + } + return p; +} + +void *GC::calloc(size_t size, size_t n) +{ + unsigned len; + void *p; + + thread_invariant(gcx); + len = size * n; + p = malloc(len); + if (p) + { //printf("calloc: %x len %d\n", p, len); + memset(p, 0, len); + } + return p; +} + +void *GC::realloc(void *p, size_t size) +{ + thread_invariant(gcx); + if (!size) + { if (p) + { free(p); + p = NULL; + } + } + else if (!p) + { + p = malloc(size); + } + else + { void *p2; + unsigned psize; + +//WPRINTF(L"GC::realloc(p = %x, size = %u)\n", p, size); + sentinel_invariant(p); +#if SENTINEL + psize = sentinel_size(p); + if (psize != size) +#else + psize = gcx->findSize(p); // find allocated size + if (psize < size || // if new size is bigger + psize > size * 2) // or less than half +#endif + { + p2 = malloc(size); +#if CHECK_OUT_OF_MEM + if (!p2) + return NULL; +#endif + if (psize < size) + size = psize; + //WPRINTF(L"\tcopying %d bytes\n",size); + memcpy(p2, p, size); + //free(p); // causes 507 crash + p = p2; + } + } + return p; +} + +void *GC::malloc_atomic(size_t size) +{ +#if ATOMIC + //WPRINTF(L"GC::malloc_atomic(size = %d)\n", size); + void *p; + + p = malloc(size); + if (p) + { + Pool *pool = gcx->findPool(p); + pool->atomic.set(((char *)p - pool->baseAddr) / 16); + } + return p; +#else + return malloc(size); +#endif +} + +void *GC::malloc(size_t size) +{ void *p; + Bins bin; + + //WPRINTF(L"GC::malloc(size = %d)\n", size); + //PRINTF("GC::malloc(size = %d, file = '%s', line = %d)\n", size, GC::file, GC::line); + //printf("gcx->self = %x, pthread_self() = %x\n", gcx->self, pthread_self()); + thread_invariant(gcx); + if (size) + { + size += SENTINEL_EXTRA; + + // Compute size bin + bin = gcx->findBin(size); + + if (bin < B_PAGE) + { + p = gcx->bucket[bin]; + if (p == NULL) + { + if (!gcx->allocPage(bin)) // try to find a new page + { unsigned freedpages; + + freedpages = gcx->fullcollectshell(); // collect to find a new page + //if (freedpages < gcx->npools * 16) + if (freedpages < gcx->npages / 20 + 1) + { + gcx->newPool(1); + } + } + if (!gcx->bucket[bin] && !gcx->allocPage(bin)) + { int result; + + gcx->newPool(1); // allocate new pool to find a new page + result = gcx->allocPage(bin); +#if CHECK_OUT_OF_MEM + if (!result) + //return NULL; + longjmp(g_setjmp_buf, 1); +#else + assert(result); +#endif + } + p = gcx->bucket[bin]; + } + + // Return next item from free list + gcx->bucket[bin] = ((List *)p)->next; + memset((char *)p + size, 0, binsize[bin] - size); + #if MEMSTOMP + memset(p, 0xF0, size); + #endif + } + else + { + p = gcx->bigAlloc(size); + if (!p) +#if CHECK_OUT_OF_MEM + longjmp(g_setjmp_buf, 1); +#else + return NULL; +#endif + } + size -= SENTINEL_EXTRA; + p = sentinel_add(p); + sentinel_init(p, size); + //WPRINTF(L"\tmalloc => %x, %x\n", sentinel_sub(p), *(unsigned *)sentinel_sub(p)); + gcx->log_malloc(p, size); + return p; + } + return NULL; +} + +void GC::free(void *p) +{ + Pool *pool; + unsigned pagenum; + Bins bin; + unsigned bit; + + thread_invariant(gcx); + if (!p) + return; + + // Find which page it is in + pool = gcx->findPool(p); + if (!pool) // if not one of ours + return; // ignore + sentinel_invariant(p); + p = sentinel_sub(p); + pagenum = ((char *)p - pool->baseAddr) / PAGESIZE; + + if (pool->finals.nbits && gcx->finalizer) + { + bit = (unsigned)((char *)p - pool->baseAddr) / 16; + if (pool->finals.testClear(bit)) + { + (*gcx->finalizer)(sentinel_add(p), NULL); + } + } + + bin = (Bins)pool->pagetable[pagenum]; + if (bin == B_PAGE) // if large alloc + { int npages; + unsigned n; + + // Free pages + npages = 1; + n = pagenum; + while (++n < pool->ncommitted && pool->pagetable[n] == B_PAGEPLUS) + npages++; + #if MEMSTOMP + memset(p, 0xF2, npages * PAGESIZE); + #endif + pool->freePages(pagenum, npages); + } + else + { // Add to free list + List *list = (List *)p; + + #if MEMSTOMP + memset(p, 0xF2, binsize[bin]); + #endif + + list->next = gcx->bucket[bin]; + gcx->bucket[bin] = list; + } + gcx->log_free(sentinel_add(p)); +} + +void *GC::mallocdup(void *o, size_t size) +{ + void *p; + + thread_invariant(gcx); + p = malloc(size); +#if CHECK_OUT_OF_MEM + if (!p) + return NULL; +#endif + return memcpy(p, o, size); +} + +/**************************************** + * Verify that pointer p: + * 1) belongs to this memory pool + * 2) points to the start of an allocated piece of memory + * 3) is not on a free list + */ + +void GC::check(void *p) +{ + if (p) + { + sentinel_invariant(p); +#if PTRCHECK >= 1 + Pool *pool; + unsigned pagenum; + Bins bin; + unsigned size; + + p = sentinel_sub(p); + pool = gcx->findPool(p); + assert(pool); + pagenum = ((char *)p - pool->baseAddr) / PAGESIZE; + bin = (Bins)pool->pagetable[pagenum]; + assert(bin <= B_PAGE); + size = binsize[bin]; + assert(((unsigned)p & (size - 1)) == 0); + +#if PTRCHECK >= 2 + if (bin < B_PAGE) + { + // Check that p is not on a free list + List *list; + + for (list = gcx->bucket[bin]; list; list = list->next) + { + assert((void *)list != p); + } + } +#endif +#endif + } +} + +void GC::error() +{ int i = 0; + + assert(i); +} + +void GC::addRoot(void *p) +{ + thread_invariant(gcx); + gcx->addRoot(p); +} + +void GC::removeRoot(void *p) +{ + gcx->removeRoot(p); +} + +void GC::addRange(void *pbot, void *ptop) +{ + thread_invariant(gcx); + gcx->addRange(pbot, ptop); +} + +void GC::removeRange(void *pbot) +{ + gcx->removeRange(pbot); +} + +void GC::fullcollect() +{ + thread_invariant(gcx); + + gcx->fullcollectshell(); + +#if 0 + { + GCStats stats; + + getStats(&stats); + PRINTF("poolsize = %x, usedsize = %x, freelistsize = %x\n", + stats.poolsize, stats.usedsize, stats.freelistsize); + } +#endif +} + +void GC::fullcollectNoStack() +{ + //WPRINTF(L"fullcollectNoStack()\n"); + gcx->noStack++; + fullcollect(); + gcx->log_collect(); + gcx->noStack--; +} + +void GC::gencollect() +{ + gcx->fullcollectshell(); +} + +void GC::minimize() +{ + // Not implemented, ignore +} + + +void GC::setFinalizer(void *p, GC_FINALIZER pFn) +{ + thread_invariant(gcx); + + gcx->finalizer = pFn; + gcx->doFinalize(p); +} + +/***************************************** + * Retrieve statistics about garbage collection. + * Useful for debugging and tuning. + */ + +void GC::getStats(GCStats *stats) +{ + unsigned psize = 0; + unsigned usize = 0; + unsigned flsize = 0; + + unsigned n; + unsigned bsize = 0; + +//WPRINTF(L"getStats()\n"); + memset(stats, 0, sizeof(*stats)); + for (n = 0; n < gcx->npools; n++) + { Pool *pool = gcx->pooltable[n]; + + psize += pool->ncommitted * PAGESIZE; + for (unsigned j = 0; j < pool->ncommitted; j++) + { + Bins bin = (Bins)pool->pagetable[j]; + if (bin == B_FREE) + stats->freeblocks++; + else if (bin == B_PAGE) + stats->pageblocks++; + else if (bin < B_PAGE) + bsize += PAGESIZE; + } + } + + for (n = 0; n < B_PAGE; n++) + { +//WPRINTF(L"bin %d\n", n); + for (List *list = gcx->bucket[n]; list; list = list->next) + { +//WPRINTF(L"\tlist %x\n", list); + flsize += binsize[n]; + } + } + + usize = bsize - flsize; + + stats->poolsize = psize; + stats->usedsize = bsize - flsize; + stats->freelistsize = flsize; +} + +/* ============================ Gcx =============================== */ + +void Gcx::init() +{ int dummy; + + memset(this, 0, sizeof(Gcx)); + stackBottom = (char *)&dummy; + log_init(); +#if THREADINVARIANT + self = pthread_self(); +#endif + invariant(); +} + +Gcx::~Gcx() +{ + invariant(); + + for (unsigned i = 0; i < npools; i++) + { Pool *pool = pooltable[i]; + + delete pool; + } + if (pooltable) + ::free(pooltable); + + if (roots) + ::free(roots); + + if (ranges) + ::free(ranges); +} + +void Gcx::invariant() +{ +#if INVARIANT + unsigned i; + + thread_invariant(this); // assure we're called on the right thread + for (i = 0; i < npools; i++) + { Pool *pool = pooltable[i]; + + pool->invariant(); + if (i == 0) + { + assert(minAddr == pool->baseAddr); + } + if (i + 1 < npools) + { + assert(pool->cmp(pooltable[i + 1]) < 0); + } + else if (i + 1 == npools) + { + assert(maxAddr == pool->topAddr); + } + } + + if (roots) + { + assert(rootdim != 0); + assert(nroots <= rootdim); + } + + if (ranges) + { + assert(rangedim != 0); + assert(nranges <= rangedim); + + for (i = 0; i < nranges; i++) + { + assert(ranges[i].pbot); + assert(ranges[i].ptop); + assert(ranges[i].pbot <= ranges[i].ptop); + } + } + + for (i = 0; i < B_PAGE; i++) + { + for (List *list = bucket[i]; list; list = list->next) + { + } + } +#endif +} + +/*************************************** + */ + +void Gcx::addRoot(void *p) +{ + if (nroots == rootdim) + { + unsigned newdim = rootdim * 2 + 16; + void **newroots; + + newroots = (void **)::malloc(newdim * sizeof(newroots[0])); +#if CHECK_OUT_OF_MEM + if (!newroots) + longjmp(g_setjmp_buf, 1); +#else + assert(newroots); +#endif + if (roots) + { memcpy(newroots, roots, nroots * sizeof(newroots[0])); + ::free(roots); + } + roots = newroots; + rootdim = newdim; + } + roots[nroots] = p; + nroots++; +} + +void Gcx::removeRoot(void *p) +{ + unsigned i; + for (i = nroots; i--;) + { + if (roots[i] == p) + { + nroots--; + memmove(roots + i, roots + i + 1, (nroots - i) * sizeof(roots[0])); + return; + } + } + assert(zero); +} + + +/*************************************** + */ + +void Gcx::addRange(void *pbot, void *ptop) +{ + //WPRINTF(L"Thread %x ", pthread_self()); + //WPRINTF(L"%x->Gcx::addRange(%x, %x), nranges = %d\n", this, pbot, ptop, nranges); + if (nranges == rangedim) + { + unsigned newdim = rangedim * 2 + 16; + Range *newranges; + + newranges = (Range *)::malloc(newdim * sizeof(newranges[0])); +#if CHECK_OUT_OF_MEM + if (!newranges) + longjmp(g_setjmp_buf, 1); +#else + assert(newranges); +#endif + if (ranges) + { memcpy(newranges, ranges, nranges * sizeof(newranges[0])); + ::free(ranges); + } + ranges = newranges; + rangedim = newdim; + } + ranges[nranges].pbot = pbot; + ranges[nranges].ptop = ptop; + nranges++; +} + +void Gcx::removeRange(void *pbot) +{ + //WPRINTF(L"Thread %x ", pthread_self()); + //WPRINTF(L"%x->Gcx::removeRange(%x), nranges = %d\n", this, pbot, nranges); + for (unsigned i = nranges; i--;) + { + if (ranges[i].pbot == pbot) + { + nranges--; + memmove(ranges + i, ranges + i + 1, (nranges - i) * sizeof(ranges[0])); + return; + } + } + //WPRINTF(L"Wrong thread\n"); + + // This is a fatal error, but ignore it at Sun's request. + // The problem is that we can get a Close() call on a thread + // other than the one the range was allocated on. + //assert(zero); +} + + +/*********************************** + * Allocate a new pool with at least npages in it. + * Sort it into pooltable[]. + * Return NULL if failed. + */ + +Pool *Gcx::newPool(unsigned npages) +{ + Pool *pool; + Pool **newpooltable; + unsigned newnpools; + unsigned i; + + //WPRINTF(L"************Gcx::newPool(npages = %d)****************\n", npages); + + // Round up to COMMITSIZE pages + npages = (npages + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); + + // Minimum of POOLSIZE + if (npages < POOLSIZE/PAGESIZE) + npages = POOLSIZE/PAGESIZE; + + // Allocate successively larger pools up to 8 megs + if (npools) + { unsigned n; + + n = npools; + if (n > 8) + n = 8; // cap pool size at 8 megs + n *= (POOLSIZE / PAGESIZE); + if (npages < n) + npages = n; + } + + pool = (Pool *)::malloc(sizeof(Pool)); + if (pool) + { + pool->init(npages); + if (!pool->baseAddr) + goto Lerr; + + newnpools = npools + 1; + newpooltable = (Pool **)::realloc(pooltable, newnpools * sizeof(Pool *)); + if (!newpooltable) + goto Lerr; + + // Sort pool into newpooltable[] + for (i = 0; i < npools; i++) + { + if (pool->cmp(newpooltable[i]) < 0) + break; + } + memmove(newpooltable + i + 1, newpooltable + i, (npools - i) * sizeof(Pool *)); + newpooltable[i] = pool; + + pooltable = newpooltable; + npools = newnpools; + this->npages += npages; + + minAddr = pooltable[0]->baseAddr; + maxAddr = pooltable[npools - 1]->topAddr; + } + return pool; + + Lerr: + delete pool; + return NULL; +} + +/**************************************** + * Allocate a chunk of memory that is larger than a page. + * Return NULL if out of memory. + */ + +void *Gcx::bigAlloc(unsigned size) +{ + Pool *pool; + unsigned npages; + unsigned n; + unsigned pn; + unsigned freedpages; + void *p; + int state; + + npages = (size + PAGESIZE - 1) / PAGESIZE; + + for (state = 0; ; ) + { + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pn = pool->allocPages(npages); + if (pn != ~0u) + goto L1; + } + + // Failed + switch (state) + { + case 0: + // Try collecting + freedpages = fullcollectshell(); + if (freedpages >= npools * ((POOLSIZE / PAGESIZE) / 2)) + { state = 1; + continue; + } + // Allocate new pool + pool = newPool(npages); + if (!pool) + { state = 2; + continue; + } + pn = pool->allocPages(npages); + assert(pn != ~0u); + goto L1; + + case 1: + // Allocate new pool + pool = newPool(npages); + if (!pool) + goto Lnomemory; + pn = pool->allocPages(npages); + assert(pn != ~0u); + goto L1; + + case 2: + goto Lnomemory; + } + } + + L1: + pool->pagetable[pn] = B_PAGE; + if (npages > 1) + memset(&pool->pagetable[pn + 1], B_PAGEPLUS, npages - 1); + p = pool->baseAddr + pn * PAGESIZE; + memset((char *)p + size, 0, npages * PAGESIZE - size); + #if MEMSTOMP + memset(p, 0xF1, size); + #endif + //printf("\tp = %x\n", p); + return p; + + Lnomemory: + //assert(zero); + return NULL; +} + +/******************************* + * Allocate a page of bin's. + * Returns: + * 0 failed + */ + +int Gcx::allocPage(Bins bin) +{ + Pool *pool; + unsigned n; + unsigned pn; + char *p; + char *ptop; + + //printf("Gcx::allocPage(bin = %d)\n", bin); + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pn = pool->allocPages(1); + if (pn != ~0u) + goto L1; + } + return 0; // failed + + L1: + pool->pagetable[pn] = (unsigned char)bin; + + // Convert page to free list + unsigned size = binsize[bin]; + List **b = &bucket[bin]; + + p = pool->baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + for (; p < ptop; p += size) + { List *list = (List *)p; + + list->next = *b; + *b = list; + } + return 1; +} + +/******************************* + * Find Pool that pointer is in. + * Return NULL if not in a Pool. + * Assume pooltable[] is sorted. + */ + +Pool *Gcx::findPool(void *p) +{ + if (p >= minAddr && p < maxAddr) + { + if (npools == 1) + { + return pooltable[0]; + } + + for (unsigned i = 0; i < npools; i++) + { Pool *pool; + + pool = pooltable[i]; + if (p < pool->topAddr) + { if (pool->baseAddr <= p) + return pool; + break; + } + } + } + return NULL; +} + +/******************************* + * Find size of pointer p. + * Returns 0 if not a gc'd pointer + */ + +unsigned Gcx::findSize(void *p) +{ + Pool *pool; + unsigned size = 0; + + pool = findPool(p); + if (pool) + { + unsigned pagenum; + Bins bin; + + pagenum = ((unsigned)((char *)p - pool->baseAddr)) / PAGESIZE; + bin = (Bins)pool->pagetable[pagenum]; + size = binsize[bin]; + if (bin == B_PAGE) + { unsigned npages = pool->ncommitted; + unsigned char *pt; + unsigned i; + + pt = &pool->pagetable[0]; + for (i = pagenum + 1; i < npages; i++) + { + if (pt[i] != B_PAGEPLUS) + break; + } + size = (i - pagenum) * PAGESIZE; + } + } + return size; +} + + +/******************************* + * Compute bin for size. + */ + +Bins Gcx::findBin(unsigned size) +{ Bins bin; + + if (size <= 256) + { + if (size <= 64) + { + if (size <= 16) + bin = B_16; + else if (size <= 32) + bin = B_32; + else + bin = B_64; + } + else + { + if (size <= 128) + bin = B_128; + else + bin = B_256; + } + } + else + { + if (size <= 1024) + { + if (size <= 512) + bin = B_512; + else + bin = B_1024; + } + else + { + if (size <= 2048) + bin = B_2048; + else + bin = B_PAGE; + } + } + return bin; +} + +/************************************ + * Search a range of memory values and mark any pointers into the GC pool. + */ + +void Gcx::mark(void *pbot, void *ptop) +{ + void **p1 = (void **)pbot; + void **p2 = (void **)ptop; + unsigned changes = 0; + Pool *pool; + + //if (log) printf("Gcx::mark(%p .. %p)\n", pbot, ptop); + if (npools == 1) + pool = pooltable[0]; + + for (; p1 < p2; p1++) + { + char *p = (char *)(*p1); + + //if (log) WPRINTF(L"\tmark %x\n", p); + if (p >= minAddr && p < maxAddr /*&& ((int)p & 3) == 0*/) + { + if (npools != 1) + { pool = findPool(p); + if (!pool) + continue; + } + + unsigned offset = (unsigned)(p - pool->baseAddr); + unsigned bit; + unsigned pn = offset / PAGESIZE; + Bins bin = (Bins)pool->pagetable[pn]; + + //printf("\t\tfound pool %x, base=%x, pn = %d, bin = %d, bit = x%x\n", pool, pool->baseAddr, pn, bin, bit); + + // Adjust bit to be at start of allocated memory block + if (bin <= B_PAGE) + { + bit = (offset & notbinsize[bin]) >> 4; + //printf("\t\tbit = x%x\n", bit); + } + else if (bin == B_PAGEPLUS) + { + do + { --pn; + } while ((Bins)pool->pagetable[pn] == B_PAGEPLUS); + bit = pn * (PAGESIZE / 16); + } + else + { + // Don't mark bits in B_FREE or B_UNCOMMITTED pages + continue; + } + + //printf("\t\tmark(x%x) = %d\n", bit, pool->mark.test(bit)); + if (!pool->mark.testSet(bit)) + { + //if (log) PRINTF("\t\tmarking %p\n", p); + //pool->mark.set(bit); +#if ATOMIC + if (!pool->atomic.test(bit)) +#endif + { + pool->scan.set(bit); + changes += 1; + } + log_parent(sentinel_add(pool->baseAddr + bit * 16), sentinel_add(pbot)); + } + } + } + anychanges += changes; +} + +/********************************* + * Return number of full pages free'd. + */ + +unsigned Gcx::fullcollectshell() +{ +#if __GCC__ + asm("pushl %eax"); + asm("pushl %ebx"); + asm("pushl %ecx"); + asm("pushl %edx"); + asm("pushl %ebp"); + asm("pushl %esi"); + asm("pushl %edi"); + // This function must ensure that all register variables are on the stack + // before &dummy + unsigned dummy; + dummy = fullcollect(&dummy - 7); + asm("addl $28,%esp"); +#elif _MSC_VER || __DMC__ + __asm push eax; + __asm push ebx; + __asm push ecx; + __asm push edx; + __asm push ebp; + __asm push esi; + __asm push edi; + // This function must ensure that all register variables are on the stack + // before &dummy + unsigned dummy; + dummy = fullcollect(&dummy - 7); + __asm add esp,28; +#else + // This function must ensure that all register variables are on the stack + // before &dummy + unsigned dummy; + dummy = fullcollect(&dummy); +#endif + return dummy; +} + +unsigned Gcx::fullcollect(void *stackTop) +{ + unsigned n; + Pool *pool; + unsigned freedpages = 0; + unsigned freed = 0; + unsigned recoveredpages = 0; + +int x1 = 0; +int x2 = 0; +int x3 = 0; + + //PRINTF("Gcx::fullcollect() npools = %d\n", npools); +//PerfTimer pf(L"fullcollect"); + invariant(); + anychanges = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pool->mark.zero(); + pool->scan.zero(); + pool->freebits.zero(); + } + + // Mark each free entry, so it doesn't get scanned + for (n = 0; n < B_PAGE; n++) + { + List *list = bucket[n]; + List *prev = NULL; + + //WPRINTF(L"bin = %d\n", n); + for (; list; list = list->next) + { + if (list->prev != prev) + list->prev = prev; + prev = list; + pool = findPool(list); + assert(pool); + //WPRINTF(L" list = %x, bit = %d\n", list, (unsigned)((char *)list - pool->baseAddr) / 16); + pool->freebits.set((unsigned)((char *)list - pool->baseAddr) / 16); + assert(pool->freebits.test((unsigned)((char *)list - pool->baseAddr) / 16)); + } + } + +#if SENTINEL + // Every memory item should either be on the free list or have the sentinel + // set. + for (n = 0; n < npools; n++) + { unsigned pn; + unsigned ncommitted; + + pool = pooltable[n]; + ncommitted = pool->ncommitted; + for (pn = 0; pn < ncommitted; pn++) + { + char *p; + char *ptop; + Bins bin = (Bins)pool->pagetable[pn]; + unsigned bit; + + p = pool->baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + + //WPRINTF(L"pn = %d, bin = %d\n", pn, bin); + if (bin < B_PAGE) + { + unsigned size = binsize[bin]; + unsigned bitstride = size / 16; + bit = pn * (PAGESIZE/16); + + for (; p < ptop; p += size, bit += bitstride) + { + if (pool->freebits.test(bit)) + ; + else + { + //WPRINTF(L"p = %x, *p = %x\n", p, *(unsigned *)p); + sentinel_invariant(sentinel_add(p)); + } + } + } + } + } +#endif + + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + pool->mark.copy(&pool->freebits); + } + + if (!noStack) + { + // Scan stack + //WPRINTF(L"scan stack bot = %x, top = %x\n", stackTop, stackBottom); +#if STACKGROWSDOWN + mark(stackTop, stackBottom); +#else + mark(stackBottom, stackTop); +#endif + } + + // Scan roots[] + //WPRINTF(L"scan roots[]\n"); + mark(roots, roots + nroots); + + // Scan ranges[] + //WPRINTF(L"scan ranges[]\n"); + //log++; + for (n = 0; n < nranges; n++) + { + //WPRINTF(L"\t%x .. %x\n", ranges[n].pbot, ranges[n].ptop); + mark(ranges[n].pbot, ranges[n].ptop); + } + //log--; + + //WPRINTF(L"\tscan heap\n"); +{ +//PerfTimer pf(L"fullcollect: scanning "); + while (anychanges) + { + //WPRINTF(L"anychanges = %d\n", anychanges); + anychanges = 0; + for (n = 0; n < npools; n++) + { + unsigned *bbase; + unsigned *b; + unsigned *btop; + + pool = pooltable[n]; + + bbase = pool->scan.base(); + btop = bbase + pool->scan.nwords; + for (b = bbase; b < btop;) + { Bins bin; + unsigned pn; + unsigned u; + unsigned bitm; + char *o; + char *ostart; + + bitm = *b; + if (!bitm) + { b++; + continue; + } + //WPRINTF(L"bitm = x%08x, b = %x\n", bitm, b); + pn = (b - bbase) / (PAGESIZE / (32 * 16)); + bin = (Bins)pool->pagetable[pn]; + o = pool->baseAddr + (b - bbase) * 32 * 16; + *b = 0; + + if (bin < B_PAGE) + { int size = binsize[bin]; + unsigned index; + + do + { + index = _inline_bsf(bitm); + o += index * 16; + mark(o, o + size); + o += 16; + + // Cannot combine these two, because 0x80000000<<32 is + // still 0x80000000 + bitm >>= 1; + } while ((bitm >>= index) != 0); + } + else if (bin == B_PAGE) + { + u = 1; + while (pn + u < pool->ncommitted && + pool->pagetable[pn + u] == B_PAGEPLUS) + u++; + o = pool->baseAddr + pn * PAGESIZE; + mark(o, o + u * PAGESIZE); + b = bbase + (pn + u) * (PAGESIZE / (32 * 16)); + } + else + { + assert(0); + } + } + } + } +} + + // Free up everything not marked +{ +//PerfTimer pf(L"fullcollect: freeing "); + //WPRINTF(L"\tfree'ing\n"); + for (n = 0; n < npools; n++) + { unsigned pn; + unsigned ncommitted; + unsigned *bbase; + unsigned *fbase; + int delta; + + pool = pooltable[n]; + bbase = pool->mark.base(); + delta = pool->freebits.base() - bbase; + ncommitted = pool->ncommitted; + for (pn = 0; pn < ncommitted; pn++, bbase += PAGESIZE / (32 * 16)) + { + Bins bin = (Bins)pool->pagetable[pn]; + + if (bin < B_PAGE) + { char *p; + char *ptop; + unsigned bit; + unsigned bitstride; + unsigned size = binsize[bin]; + + p = pool->baseAddr + pn * PAGESIZE; + ptop = p + PAGESIZE; + bit = pn * (PAGESIZE/16); + bitstride = size / 16; + +#if 1 + // If free'd entire page + fbase = bbase + delta; +#if INVARIANT + assert(bbase == pool->mark.base() + pn * 8); +#endif +#if 1 + if ((bbase[0] ^ fbase[0]) == 0 && + (bbase[1] ^ fbase[1]) == 0 && + (bbase[2] ^ fbase[2]) == 0 && + (bbase[3] ^ fbase[3]) == 0 && + (bbase[4] ^ fbase[4]) == 0 && + (bbase[5] ^ fbase[5]) == 0 && + (bbase[6] ^ fbase[6]) == 0 && + (bbase[7] ^ fbase[7]) == 0) +#else + if ((bbase[0]) == 0 && + (bbase[1]) == 0 && + (bbase[2]) == 0 && + (bbase[3]) == 0 && + (bbase[4]) == 0 && + (bbase[5]) == 0 && + (bbase[6]) == 0 && + (bbase[7]) == 0) +#endif + { +x1++; + for (; p < ptop; p += size, bit += bitstride) + { +#if LISTPREV + if (pool->freebits.test(bit)) + { // Remove from free list + List *list = (List *)p; + + //WPRINTF(L"bbase = %x, bbase[0] = %x, mark = %d\n", bbase, bbase[0], pool->mark.test(bit)); + //WPRINTF(L"p = %x, bin = %d, size = %d, bit = %d\n", p, bin, size, bit); + + if (bucket[bin] == list) + bucket[bin] = list->next; + if (list->next) + list->next->prev = list->prev;; + if (list->prev) + list->prev->next = list->next; + continue; + } +#endif +#if ATOMIC + pool->atomic.clear(bit); +#endif + if (finalizer && pool->finals.nbits && + pool->finals.testClear(bit)) + { + (*finalizer)((List *)sentinel_add(p), NULL); + } + + List *list = (List *)p; + //printf("\tcollecting %x\n", list); + log_free(sentinel_add(list)); + + #if MEMSTOMP + memset(p, 0xF3, size); + #endif + } + pool->pagetable[pn] = B_FREE; + recoveredpages++; + //printf("freeing entire page %d\n", pn); + continue; + } +#endif + if (bbase[0] == binset[bin][0] && + bbase[1] == binset[bin][1] && + bbase[2] == binset[bin][2] && + bbase[3] == binset[bin][3] && + bbase[4] == binset[bin][4] && + bbase[5] == binset[bin][5] && + bbase[6] == binset[bin][6] && + bbase[7] == binset[bin][7]) + { +x2++; + continue; + } +x3++; + for (; p < ptop; p += size, bit += bitstride) + { + if (!pool->mark.test(bit)) + { + sentinel_invariant(sentinel_add(p)); + +#if ATOMIC + pool->atomic.clear(bit); +#endif + pool->freebits.set(bit); + if (finalizer && pool->finals.nbits && + pool->finals.testClear(bit)) + { + (*finalizer)((List *)sentinel_add(p), NULL); + } + + List *list = (List *)p; + //WPRINTF(L"\tcollecting %x, bin = %d\n", list, bin); + log_free(sentinel_add(list)); + + #if MEMSTOMP + memset(p, 0xF3, size); + #endif + +#if LISTPREV + // Add to free list + list->next = bucket[bin]; + list->prev = NULL; + if (list->next) + list->next->prev = list; + bucket[bin] = list; +#endif + freed += size; + } + } + } + else if (bin == B_PAGE) + { unsigned bit = pn * (PAGESIZE / 16); + + if (!pool->mark.test(bit)) + { char *p = pool->baseAddr + pn * PAGESIZE; + + sentinel_invariant(sentinel_add(p)); +#if ATOMIC + pool->atomic.clear(bit); +#endif + if (finalizer && pool->finals.nbits && + pool->finals.testClear(bit)) + { + (*finalizer)(sentinel_add(p), NULL); + } + + //printf("\tcollecting big %x\n", p); + log_free(sentinel_add(p)); + pool->pagetable[pn] = B_FREE; + freedpages++; + #if MEMSTOMP + memset(p, 0xF3, PAGESIZE); + #endif + while (pn + 1 < ncommitted && pool->pagetable[pn + 1] == B_PAGEPLUS) + { + pn++; + bbase += PAGESIZE / (32 * 16); + pool->pagetable[pn] = B_FREE; + freedpages++; + + #if MEMSTOMP + p += PAGESIZE; + memset(p, 0xF3, PAGESIZE); + #endif + } + } + } + } + } +} + +#if !LISTPREV + // Zero buckets + memset(bucket, 0, sizeof(bucket)); + + // Free complete pages, rebuild free list + //WPRINTF(L"\tfree complete pages\n"); +PerfTimer pf(L"fullcollect: recoverpages"); + recoveredpages = 0; + for (n = 0; n < npools; n++) + { unsigned pn; + unsigned ncommitted; + + pool = pooltable[n]; + ncommitted = pool->ncommitted; + for (pn = 0; pn < ncommitted; pn++) + { + Bins bin = (Bins)pool->pagetable[pn]; + unsigned bit; + unsigned u; + + if (bin < B_PAGE) + { + unsigned size = binsize[bin]; + unsigned bitstride = size / 16; + unsigned bitbase = pn * (PAGESIZE / 16); + unsigned bittop = bitbase + (PAGESIZE / 16); + char *p; + + bit = bitbase; + for (bit = bitbase; bit < bittop; bit += bitstride) + { if (!pool->freebits.test(bit)) + goto Lnotfree; + } + pool->pagetable[pn] = B_FREE; + recoveredpages++; + continue; + + Lnotfree: + p = pool->baseAddr + pn * PAGESIZE; + for (bit = bitbase; bit < bittop; bit += bitstride) + { if (pool->freebits.test(bit)) + { List *list; + + u = (bit - bitbase) * 16; + list = (List *)(p + u); + if (list->next != bucket[bin]) // avoid unnecessary writes + list->next = bucket[bin]; + bucket[bin] = list; + } + } + } + } + } +#endif + +#undef printf +// printf("recovered pages = %d\n", recoveredpages); +// printf("\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); +#define printf 1 || printf + + //WPRINTF(L"\tfree'd %u bytes, %u pages from %u pools\n", freed, freedpages, npools); + invariant(); + +#if 0 +if (noStack) +{ + WPRINTF(L"\tdone, freedpages = %d, recoveredpages = %d, freed = %d, total = %d\n", freedpages, recoveredpages, freed / PAGESIZE, freedpages + recoveredpages + freed / PAGESIZE); + WPRINTF(L"\tx1 = %d, x2 = %d, x3 = %d\n", x1, x2, x3); + int psize = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + psize += pool->topAddr - pool->baseAddr; + } + WPRINTF(L"total memory = x%x (npages=%d) in %d pools\n", psize, npages, npools); + + psize = 0; + for (n = 0; n < npools; n++) + { + pool = pooltable[n]; + for (unsigned i = 0; i < pool->ncommitted; i++) + { + if (pool->pagetable[i] < B_FREE) + { psize++; + if (psize <= 25) + WPRINTF(L"\tbin = %d\n", pool->pagetable[i]); + } + } + } + WPRINTF(L"total used pages = %d\n", psize); +} +#endif + return freedpages + recoveredpages + freed / PAGESIZE; +} + +/********************************* + * Run finalizer on p when it is free'd. + */ + +void Gcx::doFinalize(void *p) +{ + Pool *pool = findPool(p); + assert(pool); + + // Only allocate finals[] if we actually need it + if (!pool->finals.nbits) + pool->finals.alloc(pool->mark.nbits); + + pool->finals.set(((char *)p - pool->baseAddr) / 16); +} + +/* ============================ Pool =============================== */ + +void Pool::init(unsigned npages) +{ + unsigned poolsize; + + //printf("Pool::Pool(%u)\n", npages); + poolsize = npages * PAGESIZE; + assert(poolsize >= POOLSIZE); + baseAddr = (char *)os_mem_map(poolsize); + + if (!baseAddr) + { +#if CHECK_OUT_OF_MEM + longjmp(g_setjmp_buf, 1); +#endif + WPRINTF(L"GC fail: poolsize = x%x, errno = %d\n", poolsize, errno); +#if USEROOT + PRINTF("message = '%s'\n", sys_errlist[errno]); +#else + printf("message = '%s'\n", sys_errlist[errno]); +#endif + npages = 0; + poolsize = 0; + } + //assert(baseAddr); + topAddr = baseAddr + poolsize; + + mark.alloc(poolsize / 16); + scan.alloc(poolsize / 16); + freebits.alloc(poolsize / 16); +#if ATOMIC + atomic.alloc(poolsize / 16); +#endif + + pagetable = (unsigned char *)::malloc(npages); + memset(pagetable, B_UNCOMMITTED, npages); + + this->npages = npages; + ncommitted = 0; + + invariant(); +} + +Pool::~Pool() +{ + invariant(); + if (baseAddr) + { + int result; + + if (ncommitted) + { + result = os_mem_decommit(baseAddr, 0, ncommitted * PAGESIZE); + assert(result == 0); + } + + if (npages) + { + result = os_mem_unmap(baseAddr, npages * PAGESIZE); + assert(result == 0); + } + } + if (pagetable) + ::free(pagetable); +} + +void Pool::invariant() +{ +#if INVARIANT + mark.invariant(); + scan.invariant(); + + assert(baseAddr < topAddr); + assert(baseAddr + npages * PAGESIZE == topAddr); + assert(ncommitted <= npages); + + for (unsigned i = 0; i < npages; i++) + { Bins bin = (Bins)pagetable[i]; + + assert(bin < B_MAX); +#if 0 + // Buggy GCC doesn't compile this right with -O + if (i < ncommitted) + assert(bin != B_UNCOMMITTED); + else + assert(bin == B_UNCOMMITTED); +#endif + } +#endif +} + +/*************************** + * Used for sorting pooltable[] + */ + +int Pool::cmp(Pool *p2) +{ + return baseAddr - p2->baseAddr; +} + +/************************************** + * Allocate n pages from Pool. + * Returns ~0u on failure. + */ + +unsigned Pool::allocPages(unsigned n) +{ + unsigned i; + unsigned n2; + + //printf("Pool::allocPages(n = %d)\n", n); + n2 = n; + for (i = 0; i < ncommitted; i++) + { + if (pagetable[i] == B_FREE) + { + if (--n2 == 0) + { //printf("\texisting pn = %d\n", i - n + 1); + return i - n + 1; + } + } + else + n2 = n; + } + if (ncommitted + n < npages) + { + unsigned tocommit; + + tocommit = (n + (COMMITSIZE/PAGESIZE) - 1) & ~(COMMITSIZE/PAGESIZE - 1); + if (ncommitted + tocommit > npages) + tocommit = npages - ncommitted; + //printf("\tlooking to commit %d more pages\n", tocommit); + //fflush(stdout); + if (os_mem_commit(baseAddr, ncommitted * PAGESIZE, tocommit * PAGESIZE) == 0) + { + memset(pagetable + ncommitted, B_FREE, tocommit); + i = ncommitted; + ncommitted += tocommit; + + while (i && pagetable[i - 1] == B_FREE) + i--; + + return i; + } + //printf("\tfailed to commit %d pages\n", tocommit); + } + + return ~0u; +} + +/********************************** + * Free npages pages starting with pagenum. + */ + +void Pool::freePages(unsigned pagenum, unsigned npages) +{ + memset(&pagetable[pagenum], B_FREE, npages); +} + +/* ======================= Leak Detector =========================== */ + +#if LOGGING + +struct Log +{ + void *p; + unsigned size; + unsigned line; + char *file; + void *parent; + + void print(); +}; + +void Log::print() +{ + WPRINTF(L" p = %x, size = %d, parent = %x ", p, size, parent); + if (file) + { + PRINTF("%s(%u)", file, line); + } + WPRINTF(L"\n"); +} + +LogArray::LogArray() +{ + data = NULL; + dim = 0; + allocdim = 0; +} + +LogArray::~LogArray() +{ + if (data) + ::free(data); + data = NULL; +} + +void LogArray::reserve(unsigned nentries) +{ + //WPRINTF(L"LogArray::reserve(%d)\n", nentries); + assert(dim <= allocdim); + if (allocdim - dim < nentries) + { + allocdim = (dim + nentries) * 2; + assert(dim + nentries <= allocdim); + if (!data) + { + data = (Log *)::malloc(allocdim * sizeof(*data)); + } + else + { Log *newdata; + + newdata = (Log *)::malloc(allocdim * sizeof(*data)); + assert(newdata); + memcpy(newdata, data, dim * sizeof(Log)); + ::free(data); + data = newdata; + } + assert(!allocdim || data); + } +} + +void LogArray::push(Log log) +{ + reserve(1); + data[dim++] = log; +} + +void LogArray::remove(unsigned i) +{ + memmove(data + i, data + i + 1, (dim - i) * sizeof(data[0])); + dim--; +} + +unsigned LogArray::find(void *p) +{ + for (unsigned i = 0; i < dim; i++) + { + if (data[i].p == p) + return i; + } + return ~0u; // not found +} + +void LogArray::copy(LogArray *from) +{ + if (from->dim > dim) + { reserve(from->dim - dim); + assert(from->dim <= allocdim); + } + memcpy(data, from->data, from->dim * sizeof(data[0])); + dim = from->dim; +} + + +/****************************/ + +void Gcx::log_init() +{ + //WPRINTF(L"+log_init()\n"); + current.reserve(1000); + prev.reserve(1000); + //WPRINTF(L"-log_init()\n"); +} + +void Gcx::log_parent(void *p, void *parent) +{ + //WPRINTF(L"+log_parent()\n"); + unsigned i; + + i = current.find(p); + if (i == ~0u) + { + WPRINTF(L"parent'ing unallocated memory %x, parent = %x\n", p, parent); + Pool *pool; + pool = findPool(p); + assert(pool); + unsigned offset = (unsigned)((char *)p - pool->baseAddr); + unsigned bit; + unsigned pn = offset / PAGESIZE; + Bins bin = (Bins)pool->pagetable[pn]; + bit = (offset & notbinsize[bin]); + WPRINTF(L"\tbin = %d, offset = x%x, bit = x%x\n", bin, offset, bit); + } + else + { + current.data[i].parent = parent; + } + //WPRINTF(L"-log_parent()\n"); +} + +void Gcx::log_malloc(void *p, unsigned size) +{ + //WPRINTF(L"+log_malloc(p = %x, size = %d)\n", p, size); + Log log; + + log.p = p; + log.size = size; + log.line = GC::line; + log.file = GC::file; + log.parent = NULL; + + GC::line = 0; + GC::file = NULL; + + current.push(log); + //WPRINTF(L"-log_malloc()\n"); +} + +void Gcx::log_free(void *p) +{ + //WPRINTF(L"+log_free(%x)\n", p); + unsigned i; + + i = current.find(p); + if (i == ~0u) + { + WPRINTF(L"free'ing unallocated memory %x\n", p); + } + else + current.remove(i); + //WPRINTF(L"-log_free()\n"); +} + +void Gcx::log_collect() +{ + //WPRINTF(L"+log_collect()\n"); + // Print everything in current that is not in prev + + WPRINTF(L"New pointers this cycle: --------------------------------\n"); + int used = 0; + for (unsigned i = 0; i < current.dim; i++) + { + unsigned j; + + j = prev.find(current.data[i].p); + if (j == ~0u) + current.data[i].print(); + else + used++; + } + + WPRINTF(L"All roots this cycle: --------------------------------\n"); + for (unsigned i = 0; i < current.dim; i++) + { + void *p; + unsigned j; + + p = current.data[i].p; + if (!findPool(current.data[i].parent)) + { + j = prev.find(current.data[i].p); + if (j == ~0u) + WPRINTF(L"N"); + else + WPRINTF(L" ");; + current.data[i].print(); + } + } + + WPRINTF(L"Used = %d-------------------------------------------------\n", used); + prev.copy(¤t); + + WPRINTF(L"-log_collect()\n"); +} + +#endif diff --git a/dmd2/root/gc.h b/dmd2/root/gc.h new file mode 100644 index 0000000000..9f5e926ba8 --- /dev/null +++ b/dmd2/root/gc.h @@ -0,0 +1,68 @@ +// Copyright (c) 2000-2011 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 GC_H +#define GC_H + +struct Gcx; // private data + +typedef void (*GC_FINALIZER)(void *p, void *dummy); + +struct GCStats +{ + unsigned poolsize; // total size of pool + unsigned usedsize; // bytes allocated + unsigned freeblocks; // number of blocks marked FREE + unsigned freelistsize; // total of memory on free lists + unsigned pageblocks; // number of blocks marked PAGE +}; + +struct GC +{ + // For passing to debug code + static unsigned line; + static char *file; +// #define GC_LOG() ((GC::line = __LINE__), (GC::file = __FILE__)) + #define GC_LOG() ((void)0) + + Gcx *gcx; // implementation + + ~GC(); + + void init(); + + char *strdup(const char *s); + void *malloc(size_t size); + void *malloc_atomic(size_t size); + void *calloc(size_t size, size_t n); + void *realloc(void *p, size_t size); + void free(void *p); + void *mallocdup(void *o, size_t size); + void check(void *p); + void error(); + + void setStackBottom(void *p); + + void addRoot(void *p); // add p to list of roots + void removeRoot(void *p); // remove p from list of roots + + void addRange(void *pbot, void *ptop); // add range to scan for roots + void removeRange(void *pbot); // remove range + + void fullcollect(); // do full garbage collection + void fullcollectNoStack(); // do full garbage collection; no scan stack + void gencollect(); // do generational garbage collection + void minimize(); // minimize physical memory usage + + void setFinalizer(void *p, GC_FINALIZER pFn); + + void getStats(GCStats *stats); +}; + +#endif + diff --git a/dmd2/root/gccbitops.h b/dmd2/root/gccbitops.h new file mode 100644 index 0000000000..89fd82b7dc --- /dev/null +++ b/dmd2/root/gccbitops.h @@ -0,0 +1,69 @@ +// Copyright (c) 2000-2011 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. + +// Bit operations for GCC and I386 + +#ifndef GCCBITOPS_H +#define GCCBITOPS_H 1 + +inline int _inline_bsf(int w) +{ int index; + + __asm__ __volatile__ + ( + "bsfl %1, %0 \n\t" + : "=r" (index) + : "r" (w) + ); + return index; +} + + +inline int _inline_bt(unsigned *p, int i) +{ + char result; + + __asm__ __volatile__ + ( + "btl %2,%1 \n\t" + "setc %0 \n\t" + :"=r" (result) + :"m" (*p), "r" (i) + ); + return result; +} + +inline int _inline_bts(unsigned *p, int i) +{ + char result; + + __asm__ __volatile__ + ( + "btsl %2,%1 \n\t" + "setc %0 \n\t" + :"=r" (result) + :"m" (*p), "r" (i) + ); + return result; +} + +inline int _inline_btr(unsigned *p, int i) +{ + char result; + + __asm__ __volatile__ + ( + "btrl %2,%1 \n\t" + "setc %0 \n\t" + :"=r" (result) + :"m" (*p), "r" (i) + ); + return result; +} + +#endif diff --git a/dmd2/root/linux.c b/dmd2/root/linux.c new file mode 100644 index 0000000000..5d50ad5121 --- /dev/null +++ b/dmd2/root/linux.c @@ -0,0 +1,96 @@ +// Copyright (c) 2000-2011 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 + + +/************************************* + * This is all necessary to get fd initialized at startup. + */ + +#define FDMAP 0 + +#if FDMAP +#include + +struct OS_INIT +{ + static int fd; + + OS_INIT(); +}; + +OS_INIT os_init; + +int OS_INIT::fd = 0; + +OS_INIT::OS_INIT() +{ + fd = open("/dev/zero", O_RDONLY); +} +#endif + +/*********************************** + * Map memory. + */ + +void *os_mem_map(unsigned nbytes) +{ void *p; + + errno = 0; +#if FDMAP + p = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE, OS_INIT::fd, 0); +#else + p = mmap(NULL, nbytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); +#endif + return (p == MAP_FAILED) ? NULL : p; +} + +/*********************************** + * Commit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_commit(void *base, unsigned offset, unsigned nbytes) +{ + return 0; +} + + +/*********************************** + * Decommit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) +{ + return 0; +} + +/*********************************** + * Unmap memory allocated with os_mem_map(). + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_unmap(void *base, unsigned nbytes) +{ + return munmap(base, nbytes); +} + + + + diff --git a/dmd2/root/mscbitops.h b/dmd2/root/mscbitops.h new file mode 100644 index 0000000000..bc84a3daac --- /dev/null +++ b/dmd2/root/mscbitops.h @@ -0,0 +1,25 @@ +// Copyright (c) 2000-2011 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. + +// Bit operations for MSC and I386 + +#ifndef MSCBITOPS_H +#define MSCBITOPS_H 1 + +inline int _inline_bsf(int w) +{ int index; + + index = 0; + while (!(w & 1)) + { index++; + w >>= 1; + } + return index; +} + +#endif diff --git a/dmd2/root/os.h b/dmd2/root/os.h new file mode 100644 index 0000000000..4ee7d8f1b6 --- /dev/null +++ b/dmd2/root/os.h @@ -0,0 +1,25 @@ +// Copyright (c) 2000-2011 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. + + +// OS specific routines + +void *os_mem_map(unsigned nbytes); +int os_mem_commit(void *base, unsigned offset, unsigned nbytes); +int os_mem_decommit(void *base, unsigned offset, unsigned nbytes); +int os_mem_unmap(void *base, unsigned nbytes); + + +// Threading + +#if defined linux +#include +#else +typedef long pthread_t; +pthread_t pthread_self(); +#endif diff --git a/dmd2/root/rmem.h b/dmd2/root/rmem.h index 85ebda620e..d22145a915 100644 --- a/dmd2/root/rmem.h +++ b/dmd2/root/rmem.h @@ -1,4 +1,4 @@ -// Copyright (C) 2000-2001 by Chromium Communications +// Copyright (C) 2000-2011 by Digital Mars // All Rights Reserved #ifndef ROOT_MEM_H diff --git a/dmd2/root/root.c b/dmd2/root/root.c index 1988f672b9..926dae281b 100644 --- a/dmd2/root/root.c +++ b/dmd2/root/root.c @@ -1087,7 +1087,7 @@ int File::read() } if (!ref) - mem.free(buffer); + ::free(buffer); ref = 0; // we own the buffer now //printf("\tfile opened\n"); @@ -1097,7 +1097,7 @@ int File::read() goto err2; } size = buf.st_size; - buffer = (unsigned char *) mem.malloc(size + 2); + buffer = (unsigned char *) ::malloc(size + 2); if (!buffer) { printf("\tmalloc error, errno = %d\n",errno); @@ -1130,7 +1130,7 @@ int File::read() err2: close(fd); err: - mem.free(buffer); + ::free(buffer); buffer = NULL; len = 0; @@ -1151,11 +1151,11 @@ err1: goto err1; if (!ref) - mem.free(buffer); + ::free(buffer); ref = 0; size = GetFileSize(h,NULL); - buffer = (unsigned char *) mem.malloc(size + 2); + buffer = (unsigned char *) ::malloc(size + 2); if (!buffer) goto err2; @@ -1184,7 +1184,7 @@ err1: err2: CloseHandle(h); err: - mem.free(buffer); + ::free(buffer); buffer = NULL; len = 0; diff --git a/dmd2/root/stringtable.c b/dmd2/root/stringtable.c index 54fe78f69d..f1c0044aaf 100644 --- a/dmd2/root/stringtable.c +++ b/dmd2/root/stringtable.c @@ -1,5 +1,5 @@ -// Copyright (c) 1999-2008 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -18,7 +18,7 @@ #include "lstring.h" #include "stringtable.h" -StringTable::StringTable(unsigned size) +void StringTable::init(unsigned size) { table = (void **)mem.calloc(size, sizeof(void *)); tabledim = size; diff --git a/dmd2/root/stringtable.h b/dmd2/root/stringtable.h index f81b191484..ce7145879c 100644 --- a/dmd2/root/stringtable.h +++ b/dmd2/root/stringtable.h @@ -1,4 +1,4 @@ -// Copyright (c) 1999-2008 by Digital Mars +// Copyright (c) 1999-2011 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com @@ -28,13 +28,13 @@ struct StringValue Lstring lstring; }; -struct StringTable : Object +struct StringTable { void **table; unsigned count; unsigned tabledim; - StringTable(unsigned size = 37); + void init(unsigned size = 37); ~StringTable(); StringValue *lookup(const dchar *s, unsigned len); diff --git a/dmd2/root/win32.c b/dmd2/root/win32.c new file mode 100644 index 0000000000..ddff10d59e --- /dev/null +++ b/dmd2/root/win32.c @@ -0,0 +1,72 @@ +// Copyright (c) 2000-2011 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 "os.h" + +/*********************************** + * Map memory. + */ + +void *os_mem_map(unsigned nbytes) +{ + return VirtualAlloc(NULL, nbytes, MEM_RESERVE, PAGE_READWRITE); +} + +/*********************************** + * Commit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_commit(void *base, unsigned offset, unsigned nbytes) +{ + void *p; + + p = VirtualAlloc((char *)base + offset, nbytes, MEM_COMMIT, PAGE_READWRITE); + return (p == NULL); +} + + +/*********************************** + * Decommit memory. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_decommit(void *base, unsigned offset, unsigned nbytes) +{ + return VirtualFree((char *)base + offset, nbytes, MEM_DECOMMIT) == 0; +} + +/*********************************** + * Unmap memory allocated with os_mem_map(). + * Memory must have already been decommitted. + * Returns: + * 0 success + * !=0 failure + */ + +int os_mem_unmap(void *base, unsigned nbytes) +{ + (void)nbytes; + return VirtualFree(base, 0, MEM_RELEASE) == 0; +} + + +/******************************************** + */ + +pthread_t pthread_self() +{ + return (pthread_t) GetCurrentThreadId(); +} diff --git a/dmd2/statement.c b/dmd2/statement.c index ebd4fc41f9..b16c2346a6 100644 --- a/dmd2/statement.c +++ b/dmd2/statement.c @@ -679,7 +679,8 @@ int CompoundStatement::blockExit(bool mustNotThrow) if (global.params.warnings && result & BEfallthru && slast) { slast = slast->last(); - if (slast && (s->isCaseStatement() || s->isDefaultStatement())) + if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) && + (s->isCaseStatement() || s->isDefaultStatement())) { // Allow if last case/default was empty CaseStatement *sc = slast->isCaseStatement(); @@ -1210,7 +1211,7 @@ void DoStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) body->toCBuffer(buf, hgs); buf->writestring("while ("); condition->toCBuffer(buf, hgs); - buf->writebyte(')'); + buf->writestring(");"); } /******************************** ForStatement ***************************/ @@ -1516,18 +1517,22 @@ Lretry: Dsymbol *var; if (te) { Type *tb = e->type->toBasetype(); + Dsymbol *s = NULL; if ((tb->ty == Tfunction || tb->ty == Tsarray) && e->op == TOKvar) - { VarExp *ve = (VarExp *)e; - var = new AliasDeclaration(loc, arg->ident, ve->var); - } + s = ((VarExp *)e)->var; + else if (e->op == TOKtemplate) + s =((TemplateExp *)e)->td; + else if (e->op == TOKimport) + s =((ScopeExp *)e)->sds; + + if (s) + var = new AliasDeclaration(loc, arg->ident, s); else { arg->type = e->type; Initializer *ie = new ExpInitializer(0, e); VarDeclaration *v = new VarDeclaration(loc, arg->type, arg->ident, ie); - if (e->isConst()) - v->storage_class |= STCconst; - if (e->op == TOKstring) + if (e->isConst() || e->op == TOKstring) v->storage_class |= STCmanifest; var = v; } @@ -1849,6 +1854,9 @@ Lagain: if (!ve->type || ve->type->ty == Terror) goto Lrangeerr; + // Resolve inout qualifier of front type + ve->type = ve->type->substWildTo(tab->mod); + Expressions *exps = new Expressions(); exps->push(ve); int pos = 0; @@ -1858,7 +1866,7 @@ Lagain: if (pos == -1) break; } - if (exps->dim > dim) + if (exps->dim != dim) goto Lrangeerr; for (size_t i = 0; i < dim; i++) @@ -1893,6 +1901,9 @@ Lagain: printf("increment: %s\n", increment->toChars()); printf("body: %s\n", forbody->toChars()); #endif + if (prelude) + s = new CompoundStatement(loc, + new ExpStatement(prelude->loc, prelude), s); s = s->semantic(sc); break; @@ -3718,13 +3729,10 @@ Statement *ReturnStatement::semantic(Scope *sc) * (but first ensure it doesn't fail the "check for * escaping reference" test) */ - unsigned errors = global.errors; - global.gag++; + unsigned errors = global.startGagging(); exp->checkEscapeRef(); - global.gag--; - if (errors != global.errors) + if (global.endGagging(errors)) { tf->isref = FALSE; // return by value - global.errors = errors; } } else @@ -3832,7 +3840,7 @@ Statement *ReturnStatement::semantic(Scope *sc) { if (((TypeFunction *)fd->type)->isref && !fd->isCtorDeclaration()) { // Function returns a reference - if (tbret->isMutable()) + if (tret->isMutable()) exp = exp->modifiableLvalue(sc, exp); else exp = exp->toLvalue(sc, exp); @@ -3893,8 +3901,14 @@ Statement *ReturnStatement::semantic(Scope *sc) * exp; return; */ Statement *s = new ExpStatement(loc, exp); - exp = NULL; s = s->semantic(sc); + + if (exp->type->ty != Tvoid) + { + error("cannot return non-void from void function"); + } + + exp = NULL; return new CompoundStatement(loc, s, this); } diff --git a/dmd2/struct.c b/dmd2/struct.c index d75dc02458..352c21edfe 100644 --- a/dmd2/struct.c +++ b/dmd2/struct.c @@ -21,6 +21,8 @@ #include "statement.h" #include "template.h" +FuncDeclaration *StructDeclaration::xerreq; // object.xopEquals + /********************************* AggregateDeclaration ****************************/ AggregateDeclaration::AggregateDeclaration(Loc loc, Identifier *id) @@ -314,9 +316,11 @@ StructDeclaration::StructDeclaration(Loc loc, Identifier *id) zeroInit = 0; // assume false until we do semantic processing #if DMDV2 hasIdentityAssign = 0; + hasIdentityEquals = 0; cpctor = NULL; postblit = NULL; - eq = NULL; + + xeq = NULL; #endif // For forward references @@ -497,6 +501,65 @@ void StructDeclaration::semantic(Scope *sc) #endif } + if (sizeok == 2) + { // semantic() failed because of forward references. + // Unwind what we did, and defer it for later + fields.setDim(0); + structsize = 0; + alignsize = 0; + structalign = 0; + + scope = scx ? scx : new Scope(*sc); + scope->setNoFree(); + scope->module->addDeferredSemantic(this); + + Module::dprogress = dprogress_save; + //printf("\tdeferring %s\n", toChars()); + return; + } + + // 0 sized struct's are set to 1 byte + if (structsize == 0) + { + structsize = 1; + alignsize = 1; + } + + // Round struct size up to next alignsize boundary. + // This will ensure that arrays of structs will get their internals + // aligned properly. + structsize = (structsize + alignsize - 1) & ~(alignsize - 1); + + sizeok = 1; + Module::dprogress++; + + //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); + + // Determine if struct is all zeros or not + zeroInit = 1; + for (size_t i = 0; i < fields.dim; i++) + { + Dsymbol *s = fields.tdata()[i]; + VarDeclaration *vd = s->isVarDeclaration(); + if (vd && !vd->isDataseg()) + { + if (vd->init) + { + // Should examine init to see if it is really all 0's + zeroInit = 0; + break; + } + else + { + if (!vd->type->isZeroInit(loc)) + { + zeroInit = 0; + break; + } + } + } + } + #if DMDV1 /* This doesn't work for DMDV2 because (ref S) and (S) parameter * lists will overload the same. @@ -560,107 +623,18 @@ void StructDeclaration::semantic(Scope *sc) } #endif #if DMDV2 - /* Try to find the opEquals function. Build it if necessary. - */ - TypeFunction *tfeqptr; - { // bool opEquals(const T*) const; - Parameters *parameters = new Parameters; -#if STRUCTTHISREF - // bool opEquals(ref const T) const; - Parameter *param = new Parameter(STCref, type->constOf(), NULL, NULL); -#else - // bool opEquals(const T*) const; - Parameter *param = new Parameter(STCin, type->pointerTo(), NULL, NULL); -#endif - - parameters->push(param); - tfeqptr = new TypeFunction(parameters, Type::tbool, 0, LINKd); - tfeqptr->mod = MODconst; - tfeqptr = (TypeFunction *)tfeqptr->semantic(0, sc2); - - Dsymbol *s = search_function(this, Id::eq); - FuncDeclaration *fdx = s ? s->isFuncDeclaration() : NULL; - if (fdx) - { - eq = fdx->overloadExactMatch(tfeqptr, getModule()); - if (!eq) - fdx->error("type signature should be %s not %s", tfeqptr->toChars(), fdx->type->toChars()); - } - - TemplateDeclaration *td = s ? s->isTemplateDeclaration() : NULL; - // BUG: should also check that td is a function template, not just a template - - if (!eq && !td) - eq = buildOpEquals(sc2); - } - dtor = buildDtor(sc2); postblit = buildPostBlit(sc2); cpctor = buildCpCtor(sc2); + buildOpAssign(sc2); + hasIdentityEquals = (buildOpEquals(sc2) != NULL); + + xeq = buildXopEquals(sc2); #endif sc2->pop(); - if (sizeok == 2) - { // semantic() failed because of forward references. - // Unwind what we did, and defer it for later - fields.setDim(0); - structsize = 0; - alignsize = 0; - structalign = 0; - - scope = scx ? scx : new Scope(*sc); - scope->setNoFree(); - scope->module->addDeferredSemantic(this); - - Module::dprogress = dprogress_save; - //printf("\tdeferring %s\n", toChars()); - return; - } - - // 0 sized struct's are set to 1 byte - if (structsize == 0) - { - structsize = 1; - alignsize = 1; - } - - // Round struct size up to next alignsize boundary. - // This will ensure that arrays of structs will get their internals - // aligned properly. - structsize = (structsize + alignsize - 1) & ~(alignsize - 1); - - sizeok = 1; - Module::dprogress++; - - //printf("-StructDeclaration::semantic(this=%p, '%s')\n", this, toChars()); - - // Determine if struct is all zeros or not - zeroInit = 1; - for (size_t i = 0; i < fields.dim; i++) - { - Dsymbol *s = fields.tdata()[i]; - VarDeclaration *vd = s->isVarDeclaration(); - if (vd && !vd->isDataseg()) - { - if (vd->init) - { - // Should examine init to see if it is really all 0's - zeroInit = 0; - break; - } - else - { - if (!vd->type->isZeroInit(loc)) - { - zeroInit = 0; - break; - } - } - } - } - /* Look for special member functions. */ #if DMDV2 diff --git a/dmd2/template.c b/dmd2/template.c index 2eb509a568..c621c05ad1 100644 --- a/dmd2/template.c +++ b/dmd2/template.c @@ -603,39 +603,45 @@ void TemplateDeclaration::makeParamNamesVisibleInConstraint(Scope *paramscope, E onemember->toAlias()->isFuncDeclaration() : NULL; if (fd) { + /* + Making parameters is similar to FuncDeclaration::semantic3 + */ paramscope->parent = fd; - int fvarargs; // function varargs - Parameters *fparameters = fd->getParameters(&fvarargs); + + TypeFunction *tf = (TypeFunction *)fd->type->syntaxCopy(); + + // Shouldn't run semantic on default arguments and return type. + for (int i = 0; iparameters->dim; i++) + tf->parameters->tdata()[i]->defaultArg = NULL; + tf->next = NULL; + + // Resolve parameter types and 'auto ref's. + tf->fargs = fargs; + tf = (TypeFunction *)tf->semantic(loc, paramscope); + + Parameters *fparameters = tf->parameters; + int fvarargs = tf->varargs; + size_t nfparams = Parameter::dim(fparameters); // Num function parameters for (size_t i = 0; i < nfparams; i++) { - Parameter *fparam = Parameter::getNth(fparameters, i)->syntaxCopy(); + Parameter *fparam = Parameter::getNth(fparameters, i); + // Remove addMod same as func.d L1065 of FuncDeclaration::semantic3 + //Type *vtype = fparam->type; + //if (fd->type && fd->isPure()) + // vtype = vtype->addMod(MODconst); + fparam->storageClass &= (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); + fparam->storageClass |= STCparameter; + if (fvarargs == 2 && i + 1 == nfparams) + fparam->storageClass |= STCvariadic; + } + for (size_t i = 0; i < fparameters->dim; i++) + { + Parameter *fparam = fparameters->tdata()[i]; if (!fparam->ident) continue; // don't add it, if it has no name - Type *vtype = fparam->type->syntaxCopy(); - // isPure will segfault if called on a ctor, because fd->type is null. - if (fd->type && fd->isPure()) - vtype = vtype->addMod(MODconst); - VarDeclaration *v = new VarDeclaration(loc, vtype, fparam->ident, NULL); - v->storage_class |= STCparameter; - // Not sure if this condition is correct/necessary. - // It's from func.c - if (//fd->type && fd->type->ty == Tfunction && - fvarargs == 2 && i + 1 == nfparams) - v->storage_class |= STCvariadic; - - v->storage_class |= fparam->storageClass & (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); - if (fparam->storageClass & STCauto) - { - if (fargs && i < fargs->dim) - { Expression *farg = fargs->tdata()[i]; - if (farg->isLvalue()) - ; // ref parameter - else - v->storage_class &= ~STCref; // value parameter - } - } - + VarDeclaration *v = new VarDeclaration(loc, fparam->type, fparam->ident, NULL); + v->storage_class = fparam->storageClass; v->semantic(paramscope); if (!paramscope->insert(v)) error("parameter %s.%s is already defined", toChars(), v->toChars()); @@ -912,6 +918,9 @@ MATCH TemplateDeclaration::deduceFunctionTemplateMatch(Scope *sc, Loc loc, Objec Parameters *fparameters; // function parameter list int fvarargs; // function varargs Objects dedtypes; // for T:T*, the dedargs is the T*, dedtypes is the T + unsigned wildmatch = 0; + + TypeFunction *tf = (TypeFunction *)fd->type; #if 0 printf("\nTemplateDeclaration::deduceFunctionTemplateMatch() %s\n", toChars()); @@ -1114,7 +1123,7 @@ L2: { Type *tthis = ethis->type; unsigned mod = fd->type->mod; - StorageClass stc = scope->stc; + StorageClass stc = scope->stc | fd->storage_class2; // Propagate parent storage class (see bug 5504) Dsymbol *p = parent; while (p->isTemplateDeclaration() || p->isTemplateInstance()) @@ -1202,8 +1211,11 @@ L2: #endif MATCH m; - m = argtype->deduceType(paramscope, fparam->type, parameters, &dedtypes); + m = argtype->deduceType(paramscope, fparam->type, parameters, &dedtypes, + tf->hasWild() ? &wildmatch : NULL); //printf("\tdeduceType m = %d\n", m); + //if (tf->hasWild()) + // printf("\twildmatch = x%x m = %d\n", wildmatch, m); /* If no match, see if there's a conversion to a delegate */ @@ -1791,7 +1803,7 @@ int templateParameterLookup(Type *tparam, TemplateParameters *parameters) */ MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes) + Objects *dedtypes, unsigned *wildmatch) { #if 0 printf("Type::deduceType()\n"); @@ -1827,7 +1839,7 @@ MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, */ tparam = tparam->semantic(loc, sc); assert(tparam->ty != Tident); - return deduceType(sc, tparam, parameters, dedtypes); + return deduceType(sc, tparam, parameters, dedtypes, wildmatch); } TemplateParameter *tp = parameters->tdata()[i]; @@ -1841,6 +1853,59 @@ MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, // 7*7 == 49 cases #define X(U,T) ((U) << 4) | (T) + + if (wildmatch && (tparam->mod & MODwild)) + { + switch (X(tparam->mod, mod)) + { + case X(MODwild, MODwild): + case X(MODwild | MODshared, MODwild | MODshared): + case X(MODwild, 0): + case X(MODwild, MODconst): + case X(MODwild, MODimmutable): + case X(MODwild | MODshared, MODshared): + case X(MODwild | MODshared, MODconst | MODshared): + + if (!at) + { + if (mod & MODwild) + *wildmatch |= MODwild; + else if (mod == 0) + *wildmatch |= MODmutable; + else + *wildmatch |= (mod & ~MODshared); + tt = mutableOf(); + dedtypes->tdata()[i] = tt; + goto Lconst; + } + + //printf("\t> tt = %s, at = %s\n", tt->toChars(), at->toChars()); + //printf("\t> tt->implicitConvTo(at->constOf()) = %d\n", tt->implicitConvTo(at->constOf())); + //printf("\t> at->implicitConvTo(tt->constOf()) = %d\n", at->implicitConvTo(tt->constOf())); + + if (tt->equals(at)) + { + goto Lconst; + } + else if (tt->implicitConvTo(at->constOf())) + { + dedtypes->tdata()[i] = at->constOf()->mutableOf(); + *wildmatch |= MODconst; + goto Lconst; + } + else if (at->implicitConvTo(tt->constOf())) + { + dedtypes->tdata()[i] = tt->constOf()->mutableOf(); + *wildmatch |= MODconst; + goto Lconst; + } + goto Lnomatch; + + default: + break; + } + } + switch (X(tparam->mod, mod)) { case X(0, 0): @@ -2003,7 +2068,7 @@ MATCH Type::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, } if (nextOf()) - return nextOf()->deduceType(sc, tparam->nextOf(), parameters, dedtypes); + return nextOf()->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); Lexact: return MATCHexact; @@ -2019,19 +2084,19 @@ Lconst: #if DMDV2 MATCH TypeDArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes) + Objects *dedtypes, unsigned *wildmatch) { #if 0 printf("TypeDArray::deduceType()\n"); printf("\tthis = %d, ", ty); print(); printf("\ttparam = %d, ", tparam->ty); tparam->print(); #endif - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } #endif MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes) + Objects *dedtypes, unsigned *wildmatch) { #if 0 printf("TypeSArray::deduceType()\n"); @@ -2100,7 +2165,7 @@ MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame else { dedtypes->tdata()[i] = dim; } - return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes); + return next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); } } } @@ -2109,19 +2174,19 @@ MATCH TypeSArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame else if (tparam->ty == Tarray) { MATCH m; - m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes); + m = next->deduceType(sc, tparam->nextOf(), parameters, dedtypes, wildmatch); if (m == MATCHexact) m = MATCHconvert; return m; } } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); Lnomatch: return MATCHnomatch; } -MATCH TypeAArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeAArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { #if 0 printf("TypeAArray::deduceType()\n"); @@ -2133,15 +2198,15 @@ MATCH TypeAArray::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame if (tparam && tparam->ty == Taarray) { TypeAArray *tp = (TypeAArray *)tparam; - if (!index->deduceType(sc, tp->index, parameters, dedtypes)) + if (!index->deduceType(sc, tp->index, parameters, dedtypes, wildmatch)) { return MATCHnomatch; } } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } -MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { //printf("TypeFunction::deduceType()\n"); //printf("\tthis = %d, ", ty); print(); @@ -2237,14 +2302,14 @@ MATCH TypeFunction::deduceType(Scope *sc, Type *tparam, TemplateParameters *para Parameter *a = Parameter::getNth(this->parameters, i); Parameter *ap = Parameter::getNth(tp->parameters, i); if (a->storageClass != ap->storageClass || - !a->type->deduceType(sc, ap->type, parameters, dedtypes)) + !a->type->deduceType(sc, ap->type, parameters, dedtypes, wildmatch)) return MATCHnomatch; } } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } -MATCH TypeIdentifier::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeIdentifier::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { // Extra check if (tparam && tparam->ty == Tident) @@ -2260,12 +2325,12 @@ MATCH TypeIdentifier::deduceType(Scope *sc, Type *tparam, TemplateParameters *pa return MATCHnomatch; } } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } MATCH TypeInstance::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, - Objects *dedtypes) + Objects *dedtypes, unsigned *wildmatch) { #if 0 printf("TypeInstance::deduceType()\n"); @@ -2406,7 +2471,7 @@ MATCH TypeInstance::deduceType(Scope *sc, if (t1 && t2) { - if (!t1->deduceType(sc, t2, parameters, dedtypes)) + if (!t1->deduceType(sc, t2, parameters, dedtypes, wildmatch)) goto Lnomatch; } else if (e1 && e2) @@ -2479,14 +2544,14 @@ MATCH TypeInstance::deduceType(Scope *sc, goto Lnomatch; } } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); Lnomatch: //printf("no match\n"); return MATCHnomatch; } -MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { //printf("TypeStruct::deduceType()\n"); //printf("\tthis->parent = %s, ", sym->parent->toChars()); print(); @@ -2503,7 +2568,7 @@ MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame if (ti && ti->toAlias() == sym) { TypeInstance *t = new TypeInstance(0, ti); - return t->deduceType(sc, tparam, parameters, dedtypes); + return t->deduceType(sc, tparam, parameters, dedtypes, wildmatch); } /* Match things like: @@ -2520,7 +2585,7 @@ MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; - MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes); + MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes, wildmatch); tpi->idents.dim++; return m; } @@ -2536,10 +2601,10 @@ MATCH TypeStruct::deduceType(Scope *sc, Type *tparam, TemplateParameters *parame if (sym != tp->sym) return MATCHnomatch; } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } -MATCH TypeEnum::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeEnum::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { // Extra check if (tparam && tparam->ty == Tenum) @@ -2549,10 +2614,10 @@ MATCH TypeEnum::deduceType(Scope *sc, Type *tparam, TemplateParameters *paramete if (sym != tp->sym) return MATCHnomatch; } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } -MATCH TypeTypedef::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeTypedef::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { // Extra check if (tparam && tparam->ty == Ttypedef) @@ -2562,7 +2627,7 @@ MATCH TypeTypedef::deduceType(Scope *sc, Type *tparam, TemplateParameters *param if (sym != tp->sym) return MATCHnomatch; } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } /* Helper for TypeClass::deduceType(). @@ -2622,7 +2687,7 @@ void deduceBaseClassParameters(BaseClass *b, } -MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes) +MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *parameters, Objects *dedtypes, unsigned *wildmatch) { //printf("TypeClass::deduceType(this = %s)\n", toChars()); @@ -2637,7 +2702,7 @@ MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *paramet if (ti && ti->toAlias() == sym) { TypeInstance *t = new TypeInstance(0, ti); - MATCH m = t->deduceType(sc, tparam, parameters, dedtypes); + MATCH m = t->deduceType(sc, tparam, parameters, dedtypes, wildmatch); // Even if the match fails, there is still a chance it could match // a base class. if (m != MATCHnomatch) @@ -2658,7 +2723,7 @@ MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *paramet /* Slice off the .foo in S!(T).foo */ tpi->idents.dim--; - MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes); + MATCH m = tparent->deduceType(sc, tpi, parameters, dedtypes, wildmatch); tpi->idents.dim++; return m; } @@ -2666,7 +2731,7 @@ MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *paramet } // If it matches exactly or via implicit conversion, we're done - MATCH m = Type::deduceType(sc, tparam, parameters, dedtypes); + MATCH m = Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); if (m != MATCHnomatch) return m; @@ -2715,7 +2780,7 @@ MATCH TypeClass::deduceType(Scope *sc, Type *tparam, TemplateParameters *paramet //printf("\t%d\n", (MATCH) implicitConvTo(tp)); return implicitConvTo(tp); } - return Type::deduceType(sc, tparam, parameters, dedtypes); + return Type::deduceType(sc, tparam, parameters, dedtypes, wildmatch); } /* ======================== TemplateParameter =============================== */ @@ -3703,6 +3768,7 @@ TemplateInstance::TemplateInstance(Loc loc, Identifier *ident) this->havetempdecl = 0; this->isnested = NULL; this->errors = 0; + this->speculative = 0; #if IN_LLVM // LDC @@ -3737,6 +3803,7 @@ TemplateInstance::TemplateInstance(Loc loc, TemplateDeclaration *td, Objects *ti this->havetempdecl = 1; this->isnested = NULL; this->errors = 0; + this->speculative = 0; #if IN_LLVM // LDC @@ -3934,6 +4001,27 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) // It's a match inst = ti; parent = ti->parent; + + // If both this and the previous instantiation were speculative, + // use the number of errors that happened last time. + if (inst->speculative && global.gag) + { + global.errors += inst->errors; + global.gaggedErrors += inst->errors; + } + + // If the first instantiation was speculative, but this is not: + if (inst->speculative && !global.gag) + { + // If the first instantiation had failed, re-run semantic, + // so that error messages are shown. + if (inst->errors) + goto L1; + // It had succeeded, mark it is a non-speculative instantiation, + // and reuse it. + inst->speculative = 0; + } + #if LOG printf("\tit's a match with instance %p\n", inst); #endif @@ -3951,6 +4039,10 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) #endif unsigned errorsave = global.errors; inst = this; + // Mark as speculative if we are instantiated from inside is(typeof()) + if (global.gag && sc->intypeof) + speculative = 1; + int tempdecl_instance_idx = tempdecl->instances.dim; tempdecl->instances.push(this); parent = tempdecl->parent; @@ -4233,7 +4325,12 @@ void TemplateInstance::semantic(Scope *sc, Expressions *fargs) // (see bugzilla 4302 and 6602). tempdecl->instances.remove(tempdecl_instance_idx); if (target_symbol_list) + { + // Because we added 'this' in the last position above, we + // should be able to remove it without messing other indices up. + assert(target_symbol_list->tdata()[target_symbol_list_idx] == this); target_symbol_list->remove(target_symbol_list_idx); + } semanticRun = 0; inst = NULL; } @@ -4293,7 +4390,9 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f tiargs->tdata()[j] = ea; } else if (sa) - { tiargs->tdata()[j] = sa; + { + Ldsym: + tiargs->tdata()[j] = sa; TupleDeclaration *d = sa->toAlias()->isTupleDeclaration(); if (d) { @@ -4339,13 +4438,17 @@ void TemplateInstance::semanticTiargs(Loc loc, Scope *sc, Objects *tiargs, int f ea = ea->semantic(sc); if (flags & 1) // only used by __traits, must not interpret the args ea = ea->optimize(WANTvalue); - else if (ea->op != TOKvar) + else if (ea->op != TOKvar && ea->op != TOKtuple) ea = ea->optimize(WANTvalue | WANTinterpret); tiargs->tdata()[j] = ea; if (ea->op == TOKtype) { ta = ea->type; goto Ltype; } + if (ea->op == TOKimport) + { sa = ((ScopeExp *)ea)->sds; + goto Ldsym; + } if (ea->op == TOKtuple) { // Expand tuple TupleExp *te = (TupleExp *)ea; @@ -4901,6 +5004,10 @@ int TemplateInstance::needsTypeInference(Scope *sc) return FALSE; } + for (size_t i = 0; i < td->parameters->dim; i++) + if (td->parameters->tdata()[i]->isTemplateThisParameter()) + return TRUE; + /* Determine if the instance arguments, tiargs, are all that is necessary * to instantiate the template. */ @@ -4908,7 +5015,7 @@ int TemplateInstance::needsTypeInference(Scope *sc) //printf("tp = %p, td->parameters->dim = %d, tiargs->dim = %d\n", tp, td->parameters->dim, tiargs->dim); TypeFunction *fdtype = (TypeFunction *)fd->type; if (Parameter::dim(fdtype->parameters) && - (tp || tiargs->dim < td->parameters->dim)) + ((tp && td->parameters->dim > 1) || tiargs->dim < td->parameters->dim)) return TRUE; /* If there is more than one function template which matches, we may * need type inference (see Bugzilla 4430) @@ -4967,10 +5074,26 @@ void TemplateInstance::semantic3(Scope *sc) sc = sc->push(argsym); sc = sc->push(this); sc->tinst = this; + int oldgag = global.gag; + int olderrors = global.errors; + /* If this is a speculative instantiation, gag errors. + * Future optimisation: If the results are actually needed, errors + * would already be gagged, so we don't really need to run semantic + * on the members. + */ + if (speculative && !oldgag) + olderrors = global.startGagging(); for (size_t i = 0; i < members->dim; i++) { Dsymbol *s = members->tdata()[i]; s->semantic3(sc); + if (speculative && global.errors != olderrors) + break; + } + if (speculative && !oldgag) + { // If errors occurred, this instantiation failed + errors += global.errors - olderrors; + global.endGagging(olderrors); } sc = sc->pop(); sc->pop(); diff --git a/dmd2/template.h b/dmd2/template.h index 436a86f6c8..141630cb93 100644 --- a/dmd2/template.h +++ b/dmd2/template.h @@ -301,6 +301,7 @@ struct TemplateInstance : ScopeDsymbol int havetempdecl; // 1 if used second constructor Dsymbol *isnested; // if referencing local symbols, this is the context int errors; // 1 if compiled with errors + int speculative; // 1 if only instantiated with errors gagged #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 diff --git a/dmd2/traits.c b/dmd2/traits.c index d44a5f23e9..e0c7d42eec 100644 --- a/dmd2/traits.c +++ b/dmd2/traits.c @@ -263,7 +263,10 @@ Expression *TraitsExp::semantic(Scope *sc) e = e->trySemantic(sc); if (!e) { if (global.gag) + { global.errors++; + global.gaggedErrors++; + } goto Lfalse; } else @@ -400,8 +403,7 @@ Expression *TraitsExp::semantic(Scope *sc) { Object *o = args->tdata()[i]; Expression *e; - unsigned errors = global.errors; - global.gag++; + unsigned errors = global.startGagging(); Type *t = isType(o); if (t) @@ -422,10 +424,8 @@ Expression *TraitsExp::semantic(Scope *sc) } } - global.gag--; - if (errors != global.errors) + if (global.endGagging(errors)) { - global.errors = errors; goto Lfalse; } } diff --git a/druntime b/druntime index 3ce61b8239..ade0aff614 160000 --- a/druntime +++ b/druntime @@ -1 +1 @@ -Subproject commit 3ce61b82390457e4a24f15f6415234315dc054f9 +Subproject commit ade0aff6142d0a66df24fa1f5ace7d506d70ca98 diff --git a/gen/arrays.cpp b/gen/arrays.cpp index 6b1d949c34..80b5bc0911 100644 --- a/gen/arrays.cpp +++ b/gen/arrays.cpp @@ -114,64 +114,20 @@ void DtoArrayInit(Loc& loc, DValue* array, DValue* value, int op) LOG_SCOPE; #if DMDV2 - if (op != -1 && op != TOKblit && arrayNeedsPostblit(array->type)) { DtoArraySetAssign(loc, array, value, op); return; } - - LLValue* ptr = DtoArrayPtr(array); - LLValue* dim; - if (array->type->ty == Tsarray) { - // Calculate length of the static array - LLValue* rv = array->getRVal(); - LLArrayType* t = isaArray(rv->getType()->getContainedType(0)); - uint64_t c = t->getNumElements(); - while (t = isaArray(t->getContainedType(0))) - c *= t->getNumElements(); - assert(c > 0); - dim = DtoConstSize_t(c); - ptr = DtoBitCast(ptr, DtoType(DtoArrayElementType(array->type)->pointerTo())); - } else { - dim = DtoArrayLen(array); - } - -#else // DMDV1 +#endif LLValue* dim = DtoArrayLen(array); LLValue* ptr = DtoArrayPtr(array); - -#endif - - LLValue* val; - - // give slices and complex values storage (and thus an address to pass) - if (value->isSlice() || value->type->ty == Tdelegate) - { - val = DtoAlloca(value->getType(), ".tmpparam"); - DVarValue lval(value->getType(), val); - DtoAssign(loc, &lval, value); - } - else - { - val = value->getRVal(); - } - assert(val); - - // prepare runtime call - LLSmallVector args; - args.push_back(ptr); - args.push_back(dim); - args.push_back(val); - - // determine the right runtime function to call - const char* funcname = NULL; Type* arrayelemty = array->getType()->nextOf()->toBasetype(); - Type* valuety = value->getType()->toBasetype(); // lets first optimize all zero initializations down to a memset. // this simplifies codegen later on as llvm null's have no address! + LLValue *val = value->getRVal(); if (isaConstant(val) && isaConstant(val)->isNullValue()) { size_t X = getTypePaddedSize(val->getType()); @@ -180,103 +136,48 @@ void DtoArrayInit(Loc& loc, DValue* array, DValue* value, int op) return; } - // if not a zero initializer, call the appropriate runtime function! - switch (valuety->ty) - { - case Tbool: - val = gIR->ir->CreateZExt(val, LLType::getInt8Ty(gIR->context()), ".bool"); - // fall through + // create blocks + llvm::BasicBlock* oldend = gIR->scopeend(); + llvm::BasicBlock* condbb = llvm::BasicBlock::Create(gIR->context(), "arrayinit.cond", + gIR->topfunc(), oldend); + llvm::BasicBlock* bodybb = llvm::BasicBlock::Create(gIR->context(), "arrayinit.body", + gIR->topfunc(), oldend); + llvm::BasicBlock* endbb = llvm::BasicBlock::Create(gIR->context(), "arrayinit.end", + gIR->topfunc(), oldend); - case Tvoid: - case Tchar: - case Tint8: - case Tuns8: - Logger::println("Using memset for array init"); - DtoMemSet(ptr, val, dim); - return; + // initialize iterator + LLValue *itr = DtoAlloca(Type::tsize_t, "arrayinit.itr"); + DtoStore(DtoConstSize_t(0), itr); - case Twchar: - case Tint16: - case Tuns16: - funcname = "_d_array_init_i16"; - break; + // move into the for condition block, ie. start the loop + assert(!gIR->scopereturned()); + llvm::BranchInst::Create(condbb, gIR->scopebb()); - case Tdchar: - case Tint32: - case Tuns32: - funcname = "_d_array_init_i32"; - break; + // replace current scope + gIR->scope() = IRScope(condbb,bodybb); - case Tint64: - case Tuns64: - funcname = "_d_array_init_i64"; - break; + // create the condition + LLValue* cond_val = gIR->ir->CreateICmpNE(DtoLoad(itr), dim, "arrayinit.condition"); - case Tfloat32: - case Timaginary32: - funcname = "_d_array_init_float"; - break; + // conditional branch + assert(!gIR->scopereturned()); + llvm::BranchInst::Create(bodybb, endbb, cond_val, gIR->scopebb()); - case Tfloat64: - case Timaginary64: - funcname = "_d_array_init_double"; - break; + // rewrite scope + gIR->scope() = IRScope(bodybb, endbb); - case Tfloat80: - case Timaginary80: - funcname = "_d_array_init_real"; - break; + // assign array element value + DValue *arrayelem = new DVarValue(arrayelemty, DtoGEP1(ptr, DtoLoad(itr), "arrayinit.arrayelem")); + DtoAssign(loc, arrayelem, value, op); - case Tcomplex32: - funcname = "_d_array_init_cfloat"; - break; + // increment iterator + DtoStore(gIR->ir->CreateAdd(DtoLoad(itr), DtoConstSize_t(1), "arrayinit.new_itr"), itr); - case Tcomplex64: - funcname = "_d_array_init_cdouble"; - break; + // loop + llvm::BranchInst::Create(condbb, gIR->scopebb()); - case Tcomplex80: - funcname = "_d_array_init_creal"; - break; - - case Tpointer: - case Tclass: - funcname = "_d_array_init_pointer"; - args[0] = DtoBitCast(args[0], getPtrToType(getVoidPtrType())); - args[2] = DtoBitCast(args[2], getVoidPtrType()); - break; - - // this currently acts as a kind of fallback for all the bastards... - // FIXME: this is probably too slow. - case Tstruct: - case Tdelegate: - case Tarray: - case Tsarray: - funcname = "_d_array_init_mem"; - assert(arrayelemty == valuety && "ArrayInit doesn't work on elem-initialized static arrays"); - args[0] = DtoBitCast(args[0], getVoidPtrType()); - args[2] = DtoBitCast(args[2], getVoidPtrType()); - args.push_back(DtoConstSize_t(getTypePaddedSize(DtoTypeNotVoid(arrayelemty)))); - break; - - default: - error("unhandled array init: %s = %s", array->getType()->toChars(), value->getType()->toChars()); - assert(0 && "unhandled array init"); - } - - if (Logger::enabled()) - { - Logger::cout() << "ptr = " << *args[0] << std::endl; - Logger::cout() << "dim = " << *args[1] << std::endl; - Logger::cout() << "val = " << *args[2] << std::endl; - } - - LLFunction* fn = LLVM_D_GetRuntimeFunction(gIR->module, funcname); - assert(fn); - if (Logger::enabled()) - Logger::cout() << "calling array init function: " << *fn <<'\n'; - LLCallSite call = gIR->CreateCallOrInvoke(fn, args); - call.setCallingConv(llvm::CallingConv::C); + // rewrite the scope + gIR->scope() = IRScope(endbb, oldend); } ////////////////////////////////////////////////////////////////////////////////////////// diff --git a/gen/classes.cpp b/gen/classes.cpp index 979dc99585..30c1296f8c 100644 --- a/gen/classes.cpp +++ b/gen/classes.cpp @@ -293,7 +293,7 @@ DValue* DtoCastClass(DValue* val, Type* _to) // get the from class ClassDeclaration* cd = fc->sym->isClassDeclaration(); DtoResolveClass(cd); // add this - IrTypeClass* typeclass = fc->irtype->isClass(); + IrTypeClass* typeclass = stripModifiers(fc)->irtype->isClass(); // find interface impl @@ -626,24 +626,26 @@ static unsigned build_classinfo_flags(ClassDeclaration* cd) { // adapted from original dmd code unsigned flags = 0; - //flags |= isCOMclass(); // IUnknown + flags |= cd->isCOMclass(); // IUnknown bool hasOffTi = false; - if (cd->ctor) flags |= 8; + if (cd->ctor) + flags |= 8; + if (cd->isabstract) + flags |= 64; for (ClassDeclaration *cd2 = cd; cd2; cd2 = cd2->baseClass) { - if (cd2->members) - { + if (!cd2->members) + continue; for (size_t i = 0; i < cd2->members->dim; i++) { - Dsymbol *sm = (Dsymbol *)cd2->members->data[i]; - if (sm->isVarDeclaration() && !sm->isVarDeclaration()->isDataseg()) // is this enough? - hasOffTi = true; - //printf("sm = %s %s\n", sm->kind(), sm->toChars()); - if (sm->hasPointers()) - goto L2; + Dsymbol *sm = (Dsymbol *)cd2->members->data[i]; + if (sm->isVarDeclaration() && !sm->isVarDeclaration()->isDataseg()) // is this enough? + hasOffTi = true; + //printf("sm = %s %s\n", sm->kind(), sm->toChars()); + if (sm->hasPointers()) + goto L2; } } - } flags |= 2; // no pointers L2: if (hasOffTi) @@ -760,7 +762,7 @@ LLConstant* DtoDefineClassInfo(ClassDeclaration* cd) // uint flags if (cd->isInterfaceDeclaration()) - b.push_uint(0); + b.push_uint(4 | cd->isCOMinterface() | 32); else b.push_uint(build_classinfo_flags(cd)); diff --git a/gen/declarations.cpp b/gen/declarations.cpp index 59249dd382..b985ddee1e 100644 --- a/gen/declarations.cpp +++ b/gen/declarations.cpp @@ -4,7 +4,7 @@ #include "declaration.h" #include "enum.h" #include "id.h" -#include "mem.h" +#include "rmem.h" #include "template.h" #include "init.h" diff --git a/gen/main.cpp b/gen/main.cpp index 2496bc9074..3ca170a2d5 100644 --- a/gen/main.cpp +++ b/gen/main.cpp @@ -127,8 +127,22 @@ static void initFromString(char*& dest, const cl::opt& src) { } } +#if _WIN32 +extern "C" +{ + extern int _xi_a; + extern int _end; +} +#endif + int main(int argc, char** argv) { + mem.init(); // initialize storage allocator + mem.setStackBottom(&argv); +#if _WIN32 + mem.addroots((char *)&_xi_a, (char *)&_end); +#endif + // stack trace on signals llvm::sys::PrintStackTraceOnErrorSignal(); diff --git a/gen/runtime.cpp b/gen/runtime.cpp index 9b9664c90a..82a883750b 100644 --- a/gen/runtime.cpp +++ b/gen/runtime.cpp @@ -553,39 +553,10 @@ static void LLVM_D_BuildRuntimeModule() ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// - #define ARRAY_INIT(TY,suffix) \ - { \ - std::string fname = (llvm::StringRef("_d_array_init_") + llvm::StringRef(suffix)).str(); \ - std::vector types; \ - types.push_back(rt_ptr(TY)); \ - types.push_back(sizeTy); \ - types.push_back(TY); \ - LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); \ - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) \ - ->setAttributes(Attr_1_NoCapture); \ - } - - ARRAY_INIT(shortTy,"i16") - ARRAY_INIT(intTy,"i32") - ARRAY_INIT(longTy,"i64") - ARRAY_INIT(floatTy,"float") - ARRAY_INIT(doubleTy,"double") - ARRAY_INIT(realTy,"real") - ARRAY_INIT(cfloatTy,"cfloat") - ARRAY_INIT(cdoubleTy,"cdouble") - ARRAY_INIT(crealTy,"creal") - ARRAY_INIT(voidPtrTy,"pointer") - - #undef ARRAY_INIT - - // array init mem - // void _d_array_init_mem(void* a, size_t na, void* v, size_t nv) - // + // array slice copy when assertions are on! // void _d_array_slice_copy(void* dst, size_t dstlen, void* src, size_t srclen) { - llvm::StringRef fname("_d_array_init_mem"); - llvm::StringRef fname2("_d_array_slice_copy"); + llvm::StringRef fname("_d_array_slice_copy"); std::vector types; types.push_back(voidPtrTy); types.push_back(sizeTy); @@ -594,8 +565,6 @@ static void LLVM_D_BuildRuntimeModule() LLFunctionType* fty = llvm::FunctionType::get(voidTy, types, false); llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname, M) ->setAttributes(Attr_1_3_NoCapture); - llvm::Function::Create(fty, llvm::GlobalValue::ExternalLinkage, fname2, M) - ->setAttributes(Attr_1_3_NoCapture); } ///////////////////////////////////////////////////////////////////////////////////// diff --git a/gen/toir.cpp b/gen/toir.cpp index 690a161e2c..2401b96df4 100644 --- a/gen/toir.cpp +++ b/gen/toir.cpp @@ -3091,11 +3091,14 @@ STUB(ScopeExp); #if DMDV2 STUB(SymbolExp); +STUB(PowExp); +STUB(PowAssignExp); #endif #define CONSTSTUB(x) LLConstant* x::toConstElem(IRState * p) { \ error("expression '%s' is not a constant", toChars()); \ - fatal(); \ + if (!global.gag) \ + fatal(); \ return NULL; \ } CONSTSTUB(Expression); diff --git a/gen/typinf.cpp b/gen/typinf.cpp index ecf79cd837..d62ba55901 100644 --- a/gen/typinf.cpp +++ b/gen/typinf.cpp @@ -667,7 +667,7 @@ void TypeInfoStructDeclaration::llvmDefine() // opEquals #if DMDV2 - fd = sd->eq; + fd = sd->xeq; #else fd = find_method_overload(sd, Id::eq, tfcmpptr, gm); #endif diff --git a/phobos b/phobos index a8106d9d48..2bebc8f8ba 160000 --- a/phobos +++ b/phobos @@ -1 +1 @@ -Subproject commit a8106d9d48d7bf51ddfb312f334f2607b979aff1 +Subproject commit 2bebc8f8ba261b41fb0252b225232b8f46051b57