// Compiler implementation of the D programming language // Copyright (c) 1999-2012 by Digital Mars // All Rights Reserved // written by Walter Bright // http://www.digitalmars.com // License for redistribution is by either the Artistic License // in artistic.txt, or the GNU General Public License in gnu.txt. // See the included readme.txt for details. #include #include #include "mars.h" #include "init.h" #include "declaration.h" #include "attrib.h" #include "expression.h" #include "scope.h" #include "mtype.h" #include "aggregate.h" #include "identifier.h" #include "id.h" #include "module.h" #include "statement.h" #include "template.h" #include "hdrgen.h" #include "target.h" void functionToCBuffer2(TypeFunction *t, OutBuffer *buf, HdrGenState *hgs, int mod, const char *kind); /********************************* FuncDeclaration ****************************/ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, StorageClass storage_class, Type *type) : Declaration(id) { //printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type); //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; frequire = NULL; fdrequire = NULL; fdensure = NULL; fdrequireParams = NULL; fdensureParams = NULL; outId = NULL; vresult = NULL; returnLabel = NULL; scout = NULL; fensure = NULL; fbody = NULL; localsymtab = NULL; vthis = NULL; v_arguments = NULL; #ifdef IN_GCC v_argptr = NULL; v_arguments_var = NULL; #endif v_argsave = NULL; parameters = NULL; labtab = NULL; overnext = NULL; vtblIndex = -1; hasReturnExp = 0; naked = 0; inlineStatusExp = ILSuninitialized; inlineStatusStmt = ILSuninitialized; inlineNest = 0; isArrayOp = 0; dArrayOp = NULL; semanticRun = PASSinit; semantic3Errors = 0; #if DMDV1 nestedFrameRef = 0; #endif fes = NULL; introducing = 0; tintro = NULL; /* The type given for "infer the return type" is a TypeFunction with * NULL for the return type. */ inferRetType = (type && type->nextOf() == NULL); storage_class2 = 0; hasReturnExp = 0; nrvo_can = 1; nrvo_var = NULL; #if IN_DMD shidden = NULL; #endif #if DMDV2 builtin = BUILTINunknown; tookAddressOf = 0; requiresClosure = false; flags = 0; #endif returns = NULL; #if IN_LLVM // LDC isArrayOp = false; allowInlining = false; neverInline = false; availableExternally = true; // assume this unless proven otherwise #endif } Dsymbol *FuncDeclaration::syntaxCopy(Dsymbol *s) { FuncDeclaration *f; //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars()); if (s) f = (FuncDeclaration *)s; else f = new FuncDeclaration(loc, endloc, ident, storage_class, type->syntaxCopy()); f->outId = outId; f->frequire = frequire ? frequire->syntaxCopy() : NULL; f->fensure = fensure ? fensure->syntaxCopy() : NULL; f->fbody = fbody ? fbody->syntaxCopy() : NULL; assert(!fthrows); // deprecated #if IN_LLVM f->intrinsicName = intrinsicName; #endif return f; } #if IN_LLVM static int outToRefDg(void *ctx, size_t n, Parameter *p) { if (p->storageClass & STCout) { // Cannot just use syntaxCopy() here, because it would cause the // parameter type to be semantic()ed again, in the wrong scope. So, // just copy the outer layer to modify the storage class. void *cpy = malloc(sizeof(Parameter)); memcpy(cpy, (void *)p, sizeof(Parameter)); p = (Parameter *)cpy; p->storageClass &= ~STCout; p->storageClass |= STCref; } ((Parameters *)ctx)->push(p); return 0; } static Parameters *outToRef(Parameters* params) { Parameters *result = new Parameters(); Parameter::foreach(params, &outToRefDg, result); return result; } #endif // Do the semantic analysis on the external interface to the function. void FuncDeclaration::semantic(Scope *sc) { TypeFunction *f; AggregateDeclaration *ad; StructDeclaration *sd; ClassDeclaration *cd; InterfaceDeclaration *id; Dsymbol *pd; bool doesoverride; #if 0 printf("FuncDeclaration::semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, this, toPrettyChars(), sc->linkage); if (isFuncLiteralDeclaration()) printf("\tFuncLiteralDeclaration()\n"); printf("sc->parent = %s, parent = %s\n", sc->parent->toChars(), parent ? parent->toChars() : ""); printf("type: %p, %s\n", type, type->toChars()); #endif if (semanticRun != PASSinit && isFuncLiteralDeclaration()) { /* Member functions that have return types that are * forward references can have semantic() run more than * once on them. * See test\interface2.d, test20 */ return; } parent = sc->parent; Dsymbol *parent = toParent(); if (semanticRun >= PASSsemanticdone) { if (!parent->isClassDeclaration()) { return; } // need to re-run semantic() in order to set the class's vtbl[] } else { assert(semanticRun <= PASSsemantic); semanticRun = PASSsemantic; } if (scope) { sc = scope; scope = NULL; } unsigned dprogress_save = Module::dprogress; foverrides.setDim(0); // reset in case semantic() is being retried for this function storage_class |= sc->stc & ~STCref; ad = isThis(); if (ad) { storage_class |= ad->storage_class & (STC_TYPECTOR | STCsynchronized); if (StructDeclaration *sd = ad->isStructDeclaration()) sd->makeNested(); } // Remove prefix storage classes silently. if ((storage_class & STC_TYPECTOR) && !(ad || isNested())) storage_class &= ~STC_TYPECTOR; //printf("function storage_class = x%llx, sc->stc = x%llx, %x\n", storage_class, sc->stc, Declaration::isFinal()); FuncLiteralDeclaration *fld = isFuncLiteralDeclaration(); if (fld && fld->treq) linkage = ((TypeFunction *)fld->treq->nextOf())->linkage; else linkage = sc->linkage; protection = sc->protection; userAttributes = sc->userAttributes; if (!originalType) originalType = type->syntaxCopy(); if (!type->deco) { sc = sc->push(); sc->stc |= storage_class & STCdisable; // forward to function type TypeFunction *tf = (TypeFunction *)type; #if 1 /* If the parent is @safe, then this function defaults to safe * too. * If the parent's @safe-ty is inferred, then this function's @safe-ty needs * to be inferred first. */ if (tf->trust == TRUSTdefault && !(//isFuncLiteralDeclaration() || parent->isTemplateInstance() || ad && ad->parent && ad->parent->isTemplateInstance())) { for (Dsymbol *p = sc->func; p; p = p->toParent2()) { FuncDeclaration *fd = p->isFuncDeclaration(); if (fd) { if (fd->isSafeBypassingInference()) tf->trust = TRUSTsafe; // default to @safe break; } } } #endif 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; Type *tret; if (!ad || parent->isUnionDeclaration()) { error("constructors are only for class or struct definitions"); tret = Type::tvoid; } else { tret = ad->handle; assert(tret); tret = tret->addStorageClass(storage_class | sc->stc); tret = tret->addMod(type->mod); } tf->next = tret; if (ad && ad->isStructDeclaration()) sc->stc |= STCref; } sc->linkage = linkage; if (!tf->isNaked() && !(isThis() || isNested())) { OutBuffer buf; MODtoBuffer(&buf, tf->mod); error("without 'this' cannot be %s", buf.toChars()); tf->mod = 0; // remove qualifiers } /* Apply const, immutable, wild and shared storage class * to the function type. Do this before type semantic. */ StorageClass stc = storage_class; if (type->isImmutable()) stc |= STCimmutable; if (type->isConst()) stc |= STCconst; if (type->isShared() || storage_class & STCsynchronized) stc |= STCshared; if (type->isWild()) stc |= STCwild; switch (stc & STC_TYPECTOR) { case STCimmutable: case STCimmutable | STCconst: case STCimmutable | STCconst | STCshared: case STCimmutable | STCshared: case STCimmutable | STCwild: case STCimmutable | STCconst | STCwild: case STCimmutable | STCconst | STCshared | STCwild: case STCimmutable | STCshared | STCwild: // Don't use toInvariant(), as that will do a merge() type = type->makeInvariant(); break; case STCconst: case STCconst | STCwild: type = type->makeConst(); break; case STCshared | STCconst: case STCshared | STCconst | STCwild: type = type->makeSharedConst(); break; case STCshared: type = type->makeShared(); break; case STCwild: type = type->makeWild(); break; case STCshared | STCwild: type = type->makeSharedWild(); break; case 0: break; default: assert(0); } type = type->semantic(loc, sc); sc = sc->pop(); } storage_class &= ~STCref; if (type->ty != Tfunction) { error("%s must be a function instead of %s", toChars(), type->toChars()); return; } f = (TypeFunction *)type; size_t nparams = Parameter::dim(f->parameters); if (storage_class & STCscope) error("functions cannot be scope"); if (isAbstract() && !isVirtual()) { const char *sfunc; if (isStatic()) sfunc = "static"; else if (protection == PROTprivate || protection == PROTpackage) sfunc = Pprotectionnames[protection]; else sfunc = "non-virtual"; error("%s functions cannot be abstract", sfunc); } if (isOverride() && !isVirtual()) error("cannot override a non-virtual function"); if (isAbstract() && isFinal()) error("cannot be both final and abstract"); #if 0 if (isAbstract() && fbody) error("abstract functions cannot have bodies"); #endif #if 0 if (isStaticConstructor() || isStaticDestructor()) { if (!isStatic() || type->nextOf()->ty != Tvoid) error("static constructors / destructors must be static void"); if (f->arguments && f->arguments->dim) error("static constructors / destructors must have empty parameter list"); // BUG: check for invalid storage classes } #endif sd = parent->isStructDeclaration(); if (sd) { if (isCtorDeclaration()) { goto Ldone; } #if 0 // Verify no constructors, destructors, etc. if (isCtorDeclaration() //||isDtorDeclaration() //|| isInvariantDeclaration() //|| isUnitTestDeclaration() ) { error("special member functions not allowed for %ss", sd->kind()); } if (isInvariantDeclaration()) sd->invs.push(this); if (!sd->aggNew) sd->aggNew = isNewDeclaration(); if (isDelete()) { if (sd->aggDelete) error("multiple delete's for struct %s", sd->toChars()); sd->aggDelete = (DeleteDeclaration *)(this); } #endif } id = parent->isInterfaceDeclaration(); if (id) { storage_class |= STCabstract; if (isCtorDeclaration() || #if DMDV2 isPostBlitDeclaration() || #endif isDtorDeclaration() || isInvariantDeclaration() || isNewDeclaration() || isDelete()) error("constructors, destructors, postblits, invariants, new and delete functions are not allowed in interface %s", id->toChars()); if (fbody && isVirtual()) error("function body only allowed in final functions in interface %s", id->toChars()); } /* Contracts can only appear without a body when they are virtual interface functions */ if (!fbody && (fensure || frequire) && !(id && isVirtual())) error("in and out contracts require function body"); cd = parent->isClassDeclaration(); if (cd) { size_t vi; CtorDeclaration *ctor; DtorDeclaration *dtor; if (isCtorDeclaration()) { // ctor = (CtorDeclaration *)this; // if (!cd->ctor) // cd->ctor = ctor; goto Ldone; } #if 0 dtor = isDtorDeclaration(); if (dtor) { if (cd->dtor) error("multiple destructors for class %s", cd->toChars()); cd->dtor = dtor; } if (isInvariantDeclaration()) { cd->invs.push(this); } if (isNewDeclaration()) { if (!cd->aggNew) cd->aggNew = (NewDeclaration *)(this); } if (isDelete()) { if (cd->aggDelete) error("multiple delete's for class %s", cd->toChars()); cd->aggDelete = (DeleteDeclaration *)(this); } #endif if (storage_class & STCabstract) cd->isabstract = 1; // if static function, do not put in vtbl[] if (!isVirtual()) { //printf("\tnot virtual\n"); goto Ldone; } // Suppress further errors if the return type is an error if (type->nextOf() == Type::terror) goto Ldone; if (cd->baseClass) { Dsymbol *cbd = cd->baseClass; if (cbd->parent && cbd->parent->isTemplateInstance()) { for (size_t i = 0; i < cd->baseClass->vtbl.dim; i++) { FuncDeclaration *f = cd->baseClass->vtbl[i]->isFuncDeclaration(); if (f && f->ident == ident && !f->functionSemantic()) goto Ldone; } } } /* Find index of existing function in base class's vtbl[] to override * (the index will be the same as in cd's current vtbl[]) */ vi = cd->baseClass ? findVtblIndex((Dsymbols*)&cd->baseClass->vtbl, cd->baseClass->vtbl.dim) : -1; doesoverride = FALSE; switch (vi) { case -1: Lintro: /* Didn't find one, so * This is an 'introducing' function which gets a new * slot in the vtbl[]. */ // Verify this doesn't override previous final function if (cd->baseClass) { Dsymbol *s = cd->baseClass->search(loc, ident, 0); if (s) { FuncDeclaration *f = s->isFuncDeclaration(); if (f) { f = f->overloadExactMatch(type); if (f && f->isFinal() && f->prot() != PROTprivate) error("cannot override final function %s", f->toPrettyChars()); } } } if (isFinal()) { // Don't check here, as it may override an interface function //if (isOverride()) //error("is marked as override, but does not override any function"); cd->vtblFinal.push(this); } else { // Append to end of vtbl[] //printf("\tintroducing function\n"); introducing = 1; vi = cd->vtbl.dim; cd->vtbl.push(this); vtblIndex = vi; } break; case -2: // can't determine because of fwd refs cd->sizeok = SIZEOKfwd; // can't finish due to forward reference Module::dprogress = dprogress_save; return; default: { FuncDeclaration *fdv = cd->baseClass->vtbl[vi]->isFuncDeclaration(); FuncDeclaration *fdc = cd->vtbl[vi]->isFuncDeclaration(); // This function is covariant with fdv if (fdc->toParent() == parent) { //printf("vi = %d,\tthis = %p %s %s @ [%s]\n\tfdc = %p %s %s @ [%s]\n\tfdv = %p %s %s @ [%s]\n", // vi, this, this->toChars(), this->type->toChars(), this->loc.toChars(), // fdc, fdc ->toChars(), fdc ->type->toChars(), fdc ->loc.toChars(), // fdv, fdv ->toChars(), fdv ->type->toChars(), fdv ->loc.toChars()); // fdc overrides fdv exactly, then this introduces new function. if (fdc->type->mod == fdv->type->mod && this->type->mod != fdv->type->mod) goto Lintro; } // This function overrides fdv if (fdv->isFinal()) error("cannot override final function %s", fdv->toPrettyChars()); doesoverride = TRUE; #if DMDV2 if (!isOverride()) ::deprecation(loc, "overriding base class function without using override attribute is deprecated (%s overrides %s)", toPrettyChars(), fdv->toPrettyChars()); #endif if (fdc->toParent() == parent) { // If both are mixins, or both are not, then error. // If either is not, the one that is not overrides the other. bool thismixin = this->parent->isClassDeclaration() != NULL; bool fdcmixin = fdc->parent->isClassDeclaration() != NULL; if (thismixin == fdcmixin) { error("multiple overrides of same function"); } else if (!thismixin) // fdc overrides fdv { // this doesn't override any function break; } } cd->vtbl[vi] = this; vtblIndex = vi; /* Remember which functions this overrides */ foverrides.push(fdv); /* This works by whenever this function is called, * it actually returns tintro, which gets dynamically * cast to type. But we know that tintro is a base * of type, so we could optimize it by not doing a * dynamic cast, but just subtracting the isBaseOf() * offset if the value is != null. */ if (fdv->tintro) tintro = fdv->tintro; else if (!type->equals(fdv->type)) { /* Only need to have a tintro if the vptr * offsets differ */ int offset; if (fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset)) { tintro = fdv->type; } } break; } } /* Go through all the interface bases. * If this function is covariant with any members of those interface * functions, set the tintro. */ for (size_t i = 0; i < cd->interfaces_dim; i++) { BaseClass *b = cd->interfaces[i]; vi = findVtblIndex((Dsymbols *)&b->base->vtbl, b->base->vtbl.dim); switch (vi) { case -1: break; case -2: cd->sizeok = SIZEOKfwd; // can't finish due to forward reference Module::dprogress = dprogress_save; return; default: { FuncDeclaration *fdv = (FuncDeclaration *)b->base->vtbl[vi]; Type *ti = NULL; /* Remember which functions this overrides */ foverrides.push(fdv); #if DMDV2 /* Should we really require 'override' when implementing * an interface function? */ //if (!isOverride()) //warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv->toPrettyChars()); #endif if (fdv->tintro) ti = fdv->tintro; else if (!type->equals(fdv->type)) { /* Only need to have a tintro if the vptr * offsets differ */ unsigned errors = global.errors; global.gag++; // suppress printing of error messages int offset; int baseOf = fdv->type->nextOf()->isBaseOf(type->nextOf(), &offset); global.gag--; // suppress printing of error messages if (errors != global.errors) { // any error in isBaseOf() is a forward reference error, so we bail out global.errors = errors; cd->sizeok = SIZEOKfwd; // can't finish due to forward reference Module::dprogress = dprogress_save; return; } if (baseOf) { ti = fdv->type; } } if (ti) { if (tintro) { if (!tintro->nextOf()->equals(ti->nextOf()) && !tintro->nextOf()->isBaseOf(ti->nextOf(), NULL) && !ti->nextOf()->isBaseOf(tintro->nextOf(), NULL)) { error("incompatible covariant types %s and %s", tintro->toChars(), ti->toChars()); } } tintro = ti; } goto L2; } } } if (!doesoverride && isOverride()) { Dsymbol *s = NULL; for (size_t i = 0; i < cd->baseclasses->dim; i++) { s = (*cd->baseclasses)[i]->base->search_correct(ident); if (s) break; } if (s) error("does not override any function, did you mean to override '%s'?", s->toPrettyChars()); else error("does not override any function"); } L2: ; /* Go through all the interface bases. * Disallow overriding any final functions in the interface(s). */ for (size_t i = 0; i < cd->interfaces_dim; i++) { BaseClass *b = cd->interfaces[i]; if (b->base) { Dsymbol *s = search_function(b->base, ident); if (s) { FuncDeclaration *f = s->isFuncDeclaration(); if (f) { f = f->overloadExactMatch(type); if (f && f->isFinal() && f->prot() != PROTprivate) error("cannot override final function %s.%s", b->base->toChars(), f->toPrettyChars()); } } } } } else if (isOverride() && !parent->isTemplateInstance()) error("override only applies to class member functions"); /* Do not allow template instances to add virtual functions * to a class. */ if (isVirtual()) { TemplateInstance *ti = parent->isTemplateInstance(); if (ti) { // Take care of nested templates while (1) { TemplateInstance *ti2 = ti->tempdecl->parent->isTemplateInstance(); if (!ti2) break; ti = ti2; } // If it's a member template ClassDeclaration *cd = ti->tempdecl->isClassMember(); if (cd) { error("cannot use template to add virtual function to class '%s'", cd->toChars()); } } } if (isMain()) { // Check parameters to see if they are either () or (char[][] args) switch (nparams) { case 0: break; case 1: { Parameter *arg0 = Parameter::getNth(f->parameters, 0); if (arg0->type->ty != Tarray || arg0->type->nextOf()->ty != Tarray || arg0->type->nextOf()->nextOf()->ty != Tchar || arg0->storageClass & (STCout | STCref | STClazy)) goto Lmainerr; break; } default: goto Lmainerr; } if (!f->nextOf()) error("must return int or void"); else if (f->nextOf()->ty != Tint32 && f->nextOf()->ty != Tvoid) error("must return int or void, not %s", f->nextOf()->toChars()); if (f->varargs) { Lmainerr: error("parameters must be main() or main(string[] args)"); } } if (isVirtual() && semanticRun != PASSsemanticdone) { /* Rewrite contracts as nested functions, then call them. * Doing it as nested functions means that overriding functions * can call them. */ if (frequire) { #if IN_LLVM /* In LDC, we can't rely on the codegen hacks DMD has to be able * to just magically call the contract function parameterless with * the parameters being picked up from the outer stack frame. * * Thus, we actually pass all the function parameters to the * __require call, rewriting out parameters to ref ones because * they have already been zeroed in the outer function. * * Also initialize fdrequireParams here - it will get filled in * in semantic3. */ fdrequireParams = new Expressions(); Parameters *params = outToRef(((TypeFunction*)type)->parameters); Type *tf = new TypeFunction(params, Type::tvoid, 0, LINKd); #else /* in { ... } * becomes: * void __require() { ... } * __require(); */ TypeFunction *tf = new TypeFunction(NULL, Type::tvoid, 0, LINKd); #endif Loc loc = frequire->loc; FuncDeclaration *fd = new FuncDeclaration(loc, loc, Id::require, STCundefined, tf); fd->fbody = frequire; Statement *s1 = new ExpStatement(loc, fd); #if IN_LLVM Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), fdrequireParams); #else Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), (Expressions *)NULL); #endif Statement *s2 = new ExpStatement(loc, e); frequire = new CompoundStatement(loc, s1, s2); fdrequire = fd; } if (!outId && f->nextOf() && f->nextOf()->toBasetype()->ty != Tvoid) outId = Id::result; // provide a default #if IN_LLVM /* We need to initialize fdensureParams here and not in the block below * to have the parameter available when calling a base class ensure(), * even if this functions doesn't have an out contract. */ fdensureParams = new Expressions(); if (outId) fdensureParams->push(new IdentifierExp(loc, outId)); #endif if (fensure) { #if IN_LLVM /* Same as for in contracts, see above. */ Parameters *arguments = outToRef(((TypeFunction*)type)->parameters); #else /* out (result) { ... } * becomes: * tret __ensure(ref tret result) { ... } * __ensure(result); */ Parameters *arguments = new Parameters(); #endif Loc loc = fensure->loc; Parameter *a = NULL; if (outId) { a = new Parameter(STCref | STCconst, f->nextOf(), outId, NULL); arguments->insert(0, a); } TypeFunction *tf = new TypeFunction(arguments, Type::tvoid, 0, LINKd); FuncDeclaration *fd = new FuncDeclaration(loc, loc, Id::ensure, STCundefined, tf); fd->fbody = fensure; Statement *s1 = new ExpStatement(loc, fd); #if IN_LLVM Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), fdensureParams); #else Expression *eresult = NULL; if (outId) eresult = new IdentifierExp(loc, outId); Expression *e = new CallExp(loc, new VarExp(loc, fd, 0), eresult); #endif Statement *s2 = new ExpStatement(loc, e); fensure = new CompoundStatement(loc, s1, s2); fdensure = fd; } } Ldone: /* Purity and safety can be inferred for some functions by examining * the function body. */ if (fbody && (isFuncLiteralDeclaration() || parent->isTemplateInstance() || ad && ad->parent && ad->parent->isTemplateInstance() && !isVirtualMethod())) { /* isVirtualMethod() needs setting correct foverrides */ if (f->purity == PUREimpure) // purity not specified flags |= FUNCFLAGpurityInprocess; if (f->trust == TRUSTdefault) flags |= FUNCFLAGsafetyInprocess; if (!f->isnothrow) flags |= FUNCFLAGnothrowInprocess; } Module::dprogress++; //LDC relies on semanticRun variable not being reset here if(semanticRun < PASSsemanticdone) semanticRun = PASSsemanticdone; /* Save scope for possible later use (if we need the * function internals) */ scope = new Scope(*sc); scope->setNoFree(); static bool printedMain = false; // semantic might run more than once if (global.params.verbose && !printedMain) { const char *type = isMain() ? "main" : isWinMain() ? "winmain" : isDllMain() ? "dllmain" : (const char *)NULL; Module *mod = sc->module; if (type && mod) { printedMain = true; const char *name = FileName::searchPath(global.path, mod->srcfile->toChars(), 1); printf("%-10s%-10s\t%s\n", "entry", type, name); } } return; } void FuncDeclaration::semantic2(Scope *sc) { } // Do the semantic analysis on the internals of the function. void FuncDeclaration::semantic3(Scope *sc) { TypeFunction *f; VarDeclaration *argptr = NULL; VarDeclaration *_arguments = NULL; int nerrors = global.errors; if (!parent) { if (global.errors) return; //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc); assert(0); } //printf("FuncDeclaration::semantic3('%s.%s', %p, sc = %p, loc = %s)\n", parent->toChars(), toChars(), this, sc, loc.toChars()); //fflush(stdout); //printf("storage class = x%x %x\n", sc->stc, storage_class); //{ static int x; if (++x == 2) *(char*)0=0; } //printf("\tlinkage = %d\n", sc->linkage); //printf(" sc->incontract = %d\n", (sc->flags & SCOPEcontract)); if (semanticRun >= PASSsemantic3) return; semanticRun = PASSsemantic3; semantic3Errors = 0; #if IN_LLVM if (!global.inExtraInliningSemantic) availableExternally = false; #endif if (!type || type->ty != Tfunction) return; f = (TypeFunction *)type; if (!inferRetType && f->next->ty == Terror) return; #if 0 // Check the 'throws' clause if (fthrows) { for (size_t i = 0; i < fthrows->dim; i++) { Type *t = (*fthrows)[i]; t = t->semantic(loc, sc); if (!t->isClassHandle()) error("can only throw classes, not %s", t->toChars()); } } #endif if (!fbody && inferRetType && !type->nextOf()) { error("has no function body with return type inference"); return; } if (frequire) { for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; if (fdv->fbody && !fdv->frequire) { error("cannot have an in contract when overriden function %s does not have an in contract", fdv->toPrettyChars()); break; } } } frequire = mergeFrequire(frequire); fensure = mergeFensure(fensure); if (fbody || frequire || fensure) { /* Symbol table into which we place parameters and nested functions, * solely to diagnose name collisions. */ localsymtab = new DsymbolTable(); // Establish function scope ScopeDsymbol *ss = new ScopeDsymbol(); ss->parent = sc->scopesym; Scope *sc2 = sc->push(ss); sc2->func = this; sc2->parent = this; sc2->callSuper = 0; sc2->sbreak = NULL; sc2->scontinue = NULL; sc2->sw = NULL; sc2->fes = fes; sc2->linkage = LINKd; sc2->stc &= ~(STCauto | STCscope | STCstatic | STCabstract | STCdeprecated | STCoverride | STC_TYPECTOR | STCfinal | STCtls | STCgshared | STCref | STCproperty | STCsafe | STCtrusted | STCsystem); sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = STRUCTALIGN_DEFAULT; if (this->ident != Id::require && this->ident != Id::ensure) sc2->flags = sc->flags & ~SCOPEcontract; #if IN_LLVM sc2->enclosingFinally = NULL; sc2->enclosingScopeExit = NULL; #else sc2->tf = NULL; #endif sc2->noctor = 0; sc2->speculative = sc->speculative || isSpeculative() != NULL; sc2->userAttributes = NULL; if (sc2->intypeof == 1) sc2->intypeof = 2; // Declare 'this' AggregateDeclaration *ad = isThis(); if (ad) { if (isFuncLiteralDeclaration() && isNested() && !sc->intypeof) { error("function literals cannot be class members"); return; } else assert(!isNested() || sc->intypeof); // can't be both member and nested } vthis = declareThis(sc2, ad); // Declare hidden variable _arguments[] and _argptr if (f->varargs == 1) { Type *t; #if !IN_GCC && !IN_LLVM if (global.params.is64bit && !global.params.isWindows) { // Declare save area for varargs registers Type *t = new TypeIdentifier(loc, Id::va_argsave_t); t = t->semantic(loc, sc); if (t == Type::terror) { error("must import core.vararg to use variadic functions"); return; } else { v_argsave = new VarDeclaration(loc, t, Id::va_argsave, NULL); v_argsave->semantic(sc2); sc2->insert(v_argsave); v_argsave->parent = this; } } #endif if (f->linkage == LINKd) { // Declare _arguments[] v_arguments = new VarDeclaration(Loc(), Type::typeinfotypelist->type, Id::_arguments_typeinfo, NULL); v_arguments->storage_class = STCparameter; v_arguments->semantic(sc2); sc2->insert(v_arguments); v_arguments->parent = this; //t = Type::typeinfo->type->constOf()->arrayOf(); t = Type::typeinfo->type->arrayOf(); _arguments = new VarDeclaration(Loc(), t, Id::_arguments, NULL); _arguments->semantic(sc2); sc2->insert(_arguments); _arguments->parent = this; } if (f->linkage == LINKd || (f->parameters && Parameter::dim(f->parameters))) { // Declare _argptr t = Type::tvalist; argptr = new VarDeclaration(Loc(), t, Id::_argptr, NULL); argptr->semantic(sc2); sc2->insert(argptr); argptr->parent = this; } } #if IN_LLVM // Make sure semantic analysis has been run on argument types. This is // e.g. needed for TypeTuple!(int, int) to be picked up as two int // parameters by the Parameter functions. if (f->parameters) { for (size_t i = 0; i < Parameter::dim(f->parameters); i++) { Parameter *arg = (Parameter *)Parameter::getNth(f->parameters, i); Type* nw = arg->type->semantic(Loc(), sc); if (arg->type != nw) { arg->type = nw; // Examine this index again. // This is important if it turned into a tuple. // In particular, the empty tuple should be handled or the // next parameter will be skipped. // LDC_FIXME: Maybe we only need to do this for tuples, // and can add tuple.length after decrement? i--; } } } #endif #if 0 // Propagate storage class from tuple parameters to their element-parameters. if (f->parameters) { for (size_t i = 0; i < f->parameters->dim; i++) { Parameter *arg = (*f->parameters)[i]; //printf("[%d] arg->type->ty = %d %s\n", i, arg->type->ty, arg->type->toChars()); if (arg->type->ty == Ttuple) { TypeTuple *t = (TypeTuple *)arg->type; size_t dim = Parameter::dim(t->arguments); for (size_t j = 0; j < dim; j++) { Parameter *narg = Parameter::getNth(t->arguments, j); narg->storageClass = arg->storageClass; } } } } #endif /* Declare all the function parameters as variables * and install them in parameters[] */ size_t nparams = Parameter::dim(f->parameters); if (nparams) { /* parameters[] has all the tuples removed, as the back end * doesn't know about tuples */ parameters = new VarDeclarations(); parameters->reserve(nparams); for (size_t i = 0; i < nparams; i++) { Parameter *arg = Parameter::getNth(f->parameters, i); Identifier *id = arg->ident; if (!id) { /* Generate identifier for un-named parameter, * because we need it later on. */ arg->ident = id = Identifier::generateId("_param_", i); } Type *vtype = arg->type; VarDeclaration *v = new VarDeclaration(loc, vtype, id, NULL); //printf("declaring parameter %s of type %s\n", v->toChars(), v->type->toChars()); v->storage_class |= STCparameter; if (f->varargs == 2 && i + 1 == nparams) v->storage_class |= STCvariadic; v->storage_class |= arg->storageClass & (STCin | STCout | STCref | STClazy | STCfinal | STC_TYPECTOR | STCnodtor); v->semantic(sc2); if (!sc2->insert(v)) error("parameter %s.%s is already defined", toChars(), v->toChars()); else parameters->push(v); localsymtab->insert(v); v->parent = this; #if IN_LLVM if (fdrequireParams) fdrequireParams->push(new VarExp(loc, v)); if (fdensureParams) fdensureParams->push(new VarExp(loc, v)); #endif } } // Declare the tuple symbols and put them in the symbol table, // but not in parameters[]. if (f->parameters) { for (size_t i = 0; i < f->parameters->dim; i++) { Parameter *arg = (*f->parameters)[i]; if (!arg->ident) continue; // never used, so ignore if (arg->type->ty == Ttuple) { TypeTuple *t = (TypeTuple *)arg->type; size_t dim = Parameter::dim(t->arguments); Objects *exps = new Objects(); exps->setDim(dim); for (size_t j = 0; j < dim; j++) { Parameter *narg = Parameter::getNth(t->arguments, j); assert(narg->ident); VarDeclaration *v = sc2->search(Loc(), narg->ident, NULL)->isVarDeclaration(); assert(v); Expression *e = new VarExp(v->loc, v); (*exps)[j] = e; } assert(arg->ident); TupleDeclaration *v = new TupleDeclaration(loc, arg->ident, exps); //printf("declaring tuple %s\n", v->toChars()); v->isexp = 1; if (!sc2->insert(v)) error("parameter %s.%s is already defined", toChars(), v->toChars()); localsymtab->insert(v); v->parent = this; } } } // Precondition invariant Statement *fpreinv = NULL; if (addPreInvariant()) { Expression *e = NULL; if (isDtorDeclaration()) { // Call invariant directly only if it exists FuncDeclaration *inv = ad->inv; ClassDeclaration *cd = ad->isClassDeclaration(); while (!inv && cd) { cd = cd->baseClass; if (!cd) break; inv = cd->inv; } if (inv) { e = new DsymbolExp(Loc(), inv); e = new CallExp(Loc(), e); e = e->semantic(sc2); } } else { // Call invariant virtually #if IN_LLVM // We actually need a valid 'var' for codegen. ThisExp* tv = new ThisExp(Loc()); tv->var = vthis; Expression *v = tv; #else Expression *v = new ThisExp(Loc()); #endif v->type = vthis->type; if (ad->isStructDeclaration()) v = v->addressOf(sc); Expression *se = new StringExp(Loc(), (char *)"null this"); se = se->semantic(sc); se->type = Type::tchar->arrayOf(); e = new AssertExp(loc, v, se); } if (e) fpreinv = new ExpStatement(Loc(), e); } // Postcondition invariant Statement *fpostinv = NULL; if (addPostInvariant()) { Expression *e = NULL; if (isCtorDeclaration()) { // Call invariant directly only if it exists FuncDeclaration *inv = ad->inv; ClassDeclaration *cd = ad->isClassDeclaration(); while (!inv && cd) { cd = cd->baseClass; if (!cd) break; inv = cd->inv; } if (inv) { e = new DsymbolExp(Loc(), inv); e = new CallExp(Loc(), e); e = e->semantic(sc2); } } else { // Call invariant virtually #if IN_LLVM // We actually need a valid 'var' for codegen. ThisExp* tv = new ThisExp(Loc()); tv->var = vthis; Expression *v = tv; #else Expression *v = new ThisExp(Loc()); #endif v->type = vthis->type; if (ad->isStructDeclaration()) v = v->addressOf(sc); e = new AssertExp(Loc(), v); } if (e) fpostinv = new ExpStatement(Loc(), e); } if (fensure || addPostInvariant()) { if ((fensure && global.params.useOut) || fpostinv) { returnLabel = new LabelDsymbol(Id::returnLabel); } // scope of out contract (need for vresult->semantic) ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc2->scopesym; scout = sc2->push(sym); } if (fbody) { ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc2->scopesym; sc2 = sc2->push(sym); AggregateDeclaration *ad = isAggregateMember2(); /* If this is a class constructor */ if (ad && isCtorDeclaration()) { for (size_t i = 0; i < ad->fields.dim; i++) { VarDeclaration *v = ad->fields[i]; v->ctorinit = 0; } } if (!inferRetType && f->retStyle() != RETstack) nrvo_can = 0; fbody = fbody->semantic(sc2); if (!fbody) fbody = new CompoundStatement(Loc(), new Statements()); if (inferRetType) { // If no return type inferred yet, then infer a void if (!type->nextOf()) { f->next = Type::tvoid; //type = type->semantic(loc, sc); // Removed with 6902 } else if (returns && f->next->ty != Tvoid) { for (size_t i = 0; i < returns->dim; i++) { Expression *exp = (*returns)[i]->exp; if (!f->next->invariantOf()->equals(exp->type->invariantOf())) { exp = exp->castTo(sc2, f->next); exp = exp->optimize(WANTvalue); (*returns)[i]->exp = exp; } //printf("[%d] %s %s\n", i, exp->type->toChars(), exp->toChars()); } } assert(type == f); } if (isStaticCtorDeclaration()) { /* It's a static constructor. Ensure that all * ctor consts were initialized. */ Dsymbol *p = toParent(); ScopeDsymbol *pd = p->isScopeDsymbol(); if (!pd) { error("static constructor can only be member of struct/class/module, not %s %s", p->kind(), p->toChars()); } else { for (size_t i = 0; i < pd->members->dim; i++) { Dsymbol *s = (*pd->members)[i]; s->checkCtorConstInit(); } } } if (isCtorDeclaration() && ad) { #if DMDV2 // Check for errors related to 'nothrow'. int nothrowErrors = global.errors; int blockexit = fbody->blockExit(f->isnothrow); if (f->isnothrow && (global.errors != nothrowErrors) ) error("'%s' is nothrow yet may throw", toChars()); if (flags & FUNCFLAGnothrowInprocess) f->isnothrow = !(blockexit & BEthrow); #endif //printf("callSuper = x%x\n", sc2->callSuper); ClassDeclaration *cd = ad->isClassDeclaration(); // Verify that all the ctorinit fields got initialized if (!(sc2->callSuper & CSXthis_ctor)) { for (size_t i = 0; i < ad->fields.dim; i++) { VarDeclaration *v = ad->fields[i]; if (v->ctorinit == 0) { /* Current bugs in the flow analysis: * 1. union members should not produce error messages even if * not assigned to * 2. structs should recognize delegating opAssign calls as well * as delegating calls to other constructors */ if (v->isCtorinit() && !v->type->isMutable() && cd) error("missing initializer for %s field %s", MODtoChars(v->type->mod), v->toChars()); else if (v->storage_class & STCnodefaultctor) error("field %s must be initialized in constructor", v->toChars()); else if (v->type->needsNested()) error("field %s must be initialized in constructor, because it is nested struct", v->toChars()); } } } if (cd && !(sc2->callSuper & CSXany_ctor) && cd->baseClass && cd->baseClass->ctor) { sc2->callSuper = 0; // Insert implicit super() at start of fbody if (!resolveFuncCall(Loc(), sc2, cd->baseClass->ctor, NULL, NULL, NULL, 1)) { error("no match for implicit super() call in constructor"); } else { Expression *e1 = new SuperExp(Loc()); Expression *e = new CallExp(Loc(), e1); e = e->semantic(sc2); Statement *s = new ExpStatement(Loc(), e); fbody = new CompoundStatement(Loc(), s, fbody); } } /* Append: * return this; * to function body */ if (blockexit & BEfallthru) { Expression *e = new ThisExp(loc); if (cd) e->type = cd->type; Statement *s = new ReturnStatement(loc, e); s = s->semantic(sc2); fbody = new CompoundStatement(loc, fbody, s); } } else if (fes) { // For foreach(){} body, append a return 0; Expression *e = new IntegerExp(0); Statement *s = new ReturnStatement(Loc(), e); fbody = new CompoundStatement(Loc(), fbody, s); assert(!returnLabel); } else if (!hasReturnExp && type->nextOf()->ty != Tvoid) error("has no return statement, but is expected to return a value of type %s", type->nextOf()->toChars()); else if (hasReturnExp & 8) // if inline asm { flags &= ~FUNCFLAGnothrowInprocess; } else { #if DMDV2 // Check for errors related to 'nothrow'. int nothrowErrors = global.errors; int blockexit = fbody->blockExit(f->isnothrow); if (f->isnothrow && (global.errors != nothrowErrors) ) error("'%s' is nothrow yet may throw", toChars()); if (flags & FUNCFLAGnothrowInprocess) { if (type == f) f = f->copy(); f->isnothrow = !(blockexit & BEthrow); } int offend = blockexit & BEfallthru; #endif if (type->nextOf()->ty != Tvoid) { if (offend) { Expression *e; #if DMDV1 warning(loc, "no return exp; or assert(0); at end of function"); #else error("no return exp; or assert(0); at end of function"); #endif if (global.params.useAssert && !global.params.useInline) { /* Add an assert(0, msg); where the missing return * should be. */ e = new AssertExp( endloc, new IntegerExp(0), new StringExp(loc, (char *)"missing return expression") ); } else e = new HaltExp(endloc); e = new CommaExp(Loc(), e, type->nextOf()->defaultInit()); e = e->semantic(sc2); Statement *s = new ExpStatement(Loc(), e); fbody = new CompoundStatement(Loc(), fbody, s); } } } sc2 = sc2->pop(); } Statement *freq = frequire; Statement *fens = fensure; /* Do the semantic analysis on the [in] preconditions and * [out] postconditions. */ if (freq) { /* frequire is composed of the [in] contracts */ ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc2->scopesym; sc2 = sc2->push(sym); sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPErequire; // BUG: need to error if accessing out parameters // BUG: need to treat parameters as const // BUG: need to disallow returns and throws // BUG: verify that all in and ref parameters are read freq = freq->semantic(sc2); sc2 = sc2->pop(); if (!global.params.useIn) freq = NULL; } if (fens) { /* fensure is composed of the [out] contracts */ if (type->nextOf()->ty == Tvoid && outId) { error("void functions have no result"); } if (type->nextOf()->ty != Tvoid) buildResultVar(); sc2 = scout; //push sc2->flags = (sc2->flags & ~SCOPEcontract) | SCOPEensure; // BUG: need to treat parameters as const // BUG: need to disallow returns and throws fens = fens->semantic(sc2); sc2 = sc2->pop(); if (!global.params.useOut) fens = NULL; } { Statements *a = new Statements(); // Merge in initialization of 'out' parameters if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->storage_class & STCout) { assert(v->init); ExpInitializer *ie = v->init->isExpInitializer(); assert(ie); if (ie->exp->op == TOKconstruct) ie->exp->op = TOKassign; // construction occured in parameter processing a->push(new ExpStatement(Loc(), ie->exp)); } } } // we'll handle variadics ourselves #if !IN_LLVM if (argptr) { // Initialize _argptr #if IN_GCC // Handled in FuncDeclaration::toObjFile v_argptr = argptr; v_argptr->init = new VoidInitializer(loc); #else Type *t = argptr->type; if (global.params.is64bit && !global.params.isWindows) { // Initialize _argptr to point to v_argsave Expression *e1 = new VarExp(Loc(), argptr); Expression *e = new SymOffExp(Loc(), v_argsave, 6*8 + 8*16); e->type = argptr->type; e = new AssignExp(Loc(), e1, e); e = e->semantic(sc); a->push(new ExpStatement(Loc(), e)); } else { // Initialize _argptr to point past non-variadic arg VarDeclaration *p; unsigned offset = 0; Expression *e; Expression *e1 = new VarExp(Loc(), argptr); // Find the last non-ref parameter if (parameters && parameters->dim) { size_t lastNonref = parameters->dim -1; p = (*parameters)[lastNonref]; /* The trouble with out and ref parameters is that taking * the address of it doesn't work, because later processing * adds in an extra level of indirection. So we skip over them. */ while (p->storage_class & (STCout | STCref)) { offset += Target::ptrsize; if (lastNonref-- == 0) { p = v_arguments; break; } p = (*parameters)[lastNonref]; } } else p = v_arguments; // last parameter is _arguments[] if (global.params.is64bit && global.params.isWindows) { offset += Target::ptrsize; if (p->storage_class & STClazy || p->type->size() > Target::ptrsize) { /* Necessary to offset the extra level of indirection the Win64 * ABI demands */ e = new SymOffExp(Loc(),p,0); e->type = Type::tvoidptr; e = new AddrExp(Loc(), e); e->type = Type::tvoidptr; e = new AddExp(Loc(), e, new IntegerExp(offset)); e->type = Type::tvoidptr; goto L1; } } else if (p->storage_class & STClazy) // If the last parameter is lazy, it's the size of a delegate offset += Target::ptrsize * 2; else offset += p->type->size(); offset = (offset + Target::ptrsize - 1) & ~(Target::ptrsize - 1); // assume stack aligns on pointer size e = new SymOffExp(Loc(), p, offset); e->type = Type::tvoidptr; //e = e->semantic(sc); L1: e = new AssignExp(Loc(), e1, e); e->type = t; a->push(new ExpStatement(Loc(), e)); p->isargptr = TRUE; } #endif } if (_arguments) { #ifdef IN_GCC v_arguments_var = _arguments; v_arguments_var->init = new VoidInitializer(loc); #endif /* Advance to elements[] member of TypeInfo_Tuple with: * _arguments = v_arguments.elements; */ Expression *e = new VarExp(Loc(), v_arguments); e = new DotIdExp(Loc(), e, Id::elements); Expression *e1 = new VarExp(Loc(), _arguments); e = new ConstructExp(Loc(), e1, e); e = e->semantic(sc2); a->push(new ExpStatement(Loc(), e)); } #endif // !IN_LLVM // Merge contracts together with body into one compound statement if (freq || fpreinv) { if (!freq) freq = fpreinv; else if (fpreinv) freq = new CompoundStatement(Loc(), freq, fpreinv); a->push(freq); } if (fbody) a->push(fbody); if (fens || fpostinv) { if (!fens) fens = fpostinv; else if (fpostinv) fens = new CompoundStatement(Loc(), fpostinv, fens); LabelStatement *ls = new LabelStatement(Loc(), Id::returnLabel, fens); returnLabel->statement = ls; a->push(returnLabel->statement); if (type->nextOf()->ty != Tvoid && vresult) { #if IN_LLVM Expression *e = 0; if (isCtorDeclaration()) { ThisExp *te = new ThisExp(Loc()); te->type = vthis->type; te->var = vthis; e = te; } else { e = new VarExp(Loc(), vresult); } #else // Create: return vresult; Expression *e = new VarExp(Loc(), vresult); #endif if (tintro) { e = e->implicitCastTo(sc, tintro->nextOf()); e = e->semantic(sc); } ReturnStatement *s = new ReturnStatement(Loc(), e); a->push(s); } } if (isMain() && type->nextOf()->ty == Tvoid) { // Add a return 0; statement Statement *s = new ReturnStatement(Loc(), new IntegerExp(0)); a->push(s); } fbody = new CompoundStatement(Loc(), a); #if DMDV2 /* Append destructor calls for parameters as finally blocks. */ if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (*parameters)[i]; if (v->storage_class & (STCref | STCout | STClazy)) continue; if (v->noscope) continue; Expression *e = v->edtor; if (e) { Statement *s = new ExpStatement(Loc(), e); s = s->semantic(sc2); int nothrowErrors = global.errors; bool isnothrow = f->isnothrow & !(flags & FUNCFLAGnothrowInprocess); int blockexit = s->blockExit(isnothrow); if (f->isnothrow && (global.errors != nothrowErrors) ) error("'%s' is nothrow yet may throw", toChars()); if (flags & FUNCFLAGnothrowInprocess && blockexit & BEthrow) f->isnothrow = FALSE; if (fbody->blockExit(f->isnothrow) == BEfallthru) fbody = new CompoundStatement(Loc(), fbody, s); else fbody = new TryFinallyStatement(Loc(), fbody, s); } } } // from this point on all possible 'throwers' are checked flags &= ~FUNCFLAGnothrowInprocess; #endif if (isSynchronized()) { /* Wrap the entire function body in a synchronized statement */ AggregateDeclaration *ad = isThis(); ClassDeclaration *cd = ad ? ad->isClassDeclaration() : parent->isClassDeclaration(); if (cd) { if (!global.params.is64bit && global.params.isWindows && !isStatic() && !fbody->usesEH() && !global.params.trace) { /* The back end uses the "jmonitor" hack for syncing; * no need to do the sync at this level. */ } else { Expression *vsync; if (isStatic()) { // The monitor is in the ClassInfo vsync = new DotIdExp(loc, new DsymbolExp(loc, cd), Id::classinfo); } else { // 'this' is the monitor vsync = new VarExp(loc, vthis); } fbody = new PeelStatement(fbody); // don't redo semantic() fbody = new SynchronizedStatement(loc, vsync, fbody); fbody = fbody->semantic(sc2); } } else { error("synchronized function %s must be a member of a class", toChars()); } } } sc2->callSuper = 0; sc2->pop(); } /* If function survived being marked as impure, then it is pure */ if (flags & FUNCFLAGpurityInprocess) { flags &= ~FUNCFLAGpurityInprocess; if (type == f) f = f->copy(); f->purity = PUREfwdref; } if (flags & FUNCFLAGsafetyInprocess) { flags &= ~FUNCFLAGsafetyInprocess; if (type == f) f = f->copy(); f->trust = TRUSTsafe; } // reset deco to apply inference result to mangled name if (f != type) f->deco = NULL; // Do semantic type AFTER pure/nothrow inference. if (!f->deco) { sc = sc->push(); sc->stc = 0; sc->linkage = linkage; // Bugzilla 8496 type = f->semantic(loc, sc); sc = sc->pop(); } if (global.gag && global.errors != nerrors) { /* Errors happened when compiling this function. */ semanticRun = PASSsemanticdone; // Ensure errors get reported again /* Except that re-running semantic3() doesn't always produce errors a second * time through. * See Bugzilla 8348 * Need a better way to deal with this than gagging. */ } else { semanticRun = PASSsemantic3done; semantic3Errors = global.errors - nerrors; } //printf("-FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars()); //fflush(stdout); } bool FuncDeclaration::functionSemantic() { if (scope && !originalType) // semantic not yet run { TemplateInstance *spec = isSpeculative(); unsigned olderrs = global.errors; unsigned oldgag = global.gag; if (global.gag && !spec) global.gag = 0; semantic(scope); global.gag = oldgag; if (spec && global.errors != olderrs) spec->errors = global.errors - olderrs; if (olderrs != global.errors) // if errors compiling this function return false; } // if inferring return type, sematic3 needs to be run TemplateInstance *ti; AggregateDeclaration *ad; if (scope && (inferRetType && type && !type->nextOf() || (ti = parent->isTemplateInstance()) != NULL && !ti->isTemplateMixin() && ti->name == ident || (ad = isThis()) != NULL && ad->parent && ad->parent->isTemplateInstance() && !isVirtualMethod())) { return functionSemantic3(); } return true; } bool FuncDeclaration::functionSemantic3() { 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. */ TemplateInstance *spec = isSpeculative(); unsigned olderrs = global.errors; unsigned oldgag = global.gag; if (global.gag && !spec) global.gag = 0; semantic3(scope); global.gag = oldgag; // If it is a speculatively-instantiated template, and errors occur, // we need to mark the template as having errors. if (spec && global.errors != olderrs) spec->errors = global.errors - olderrs; if (olderrs != global.errors) // if errors compiling this function return false; } return true; } void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { //printf("FuncDeclaration::toCBuffer() '%s'\n", toChars()); StorageClassDeclaration::stcToCBuffer(buf, storage_class); type->toCBuffer(buf, ident, hgs); if(hgs->hdrgen == 1) { if(storage_class & STCauto) { hgs->autoMember++; bodyToCBuffer(buf, hgs); hgs->autoMember--; } else if(hgs->tpltMember == 0 && #if IN_LLVM !global.params.hdrKeepAllBodies #else global.params.useInline == 0 #endif ) buf->writestring(";"); else bodyToCBuffer(buf, hgs); } else bodyToCBuffer(buf, hgs); buf->writenl(); } VarDeclaration *FuncDeclaration::declareThis(Scope *sc, AggregateDeclaration *ad) { if (ad) { VarDeclaration *v; { assert(ad->handle); Type *thandle = ad->handle; thandle = thandle->addMod(type->mod); thandle = thandle->addStorageClass(storage_class); v = new ThisDeclaration(loc, thandle); //v = new ThisDeclaration(loc, isCtorDeclaration() ? ad->handle : thandle); v->storage_class |= STCparameter; if (thandle->ty == Tstruct) v->storage_class |= STCref; v->semantic(sc); if (!sc->insert(v)) assert(0); v->parent = this; return v; } } else if (isNested()) { /* The 'this' for a nested function is the link to the * enclosing function's stack frame. * Note that nested functions and member functions are disjoint. */ VarDeclaration *v = new ThisDeclaration(loc, Type::tvoid->pointerTo()); v->storage_class |= STCparameter; v->semantic(sc); if (!sc->insert(v)) assert(0); v->parent = this; return v; } return NULL; } int FuncDeclaration::equals(Object *o) { if (this == o) return TRUE; Dsymbol *s = isDsymbol(o); if (s) { FuncDeclaration *fd1 = this->toAliasFunc(); FuncDeclaration *fd2 = s->isFuncDeclaration(); if (fd2) { fd2 = fd2->toAliasFunc(); return fd1->toParent()->equals(fd2->toParent()) && fd1->ident->equals(fd2->ident) && fd1->type->equals(fd2->type); } } return FALSE; } void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (fbody && (!hgs->hdrgen || #if IN_LLVM global.params.hdrKeepAllBodies || #else global.params.useInline || #endif hgs->autoMember || hgs->tpltMember)) { int savetlpt = hgs->tpltMember; int saveauto = hgs->autoMember; hgs->tpltMember = 0; hgs->autoMember = 0; buf->writenl(); // in{} if (frequire) { buf->writestring("in"); buf->writenl(); frequire->toCBuffer(buf, hgs); } // out{} if (fensure) { buf->writestring("out"); if (outId) { buf->writebyte('('); buf->writestring(outId->toChars()); buf->writebyte(')'); } buf->writenl(); fensure->toCBuffer(buf, hgs); } if (frequire || fensure) { buf->writestring("body"); buf->writenl(); } buf->writebyte('{'); buf->writenl(); buf->level++; fbody->toCBuffer(buf, hgs); buf->level--; buf->writebyte('}'); buf->writenl(); hgs->tpltMember = savetlpt; hgs->autoMember = saveauto; } else { buf->writeByte(';'); buf->writenl(); } } /**************************************************** * Declare result variable lazily. */ void FuncDeclaration::buildResultVar() { if (vresult) return; assert(type->nextOf()); assert(type->nextOf()->toBasetype()->ty != Tvoid); TypeFunction *tf = (TypeFunction *)type; Loc loc = this->loc; if (fensure) loc = fensure->loc; if (!outId) outId = Id::result; // provide a default VarDeclaration *v = new VarDeclaration(loc, type->nextOf(), outId, NULL); v->noscope = 1; v->storage_class |= STCresult; #if DMDV2 if (!isVirtual()) v->storage_class |= STCconst; if (tf->isref) { v->storage_class |= STCref | STCforeach; } #endif v->semantic(scout); if (!scout->insert(v)) error("out result %s is already defined", v->toChars()); v->parent = this; vresult = v; // vresult gets initialized with the function return value // in ReturnStatement::semantic() } /**************************************************** * Merge into this function the 'in' contracts of all it overrides. * 'in's are OR'd together, i.e. only one of them needs to pass. */ Statement *FuncDeclaration::mergeFrequire(Statement *sf, Expressions *params) { if (!params) params = fdrequireParams; /* If a base function and its override both have an IN contract, then * only one of them needs to succeed. This is done by generating: * * void derived.in() { * try { * base.in(); * } * catch () { * ... body of derived.in() ... * } * } * * So if base.in() doesn't throw, derived.in() need not be executed, and the contract is valid. * If base.in() throws, then derived.in()'s body is executed. */ #if IN_LLVM /* In LDC, we can't rely on these codegen hacks - we explicitly pass * parameters on to the contract functions. */ #else /* Implementing this is done by having the overriding function call * nested functions (the fdrequire functions) nested inside the overridden * function. This requires that the stack layout of the calling function's * parameters and 'this' pointer be in the same place (as the nested * function refers to them). * This is easy for the parameters, as they are all on the stack in the same * place by definition, since it's an overriding function. The problem is * getting the 'this' pointer in the same place, since it is a local variable. * We did some hacks in the code generator to make this happen: * 1. always generate exception handler frame, or at least leave space for it * in the frame (Windows 32 SEH only) * 2. always generate an EBP style frame * 3. since 'this' is passed in a register that is subsequently copied into * a stack local, allocate that local immediately following the exception * handler block, so it is always at the same offset from EBP. */ #endif for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; /* The semantic pass on the contracts of the overridden functions must * be completed before code generation occurs (bug 3602). */ if (fdv->fdrequire && fdv->fdrequire->semanticRun != PASSsemantic3done) { assert(fdv->scope); Scope *sc = fdv->scope->push(); sc->stc &= ~STCoverride; fdv->semantic3(sc); sc->pop(); } sf = fdv->mergeFrequire(sf, params); if (sf && fdv->fdrequire) { //printf("fdv->frequire: %s\n", fdv->frequire->toChars()); /* Make the call: * try { __require(); } * catch { frequire; } */ Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdrequire, 0), params); Statement *s2 = new ExpStatement(loc, e); Catch *c = new Catch(loc, NULL, NULL, sf); c->internalCatch = true; Catches *catches = new Catches(); catches->push(c); sf = new TryCatchStatement(loc, s2, catches); } else return NULL; } return sf; } /**************************************************** * Merge into this function the 'out' contracts of all it overrides. * 'out's are AND'd together, i.e. all of them need to pass. */ Statement *FuncDeclaration::mergeFensure(Statement *sf, Expressions *params) { if (!params) params = fdensureParams; /* Same comments as for mergeFrequire(), except that we take care * of generating a consistent reference to the 'result' local by * explicitly passing 'result' to the nested function as a reference * argument. * This won't work for the 'this' parameter as it would require changing * the semantic code for the nested function so that it looks on the parameter * list for the 'this' pointer, something that would need an unknown amount * of tweaking of various parts of the compiler that I'd rather leave alone. */ for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; /* The semantic pass on the contracts of the overridden functions must * be completed before code generation occurs (bug 3602 and 5230). */ if (fdv->fdensure && fdv->fdensure->semanticRun != PASSsemantic3done) { assert(fdv->scope); Scope *sc = fdv->scope->push(); sc->stc &= ~STCoverride; fdv->semantic3(sc); sc->pop(); } sf = fdv->mergeFensure(sf, params); if (fdv->fdensure) { //printf("fdv->fensure: %s\n", fdv->fensure->toChars()); // Make the call: __ensure(result) Expression *eresult = NULL; if (outId) { #if IN_LLVM eresult = (*params)[0]; #else eresult = new IdentifierExp(loc, outId); #endif Type *t1 = fdv->type->nextOf()->toBasetype(); #if IN_LLVM // We actually check for matching types in CommaExp::toElem, // 'testcontract' breaks without this. t1 = t1->constOf(); #endif Type *t2 = this->type->nextOf()->toBasetype(); int offset; if (t1->isBaseOf(t2, &offset) && offset != 0) { /* Making temporary reference variable is necessary * to match offset difference in covariant return. * See bugzilla 5204. */ ExpInitializer *ei = new ExpInitializer(Loc(), eresult); VarDeclaration *v = new VarDeclaration(Loc(), t1, Lexer::uniqueId("__covres"), ei); DeclarationExp *de = new DeclarationExp(Loc(), v); VarExp *ve = new VarExp(Loc(), v); eresult = new CommaExp(Loc(), de, ve); } } #if IN_LLVM if (eresult) (*params)[0] = eresult; Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), params); #else Expression *e = new CallExp(loc, new VarExp(loc, fdv->fdensure, 0), eresult); #endif Statement *s2 = new ExpStatement(loc, e); if (sf) { sf = new CompoundStatement(fensure->loc, s2, sf); } else sf = s2; } } return sf; } /**************************************************** * Determine if 'this' overrides fd. * Return !=0 if it does. */ int FuncDeclaration::overrides(FuncDeclaration *fd) { int result = 0; if (fd->ident == ident) { int cov = type->covariant(fd->type); if (cov) { ClassDeclaration *cd1 = toParent()->isClassDeclaration(); ClassDeclaration *cd2 = fd->toParent()->isClassDeclaration(); if (cd1 && cd2 && cd2->isBaseOf(cd1, NULL)) result = 1; } } return result; } /************************************************* * Find index of function in vtbl[0..dim] that * this function overrides. * Prefer an exact match to a covariant one. * Returns: * -1 didn't find one * -2 can't determine because of forward references */ int FuncDeclaration::findVtblIndex(Dsymbols *vtbl, int dim) { FuncDeclaration *mismatch = NULL; StorageClass mismatchstc = 0; int mismatchvi = -1; int exactvi = -1; int bestvi = -1; for (int vi = 0; vi < dim; vi++) { FuncDeclaration *fdv = (*vtbl)[vi]->isFuncDeclaration(); if (fdv && fdv->ident == ident) { if (type->equals(fdv->type)) // if exact match { if (fdv->parent->isClassDeclaration()) return vi; // no need to look further if (exactvi >= 0) { error("cannot determine overridden function"); return exactvi; } exactvi = vi; bestvi = vi; continue; } StorageClass stc = 0; int cov = type->covariant(fdv->type, &stc); //printf("\tbaseclass cov = %d\n", cov); switch (cov) { case 0: // types are distinct break; case 1: bestvi = vi; // covariant, but not identical break; // keep looking for an exact match case 2: mismatchvi = vi; mismatchstc = stc; mismatch = fdv; // overrides, but is not covariant break; // keep looking for an exact match case 3: return -2; // forward references default: assert(0); } } } if (bestvi == -1 && mismatch) { //type->print(); //mismatch->type->print(); //printf("%s %s\n", type->deco, mismatch->type->deco); //printf("stc = %llx\n", mismatchstc); if (mismatchstc) { // Fix it by modifying the type to add the storage classes type = type->addStorageClass(mismatchstc); bestvi = mismatchvi; } else error("of type %s overrides but is not covariant with %s of type %s", type->toChars(), mismatch->toPrettyChars(), mismatch->type->toChars()); } return bestvi; } /**************************************************** * Overload this FuncDeclaration with the new one f. * Return !=0 if successful; i.e. no conflict. */ int FuncDeclaration::overloadInsert(Dsymbol *s) { //printf("FuncDeclaration::overloadInsert(s = %s) this = %s\n", s->toChars(), toChars()); AliasDeclaration *ad = s->isAliasDeclaration(); if (ad) { if (overnext) return overnext->overloadInsert(ad); if (!ad->aliassym && ad->type->ty != Tident && ad->type->ty != Tinstance) { //printf("\tad = '%s'\n", ad->type->toChars()); return FALSE; } overnext = ad; //printf("\ttrue: no conflict\n"); return TRUE; } FuncDeclaration *fd = s->isFuncDeclaration(); if (!fd) return FALSE; #if 0 /* Disable this check because: * const void foo(); * semantic() isn't run yet on foo(), so the const hasn't been * applied yet. */ if (type) { printf("type = %s\n", type->toChars()); printf("fd->type = %s\n", fd->type->toChars()); } if (type && fd->type && // can be NULL for overloaded constructors fd->type->covariant(type) && fd->type->mod == type->mod && !isFuncAliasDeclaration()) { //printf("\tfalse: conflict %s\n", kind()); return FALSE; } #endif if (overnext) return overnext->overloadInsert(fd); overnext = fd; //printf("\ttrue: no conflict\n"); return TRUE; } /******************************************** * Find function in overload list that exactly matches t. */ /*************************************************** * Visit each overloaded function in turn, and call * (*fp)(param, f) on it. * Exit when no more, or (*fp)(param, f) returns 1. * Returns: * 0 continue * 1 done */ int overloadApply(FuncDeclaration *fstart, int (*fp)(void *, FuncDeclaration *), void *param) { FuncDeclaration *f; Declaration *d; Declaration *next; for (d = fstart; d; d = next) { FuncAliasDeclaration *fa = d->isFuncAliasDeclaration(); if (fa) { if (fa->hasOverloads) { if (overloadApply(fa->funcalias, fp, param)) return 1; } else { f = fa->toAliasFunc(); if (!f) { d->error("is aliased to a function"); break; } if ((*fp)(param, f)) return 1; } next = fa->overnext; } else { AliasDeclaration *a = d->isAliasDeclaration(); if (a) { Dsymbol *s = a->toAlias(); next = s->isDeclaration(); if (next == a) break; if (next == fstart) break; } else { f = d->isFuncDeclaration(); if (!f) { d->error("is aliased to a function"); break; // BUG: should print error message? } if ((*fp)(param, f)) return 1; next = f->overnext; } } } return 0; } /******************************************** * If there are no overloads of function f, return that function, * otherwise return NULL. */ static int fpunique(void *param, FuncDeclaration *f) { FuncDeclaration **pf = (FuncDeclaration **)param; if (*pf) { *pf = NULL; return 1; // ambiguous, done } else { *pf = f; return 0; } } FuncDeclaration *FuncDeclaration::isUnique() { FuncDeclaration *result = NULL; overloadApply(this, &fpunique, &result); return result; } /******************************************** * Find function in overload list that exactly matches t. */ struct Param1 { Type *t; // type to match FuncDeclaration *f; // return value }; int fp1(void *param, FuncDeclaration *f) { Param1 *p = (Param1 *)param; Type *t = p->t; if (t->equals(f->type)) { p->f = f; return 1; } #if DMDV2 /* Allow covariant matches, as long as the return type * is just a const conversion. * This allows things like pure functions to match with an impure function type. */ if (t->ty == Tfunction) { TypeFunction *tf = (TypeFunction *)f->type; if (tf->covariant(t) == 1 && tf->nextOf()->implicitConvTo(t->nextOf()) >= MATCHconst) { p->f = f; return 1; } } #endif return 0; } FuncDeclaration *FuncDeclaration::overloadExactMatch(Type *t) { Param1 p; p.t = t; p.f = NULL; overloadApply(this, &fp1, &p); return p.f; } /******************************************** * Decide which function matches the arguments best. * flags 1: do not issue error message on no match, just return NULL * 2: do not issue error on ambiguous matches and need explicit this */ struct Param2 { Match *m; #if DMDV2 Type *tthis; int property; // 0: unintialized // 1: seen @property // 2: not @property #endif Expressions *arguments; }; int fp2(void *param, FuncDeclaration *f) { Param2 *p = (Param2 *)param; Match *m = p->m; Expressions *arguments = p->arguments; if (f != m->lastf) // skip duplicates { m->anyf = f; TypeFunction *tf = (TypeFunction *)f->type; int property = (tf->isproperty) ? 1 : 2; if (p->property == 0) p->property = property; else if (p->property != property) error(f->loc, "cannot overload both property and non-property functions"); /* For constructors, qualifier check will be opposite direction. * Qualified constructor always makes qualified object, then will be checked * that it is implicitly convertible to tthis. */ Type *tthis = f->needThis() ? p->tthis : NULL; if (tthis && f->isCtorDeclaration()) { //printf("%s tf->mod = x%x tthis->mod = x%x %d\n", tf->toChars(), // tf->mod, tthis->mod, f->isolateReturn()); if (MODimplicitConv(tf->mod, tthis->mod) || tf->isWild() && tf->isShared() == tthis->isShared() || f->isolateReturn()/* && tf->isShared() == tthis->isShared()*/) { // Uniquely constructed object can ignore shared qualifier. // TODO: Is this appropriate? tthis = NULL; } else return 0; // MATCHnomatch } MATCH match = tf->callMatch(tthis, arguments); //printf("test1: match = %d\n", match); if (match != MATCHnomatch) { if (match > m->last) goto LfIsBetter; if (match < m->last) goto LlastIsBetter; /* See if one of the matches overrides the other. */ if (m->lastf->overrides(f)) goto LlastIsBetter; else if (f->overrides(m->lastf)) goto LfIsBetter; #if DMDV2 /* Try to disambiguate using template-style partial ordering rules. * In essence, if f() and g() are ambiguous, if f() can call g(), * but g() cannot call f(), then pick f(). * This is because f() is "more specialized." */ { MATCH c1 = f->leastAsSpecialized(m->lastf); MATCH c2 = m->lastf->leastAsSpecialized(f); //printf("c1 = %d, c2 = %d\n", c1, c2); if (c1 > c2) goto LfIsBetter; if (c1 < c2) goto LlastIsBetter; } /* If the two functions are the same function, like: * int foo(int); * int foo(int x) { ... } * then pick the one with the body. */ if (tf->equals(m->lastf->type) && f->storage_class == m->lastf->storage_class && f->parent == m->lastf->parent && f->protection == m->lastf->protection && f->linkage == m->lastf->linkage) { if (f->fbody && !m->lastf->fbody) goto LfIsBetter; else if (!f->fbody && m->lastf->fbody) goto LlastIsBetter; } #endif Lambiguous: m->nextf = f; m->count++; return 0; LfIsBetter: m->last = match; m->lastf = f; m->count = 1; return 0; LlastIsBetter: return 0; } } return 0; } void overloadResolveX(Match *m, FuncDeclaration *fstart, Type *tthis, Expressions *arguments) { Param2 p; p.m = m; p.tthis = tthis; p.property = 0; p.arguments = arguments; overloadApply(fstart, &fp2, &p); } static void MODMatchToBuffer(OutBuffer *buf, unsigned char lhsMod, unsigned char rhsMod) { bool bothMutable = ((lhsMod & rhsMod) == 0); bool sharedMismatch = ((lhsMod ^ rhsMod) & MODshared); bool sharedMismatchOnly = ((lhsMod ^ rhsMod) == MODshared); if (lhsMod & MODshared) buf->writestring("shared "); else if (sharedMismatch && !(lhsMod & MODimmutable)) buf->writestring("non-shared "); if (bothMutable && sharedMismatchOnly) { } else if (lhsMod & MODimmutable) buf->writestring("immutable "); else if (lhsMod & MODconst) buf->writestring("const "); else if (lhsMod & MODwild) buf->writestring("inout "); else buf->writestring("mutable "); } FuncDeclaration *FuncDeclaration::overloadResolve(Loc loc, Type *tthis, Expressions *arguments, int flags) { #if 0 printf("FuncDeclaration::overloadResolve('%s')\n", toChars()); if (arguments) { for (size_t i = 0; i < arguments->dim; i++) { Expression *arg = (*arguments)[i]; assert(arg->type); printf("\t%s: ", arg->toChars()); arg->type->print(); } } #endif Match m; memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; overloadResolveX(&m, this, tthis, arguments); if (m.count == 1) // exactly one match { if (!(flags & 1)) m.lastf->functionSemantic(); return m.lastf; } if (m.last != MATCHnomatch && (flags & 2) && !tthis && m.lastf->needThis()) { return m.lastf; } /* Failed to find a best match. * Do nothing or print error. */ if (m.last == MATCHnomatch && (flags & 1)) { // if do not print error messages } else { OutBuffer buf; buf.writeByte('('); if (arguments && arguments->dim) { HdrGenState hgs; argExpTypesToCBuffer(&buf, arguments, &hgs); } buf.writeByte(')'); if (tthis) tthis->modToBuffer(&buf); if (m.last == MATCHnomatch) { TypeFunction *tf = (TypeFunction *)type; if (tthis && !MODimplicitConv(tthis->mod, tf->mod)) // modifier mismatch { OutBuffer thisBuf, funcBuf; MODMatchToBuffer(&thisBuf, tthis->mod, tf->mod); MODMatchToBuffer(&funcBuf, tf->mod, tthis->mod); ::error(loc, "%smethod %s is not callable using a %sobject", funcBuf.toChars(), this->toPrettyChars(), thisBuf.toChars()); } else { //printf("tf = %s, args = %s\n", tf->deco, (*arguments)[0]->type->deco); error(loc, "%s%s is not callable using argument types %s", Parameter::argsTypesToChars(tf->parameters, tf->varargs), tf->modToChars(), buf.toChars()); } } else { TypeFunction *t1 = (TypeFunction *)m.lastf->type; TypeFunction *t2 = (TypeFunction *)m.nextf->type; error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s(%d): %s%s\nand:\n\t%s(%d): %s%s", buf.toChars(), m.lastf->loc.filename, m.lastf->loc.linnum, m.lastf->toPrettyChars(), Parameter::argsTypesToChars(t1->parameters, t1->varargs), m.nextf->loc.filename, m.nextf->loc.linnum, m.nextf->toPrettyChars(), Parameter::argsTypesToChars(t2->parameters, t2->varargs)); } } return NULL; } /************************************* * Determine partial specialization order of 'this' vs g. * This is very similar to TemplateDeclaration::leastAsSpecialized(). * Returns: * match 'this' is at least as specialized as g * 0 g is more specialized than 'this' */ #if DMDV2 MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g) { #define LOG_LEASTAS 0 #if LOG_LEASTAS printf("%s.leastAsSpecialized(%s)\n", toChars(), g->toChars()); printf("%s, %s\n", type->toChars(), g->type->toChars()); #endif /* This works by calling g() with f()'s parameters, and * if that is possible, then f() is at least as specialized * as g() is. */ TypeFunction *tf = (TypeFunction *)type; TypeFunction *tg = (TypeFunction *)g->type; size_t nfparams = Parameter::dim(tf->parameters); size_t ngparams = Parameter::dim(tg->parameters); MATCH match = MATCHexact; /* If both functions have a 'this' pointer, and the mods are not * the same and g's is not const, then this is less specialized. */ if (needThis() && g->needThis() && tf->mod != tg->mod) { if (isCtorDeclaration()) { if (MODimplicitConv(tg->mod, tf->mod)) match = MATCHconst; else return MATCHnomatch; } else { if (MODimplicitConv(tf->mod, tg->mod)) match = MATCHconst; else return MATCHnomatch; } } /* Create a dummy array of arguments out of the parameters to f() */ Expressions args; args.setDim(nfparams); for (size_t u = 0; u < nfparams; u++) { Parameter *p = Parameter::getNth(tf->parameters, u); Expression *e; if (p->storageClass & (STCref | STCout)) { e = new IdentifierExp(Loc(), p->ident); e->type = p->type; } else e = p->type->defaultInitLiteral(Loc()); args[u] = e; } MATCH m = (MATCH) tg->callMatch(NULL, &args, 1); if (m) { /* A variadic parameter list is less specialized than a * non-variadic one. */ if (tf->varargs && !tg->varargs) goto L1; // less specialized #if LOG_LEASTAS printf(" matches %d, so is least as specialized\n", m); #endif return m; } L1: #if LOG_LEASTAS printf(" doesn't match, so is not as specialized\n"); #endif return MATCHnomatch; } /******************************************* * Given a symbol that could be either a FuncDeclaration or * a function template, resolve it to a function symbol. * loc instantiation location * sc instantiation scope * tiargs initial list of template arguments * tthis if !NULL, the 'this' pointer argument * fargs arguments to function * flags 1: do not issue error message on no match, just return NULL * 2: overloadResolve only */ FuncDeclaration *resolveFuncCall(Loc loc, Scope *sc, Dsymbol *s, Objects *tiargs, Type *tthis, Expressions *arguments, int flags) { if (!s) return NULL; // no match if (tiargs && arrayObjectIsError(tiargs) || arguments && arrayObjectIsError((Objects *)arguments)) { return NULL; } FuncDeclaration *f = s->isFuncDeclaration(); if (f) f = f->overloadResolve(loc, tthis, arguments, flags); else { TemplateDeclaration *td = s->isTemplateDeclaration(); assert(td); if (!sc) sc = td->scope; f = td->deduceFunctionTemplate(loc, sc, tiargs, tthis, arguments, flags); } return f; } #endif /******************************** * Labels are in a separate scope, one per function. */ LabelDsymbol *FuncDeclaration::searchLabel(Identifier *ident) { Dsymbol *s; if (!labtab) labtab = new DsymbolTable(); // guess we need one s = labtab->lookup(ident); if (!s) { s = new LabelDsymbol(ident); labtab->insert(s); } return (LabelDsymbol *)s; } /**************************************** * If non-static member function that has a 'this' pointer, * return the aggregate it is a member of. * Otherwise, return NULL. */ AggregateDeclaration *FuncDeclaration::isThis() { AggregateDeclaration *ad; //printf("+FuncDeclaration::isThis() '%s'\n", toChars()); ad = NULL; if ((storage_class & STCstatic) == 0) { ad = isMember2(); } //printf("-FuncDeclaration::isThis() %p\n", ad); return ad; } AggregateDeclaration *FuncDeclaration::isMember2() { AggregateDeclaration *ad; //printf("+FuncDeclaration::isMember2() '%s'\n", toChars()); ad = NULL; for (Dsymbol *s = this; s; s = s->parent) { //printf("\ts = '%s', parent = '%s', kind = %s\n", s->toChars(), s->parent->toChars(), s->parent->kind()); ad = s->isMember(); if (ad) { break; } if (!s->parent || (!s->parent->isTemplateInstance())) { break; } } //printf("-FuncDeclaration::isMember2() %p\n", ad); return ad; } /***************************************** * Determine lexical level difference from 'this' to nested function 'fd'. * Error if this cannot call fd. * Returns: * 0 same level * -1 increase nesting by 1 (fd is nested within 'this') * >0 decrease nesting by number */ int FuncDeclaration::getLevel(Loc loc, Scope *sc, FuncDeclaration *fd) { int level; Dsymbol *s; Dsymbol *fdparent; //printf("FuncDeclaration::getLevel(fd = '%s')\n", fd->toChars()); fdparent = fd->toParent2(); if (fdparent == this) return -1; s = this; level = 0; while (fd != s && fdparent != s->toParent2()) { //printf("\ts = %s, '%s'\n", s->kind(), s->toChars()); FuncDeclaration *thisfd = s->isFuncDeclaration(); if (thisfd) { if (!thisfd->isNested() && !thisfd->vthis && !sc->intypeof) goto Lerr; } else { AggregateDeclaration *thiscd = s->isAggregateDeclaration(); if (thiscd) { /* AggregateDeclaration::isNested returns true only when * it has a hidden pointer. * But, calling the function belongs unrelated lexical scope * is still allowed inside typeof. * * struct Map(alias fun) { * typeof({ return fun(); }) RetType; * // No member function makes Map struct 'not nested'. * } */ if (!thiscd->isNested() && !sc->intypeof) goto Lerr; } else goto Lerr; } s = s->toParent2(); assert(s); level++; } return level; Lerr: // Don't give error if in template constraint if (!((sc->flags & SCOPEstaticif) && parent->isTemplateDeclaration())) error(loc, "cannot access frame of function %s", fd->toPrettyChars()); return 1; } void FuncDeclaration::appendExp(Expression *e) { Statement *s; s = new ExpStatement(Loc(), e); appendState(s); } void FuncDeclaration::appendState(Statement *s) { if (!fbody) fbody = s; else { CompoundStatement *cs = fbody->isCompoundStatement(); if (cs) { if (!cs->statements) fbody = s; else cs->statements->push(s); } else fbody = new CompoundStatement(Loc(), fbody, s); } } const char *FuncDeclaration::toPrettyChars() { if (isMain()) return "D main"; else return Dsymbol::toPrettyChars(); } /** for diagnostics, e.g. 'int foo(int x, int y) pure' */ const char *FuncDeclaration::toFullSignature() { OutBuffer buf; HdrGenState hgs; functionToCBuffer2((TypeFunction *)type, &buf, &hgs, 0, toChars()); buf.writeByte(0); return buf.extractData(); } int FuncDeclaration::isMain() { return ident == Id::main && linkage != LINKc && !isMember() && !isNested(); } int FuncDeclaration::isWinMain() { //printf("FuncDeclaration::isWinMain() %s\n", toChars()); #if 0 int x = ident == Id::WinMain && linkage != LINKc && !isMember(); printf("%s\n", x ? "yes" : "no"); return x; #else return ident == Id::WinMain && linkage != LINKc && !isMember(); #endif } int FuncDeclaration::isDllMain() { return ident == Id::DllMain && linkage != LINKc && !isMember(); } int FuncDeclaration::isExport() { return protection == PROTexport; } int FuncDeclaration::isImportedSymbol() { //printf("isImportedSymbol()\n"); //printf("protection = %d\n", protection); return (protection == PROTexport) && !fbody; } // Determine if function goes into virtual function pointer table int FuncDeclaration::isVirtual() { if (toAliasFunc() != this) return toAliasFunc()->isVirtual(); Dsymbol *p = toParent(); #if 0 printf("FuncDeclaration::isVirtual(%s)\n", toChars()); printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROTprivate, isCtorDeclaration(), linkage != LINKd); printf("result is %d\n", isMember() && !(isStatic() || protection == PROTprivate || protection == PROTpackage) && p->isClassDeclaration() && !(p->isInterfaceDeclaration() && isFinal())); #endif return isMember() && !(isStatic() || protection == PROTprivate || protection == PROTpackage) && p->isClassDeclaration() && !(p->isInterfaceDeclaration() && isFinal()); } // Determine if a function is pedantically virtual int FuncDeclaration::isVirtualMethod() { if (toAliasFunc() != this) return toAliasFunc()->isVirtualMethod(); //printf("FuncDeclaration::isVirtualMethod() %s\n", toChars()); if (!isVirtual()) return 0; // If it's a final method, and does not override anything, then it is not virtual if (isFinal() && foverrides.dim == 0) { return 0; } return 1; } int FuncDeclaration::isFinal() { if (toAliasFunc() != this) return toAliasFunc()->isFinal(); ClassDeclaration *cd; #if 0 printf("FuncDeclaration::isFinal(%s), %x\n", toChars(), Declaration::isFinal()); printf("%p %d %d %d\n", isMember(), isStatic(), Declaration::isFinal(), ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal)); printf("result is %d\n", isMember() && (Declaration::isFinal() || ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal))); if (cd) printf("\tmember of %s\n", cd->toChars()); #if 0 !(isStatic() || protection == PROTprivate || protection == PROTpackage) && (cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal); #endif #endif return isMember() && (Declaration::isFinal() || ((cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal)); } int FuncDeclaration::isAbstract() { return storage_class & STCabstract; } int FuncDeclaration::isCodeseg() { return TRUE; // functions are always in the code segment } int FuncDeclaration::isOverloadable() { return 1; // functions can be overloaded } int FuncDeclaration::hasOverloads() { return overnext != NULL; } enum PURE FuncDeclaration::isPure() { //printf("FuncDeclaration::isPure() '%s'\n", toChars()); assert(type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)type; if (flags & FUNCFLAGpurityInprocess) setImpure(); if (tf->purity == PUREfwdref) tf->purityLevel(); enum PURE purity = tf->purity; if (purity > PUREweak && isNested()) purity = PUREweak; if (purity > PUREweak && needThis()) { // The attribute of the 'this' reference affects purity strength if (type->mod & MODimmutable) ; else if (type->mod & (MODconst | MODwild) && purity >= PUREconst) purity = PUREconst; else purity = PUREweak; } tf->purity = purity; // ^ This rely on the current situation that every FuncDeclaration has a // unique TypeFunction. return purity; } enum PURE FuncDeclaration::isPureBypassingInference() { if (flags & FUNCFLAGpurityInprocess) return PUREfwdref; else return isPure(); } /************************************** * The function is doing something impure, * so mark it as impure. * If there's a purity error, return TRUE. */ bool FuncDeclaration::setImpure() { if (flags & FUNCFLAGpurityInprocess) { flags &= ~FUNCFLAGpurityInprocess; } else if (isPure()) return TRUE; return FALSE; } int FuncDeclaration::isSafe() { assert(type->ty == Tfunction); if (flags & FUNCFLAGsafetyInprocess) setUnsafe(); return ((TypeFunction *)type)->trust == TRUSTsafe; } bool FuncDeclaration::isSafeBypassingInference() { if (flags & FUNCFLAGsafetyInprocess) return false; else return isSafe(); } int FuncDeclaration::isTrusted() { assert(type->ty == Tfunction); if (flags & FUNCFLAGsafetyInprocess) setUnsafe(); return ((TypeFunction *)type)->trust == TRUSTtrusted; } /************************************** * The function is doing something unsave, * so mark it as unsafe. * If there's a safe error, return TRUE. */ bool FuncDeclaration::setUnsafe() { if (flags & FUNCFLAGsafetyInprocess) { flags &= ~FUNCFLAGsafetyInprocess; ((TypeFunction *)type)->trust = TRUSTsystem; } else if (isSafe()) return TRUE; return FALSE; } /************************************** * Returns an indirect type one step from t. */ Type *getIndirection(Type *t) { t = t->toBasetype(); if (t->ty == Tsarray) { while (t->ty == Tsarray) t = t->nextOf()->toBasetype(); } if (t->ty == Tarray || t->ty == Tpointer) return t->nextOf()->toBasetype(); if (t->ty == Taarray || t->ty == Tclass) return t; if (t->ty == Tstruct) return t->hasPointers() ? t : NULL; // TODO // should consider TypeDelegate? return NULL; } /************************************** * Traverse this and t, and then check the indirections convertibility. */ int traverseIndirections(Type *ta, Type *tb, void *p = NULL, bool a2b = true) { if (a2b) // check ta appears in tb { //printf("\ttraverse(1) %s appears in %s\n", ta->toChars(), tb->toChars()); if (ta->constConv(tb)) return 1; else if (ta->invariantOf()->equals(tb->invariantOf())) return 0; else if (tb->ty == Tvoid && MODimplicitConv(ta->mod, tb->mod)) return 1; } else // check tb appears in ta { //printf("\ttraverse(2) %s appears in %s\n", tb->toChars(), ta->toChars()); if (tb->constConv(ta)) return 1; else if (tb->invariantOf()->equals(ta->invariantOf())) return 0; else if (ta->ty == Tvoid && MODimplicitConv(tb->mod, ta->mod)) return 1; } // context date to detect circular look up struct Ctxt { Ctxt *prev; Type *type; }; Ctxt *ctxt = (Ctxt *)p; Type *tbb = tb->toBasetype(); if (tbb != tb) return traverseIndirections(ta, tbb, ctxt, a2b); if (tb->ty == Tsarray) { while (tb->toBasetype()->ty == Tsarray) tb = tb->toBasetype()->nextOf(); } if (tb->ty == Tclass || tb->ty == Tstruct) { for (Ctxt *c = ctxt; c; c = c->prev) if (tb == c->type) return 0; Ctxt c; c.prev = ctxt; c.type = tb; AggregateDeclaration *sym = tb->toDsymbol(NULL)->isAggregateDeclaration(); for (size_t i = 0; i < sym->fields.dim; i++) { VarDeclaration *v = sym->fields[i]; Type *tprmi = v->type->addMod(tb->mod); if (!(v->storage_class & STCref)) tprmi = getIndirection(tprmi); if (!tprmi) continue; //printf("\ttb = %s, tprmi = %s\n", tb->toChars(), tprmi->toChars()); if (traverseIndirections(ta, tprmi, &c, a2b)) return 1; } } else if (tb->ty == Tarray || tb->ty == Taarray || tb->ty == Tpointer) { Type *tind = tb->nextOf(); if (traverseIndirections(ta, tind, ctxt, a2b)) return 1; } else if (tb->hasPointers()) { // FIXME: function pointer/delegate types should be considered. return 1; } if (a2b) return traverseIndirections(tb, ta, ctxt, false); return 0; } /******************************************** * Returns true if the function return value has no indirection * which comes from the parameters. */ bool FuncDeclaration::isolateReturn() { assert(type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)type; assert(tf->next); Type *treti = tf->next; treti = tf->isref ? treti : getIndirection(treti); if (!treti) return true; // target has no mutable indirection return parametersIntersect(treti); } /******************************************** * Returns true if an object typed t can have indirections * which come from the parameters. */ bool FuncDeclaration::parametersIntersect(Type *t) { assert(t); if (!isPureBypassingInference() || isNested()) return false; assert(type->ty == Tfunction); TypeFunction *tf = (TypeFunction *)type; //printf("parametersIntersect(%s) t = %s\n", tf->toChars(), t->toChars()); size_t dim = Parameter::dim(tf->parameters); for (size_t i = 0; i < dim; i++) { Parameter *fparam = Parameter::getNth(tf->parameters, i); if (!fparam->type) continue; Type *tprmi = (fparam->storageClass & (STClazy | STCout | STCref)) ? fparam->type : getIndirection(fparam->type); if (!tprmi) continue; // there is no mutable indirection //printf("\t[%d] tprmi = %d %s\n", i, tprmi->ty, tprmi->toChars()); if (traverseIndirections(tprmi, t)) return false; } if (AggregateDeclaration *ad = isCtorDeclaration() ? NULL : isThis()) { Type *tthis = ad ? ad->getType()->addMod(tf->mod) : NULL; //printf("\ttthis = %s\n", tthis->toChars()); if (traverseIndirections(tthis, t)) return false; } return true; } // Determine if function needs // a static frame pointer to its lexically enclosing function int FuncDeclaration::isNested() { FuncDeclaration *f = toAliasFunc(); //printf("\ttoParent2() = '%s'\n", f->toParent2()->toChars()); return ((f->storage_class & STCstatic) == 0) && (f->linkage == LINKd) && (f->toParent2()->isFuncDeclaration() != NULL); } int FuncDeclaration::needThis() { //printf("FuncDeclaration::needThis() '%s'\n", toChars()); return toAliasFunc()->isThis() != NULL; } int FuncDeclaration::addPreInvariant() { AggregateDeclaration *ad = isThis(); return (ad && //ad->isClassDeclaration() && global.params.useInvariants && (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && !naked && ident != Id::cpctor); } int FuncDeclaration::addPostInvariant() { AggregateDeclaration *ad = isThis(); return (ad && ad->inv && //ad->isClassDeclaration() && global.params.useInvariants && (protection == PROTprotected || protection == PROTpublic || protection == PROTexport) && !naked && ident != Id::cpctor); } /********************************** * Generate a FuncDeclaration for a runtime library function. */ // // LDC: Adjusted to give argument info to the runtime function decl. // FuncDeclaration *FuncDeclaration::genCfunc(Parameters *args, Type *treturn, const char *name) { return genCfunc(args, treturn, Lexer::idPool(name)); } FuncDeclaration *FuncDeclaration::genCfunc(Parameters *args, Type *treturn, Identifier *id) { FuncDeclaration *fd; TypeFunction *tf; Dsymbol *s; static DsymbolTable *st = NULL; //printf("genCfunc(name = '%s')\n", id->toChars()); //printf("treturn\n\t"); treturn->print(); // See if already in table if (!st) st = new DsymbolTable(); s = st->lookup(id); if (s) { fd = s->isFuncDeclaration(); assert(fd); assert(fd->type->nextOf()->equals(treturn)); } else { #if IN_LLVM tf = new TypeFunction(args, treturn, 0, LINKc); #else tf = new TypeFunction(NULL, treturn, 0, LINKc); #endif fd = new FuncDeclaration(Loc(), Loc(), id, STCstatic, tf); fd->protection = PROTpublic; fd->linkage = LINKc; st->insert(fd); } return fd; } const char *FuncDeclaration::kind() { return "function"; } /********************************************* * In the current function, we are calling 'this' function. * 1. Check to see if the current function can call 'this' function, issue error if not. * 2. If the current function is not the parent of 'this' function, then add * the current function to the list of siblings of 'this' function. * 3. If the current function is a literal, and it's accessing an uplevel scope, * then mark it as a delegate. */ void FuncDeclaration::checkNestedReference(Scope *sc, Loc loc) { //printf("FuncDeclaration::checkNestedReference() %s\n", toPrettyChars()); if (parent && parent != sc->parent && this->isNested() && this->ident != Id::require && this->ident != Id::ensure) { // The function that this function is in FuncDeclaration *fdv2 = toParent2()->isFuncDeclaration(); // The current function FuncDeclaration *fdthis = sc->parent->isFuncDeclaration(); //printf("this = %s in [%s]\n", this->toChars(), this->loc.toChars()); //printf("fdv = %s in [%s]\n", fdv->toChars(), fdv->loc.toChars()); //printf("fdthis = %s in [%s]\n", fdthis->toChars(), fdthis->loc.toChars()); if (fdv2 && fdthis && fdv2 != fdthis) { // Add this function to the list of those which called us if (fdthis != this) { bool found = false; for (int i = 0; i < siblingCallers.dim; ++i) { if (siblingCallers[i] == fdthis) found = true; } if (!found) { //printf("\tadding sibling %s\n", fdthis->toPrettyChars()); siblingCallers.push(fdthis); } } } FuncDeclaration *fdv = toParent()->isFuncDeclaration(); fdv = toParent()->isFuncDeclaration(); if (fdv && fdthis && fdv != fdthis) { int lv = fdthis->getLevel(loc, sc, fdv); if (lv == -1) return; // downlevel call if (lv == 0) return; // same level call // Uplevel call // BUG: may need to walk up outer scopes like Declaration::checkNestedReference() does // function literal has reference to enclosing scope is delegate if (FuncLiteralDeclaration *fld = fdthis->isFuncLiteralDeclaration()) fld->tok = TOKdelegate; } } } /* For all functions between outerFunc and f, mark them as needing * a closure. */ void markAsNeedingClosure(Dsymbol *f, FuncDeclaration *outerFunc) { for (Dsymbol *sx = f; sx && sx != outerFunc; sx = sx->parent) { FuncDeclaration *fy = sx->isFuncDeclaration(); if (fy && fy->closureVars.dim) { /* fy needs a closure if it has closureVars[], * because the frame pointer in the closure will be accessed. */ fy->requiresClosure = true; } } } /* Given a nested function f inside a function outerFunc, check * if any sibling callers of f have escaped. If so, mark * all the enclosing functions as needing closures. * Return true if any closures were detected. * This is recursive: we need to check the callers of our siblings. * Note that nested functions can only call lexically earlier nested * functions, so loops are impossible. */ bool checkEscapingSiblings(FuncDeclaration *f, FuncDeclaration *outerFunc) { //printf("checkEscapingSiblings(f = %s, outerfunc = %s)\n", f->toChars(), outerFunc->toChars()); bool bAnyClosures = false; for (int i = 0; i < f->siblingCallers.dim; ++i) { FuncDeclaration *g = f->siblingCallers[i]; if (g->isThis() || g->tookAddressOf) { markAsNeedingClosure(g, outerFunc); bAnyClosures = true; } bAnyClosures |= checkEscapingSiblings(g, outerFunc); } //printf("\t%d\n", bAnyClosures); return bAnyClosures; } /******************************* * Look at all the variables in this function that are referenced * by nested functions, and determine if a closure needs to be * created for them. */ #if DMDV2 int FuncDeclaration::needsClosure() { /* Need a closure for all the closureVars[] if any of the * closureVars[] are accessed by a * function that escapes the scope of this function. * We take the conservative approach and decide that a function needs * a closure if it: * 1) is a virtual function * 2) has its address taken * 3) has a parent that escapes * 4) calls another nested function that needs a closure * -or- * 5) this function returns a local struct/class * * Note that since a non-virtual function can be called by * a virtual one, if that non-virtual function accesses a closure * var, the closure still has to be taken. Hence, we check for isThis() * instead of isVirtual(). (thanks to David Friedman) */ //printf("FuncDeclaration::needsClosure() %s\n", toChars()); if (requiresClosure) goto Lyes; for (size_t i = 0; i < closureVars.dim; i++) { VarDeclaration *v = closureVars[i]; assert(v->isVarDeclaration()); //printf("\tv = %s\n", v->toChars()); for (size_t j = 0; j < v->nestedrefs.dim; j++) { FuncDeclaration *f = v->nestedrefs[j]; assert(f != this); //printf("\t\tf = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", f->toChars(), f->isVirtual(), f->isThis(), f->tookAddressOf); /* Look to see if f escapes. We consider all parents of f within * this, and also all siblings which call f; if any of them escape, * so does f. * Mark all affected functions as requiring closures. */ for (Dsymbol *s = f; s && s != this; s = s->parent) { FuncDeclaration *fx = s->isFuncDeclaration(); if (fx && (fx->isThis() || fx->tookAddressOf)) { //printf("\t\tfx = %s, isVirtual=%d, isThis=%p, tookAddressOf=%d\n", fx->toChars(), fx->isVirtual(), fx->isThis(), fx->tookAddressOf); /* Mark as needing closure any functions between this and f */ markAsNeedingClosure( (fx == f) ? fx->parent : fx, this); goto Lyes; } /* We also need to check if any sibling functions that * called us, have escaped. This is recursive: we need * to check the callers of our siblings. */ if (fx && checkEscapingSiblings(fx, this)) goto Lyes; } } } /* Look for case (5) */ if (closureVars.dim) { assert(type->ty == Tfunction); Type *tret = ((TypeFunction *)type)->next; assert(tret); tret = tret->toBasetype(); //printf("\t\treturning %s\n", tret->toChars()); if (tret->ty == Tclass || tret->ty == Tstruct) { Dsymbol *st = tret->toDsymbol(NULL); //printf("\t\treturning class/struct %s\n", tret->toChars()); for (Dsymbol *s = st->parent; s; s = s->parent) { //printf("\t\t\tparent = %s %s\n", s->kind(), s->toChars()); if (s == this) { //printf("\t\treturning local %s\n", st->toChars()); goto Lyes; } } } } return 0; Lyes: //printf("\tneeds closure\n"); return 1; } #endif /*********************************************** * Determine if function's variables are referenced by a function * nested within it. */ int FuncDeclaration::hasNestedFrameRefs() { #if DMDV2 if (closureVars.dim) #else if (nestedFrameRef) #endif return 1; /* If a virtual method has contracts, assume its variables are referenced * by those contracts, even if they aren't. Because they might be referenced * by the overridden or overriding function's contracts. * This can happen because frequire and fensure are implemented as nested functions, * and they can be called directly by an overriding function and the overriding function's * context had better match, or Bugzilla 7337 will bite. */ if ((fdrequire || fdensure) && isVirtualMethod()) return 1; if (foverrides.dim && isVirtualMethod()) { for (size_t i = 0; i < foverrides.dim; i++) { FuncDeclaration *fdv = foverrides[i]; if (fdv->hasNestedFrameRefs()) return 1; } } return 0; } /********************************************* * Return the function's parameter list, and whether * it is variadic or not. */ Parameters *FuncDeclaration::getParameters(int *pvarargs) { Parameters *fparameters; int fvarargs; if (type) { assert(type->ty == Tfunction); TypeFunction *fdtype = (TypeFunction *)type; fparameters = fdtype->parameters; fvarargs = fdtype->varargs; } if (pvarargs) *pvarargs = fvarargs; return fparameters; } /****************************** FuncAliasDeclaration ************************/ // Used as a way to import a set of functions from another scope into this one. FuncAliasDeclaration::FuncAliasDeclaration(FuncDeclaration *funcalias, int hasOverloads) : FuncDeclaration(funcalias->loc, funcalias->endloc, funcalias->ident, funcalias->storage_class, funcalias->type) { assert(funcalias != this); this->funcalias = funcalias; this->hasOverloads = hasOverloads; if (hasOverloads) { if (FuncAliasDeclaration *fad = funcalias->isFuncAliasDeclaration()) this->hasOverloads = fad->hasOverloads; } else { // for internal use assert(!funcalias->isFuncAliasDeclaration()); this->hasOverloads = 0; } userAttributes = funcalias->userAttributes; } const char *FuncAliasDeclaration::kind() { return "function alias"; } FuncDeclaration *FuncAliasDeclaration::toAliasFunc() { return funcalias->toAliasFunc(); } /****************************** FuncLiteralDeclaration ************************/ FuncLiteralDeclaration::FuncLiteralDeclaration(Loc loc, Loc endloc, Type *type, enum TOK tok, ForeachStatement *fes) : FuncDeclaration(loc, endloc, NULL, STCundefined, type) { const char *id; if (fes) id = "__foreachbody"; else if (tok == TOKreserved) id = "__lambda"; else if (tok == TOKdelegate) id = "__dgliteral"; else id = "__funcliteral"; this->ident = Lexer::uniqueId(id); this->tok = tok; this->fes = fes; this->treq = NULL; //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars()); #if IN_LLVM this->owningTemplate = NULL; #endif } Dsymbol *FuncLiteralDeclaration::syntaxCopy(Dsymbol *s) { FuncLiteralDeclaration *f; //printf("FuncLiteralDeclaration::syntaxCopy('%s')\n", toChars()); if (s) f = (FuncLiteralDeclaration *)s; else { f = new FuncLiteralDeclaration(loc, endloc, type->syntaxCopy(), tok, fes); f->ident = ident; // keep old identifier f->treq = treq; // don't need to copy } FuncDeclaration::syntaxCopy(f); return f; } int FuncLiteralDeclaration::isNested() { //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars()); return (tok != TOKfunction); } int FuncLiteralDeclaration::isVirtual() { return FALSE; } const char *FuncLiteralDeclaration::kind() { // GCC requires the (char*) casts return (tok != TOKfunction) ? (char*)"delegate" : (char*)"function"; } void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (tok != TOKreserved) { buf->writestring(kind()); buf->writeByte(' '); } TypeFunction *tf = (TypeFunction *)type; // Don't print tf->mod, tf->trust, and tf->linkage if (tf->next) tf->next->toCBuffer2(buf, hgs, 0); Parameter::argsToCBuffer(buf, hgs, tf->parameters, tf->varargs); ReturnStatement *ret = !fbody->isCompoundStatement() ? fbody->isReturnStatement() : NULL; if (ret && ret->exp) { buf->writestring(" => "); ret->exp->toCBuffer(buf, hgs); } else { hgs->tpltMember++; bodyToCBuffer(buf, hgs); hgs->tpltMember--; } } /********************************* CtorDeclaration ****************************/ CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Type *type) : FuncDeclaration(loc, endloc, Id::ctor, stc, type) { //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars()); } Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) { CtorDeclaration *f = new CtorDeclaration(loc, endloc, storage_class, type->syntaxCopy()); f->outId = outId; f->frequire = frequire ? frequire->syntaxCopy() : NULL; f->fensure = fensure ? fensure->syntaxCopy() : NULL; f->fbody = fbody ? fbody->syntaxCopy() : NULL; assert(!fthrows); // deprecated return f; } void CtorDeclaration::semantic(Scope *sc) { //printf("CtorDeclaration::semantic() %s\n", toChars()); TypeFunction *tf = (TypeFunction *)type; assert(tf && tf->ty == Tfunction); if (scope) { sc = scope; scope = NULL; } sc = sc->push(); sc->stc &= ~STCstatic; // not a static constructor sc->flags |= SCOPEctor; FuncDeclaration::semantic(sc); sc->pop(); Dsymbol *parent = toParent2(); AggregateDeclaration *ad = parent->isAggregateDeclaration(); /* See if it's the default constructor * But, template constructor should not become a default constructor. */ if (ad && tf->varargs == 0 && Parameter::dim(tf->parameters) == 0 && (!this->parent->isTemplateInstance() || this->parent->isTemplateMixin())) { StructDeclaration *sd = ad->isStructDeclaration(); if (sd) { if (fbody || !(storage_class & STCdisable)) { error("default constructor for structs only allowed with @disable and no body"); storage_class |= STCdisable; fbody = NULL; } sd->noDefaultCtor = TRUE; } else { ad->defaultCtor = this; } } } const char *CtorDeclaration::kind() { return "constructor"; } char *CtorDeclaration::toChars() { return (char *)"this"; } int CtorDeclaration::isVirtual() { return FALSE; } int CtorDeclaration::addPreInvariant() { return FALSE; } int CtorDeclaration::addPostInvariant() { return (isThis() && vthis && global.params.useInvariants); } /********************************* PostBlitDeclaration ****************************/ #if DMDV2 PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id) : FuncDeclaration(loc, endloc, id, stc, NULL) { } Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, storage_class, ident); return FuncDeclaration::syntaxCopy(dd); } void PostBlitDeclaration::semantic(Scope *sc) { //printf("PostBlitDeclaration::semantic() %s\n", toChars()); //printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor); //printf("stc = x%llx\n", sc->stc); if (scope) { sc = scope; scope = NULL; } parent = sc->parent; Dsymbol *parent = toParent(); StructDeclaration *ad = parent->isStructDeclaration(); if (!ad) { error("post blits are only for struct/union definitions, not %s %s", parent->kind(), parent->toChars()); } else if (ident == Id::_postblit && semanticRun < PASSsemantic) ad->postblits.push(this); if (!type) type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class); sc = sc->push(); sc->stc &= ~STCstatic; // not static sc->linkage = LINKd; FuncDeclaration::semantic(sc); sc->pop(); } int PostBlitDeclaration::overloadInsert(Dsymbol *s) { return FALSE; // cannot overload postblits } int PostBlitDeclaration::addPreInvariant() { return FALSE; } int PostBlitDeclaration::addPostInvariant() { return (isThis() && vthis && global.params.useInvariants); } int PostBlitDeclaration::isVirtual() { return FALSE; } void PostBlitDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("this(this)"); bodyToCBuffer(buf, hgs); } #endif /********************************* DtorDeclaration ****************************/ DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Id::dtor, STCundefined, NULL) { } DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id) : FuncDeclaration(loc, endloc, id, stc, NULL) { } Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); DtorDeclaration *dd = new DtorDeclaration(loc, endloc, storage_class, ident); return FuncDeclaration::syntaxCopy(dd); } void DtorDeclaration::semantic(Scope *sc) { //printf("DtorDeclaration::semantic() %s\n", toChars()); //printf("ident: %s, %s, %p, %p\n", ident->toChars(), Id::dtor->toChars(), ident, Id::dtor); if (scope) { sc = scope; scope = NULL; } parent = sc->parent; Dsymbol *parent = toParent(); AggregateDeclaration *ad = parent->isAggregateDeclaration(); if (!ad) { error("destructors are only for class/struct/union definitions, not %s %s", parent->kind(), parent->toChars()); fatal(); } else if (ident == Id::dtor && semanticRun < PASSsemantic) ad->dtors.push(this); if (!type) type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class); sc = sc->push(); sc->stc &= ~STCstatic; // not a static destructor sc->linkage = LINKd; FuncDeclaration::semantic(sc); sc->pop(); } int DtorDeclaration::overloadInsert(Dsymbol *s) { return FALSE; // cannot overload destructors } int DtorDeclaration::addPreInvariant() { return (isThis() && vthis && global.params.useInvariants); } int DtorDeclaration::addPostInvariant() { return FALSE; } const char *DtorDeclaration::kind() { return "destructor"; } char *DtorDeclaration::toChars() { return (char *)"~this"; } int DtorDeclaration::isVirtual() { // FALSE so that dtor's don't get put into the vtbl[] return FALSE; } void DtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("~this()"); bodyToCBuffer(buf, hgs); } /********************************* StaticCtorDeclaration ****************************/ StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Identifier::generateId("_staticCtor"), STCstatic, NULL) { } StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc, const char *name) : FuncDeclaration(loc, endloc, Identifier::generateId(name), STCstatic, NULL) { } Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); StaticCtorDeclaration *scd = new StaticCtorDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(scd); } void StaticCtorDeclaration::semantic(Scope *sc) { //printf("StaticCtorDeclaration::semantic()\n"); if (scope) { sc = scope; scope = NULL; } if (!type) type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); /* If the static ctor appears within a template instantiation, * it could get called multiple times by the module constructors * for different modules. Thus, protect it with a gate. */ if (inTemplateInstance() && semanticRun < PASSsemantic) { /* Add this prefix to the function: * static int gate; * if (++gate != 1) return; * Note that this is not thread safe; should not have threads * during static construction. */ Identifier *id = Lexer::idPool("__gate"); VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL); v->storage_class = isSharedStaticCtorDeclaration() ? STCstatic : STCtls; Statements *sa = new Statements(); Statement *s = new ExpStatement(Loc(), v); sa->push(s); Expression *e = new IdentifierExp(Loc(), id); e = new AddAssignExp(Loc(), e, new IntegerExp(1)); e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(1)); s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL); sa->push(s); if (fbody) sa->push(fbody); fbody = new CompoundStatement(Loc(), sa); } FuncDeclaration::semantic(sc); // We're going to need ModuleInfo Module *m = getModule(); if (!m) m = sc->module; if (m) { m->needmoduleinfo = 1; //printf("module1 %s needs moduleinfo\n", m->toChars()); } } AggregateDeclaration *StaticCtorDeclaration::isThis() { return NULL; } int StaticCtorDeclaration::isVirtual() { return FALSE; } bool StaticCtorDeclaration::hasStaticCtorOrDtor() { return TRUE; } int StaticCtorDeclaration::addPreInvariant() { return FALSE; } int StaticCtorDeclaration::addPostInvariant() { return FALSE; } void StaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen && !hgs->tpltMember) { buf->writestring("static this();"); buf->writenl(); return; } buf->writestring("static this()"); bodyToCBuffer(buf, hgs); } /********************************* SharedStaticCtorDeclaration ****************************/ SharedStaticCtorDeclaration::SharedStaticCtorDeclaration(Loc loc, Loc endloc) : StaticCtorDeclaration(loc, endloc, "_sharedStaticCtor") { } Dsymbol *SharedStaticCtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); SharedStaticCtorDeclaration *scd = new SharedStaticCtorDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(scd); } void SharedStaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("shared "); StaticCtorDeclaration::toCBuffer(buf, hgs); } /********************************* StaticDtorDeclaration ****************************/ StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Identifier::generateId("_staticDtor"), STCstatic, NULL) { vgate = NULL; } StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc, const char *name) : FuncDeclaration(loc, endloc, Identifier::generateId(name), STCstatic, NULL) { vgate = NULL; } Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); StaticDtorDeclaration *sdd = new StaticDtorDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(sdd); } void StaticDtorDeclaration::semantic(Scope *sc) { if (scope) { sc = scope; scope = NULL; } ClassDeclaration *cd = sc->scopesym->isClassDeclaration(); if (!type) type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); /* If the static ctor appears within a template instantiation, * it could get called multiple times by the module constructors * for different modules. Thus, protect it with a gate. */ if (inTemplateInstance() && semanticRun < PASSsemantic) { /* Add this prefix to the function: * static int gate; * if (--gate != 0) return; * Increment gate during constructor execution. * Note that this is not thread safe; should not have threads * during static destruction. */ Identifier *id = Lexer::idPool("__gate"); VarDeclaration *v = new VarDeclaration(Loc(), Type::tint32, id, NULL); v->storage_class = isSharedStaticDtorDeclaration() ? STCstatic : STCtls; Statements *sa = new Statements(); Statement *s = new ExpStatement(Loc(), v); sa->push(s); Expression *e = new IdentifierExp(Loc(), id); e = new AddAssignExp(Loc(), e, new IntegerExp(-1)); // LDC_FIXME: Previously had (uint64_t)-1, double-check this. e = new EqualExp(TOKnotequal, Loc(), e, new IntegerExp(0)); s = new IfStatement(Loc(), NULL, e, new ReturnStatement(Loc(), NULL), NULL); sa->push(s); if (fbody) sa->push(fbody); fbody = new CompoundStatement(Loc(), sa); vgate = v; } FuncDeclaration::semantic(sc); // We're going to need ModuleInfo Module *m = getModule(); if (!m) m = sc->module; if (m) { m->needmoduleinfo = 1; //printf("module2 %s needs moduleinfo\n", m->toChars()); } } AggregateDeclaration *StaticDtorDeclaration::isThis() { return NULL; } int StaticDtorDeclaration::isVirtual() { return FALSE; } bool StaticDtorDeclaration::hasStaticCtorOrDtor() { return TRUE; } int StaticDtorDeclaration::addPreInvariant() { return FALSE; } int StaticDtorDeclaration::addPostInvariant() { return FALSE; } void StaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen) return; buf->writestring("static ~this()"); bodyToCBuffer(buf, hgs); } /********************************* SharedStaticDtorDeclaration ****************************/ SharedStaticDtorDeclaration::SharedStaticDtorDeclaration(Loc loc, Loc endloc) : StaticDtorDeclaration(loc, endloc, "_sharedStaticDtor") { } Dsymbol *SharedStaticDtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); SharedStaticDtorDeclaration *sdd = new SharedStaticDtorDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(sdd); } void SharedStaticDtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (!hgs->hdrgen) { buf->writestring("shared "); StaticDtorDeclaration::toCBuffer(buf, hgs); } } /********************************* InvariantDeclaration ****************************/ InvariantDeclaration::InvariantDeclaration(Loc loc, Loc endloc, StorageClass stc, Identifier *id) : FuncDeclaration(loc, endloc, id ? id : Identifier::generateId("__invariant"), stc, NULL) { } Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s) { InvariantDeclaration *id; assert(!s); id = new InvariantDeclaration(loc, endloc, storage_class); FuncDeclaration::syntaxCopy(id); return id; } void InvariantDeclaration::semantic(Scope *sc) { if (scope) { sc = scope; scope = NULL; } parent = sc->parent; Dsymbol *parent = toParent(); AggregateDeclaration *ad = parent->isAggregateDeclaration(); if (!ad) { error("invariants are only for struct/union/class definitions"); return; } if (ident != Id::classInvariant && semanticRun < PASSsemantic) { ad->invs.push(this); } if (!type) type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd, storage_class); sc = sc->push(); sc->stc &= ~STCstatic; // not a static invariant sc->stc |= STCconst; // invariant() is always const sc->flags = (sc->flags & ~SCOPEcontract) | SCOPEinvariant; sc->linkage = LINKd; FuncDeclaration::semantic(sc); sc->pop(); } int InvariantDeclaration::isVirtual() { return FALSE; } int InvariantDeclaration::addPreInvariant() { return FALSE; } int InvariantDeclaration::addPostInvariant() { return FALSE; } void InvariantDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen) return; buf->writestring("invariant"); bodyToCBuffer(buf, hgs); } /********************************* UnitTestDeclaration ****************************/ /******************************* * Generate unique unittest function Id so we can have multiple * instances per module. */ static Identifier *unitTestId(Loc loc) { char name[24]; #if __DMC__ || _MSC_VER _snprintf(name, 24, "__unittestL%u_", loc.linnum); #else snprintf(name, 24, "__unittestL%u_", loc.linnum); #endif return Lexer::uniqueId(name); } UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc, char *codedoc) : FuncDeclaration(loc, endloc, unitTestId(loc), STCundefined, NULL) { this->codedoc = codedoc; } Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s) { UnitTestDeclaration *utd; assert(!s); utd = new UnitTestDeclaration(loc, endloc, codedoc); return FuncDeclaration::syntaxCopy(utd); } void UnitTestDeclaration::semantic(Scope *sc) { protection = sc->protection; if (scope) { sc = scope; scope = NULL; } #if IN_LLVM if (global.params.useUnitTests && sc->module->isRoot) #else if (global.params.useUnitTests) #endif { if (!type) type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); Scope *sc2 = sc->push(); // It makes no sense for unit tests to be pure or nothrow. sc2->stc &= ~(STCnothrow | STCpure); sc2->linkage = LINKd; FuncDeclaration::semantic(sc2); sc2->pop(); } #if 0 // We're going to need ModuleInfo even if the unit tests are not // compiled in, because other modules may import this module and refer // to this ModuleInfo. // (This doesn't make sense to me?) Module *m = getModule(); if (!m) m = sc->module; if (m) { //printf("module3 %s needs moduleinfo\n", m->toChars()); m->needmoduleinfo = 1; } #endif } AggregateDeclaration *UnitTestDeclaration::isThis() { return NULL; } int UnitTestDeclaration::isVirtual() { return FALSE; } int UnitTestDeclaration::addPreInvariant() { return FALSE; } int UnitTestDeclaration::addPostInvariant() { return FALSE; } void UnitTestDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen) return; buf->writestring("unittest"); bodyToCBuffer(buf, hgs); } /********************************* NewDeclaration ****************************/ NewDeclaration::NewDeclaration(Loc loc, Loc endloc, Parameters *arguments, int varargs) : FuncDeclaration(loc, endloc, Id::classNew, STCstatic, NULL) { this->arguments = arguments; this->varargs = varargs; } Dsymbol *NewDeclaration::syntaxCopy(Dsymbol *s) { NewDeclaration *f; f = new NewDeclaration(loc, endloc, NULL, varargs); FuncDeclaration::syntaxCopy(f); f->arguments = Parameter::arraySyntaxCopy(arguments); return f; } void NewDeclaration::semantic(Scope *sc) { //printf("NewDeclaration::semantic()\n"); if (scope) { sc = scope; scope = NULL; } parent = sc->parent; Dsymbol *parent = toParent(); ClassDeclaration *cd = parent->isClassDeclaration(); if (!cd && !parent->isStructDeclaration()) { error("new allocators only are for class or struct definitions"); } Type *tret = Type::tvoid->pointerTo(); if (!type) type = new TypeFunction(arguments, tret, varargs, LINKd); type = type->semantic(loc, sc); assert(type->ty == Tfunction); // Check that there is at least one argument of type size_t TypeFunction *tf = (TypeFunction *)type; if (Parameter::dim(tf->parameters) < 1) { error("at least one argument of type size_t expected"); } else { Parameter *a = Parameter::getNth(tf->parameters, 0); if (!a->type->equals(Type::tsize_t)) error("first argument must be type size_t, not %s", a->type->toChars()); } FuncDeclaration::semantic(sc); } const char *NewDeclaration::kind() { return "allocator"; } int NewDeclaration::isVirtual() { return FALSE; } int NewDeclaration::addPreInvariant() { return FALSE; } int NewDeclaration::addPostInvariant() { return FALSE; } void NewDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("new"); Parameter::argsToCBuffer(buf, hgs, arguments, varargs); bodyToCBuffer(buf, hgs); } /********************************* DeleteDeclaration ****************************/ DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, Parameters *arguments) : FuncDeclaration(loc, endloc, Id::classDelete, STCstatic, NULL) { this->arguments = arguments; } Dsymbol *DeleteDeclaration::syntaxCopy(Dsymbol *s) { DeleteDeclaration *f; f = new DeleteDeclaration(loc, endloc, NULL); FuncDeclaration::syntaxCopy(f); f->arguments = Parameter::arraySyntaxCopy(arguments); return f; } void DeleteDeclaration::semantic(Scope *sc) { //printf("DeleteDeclaration::semantic()\n"); if (scope) { sc = scope; scope = NULL; } parent = sc->parent; Dsymbol *parent = toParent(); ClassDeclaration *cd = parent->isClassDeclaration(); if (!cd && !parent->isStructDeclaration()) { error("new allocators only are for class or struct definitions"); } if (!type) type = new TypeFunction(arguments, Type::tvoid, 0, LINKd); type = type->semantic(loc, sc); assert(type->ty == Tfunction); // Check that there is only one argument of type void* TypeFunction *tf = (TypeFunction *)type; if (Parameter::dim(tf->parameters) != 1) { error("one argument of type void* expected"); } else { Parameter *a = Parameter::getNth(tf->parameters, 0); if (!a->type->equals(Type::tvoid->pointerTo())) error("one argument of type void* expected, not %s", a->type->toChars()); } FuncDeclaration::semantic(sc); } const char *DeleteDeclaration::kind() { return "deallocator"; } int DeleteDeclaration::isDelete() { return TRUE; } int DeleteDeclaration::isVirtual() { return FALSE; } int DeleteDeclaration::addPreInvariant() { return FALSE; } int DeleteDeclaration::addPostInvariant() { return FALSE; } void DeleteDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("delete"); Parameter::argsToCBuffer(buf, hgs, arguments, 0); bodyToCBuffer(buf, hgs); }