Mercurial > projects > ldc
view dmd2/func.c @ 945:03d7c4aac654
SWITCHED TO LLVM 2.5 !
Applied patch from ticket #129 to compile against latest LLVM. Thanks Frits van Bommel.
Fixed implicit return by asm block at the end of a function on x86-32. Other architectures will produce an error at the moment. Adding support for new targets is fairly simple.
Fixed return calling convention for complex numbers, ST and ST(1) were switched around.
Added some testcases.
I've run a dstress test and there are no regressions. However, the runtime does not seem to compile with symbolic debug information. -O3 -release -inline works well and is what I used for the dstress run. Tango does not compile, a small workaround is needed in tango.io.digest.Digest.Digest.hexDigest. See ticket #206 .
author | Tomas Lindquist Olsen <tomas.l.olsen@gmail.com> |
---|---|
date | Sun, 08 Feb 2009 05:26:54 +0100 |
parents | 6c09179ebba0 |
children | 638d16625da2 |
line wrap: on
line source
// Compiler implementation of the D programming language // Copyright (c) 1999-2008 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 <stdio.h> #include <assert.h> #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" #ifdef IN_GCC #include "d-dmd-gcc.h" #endif /********************************* FuncDeclaration ****************************/ FuncDeclaration::FuncDeclaration(Loc loc, Loc endloc, Identifier *id, enum STC storage_class, Type *type) : Declaration(id) { //printf("FuncDeclaration(id = '%s', type = %p)\n", id->toChars(), type); this->storage_class = storage_class; this->type = type; this->loc = loc; this->endloc = endloc; fthrows = NULL; frequire = NULL; outId = NULL; vresult = NULL; returnLabel = NULL; fensure = NULL; fbody = NULL; localsymtab = NULL; vthis = NULL; v_arguments = NULL; #if IN_GCC v_argptr = NULL; #endif parameters = NULL; labtab = NULL; overnext = NULL; vtblIndex = -1; hasReturnExp = 0; naked = 0; inlineStatus = ILSuninitialized; inlineNest = 0; inlineAsm = 0; cantInterpret = 0; semanticRun = 0; 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); scope = NULL; hasReturnExp = 0; nrvo_can = 1; nrvo_var = NULL; shidden = NULL; builtin = BUILTINunknown; tookAddressOf = 0; // LDC isArrayOp = false; allowInlining = false; } 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, (enum STC) 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 // LDC f->intrinsicName = intrinsicName; return f; } // Do the semantic analysis on the external interface to the function. void FuncDeclaration::semantic(Scope *sc) { TypeFunction *f; StructDeclaration *sd; ClassDeclaration *cd; InterfaceDeclaration *id; Dsymbol *pd; #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\n", sc->parent->toChars()); printf("type: %p, %s\n", type, type->toChars()); #endif storage_class |= sc->stc & ~STCref; //printf("function storage_class = x%x\n", storage_class); if (!originalType) originalType = type; if (!type->deco && type->nextOf()) { #if 1 /* Apply const and invariant storage class * to the function type */ type = type->semantic(loc, sc); if (storage_class & STCinvariant) { // Don't use toInvariant(), as that will do a merge() type = type->makeInvariant(); type->deco = type->merge()->deco; } else if (storage_class & STCconst) { if (!type->isInvariant()) { // Don't use toConst(), as that will do a merge() type = type->makeConst(); type->deco = type->merge()->deco; } } #else if (storage_class & (STCconst | STCinvariant)) { /* Apply const and invariant storage class * to the function's return type */ Type *tn = type->nextOf(); if (storage_class & STCconst) tn = tn->makeConst(); if (storage_class & STCinvariant) tn = tn->makeInvariant(); ((TypeNext *)type)->next = tn; } type = type->semantic(loc, sc); #endif } //type->print(); if (type->ty != Tfunction) { error("%s must be a function", toChars()); return; } f = (TypeFunction *)(type); size_t nparams = Argument::dim(f->parameters); linkage = sc->linkage; // if (!parent) { //parent = sc->scopesym; parent = sc->parent; } protection = sc->protection; Dsymbol *parent = toParent(); if (storage_class & STCscope) error("functions cannot be scope"); if (isAbstract() && !isVirtual()) error("non-virtual functions cannot be abstract"); if ((f->isConst() || f->isInvariant()) && !isThis()) error("without 'this' cannot be const/invariant"); 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 #ifdef IN_GCC AggregateDeclaration *ad; ad = parent->isAggregateDeclaration(); if (ad) ad->methods.push(this); #endif sd = parent->isStructDeclaration(); if (sd) { if (isCtorDeclaration()) { return; } #if 0 // Verify no constructors, destructors, etc. if (isCtorDeclaration() //||isDtorDeclaration() //|| isInvariantDeclaration() //|| isUnitTestDeclaration() ) { error("special member functions not allowed for %ss", sd->kind()); } if (!sd->inv) sd->inv = isInvariantDeclaration(); 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() || isPostBlitDeclaration() || isDtorDeclaration() || isInvariantDeclaration() || isUnitTestDeclaration() || isNewDeclaration() || isDelete()) error("special function not allowed in interface %s", id->toChars()); if (fbody) error("function body is not abstract in interface %s", id->toChars()); } /* Template member functions aren't virtual: * interface TestInterface { void tpl(T)(); } * and so won't work in interfaces */ if ((pd = toParent()) != NULL && pd->isTemplateInstance() && (pd = toParent2()) != NULL && (id = pd->isInterfaceDeclaration()) != NULL) { error("template member function not allowed in interface %s", id->toChars()); } cd = parent->isClassDeclaration(); if (cd) { int vi; CtorDeclaration *ctor; DtorDeclaration *dtor; InvariantDeclaration *inv; if (isCtorDeclaration()) { // ctor = (CtorDeclaration *)this; // if (!cd->ctor) // cd->ctor = ctor; return; } #if 0 dtor = isDtorDeclaration(); if (dtor) { if (cd->dtor) error("multiple destructors for class %s", cd->toChars()); cd->dtor = dtor; } inv = isInvariantDeclaration(); if (inv) { cd->inv = inv; } 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; } // Find index of existing function in vtbl[] to override vi = findVtblIndex(&cd->vtbl, cd->baseClass ? cd->baseClass->vtbl.dim : 0); switch (vi) { case -1: /* 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(); f = f->overloadExactMatch(type); if (f && f->isFinal() && f->prot() != PROTprivate) error("cannot override final function %s", f->toPrettyChars()); } } if (isFinal()) { 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 = 2; // can't finish due to forward reference return; default: { FuncDeclaration *fdv = (FuncDeclaration *)cd->vtbl.data[vi]; // This function is covariant with fdv if (fdv->isFinal()) error("cannot override final function %s", fdv->toPrettyChars()); #if DMDV2 if (!isOverride() && global.params.warnings) warning("%s: overrides base class function %s, but is not marked with 'override'", locToChars(), fdv->toPrettyChars()); #endif if (fdv->toParent() == parent) { // If both are mixins, then error. // If either is not, the one that is not overrides // the other. if (fdv->parent->isClassDeclaration()) break; if (!this->parent->isClassDeclaration() #if !BREAKABI && !isDtorDeclaration() #endif #if DMDV2 && !isPostBlitDeclaration() #endif ) error("multiple overrides of same function"); } cd->vtbl.data[vi] = (void *)this; vtblIndex = vi; /* 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 (int i = 0; i < cd->interfaces_dim; i++) { BaseClass *b = cd->interfaces[i]; vi = findVtblIndex(&b->base->vtbl, b->base->vtbl.dim); switch (vi) { case -1: break; case -2: cd->sizeok = 2; // can't finish due to forward reference return; default: { FuncDeclaration *fdv = (FuncDeclaration *)b->base->vtbl.data[vi]; Type *ti = NULL; if (fdv->tintro) ti = 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)) { ti = fdv->type; #if 0 if (offset) ti = fdv->type; else if (type->nextOf()->ty == Tclass) { ClassDeclaration *cdn = ((TypeClass *)type->nextOf())->sym; if (cdn && cdn->sizeok != 1) ti = fdv->type; } #endif } } if (ti) { if (tintro && !tintro->equals(ti)) { error("incompatible covariant types %s and %s", tintro->toChars(), ti->toChars()); } tintro = ti; } goto L2; } } } if (introducing && isOverride()) { error("does not override any function"); } L2: ; } 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: { Argument *arg0 = Argument::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()->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(char[][] args)"); } } if (ident == Id::assign && (sd || cd)) { // Disallow identity assignment operator. // opAssign(...) if (nparams == 0) { if (f->varargs == 1) goto Lassignerr; } else { Argument *arg0 = Argument::getNth(f->parameters, 0); Type *t0 = arg0->type->toBasetype(); Type *tb = sd ? sd->type : cd->type; if (arg0->type->implicitConvTo(tb) || (sd && t0->ty == Tpointer && t0->nextOf()->implicitConvTo(tb)) ) { if (nparams == 1) goto Lassignerr; Argument *arg1 = Argument::getNth(f->parameters, 1); if (arg1->defaultArg) goto Lassignerr; } } } Ldone: /* Save scope for possible later use (if we need the * function internals) */ scope = new Scope(*sc); scope->setNoFree(); return; Lassignerr: if (sd) { sd->hasIdentityAssign = 1; // don't need to generate it goto Ldone; } error("identity assignment operator overload is illegal"); } void FuncDeclaration::semantic2(Scope *sc) { } // Do the semantic analysis on the internals of the function. void FuncDeclaration::semantic3(Scope *sc) { TypeFunction *f; AggregateDeclaration *ad; VarDeclaration *argptr = NULL; VarDeclaration *_arguments = NULL; if (!parent) { if (global.errors) return; //printf("FuncDeclaration::semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc); assert(0); } //printf("FuncDeclaration::semantic3('%s.%s', sc = %p, loc = %s)\n", parent->toChars(), toChars(), sc, loc.toChars()); //fflush(stdout); //{ static int x; if (++x == 2) *(char*)0=0; } //printf("\tlinkage = %d\n", sc->linkage); //printf(" sc->incontract = %d\n", sc->incontract); if (semanticRun) return; semanticRun = 1; if (!type || type->ty != Tfunction) return; f = (TypeFunction *)(type); // Check the 'throws' clause if (fthrows) { for (int i = 0; i < fthrows->dim; i++) { Type *t = (Type *)fthrows->data[i]; t = t->semantic(loc, sc); if (!t->isClassHandle()) error("can only throw classes, not %s", t->toChars()); } } if (fbody || frequire) { /* 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 | STCconst | STCfinal | STCinvariant | STCtls); sc2->protection = PROTpublic; sc2->explicitProtection = 0; sc2->structalign = 8; sc2->incontract = 0; sc2->tf = NULL; sc2->tfOfTry = NULL; sc2->noctor = 0; // Declare 'this' ad = isThis(); if (ad) { VarDeclaration *v; if (isFuncLiteralDeclaration() && isNested()) { error("literals cannot be class members"); return; } else { assert(!isNested()); // can't be both member and nested assert(ad->handle); Type *thandle = ad->handle; if (storage_class & STCconst || type->isConst()) { #if STRUCTTHISREF thandle = thandle->constOf(); #else if (thandle->ty == Tclass) thandle = thandle->constOf(); else { assert(thandle->ty == Tpointer); thandle = thandle->nextOf()->constOf()->pointerTo(); } #endif } else if (storage_class & STCinvariant || type->isInvariant()) { #if STRUCTTHISREF thandle = thandle->invariantOf(); #else if (thandle->ty == Tclass) thandle = thandle->invariantOf(); else { assert(thandle->ty == Tpointer); thandle = thandle->nextOf()->invariantOf()->pointerTo(); } #endif } v = new ThisDeclaration(thandle); v->storage_class |= STCparameter; #if STRUCTTHISREF if (thandle->ty == Tstruct) v->storage_class |= STCref; #endif v->semantic(sc2); if (!sc2->insert(v)) assert(0); v->parent = this; vthis = v; } } else if (isNested()) { /* The 'this' for a nested function is the link to the * enclosing function's stack frame. * Note that nested functions and member functions are disjoint. */ VarDeclaration *v = new ThisDeclaration(Type::tvoid->pointerTo()); v->storage_class |= STCparameter; v->semantic(sc2); if (!sc2->insert(v)) assert(0); v->parent = this; vthis = v; } // Declare hidden variable _arguments[] and _argptr if (f->varargs == 1) { Type *t; if (f->linkage == LINKd) { // Declare _arguments[] #if BREAKABI v_arguments = new VarDeclaration(0, 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(0, t, Id::_arguments, NULL); _arguments->semantic(sc2); sc2->insert(_arguments); _arguments->parent = this; #else t = Type::typeinfo->type->arrayOf(); v_arguments = new VarDeclaration(0, t, Id::_arguments, NULL); v_arguments->storage_class = STCparameter | STCin; v_arguments->semantic(sc2); sc2->insert(v_arguments); v_arguments->parent = this; #endif } if (f->linkage == LINKd || (parameters && parameters->dim)) { // Declare _argptr #if IN_GCC t = d_gcc_builtin_va_list_d_type; #else t = Type::tvoid->pointerTo(); #endif argptr = new VarDeclaration(0, t, Id::_argptr, NULL); argptr->semantic(sc2); sc2->insert(argptr); argptr->parent = this; } } // Propagate storage class from tuple parameters to their element-parameters. if (f->parameters) { for (size_t i = 0; i < f->parameters->dim; i++) { Argument *arg = (Argument *)f->parameters->data[i]; if (arg->type->ty == Ttuple) { TypeTuple *t = (TypeTuple *)arg->type; size_t dim = Argument::dim(t->arguments); for (size_t j = 0; j < dim; j++) { Argument *narg = Argument::getNth(t->arguments, j); narg->storageClass = arg->storageClass; } } } } /* Declare all the function parameters as variables * and install them in parameters[] */ size_t nparams = Argument::dim(f->parameters); if (nparams) { /* parameters[] has all the tuples removed, as the back end * doesn't know about tuples */ parameters = new Dsymbols(); parameters->reserve(nparams); for (size_t i = 0; i < nparams; i++) { Argument *arg = Argument::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); } VarDeclaration *v = new VarDeclaration(loc, arg->type, 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 | STCconst | STCinvariant | 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; } } // 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++) { Argument *arg = (Argument *)f->parameters->data[i]; if (!arg->ident) continue; // never used, so ignore if (arg->type->ty == Ttuple) { TypeTuple *t = (TypeTuple *)arg->type; size_t dim = Argument::dim(t->arguments); Objects *exps = new Objects(); exps->setDim(dim); for (size_t j = 0; j < dim; j++) { Argument *narg = Argument::getNth(t->arguments, j); assert(narg->ident); VarDeclaration *v = sc2->search(0, narg->ident, NULL)->isVarDeclaration(); assert(v); Expression *e = new VarExp(v->loc, v); exps->data[j] = (void *)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; } } } /* Do the semantic analysis on the [in] preconditions and * [out] postconditions. */ sc2->incontract++; if (frequire) { /* frequire is composed of the [in] contracts */ // 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 frequire = frequire->semantic(sc2); labtab = NULL; // so body can't refer to labels } if (fensure || addPostInvariant()) { /* fensure is composed of the [out] contracts */ ScopeDsymbol *sym = new ScopeDsymbol(); sym->parent = sc2->scopesym; sc2 = sc2->push(sym); assert(type->nextOf()); if (type->nextOf()->ty == Tvoid) { if (outId) error("void functions have no result"); } else { if (!outId) outId = Id::result; // provide a default } if (outId) { // Declare result variable VarDeclaration *v; Loc loc = this->loc; if (fensure) loc = fensure->loc; v = new VarDeclaration(loc, type->nextOf(), outId, NULL); v->noauto = 1; sc2->incontract--; v->semantic(sc2); sc2->incontract++; if (!sc2->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() } // BUG: need to treat parameters as const // BUG: need to disallow returns and throws if (fensure) { fensure = fensure->semantic(sc2); labtab = NULL; // so body can't refer to labels } if (!global.params.useOut) { fensure = NULL; // discard vresult = NULL; } // Postcondition invariant if (addPostInvariant()) { Expression *e = NULL; if (isCtorDeclaration()) { // Call invariant directly only if it exists InvariantDeclaration *inv = ad->inv; ClassDeclaration *cd = ad->isClassDeclaration(); while (!inv && cd) { cd = cd->baseClass; if (!cd) break; inv = cd->inv; } if (inv) { e = new DsymbolExp(0, inv); e = new CallExp(0, e); e = e->semantic(sc2); } } else { // Call invariant virtually Expression *v = new ThisExp(0); v->type = vthis->type; #if STRUCTTHISREF if (ad->isStructDeclaration()) v = v->addressOf(sc); #endif e = new AssertExp(0, v); } if (e) { ExpStatement *s = new ExpStatement(0, e); if (fensure) fensure = new CompoundStatement(0, s, fensure); else fensure = s; } } if (fensure) { returnLabel = new LabelDsymbol(Id::returnLabel); LabelStatement *ls = new LabelStatement(0, Id::returnLabel, fensure); ls->isReturnLabel = 1; returnLabel->statement = ls; } sc2 = sc2->pop(); } sc2->incontract--; if (fbody) { ClassDeclaration *cd = isClassMember(); /* If this is a class constructor */ if (isCtorDeclaration() && cd) { for (int i = 0; i < cd->fields.dim; i++) { VarDeclaration *v = (VarDeclaration *)cd->fields.data[i]; v->ctorinit = 0; } } if (inferRetType || f->retStyle() != RETstack) nrvo_can = 0; fbody = fbody->semantic(sc2); if (inferRetType) { // If no return type inferred yet, then infer a void if (!type->nextOf()) { ((TypeFunction *)type)->next = Type::tvoid; type = type->semantic(loc, sc); } f = (TypeFunction *)type; } if (isStaticCtorDeclaration()) { /* It's a static constructor. Ensure that all * ctor consts were initialized. */ Dsymbol *p = toParent(); ScopeDsymbol *ad = p->isScopeDsymbol(); if (!ad) { error("static constructor can only be member of struct/class/module, not %s %s", p->kind(), p->toChars()); } else { for (int i = 0; i < ad->members->dim; i++) { Dsymbol *s = (Dsymbol *)ad->members->data[i]; s->checkCtorConstInit(); } } } if (isCtorDeclaration() && cd) { //printf("callSuper = x%x\n", sc2->callSuper); // Verify that all the ctorinit fields got initialized if (!(sc2->callSuper & CSXthis_ctor)) { for (int i = 0; i < cd->fields.dim; i++) { VarDeclaration *v = (VarDeclaration *)cd->fields.data[i]; if (v->ctorinit == 0 && v->isCtorinit()) error("missing initializer for final field %s", v->toChars()); } } if (!(sc2->callSuper & CSXany_ctor) && cd->baseClass && cd->baseClass->ctor) { sc2->callSuper = 0; // Insert implicit super() at start of fbody Expression *e1 = new SuperExp(0); Expression *e = new CallExp(0, e1); unsigned errors = global.errors; global.gag++; e = e->semantic(sc2); global.gag--; if (errors != global.errors) error("no match for implicit super() call in constructor"); Statement *s = new ExpStatement(0, e); fbody = new CompoundStatement(0, s, fbody); } } else if (fes) { // For foreach(){} body, append a return 0; Expression *e = new IntegerExp(0); Statement *s = new ReturnStatement(0, e); fbody = new CompoundStatement(0, fbody, s); assert(!returnLabel); } else if (!hasReturnExp && type->nextOf()->ty != Tvoid) error("expected to return a value of type %s", type->nextOf()->toChars()); else if (!inlineAsm) { int offend = fbody ? fbody->blockExit() & BEfallthru : TRUE; //int offend = fbody ? fbody->fallOffEnd() : TRUE; if (type->nextOf()->ty == Tvoid) { if (offend && isMain()) { // Add a return 0; statement Statement *s = new ReturnStatement(0, new IntegerExp(0)); fbody = new CompoundStatement(0, fbody, s); } } else { if (offend) { Expression *e; if (global.params.warnings) { warning("%s: no return at end of function", locToChars()); } 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(0, e, type->nextOf()->defaultInit()); e = e->semantic(sc2); Statement *s = new ExpStatement(0, e); fbody = new CompoundStatement(0, fbody, s); } } } } { Statements *a = new Statements(); // Merge in initialization of 'out' parameters if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (VarDeclaration *)parameters->data[i]; if (v->storage_class & STCout) { assert(v->init); ExpInitializer *ie = v->init->isExpInitializer(); assert(ie); a->push(new ExpStatement(0, ie->exp)); } } } // we'll handle variadics ourselves #if !IN_LLVM if (argptr) { // Initialize _argptr to point past non-variadic arg #if IN_GCC // Handled in FuncDeclaration::toObjFile v_argptr = argptr; v_argptr->init = new VoidInitializer(loc); #else Expression *e1; Expression *e; Type *t = argptr->type; VarDeclaration *p; unsigned offset; e1 = new VarExp(0, argptr); if (parameters && parameters->dim) p = (VarDeclaration *)parameters->data[parameters->dim - 1]; else p = v_arguments; // last parameter is _arguments[] offset = p->type->size(); offset = (offset + 3) & ~3; // assume stack aligns on 4 e = new SymOffExp(0, p, offset); e = new AssignExp(0, e1, e); e->type = t; a->push(new ExpStatement(0, e)); #endif // IN_GCC } if (_arguments) { /* Advance to elements[] member of TypeInfo_Tuple with: * _arguments = v_arguments.elements; */ Expression *e = new VarExp(0, v_arguments); e = new DotIdExp(0, e, Id::elements); Expression *e1 = new VarExp(0, _arguments); e = new AssignExp(0, e1, e); e->op = TOKconstruct; e = e->semantic(sc); a->push(new ExpStatement(0, e)); } #endif // !IN_LLVM // Merge contracts together with body into one compound statement #ifdef _DH if (frequire && global.params.useIn) { frequire->incontract = 1; a->push(frequire); } #else if (frequire && global.params.useIn) a->push(frequire); #endif // Precondition invariant if (addPreInvariant()) { Expression *e = NULL; if (isDtorDeclaration()) { // Call invariant directly only if it exists InvariantDeclaration *inv = ad->inv; ClassDeclaration *cd = ad->isClassDeclaration(); while (!inv && cd) { cd = cd->baseClass; if (!cd) break; inv = cd->inv; } if (inv) { e = new DsymbolExp(0, inv); e = new CallExp(0, e); e = e->semantic(sc2); } } else { // Call invariant virtually Expression *v = new ThisExp(0); v->type = vthis->type; #if STRUCTTHISREF if (ad->isStructDeclaration()) v = v->addressOf(sc); #endif Expression *se = new StringExp(0, (char *)"null this"); se = se->semantic(sc); se->type = Type::tchar->arrayOf(); e = new AssertExp(loc, v, se); } if (e) { ExpStatement *s = new ExpStatement(0, e); a->push(s); } } if (fbody) a->push(fbody); if (fensure) { a->push(returnLabel->statement); if (type->nextOf()->ty != Tvoid) { // Create: return vresult; assert(vresult); Expression *e = new VarExp(0, vresult); if (tintro) { e = e->implicitCastTo(sc, tintro->nextOf()); e = e->semantic(sc); } ReturnStatement *s = new ReturnStatement(0, e); a->push(s); } } fbody = new CompoundStatement(0, a); /* Append destructor calls for parameters as finally blocks. */ if (parameters) { for (size_t i = 0; i < parameters->dim; i++) { VarDeclaration *v = (VarDeclaration *)parameters->data[i]; if (v->storage_class & (STCref | STCout)) continue; /* Don't do this for static arrays, since static * arrays are called by reference. Remove this * when we change them to call by value. */ if (v->type->toBasetype()->ty == Tsarray) continue; Expression *e = v->callAutoDtor(sc); if (e) { Statement *s = new ExpStatement(0, e); s = s->semantic(sc); if (fbody->blockExit() == BEfallthru) fbody = new CompoundStatement(0, fbody, s); else fbody = new TryFinallyStatement(0, fbody, s); } } } } sc2->callSuper = 0; sc2->pop(); } semanticRun = 2; } void FuncDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { //printf("FuncDeclaration::toCBuffer() '%s'\n", toChars()); type->toCBuffer(buf, ident, hgs); bodyToCBuffer(buf, hgs); } void FuncDeclaration::bodyToCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (fbody && (!hgs->hdrgen || hgs->tpltMember || canInline(1,1)) ) { 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(); fbody->toCBuffer(buf, hgs); buf->writebyte('}'); buf->writenl(); } else { buf->writeByte(';'); buf->writenl(); } } /**************************************************** * 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. * Returns: * -1 didn't find one * -2 can't determine because of forward references */ int FuncDeclaration::findVtblIndex(Array *vtbl, int dim) { for (int vi = 0; vi < dim; vi++) { FuncDeclaration *fdv = ((Dsymbol *)vtbl->data[vi])->isFuncDeclaration(); if (fdv && fdv->ident == ident) { int cov = type->covariant(fdv->type); //printf("\tbaseclass cov = %d\n", cov); switch (cov) { case 0: // types are distinct break; case 1: return vi; case 2: //type->print(); //fdv->type->print(); //printf("%s %s\n", type->deco, fdv->type->deco); error("of type %s overrides but is not covariant with %s of type %s", type->toChars(), fdv->toPrettyChars(), fdv->type->toChars()); break; case 3: return -2; // forward references default: assert(0); } } } return -1; } /**************************************************** * Overload this FuncDeclaration with the new one f. * Return !=0 if successful; i.e. no conflict. */ int FuncDeclaration::overloadInsert(Dsymbol *s) { FuncDeclaration *f; AliasDeclaration *a; //printf("FuncDeclaration::overloadInsert(%s)\n", s->toChars()); a = s->isAliasDeclaration(); if (a) { if (overnext) return overnext->overloadInsert(a); if (!a->aliassym && a->type->ty != Tident && a->type->ty != Tinstance) { //printf("\ta = '%s'\n", a->type->toChars()); return FALSE; } overnext = a; //printf("\ttrue: no conflict\n"); return TRUE; } f = s->isFuncDeclaration(); if (!f) 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("f->type = %s\n", f->type->toChars()); } if (type && f->type && // can be NULL for overloaded constructors f->type->covariant(type) && f->type->mod == type->mod && !isFuncAliasDeclaration()) { //printf("\tfalse: conflict %s\n", kind()); return FALSE; } #endif if (overnext) return overnext->overloadInsert(f); overnext = f; //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 (overloadApply(fa->funcalias, fp, param)) 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, if it's just a const conversion * of the return 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. */ struct Param2 { Match *m; Expression *ethis; Expressions *arguments; }; int fp2(void *param, FuncDeclaration *f) { Param2 *p = (Param2 *)param; Match *m = p->m; Expressions *arguments = p->arguments; MATCH match; if (f != m->lastf) // skip duplicates { m->anyf = f; TypeFunction *tf = (TypeFunction *)f->type; match = (MATCH) tf->callMatch(f->needThis() ? p->ethis : NULL, arguments); //printf("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; /* 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; } 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, Expression *ethis, Expressions *arguments) { Param2 p; p.m = m; p.ethis = ethis; p.arguments = arguments; overloadApply(fstart, &fp2, &p); } FuncDeclaration *FuncDeclaration::overloadResolve(Loc loc, Expression *ethis, Expressions *arguments, int flags) { TypeFunction *tf; Match m; #if 0 printf("FuncDeclaration::overloadResolve('%s')\n", toChars()); if (arguments) { int i; for (i = 0; i < arguments->dim; i++) { Expression *arg; arg = (Expression *)arguments->data[i]; assert(arg->type); printf("\t%s: ", arg->toChars()); arg->type->print(); } } #endif memset(&m, 0, sizeof(m)); m.last = MATCHnomatch; overloadResolveX(&m, this, ethis, arguments); if (m.count == 1) // exactly one match { return m.lastf; } else { OutBuffer buf; if (arguments) { HdrGenState hgs; argExpTypesToCBuffer(&buf, arguments, &hgs); } if (m.last == MATCHnomatch) { if (flags & 1) // if do not print error messages return NULL; // no match tf = (TypeFunction *)type; //printf("tf = %s, args = %s\n", tf->deco, ((Expression *)arguments->data[0])->type->deco); error(loc, "%s does not match parameter types (%s)", Argument::argsTypesToChars(tf->parameters, tf->varargs), buf.toChars()); return m.anyf; // as long as it's not a FuncAliasDeclaration } else { #if 1 TypeFunction *t1 = (TypeFunction *)m.lastf->type; TypeFunction *t2 = (TypeFunction *)m.nextf->type; error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s%s\nand:\n\t%s%s", buf.toChars(), m.lastf->toPrettyChars(), Argument::argsTypesToChars(t1->parameters, t1->varargs), m.nextf->toPrettyChars(), Argument::argsTypesToChars(t2->parameters, t2->varargs)); #else error(loc, "overloads %s and %s both match argument list for %s", m.lastf->type->toChars(), m.nextf->type->toChars(), m.lastf->toChars()); #endif return m.lastf; } } } /************************************* * 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' */ MATCH FuncDeclaration::leastAsSpecialized(FuncDeclaration *g) { #define LOG_LEASTAS 0 #if LOG_LEASTAS printf("%s.leastAsSpecialized(%s)\n", toChars(), g->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 = Argument::dim(tf->parameters); size_t ngparams = Argument::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()) { if (tf->mod != tg->mod) { if (tg->mod == MODconst) match = MATCHconst; else return MATCHnomatch; } } /* Create a dummy array of arguments out of the parameters to f() */ Expressions args; args.setDim(nfparams); for (int u = 0; u < nfparams; u++) { Argument *p = Argument::getNth(tf->parameters, u); Expression *e; if (p->storageClass & (STCref | STCout)) { e = new IdentifierExp(0, p->ident); e->type = p->type; } else e = p->type->defaultInit(); args.data[u] = e; } MATCH m = (MATCH) tg->callMatch(NULL, &args); 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; } /******************************** * 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) { //printf("test4\n"); break; } if (!s->parent || (!s->parent->isTemplateInstance())) { //printf("test5\n"); 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, 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'\n", s->toChars()); FuncDeclaration *thisfd = s->isFuncDeclaration(); if (thisfd) { if (!thisfd->isNested() && !thisfd->vthis) goto Lerr; } else { ClassDeclaration *thiscd = s->isClassDeclaration(); if (thiscd) { if (!thiscd->isNested()) goto Lerr; } else goto Lerr; } s = s->toParent2(); assert(s); level++; } return level; Lerr: error(loc, "cannot access frame of function %s", fd->toChars()); return 1; } void FuncDeclaration::appendExp(Expression *e) { Statement *s; s = new ExpStatement(0, e); appendState(s); } void FuncDeclaration::appendState(Statement *s) { CompoundStatement *cs; if (!fbody) { Statements *a; a = new Statements(); fbody = new CompoundStatement(0, a); } cs = fbody->isCompoundStatement(); cs->statements->push(s); } 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 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) && toParent()->isClassDeclaration()); #endif return isMember() && !(isStatic() || protection == PROTprivate || protection == PROTpackage) && toParent()->isClassDeclaration(); } int FuncDeclaration::isFinal() { ClassDeclaration *cd; #if 0 printf("FuncDeclaration::isFinal(%s)\n", toChars()); printf("%p %d %d %d %d\n", isMember(), isStatic(), protection == PROTprivate, isCtorDeclaration(), linkage != LINKd); printf("result is %d\n", isMember() && !(isStatic() || protection == PROTprivate || protection == PROTpackage) && (cd = toParent()->isClassDeclaration()) != NULL && cd->storage_class & STCfinal); #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 } // Determine if function needs // a static frame pointer to its lexically enclosing function int FuncDeclaration::isNested() { //if (!toParent()) //printf("FuncDeclaration::isNested('%s') parent=%p\n", toChars(), parent); //printf("\ttoParent2() = '%s'\n", toParent2()->toChars()); return ((storage_class & STCstatic) == 0) && (toParent2()->isFuncDeclaration() != NULL); } int FuncDeclaration::needThis() { //printf("FuncDeclaration::needThis() '%s'\n", toChars()); int i = isThis() != NULL; //printf("\t%d\n", i); if (!i && isFuncAliasDeclaration()) i = ((FuncAliasDeclaration *)this)->funcalias->needThis(); return i; } int FuncDeclaration::addPreInvariant() { AggregateDeclaration *ad = isThis(); return (ad && //ad->isClassDeclaration() && global.params.useInvariants && (protection == PROTpublic || protection == PROTexport) && !naked && ident != Id::cpctor); } int FuncDeclaration::addPostInvariant() { AggregateDeclaration *ad = isThis(); return (ad && ad->inv && //ad->isClassDeclaration() && global.params.useInvariants && (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(Arguments *args, Type *treturn, const char *name) { return genCfunc(args, treturn, Lexer::idPool(name)); } FuncDeclaration *FuncDeclaration::genCfunc(Arguments *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 { tf = new TypeFunction(args, treturn, 0, LINKc); fd = new FuncDeclaration(0, 0, id, STCstatic, tf); fd->protection = PROTpublic; fd->linkage = LINKc; st->insert(fd); } return fd; } const char *FuncDeclaration::kind() { return "function"; } /******************************* * 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 any function that: * 1) is a virtual function * 2) has its address taken * 3) has a parent that escapes * * 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()); for (int i = 0; i < closureVars.dim; i++) { VarDeclaration *v = (VarDeclaration *)closureVars.data[i]; assert(v->isVarDeclaration()); //printf("\tv = %s\n", v->toChars()); for (int j = 0; j < v->nestedrefs.dim; j++) { FuncDeclaration *f = (FuncDeclaration *)v->nestedrefs.data[j]; assert(f != this); //printf("\t\tf = %s, %d, %d\n", f->toChars(), f->isVirtual(), f->tookAddressOf); if (f->isThis() || f->tookAddressOf) goto Lyes; // assume f escapes this function's scope // Look to see if any parents of f that are below this escape for (Dsymbol *s = f->parent; s && s != this; s = s->parent) { f = s->isFuncDeclaration(); if (f && (f->isThis() || f->tookAddressOf)) goto Lyes; } } } return 0; Lyes: //printf("\tneeds closure\n"); return 1; } #endif /****************************** FuncAliasDeclaration ************************/ // Used as a way to import a set of functions from another scope into this one. FuncAliasDeclaration::FuncAliasDeclaration(FuncDeclaration *funcalias) : FuncDeclaration(funcalias->loc, funcalias->endloc, funcalias->ident, (enum STC)funcalias->storage_class, funcalias->type) { assert(funcalias != this); this->funcalias = funcalias; } const char *FuncAliasDeclaration::kind() { return "function alias"; } /****************************** 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 == TOKdelegate) id = "__dgliteral"; else id = "__funcliteral"; this->ident = Identifier::generateId(id); this->tok = tok; this->fes = fes; //printf("FuncLiteralDeclaration() id = '%s', type = '%s'\n", this->ident->toChars(), type->toChars()); } 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); FuncDeclaration::syntaxCopy(f); return f; } int FuncLiteralDeclaration::isNested() { //printf("FuncLiteralDeclaration::isNested() '%s'\n", toChars()); return (tok == TOKdelegate); } int FuncLiteralDeclaration::isVirtual() { return FALSE; } const char *FuncLiteralDeclaration::kind() { // GCC requires the (char*) casts return (tok == TOKdelegate) ? (char*)"delegate" : (char*)"function"; } void FuncLiteralDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { static Identifier *idfunc; static Identifier *iddel; if (!idfunc) idfunc = new Identifier("function", 0); if (!iddel) iddel = new Identifier("delegate", 0); type->toCBuffer(buf, ((tok == TOKdelegate) ? iddel : idfunc), hgs); bodyToCBuffer(buf, hgs); } /********************************* CtorDeclaration ****************************/ CtorDeclaration::CtorDeclaration(Loc loc, Loc endloc, Arguments *arguments, int varargs) : FuncDeclaration(loc, endloc, Id::ctor, STCundefined, NULL) { this->arguments = arguments; this->varargs = varargs; //printf("CtorDeclaration(loc = %s) %s\n", loc.toChars(), toChars()); } Dsymbol *CtorDeclaration::syntaxCopy(Dsymbol *s) { CtorDeclaration *f; f = new CtorDeclaration(loc, endloc, NULL, varargs); f->outId = outId; f->frequire = frequire ? frequire->syntaxCopy() : NULL; f->fensure = fensure ? fensure->syntaxCopy() : NULL; f->fbody = fbody ? fbody->syntaxCopy() : NULL; assert(!fthrows); // deprecated f->arguments = Argument::arraySyntaxCopy(arguments); return f; } void CtorDeclaration::semantic(Scope *sc) { AggregateDeclaration *ad; Type *tret; //printf("CtorDeclaration::semantic()\n"); if (type) return; sc = sc->push(); sc->stc &= ~STCstatic; // not a static constructor parent = sc->parent; Dsymbol *parent = toParent(); ad = parent->isAggregateDeclaration(); if (!ad || parent->isUnionDeclaration()) { error("constructors are only for class or struct definitions"); fatal(); tret = Type::tvoid; } else { tret = ad->handle; assert(tret); } type = new TypeFunction(arguments, tret, varargs, LINKd); #if STRUCTTHISREF if (ad && ad->isStructDeclaration()) ((TypeFunction *)type)->isref = 1; #endif if (!originalType) originalType = type; sc->flags |= SCOPEctor; type = type->semantic(loc, sc); sc->flags &= ~SCOPEctor; // Append: // return this; // to the function body if (fbody) { Expression *e = new ThisExp(0); Statement *s = new ReturnStatement(0, e); fbody = new CompoundStatement(0, fbody, s); } FuncDeclaration::semantic(sc); sc->pop(); // See if it's the default constructor if (ad && varargs == 0 && Argument::dim(arguments) == 0) { if (ad->isStructDeclaration()) error("default constructor not allowed for structs"); 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 (vthis && global.params.useInvariants); } void CtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { buf->writestring("this"); Argument::argsToCBuffer(buf, hgs, arguments, varargs); bodyToCBuffer(buf, hgs); } /********************************* PostBlitDeclaration ****************************/ PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Id::_postblit, STCundefined, NULL) { } PostBlitDeclaration::PostBlitDeclaration(Loc loc, Loc endloc, Identifier *id) : FuncDeclaration(loc, endloc, id, STCundefined, NULL) { } Dsymbol *PostBlitDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); PostBlitDeclaration *dd = new PostBlitDeclaration(loc, endloc, 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); 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) ad->postblits.push(this); type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); 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 (vthis && global.params.useInvariants); } int PostBlitDeclaration::isVirtual() { return FALSE; } void PostBlitDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen) return; buf->writestring("=this()"); bodyToCBuffer(buf, hgs); } /********************************* DtorDeclaration ****************************/ DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Id::dtor, STCundefined, NULL) { } DtorDeclaration::DtorDeclaration(Loc loc, Loc endloc, Identifier *id) : FuncDeclaration(loc, endloc, id, STCundefined, NULL) { } Dsymbol *DtorDeclaration::syntaxCopy(Dsymbol *s) { assert(!s); DtorDeclaration *dd = new DtorDeclaration(loc, endloc, 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); 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) ad->dtors.push(this); type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); 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 (vthis && global.params.useInvariants); } int DtorDeclaration::addPostInvariant() { return FALSE; } int DtorDeclaration::isVirtual() { /* This should be FALSE so that dtor's don't get put into the vtbl[], * but doing so will require recompiling everything. */ #if BREAKABI return FALSE; #else return FuncDeclaration::isVirtual(); #endif } void DtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen) return; buf->writestring("~this()"); bodyToCBuffer(buf, hgs); } /********************************* StaticCtorDeclaration ****************************/ StaticCtorDeclaration::StaticCtorDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Identifier::generateId("_staticCtor"), STCstatic, NULL) { } Dsymbol *StaticCtorDeclaration::syntaxCopy(Dsymbol *s) { StaticCtorDeclaration *scd; assert(!s); scd = new StaticCtorDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(scd); } void StaticCtorDeclaration::semantic(Scope *sc) { //printf("StaticCtorDeclaration::semantic()\n"); 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()) { /* 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(0, Type::tint32, id, NULL); v->storage_class = STCstatic; Statements *sa = new Statements(); Statement *s = new DeclarationStatement(0, v); sa->push(s); Expression *e = new IdentifierExp(0, id); e = new AddAssignExp(0, e, new IntegerExp(1)); e = new EqualExp(TOKnotequal, 0, e, new IntegerExp(1)); s = new IfStatement(0, NULL, e, new ReturnStatement(0, NULL), NULL); sa->push(s); if (fbody) sa->push(fbody); fbody = new CompoundStatement(0, sa); } FuncDeclaration::semantic(sc); // We're going to need ModuleInfo Module *m = getModule(); if (!m) m = sc->module; if (m) { m->needmoduleinfo = 1; #ifdef IN_GCC m->strictlyneedmoduleinfo = 1; #endif } } AggregateDeclaration *StaticCtorDeclaration::isThis() { return NULL; } int StaticCtorDeclaration::isStaticConstructor() { return TRUE; } int StaticCtorDeclaration::isVirtual() { return FALSE; } int StaticCtorDeclaration::addPreInvariant() { return FALSE; } int StaticCtorDeclaration::addPostInvariant() { return FALSE; } void StaticCtorDeclaration::toCBuffer(OutBuffer *buf, HdrGenState *hgs) { if (hgs->hdrgen) { buf->writestring("static this();\n"); return; } buf->writestring("static this()"); bodyToCBuffer(buf, hgs); } /********************************* StaticDtorDeclaration ****************************/ StaticDtorDeclaration::StaticDtorDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Identifier::generateId("_staticDtor"), STCstatic, NULL) { vgate = NULL; } Dsymbol *StaticDtorDeclaration::syntaxCopy(Dsymbol *s) { StaticDtorDeclaration *sdd; assert(!s); sdd = new StaticDtorDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(sdd); } void StaticDtorDeclaration::semantic(Scope *sc) { ClassDeclaration *cd; Type *tret; cd = sc->scopesym->isClassDeclaration(); if (!cd) { } 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()) { /* 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(0, Type::tint32, id, NULL); v->storage_class = STCstatic; Statements *sa = new Statements(); Statement *s = new DeclarationStatement(0, v); sa->push(s); Expression *e = new IdentifierExp(0, id); e = new AddAssignExp(0, e, new IntegerExp((uint64_t)-1)); e = new EqualExp(TOKnotequal, 0, e, new IntegerExp(0)); s = new IfStatement(0, NULL, e, new ReturnStatement(0, NULL), NULL); sa->push(s); if (fbody) sa->push(fbody); fbody = new CompoundStatement(0, 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; #ifdef IN_GCC m->strictlyneedmoduleinfo = 1; #endif } } AggregateDeclaration *StaticDtorDeclaration::isThis() { return NULL; } int StaticDtorDeclaration::isStaticDestructor() { return TRUE; } int StaticDtorDeclaration::isVirtual() { return FALSE; } 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); } /********************************* InvariantDeclaration ****************************/ InvariantDeclaration::InvariantDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, Id::classInvariant, STCundefined, NULL) { } Dsymbol *InvariantDeclaration::syntaxCopy(Dsymbol *s) { InvariantDeclaration *id; assert(!s); id = new InvariantDeclaration(loc, endloc); FuncDeclaration::syntaxCopy(id); return id; } void InvariantDeclaration::semantic(Scope *sc) { AggregateDeclaration *ad; Type *tret; parent = sc->parent; Dsymbol *parent = toParent(); ad = parent->isAggregateDeclaration(); if (!ad) { error("invariants only are for struct/union/class definitions"); return; } else if (ad->inv && ad->inv != this) { error("more than one invariant for %s", ad->toChars()); } ad->inv = this; type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); sc = sc->push(); sc->stc &= ~STCstatic; // not a static invariant sc->incontract++; 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() { return Lexer::uniqueId("__unittest"); } UnitTestDeclaration::UnitTestDeclaration(Loc loc, Loc endloc) : FuncDeclaration(loc, endloc, unitTestId(), STCundefined, NULL) { } Dsymbol *UnitTestDeclaration::syntaxCopy(Dsymbol *s) { UnitTestDeclaration *utd; assert(!s); utd = new UnitTestDeclaration(loc, endloc); return FuncDeclaration::syntaxCopy(utd); } void UnitTestDeclaration::semantic(Scope *sc) { if (global.params.useUnitTests) { type = new TypeFunction(NULL, Type::tvoid, FALSE, LINKd); Scope *sc2 = sc->push(); sc2->linkage = LINKd; FuncDeclaration::semantic(sc2); sc2->pop(); } // 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. Module *m = getModule(); if (!m) m = sc->module; if (m) m->needmoduleinfo = 1; } 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, Arguments *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 = Argument::arraySyntaxCopy(arguments); return f; } void NewDeclaration::semantic(Scope *sc) { ClassDeclaration *cd; Type *tret; //printf("NewDeclaration::semantic()\n"); parent = sc->parent; Dsymbol *parent = toParent(); cd = parent->isClassDeclaration(); if (!cd && !parent->isStructDeclaration()) { error("new allocators only are for class or struct definitions"); } tret = Type::tvoid->pointerTo(); 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 uint TypeFunction *tf = (TypeFunction *)type; if (Argument::dim(tf->parameters) < 1) { error("at least one argument of type size_t expected"); } else { Argument *a = Argument::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"); Argument::argsToCBuffer(buf, hgs, arguments, varargs); bodyToCBuffer(buf, hgs); } /********************************* DeleteDeclaration ****************************/ DeleteDeclaration::DeleteDeclaration(Loc loc, Loc endloc, Arguments *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 = Argument::arraySyntaxCopy(arguments); return f; } void DeleteDeclaration::semantic(Scope *sc) { ClassDeclaration *cd; //printf("DeleteDeclaration::semantic()\n"); parent = sc->parent; Dsymbol *parent = toParent(); cd = parent->isClassDeclaration(); if (!cd && !parent->isStructDeclaration()) { error("new allocators only are for class or struct definitions"); } 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 (Argument::dim(tf->parameters) != 1) { error("one argument of type void* expected"); } else { Argument *a = Argument::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"); Argument::argsToCBuffer(buf, hgs, arguments, 0); bodyToCBuffer(buf, hgs); }