Mercurial > projects > ddmd
diff dmd/FuncDeclaration.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children | 2cc604139636 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmd/FuncDeclaration.d Sat Oct 24 08:42:06 2009 +0400 @@ -0,0 +1,3248 @@ +module dmd.FuncDeclaration; + +import dmd.Declaration; +import dmd.DotIdExp; +import dmd.TryFinallyStatement; +import dmd.StaticDtorDeclaration; +import dmd.PeelStatement; +import dmd.SynchronizedStatement; +import dmd.TOK; +import dmd.SymOffExp; +import dmd.AssignExp; +import dmd.ExpInitializer; +import dmd.BE; +import dmd.Id; +import dmd.StringExp; +import dmd.DsymbolExp; +import dmd.HaltExp; +import dmd.CommaExp; +import dmd.ReturnStatement; +import dmd.IntegerExp; +import dmd.ExpStatement; +import dmd.CSX; +import dmd.CompoundStatement; +import dmd.LabelStatement; +import dmd.ThisExp; +import dmd.SuperExp; +import dmd.IdentifierExp; +import dmd.AssertExp; +import dmd.CallExp; +import dmd.RET; +import dmd.VarExp; +import dmd.TupleDeclaration; +import dmd.ThisDeclaration; +import dmd.TypeTuple; +import dmd.TemplateInstance; +import dmd.ScopeDsymbol; +import dmd.AliasDeclaration; +import dmd.MOD; +import dmd.PROT; +import dmd.Lexer; +import dmd.LINK; +import dmd.CtorDeclaration; +import dmd.Global; +import dmd.DtorDeclaration; +import dmd.InvariantDeclaration; +import dmd.TY; +import dmd.PtrExp; +import dmd.DeclarationExp; +import dmd.InlineDoState; +import dmd.Argument; +import dmd.StructDeclaration; +import dmd.ClassDeclaration; +import dmd.InterfaceDeclaration; +import dmd.Array; +import dmd.Statement; +import dmd.Identifier; +import dmd.VarDeclaration; +import dmd.LabelDsymbol; +import dmd.DsymbolTable; +import dmd.ArrayTypes; +import dmd.Loc; +import dmd.ILS; +import dmd.ForeachStatement; +import dmd.Type; +import dmd.BUILTIN; +import dmd.TypeFunction; +import dmd.Expression; +import dmd.STC; +import dmd.Dsymbol; +import dmd.Scope; +import dmd.OutBuffer; +import dmd.HdrGenState; +import dmd.MATCH; +import dmd.AggregateDeclaration; +import dmd.InterState; +import dmd.InlineScanState; +import dmd.IRState; +import dmd.Util; +import dmd.BaseClass; +import dmd.Module; +import dmd.ILS; +import dmd.InlineCostState; + +import dmd.expression.Util; + +import dmd.declaration.Match; + +import dmd.backend.Symbol; +import dmd.backend.func_t; +import dmd.backend.Util; +import dmd.backend.glue; +import dmd.backend.SC; +import dmd.backend.F; +import dmd.backend.Cstate; +import dmd.backend.TYM; +import dmd.backend.OPER; +import dmd.backend.TYFL; +import dmd.backend.TYPE; +import dmd.backend.SFL; +import dmd.backend.mTY; +import dmd.backend.FL; +import dmd.backend.REG; +import dmd.backend.block; +import dmd.backend.Blockx; +import dmd.backend.Config; +import dmd.backend.BC; +import dmd.backend.elem; +import dmd.backend.targ_types; +import dmd.backend.mTYman; +import dmd.backend.RTLSYM; +import dmd.backend.LIST; + +import core.stdc.stdio; +import core.stdc.string; + +import std.string; + +class FuncDeclaration : Declaration +{ + Array fthrows; // Array of Type's of exceptions (not used) + Statement frequire; + Statement fensure; + Statement fbody; + + Identifier outId; // identifier for out statement + VarDeclaration vresult; // variable corresponding to outId + LabelDsymbol returnLabel; // where the return goes + + DsymbolTable localsymtab; // used to prevent symbols in different + // scopes from having the same name + VarDeclaration vthis; // 'this' parameter (member and nested) + VarDeclaration v_arguments; // '_arguments' parameter +version (IN_GCC) { + VarDeclaration v_argptr; // '_argptr' variable +} + Dsymbols parameters; // Array of VarDeclaration's for parameters + DsymbolTable labtab; // statement label symbol table + Declaration overnext; // next in overload list + Loc endloc; // location of closing curly bracket + int vtblIndex = -1; // for member functions, index into vtbl[] + int naked; // !=0 if naked + int inlineAsm; // !=0 if has inline assembler + ILS inlineStatus = ILS.ILSuninitialized; + int inlineNest; // !=0 if nested inline + int cantInterpret; // !=0 if cannot interpret function + int semanticRun; // 1 semantic() run + // 2 semantic2() run + // 3 semantic3() started + // 4 semantic3() done + // 5 toObjFile() run + // this function's frame ptr + ForeachStatement fes; // if foreach body, this is the foreach + int introducing; // !=0 if 'introducing' function + Type tintro; // if !=null, then this is the type + // of the 'introducing' function + // this one is overriding + int inferRetType; // !=0 if return type is to be inferred + + // Things that should really go into Scope + int hasReturnExp; // 1 if there's a return exp; statement + // 2 if there's a throw statement + // 4 if there's an assert(0) + // 8 if there's inline asm + + // Support for NRVO (named return value optimization) + bool nrvo_can = true; // !=0 means we can do it + VarDeclaration nrvo_var; // variable to replace with shidden + Symbol* shidden; // hidden pointer passed to function + +version (DMDV2) { + BUILTIN builtin; // set if this is a known, builtin + // function we can evaluate at compile + // time + + int tookAddressOf; // set if someone took the address of + // this function + Dsymbols closureVars; // local variables in this function + // which are referenced by nested + // functions +} else { + int nestedFrameRef; // !=0 if nested variables referenced +} + + this(Loc loc, Loc endloc, Identifier id, STC storage_class, Type type) + { + super(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; + this.loc = loc; + this.endloc = endloc; + + /* The type given for "infer the return type" is a TypeFunction with + * null for the return type. + */ + inferRetType = (type && type.nextOf() is null); + + closureVars = new Dsymbols(); + +version (DMDV2) { + builtin = BUILTIN.BUILTINunknown; +} + } + + Dsymbol syntaxCopy(Dsymbol s) + { + FuncDeclaration f; + + //printf("FuncDeclaration::syntaxCopy('%s')\n", toChars()); + if (s) + f = cast(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 + + return f; + } + + // Do the semantic analysis on the external interface to the function. + void semantic(Scope sc) + { + TypeFunction f; + StructDeclaration sd; + ClassDeclaration cd; + InterfaceDeclaration id; + Dsymbol pd; + +static if (false) { + 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()); +} + + if (semanticRun && 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; + } + assert(semanticRun <= 1); + semanticRun = 1; + + storage_class |= sc.stc & ~STC.STCref; + //printf("function storage_class = x%x\n", storage_class); + + if (!originalType) + originalType = type; + if (!type.deco) + { + /* Apply const and invariant storage class + * to the function type + */ + type = type.semantic(loc, sc); + STC stc = storage_class; + if (type.isInvariant()) + stc |= STC.STCimmutable; + if (type.isConst()) + stc |= STC.STCconst; + if (type.isShared() || storage_class & STC.STCsynchronized) + stc |= STC.STCshared; + switch (stc & STC.STC_TYPECTOR) + { + case STC.STCimmutable: + case STC.STCimmutable | STC.STCconst: + case STC.STCimmutable | STC.STCconst | STC.STCshared: + case STC.STCimmutable | STC.STCshared: + // Don't use toInvariant(), as that will do a merge() + type = type.makeInvariant(); + type.deco = type.merge().deco; + break; + + case STC.STCconst: + type = type.makeConst(); + type.deco = type.merge().deco; + break; + + case STC.STCshared | STC.STCconst: + type = type.makeSharedConst(); + type.deco = type.merge().deco; + break; + + case STC.STCshared: + type = type.makeShared(); + type.deco = type.merge().deco; + break; + + case STC.STCundefined: + break; + + default: + assert(0); + } + } + //type.print(); + if (type.ty != TY.Tfunction) + { + error("%s must be a function", toChars()); + return; + } + f = cast(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 & STC.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/immutable"); + + if (isAbstract() && isFinal()) + error("cannot be both final and abstract"); +static if (false) { + if (isAbstract() && fbody) + error("abstract functions cannot have bodies"); +} + +static if (false) { + 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 + } +} + +version (IN_GCC) { + AggregateDeclaration ad; + + ad = parent.isAggregateDeclaration(); + if (ad) + ad.methods.push(cast(void*)this); +} + sd = parent.isStructDeclaration(); + if (sd) + { + if (isCtorDeclaration()) + { + return; + } +static if (false) { + // 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 = cast(DeleteDeclaration)this; + } +} + } + + id = parent.isInterfaceDeclaration(); + if (id) + { + storage_class |= STC.STCabstract; + + if (isCtorDeclaration() || +///static if (DMDV2) { + 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()) !is null && + pd.isTemplateInstance() && + (pd = toParent2()) !is null && + (id = pd.isInterfaceDeclaration()) !is 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 = cast(CtorDeclaration)this; + // if (!cd.ctor) + // cd.ctor = ctor; + return; + } + +static if (false) { + 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 = cast(NewDeclaration)this; + } + + if (isDelete()) + { + if (cd.aggDelete) + error("multiple delete's for class %s", cd.toChars()); + cd.aggDelete = cast(DeleteDeclaration)this; + } +} + + if (storage_class & STC.STCabstract) + cd.isabstract = true; + + // 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 ff = s.isFuncDeclaration(); + ff = ff.overloadExactMatch(type); + if (ff && ff.isFinal() && ff.prot() != PROT.PROTprivate) + error("cannot override final function %s", ff.toPrettyChars()); + } + } + + if (isFinal()) + { + if (isOverride()) + error("does not override any function"); + cd.vtblFinal.push(cast(void*)this); + } + else + { + // Append to end of vtbl[] + //printf("\tintroducing function\n"); + introducing = 1; + vi = cd.vtbl.dim; + cd.vtbl.push(cast(void*)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 = cast(FuncDeclaration)cd.vtbl.data[vi]; + // This function is covariant with fdv + if (fdv.isFinal()) + error("cannot override final function %s", fdv.toPrettyChars()); + +version (DMDV2) { + if (!isOverride()) + warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv.toPrettyChars()); +} + + 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() +///static if (!BREAKABI) { + && !isDtorDeclaration() +///} +///version (DMDV2) { + && !isPostBlitDeclaration() +///} + ) + error("multiple overrides of same function"); + } + cd.vtbl.data[vi] = cast(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 = cast(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; +static if (false) { + if (offset) + ti = fdv.type; + else if (type.nextOf().ty == Tclass) + { + ClassDeclaration cdn = (cast(TypeClass)type.nextOf()).sym; + if (cdn && cdn.sizeok != 1) + ti = fdv.type; + } +} + } + } + 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 cdd = ti.tempdecl.isClassMember(); + if (cdd) + { + error("cannot use template to add virtual function to class '%s'", cdd.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 != TY.Tarray || + arg0.type.nextOf().ty != TY.Tarray || + arg0.type.nextOf().nextOf().ty != TY.Tchar || + arg0.storageClass & (STC.STCout | STC.STCref | STC.STClazy)) + goto Lmainerr; + break; + } + + default: + goto Lmainerr; + } + + if (f.nextOf().ty != TY.Tint32 && f.nextOf().ty != 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 == 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 semantic2(Scope sc) + { + } + + // Do the semantic analysis on the internals of the function. + void semantic3(Scope sc) + { + TypeFunction f; + 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); + //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.incontract); + if (semanticRun >= 3) + return; + semanticRun = 3; + + if (!type || type.ty != TY.Tfunction) + return; + f = cast(TypeFunction)(type); + + // Check the 'throws' clause + if (fthrows) + { + for (int i = 0; i < fthrows.dim; i++) + { + Type t = cast(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 = LINK.LINKd; + sc2.stc &= ~(STC.STCauto | STC.STCscope | STC.STCstatic | STC.STCabstract | STC.STCdeprecated | STC.STC_TYPECTOR | STC.STCfinal | STC.STCtls | STC.STCgshared | STC.STCref); + sc2.protection = PROT.PROTpublic; + sc2.explicitProtection = 0; + sc2.structalign = 8; + sc2.incontract = 0; + sc2.tf = null; + sc2.noctor = 0; + + // Declare 'this' + AggregateDeclaration 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; +version (STRUCTTHISREF) { + thandle = thandle.addMod(type.mod); + thandle = thandle.addStorageClass(storage_class); + if (isPure()) + thandle = thandle.addMod(MOD.MODconst); +} else { + if (storage_class & STC.STCconst || type.isConst()) + { + assert(0); // BUG: shared not handled + if (thandle.ty == TY.Tclass) + thandle = thandle.constOf(); + else + { assert(thandle.ty == TY.Tpointer); + thandle = thandle.nextOf().constOf().pointerTo(); + } + } + else if (storage_class & STC.STCimmutable || type.isInvariant()) + { + if (thandle.ty == TY.Tclass) + thandle = thandle.invariantOf(); + else + { assert(thandle.ty == TY.Tpointer); + thandle = thandle.nextOf().invariantOf().pointerTo(); + } + } + else if (storage_class & STC.STCshared || type.isShared()) + { + assert(0); // not implemented + } +} + v = new ThisDeclaration(loc, thandle); + v.storage_class |= STC.STCparameter; +version (STRUCTTHISREF) { + if (thandle.ty == TY.Tstruct) + v.storage_class |= STC.STCref; +} + v.semantic(sc2); + if (!sc2.insert(v)) + assert(0); + v.parent = this; + vthis = v; + } + } + else if (isNested()) + { + /* The 'this' for a nested function is the link to the + * enclosing function's stack frame. + * Note that nested functions and member functions are disjoint. + */ + VarDeclaration v = new ThisDeclaration(loc, Type.tvoid.pointerTo()); + v.storage_class |= STC.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) + { +version (TARGET_NET) { + varArgs(sc2, f, argptr, _arguments); +} else { + Type t; + + if (f.linkage == LINK.LINKd) + { + // Declare _arguments[] +version (BREAKABI) { + v_arguments = new VarDeclaration(Loc(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(Loc(0), t, Id._arguments, null); + _arguments.semantic(sc2); + sc2.insert(_arguments); + _arguments.parent = this; +} else { + t = Type.typeinfo.type.arrayOf(); + v_arguments = new VarDeclaration(Loc(0), t, Id._arguments, null); + v_arguments.storage_class = STC.STCparameter | STC.STCin; + v_arguments.semantic(sc2); + sc2.insert(v_arguments); + v_arguments.parent = this; + } + } + if (f.linkage == LINK.LINKd || (parameters && parameters.dim)) + { // Declare _argptr +version (IN_GCC) { + t = d_gcc_builtin_va_list_d_type; +} else { + t = Type.tvoid.pointerTo(); +} + argptr = new VarDeclaration(Loc(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 = cast(Argument)f.parameters.data[i]; + + //printf("[%d] arg.type.ty = %d %s\n", i, arg.type.ty, arg.type.toChars()); + if (arg.type.ty == TY.Ttuple) + { TypeTuple t = cast(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); + } + Type vtype = arg.type; + if (isPure()) + vtype = vtype.addMod(MOD.MODconst); + VarDeclaration v = new VarDeclaration(loc, vtype, id, null); + //printf("declaring parameter %s of type %s\n", v.toChars(), v.type.toChars()); + v.storage_class |= STC.STCparameter; + if (f.varargs == 2 && i + 1 == nparams) + v.storage_class |= STC.STCvariadic; + v.storage_class |= arg.storageClass & (STC.STCin | STC.STCout | STC.STCref | STC.STClazy | STC.STCfinal | STC.STC_TYPECTOR | STC.STCnodtor); + v.semantic(sc2); + if (!sc2.insert(v)) + error("parameter %s.%s is already defined", toChars(), v.toChars()); + else + parameters.push(cast(void*)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 = cast(Argument)f.parameters.data[i]; + + if (!arg.ident) + continue; // never used, so ignore + if (arg.type.ty == TY.Ttuple) + { TypeTuple t = cast(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(Loc(0), narg.ident, null).isVarDeclaration(); + assert(v); + Expression e = new VarExp(v.loc, v); + exps.data[j] = cast(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 == 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 = true; +version (DMDV2) { + if (f.isref) + { + v.storage_class |= STC.STCref | STC.STCforeach; + } +} + 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(Loc(0), inv); + e = new CallExp(Loc(0), e); + e = e.semantic(sc2); + } + } + else + { // Call invariant virtually + Expression v = new ThisExp(Loc(0)); + v.type = vthis.type; +version (STRUCTTHISREF) { + if (ad.isStructDeclaration()) + v = v.addressOf(sc); +} + e = new AssertExp(Loc(0), v); + } + if (e) + { + ExpStatement s = new ExpStatement(Loc(0), e); + if (fensure) + fensure = new CompoundStatement(Loc(0), s, fensure); + else + fensure = s; + } + } + + if (fensure) + { returnLabel = new LabelDsymbol(Id.returnLabel); + LabelStatement ls = new LabelStatement(Loc(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 = cast(VarDeclaration)cd.fields.data[i]; + + v.ctorinit = 0; + } + } + + if (inferRetType || f.retStyle() != RET.RETstack) + nrvo_can = 0; + + fbody = fbody.semantic(sc2); + if (!fbody) + fbody = new CompoundStatement(Loc(0), new Statements()); + + if (inferRetType) + { // If no return type inferred yet, then infer a void + if (!type.nextOf()) + { + (cast(TypeFunction)type).next = Type.tvoid; + type = type.semantic(loc, sc); + } + f = cast(TypeFunction)type; + } + + if (isStaticCtorDeclaration()) + { + /* It's a static constructor. Ensure that all + * ctor consts were initialized. + */ + + Dsymbol p = toParent(); + ScopeDsymbol add = p.isScopeDsymbol(); + if (!add) + { + error("static constructor can only be member of struct/class/module, not %s %s", p.kind(), p.toChars()); + } + else + { + for (int i = 0; i < add.members.dim; i++) + { Dsymbol s = cast(Dsymbol)add.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 & CSX.CSXthis_ctor)) + { + for (int i = 0; i < cd.fields.dim; i++) + { VarDeclaration v = cast(VarDeclaration)cd.fields.data[i]; + + if (v.ctorinit == 0 && v.isCtorinit()) + error("missing initializer for final field %s", v.toChars()); + } + } + + if (!(sc2.callSuper & CSX.CSXany_ctor) && + cd.baseClass && cd.baseClass.ctor) + { + sc2.callSuper = 0; + + // Insert implicit super() at start of fbody + Expression e1 = new SuperExp(Loc(0)); + Expression e = new CallExp(Loc(0), e1); + + e = e.trySemantic(sc2); + if (!e) + error("no match for implicit super() call in constructor"); + else + { + Statement s = new ExpStatement(Loc(0), e); + fbody = new CompoundStatement(Loc(0), s, fbody); + } + } + } + else if (fes) + { // For foreach(){} body, append a return 0; + Expression e = new IntegerExp(0); + Statement s = new ReturnStatement(Loc(0), e); + fbody = new CompoundStatement(Loc(0), fbody, s); + assert(!returnLabel); + } + else if (!hasReturnExp && type.nextOf().ty != TY.Tvoid) + error("expected to return a value of type %s", type.nextOf().toChars()); + else if (!inlineAsm) + { +version (DMDV2) { + BE blockexit = fbody ? fbody.blockExit() : BE.BEfallthru; + if (f.isnothrow && blockexit & BE.BEthrow) + error("'%s' is nothrow yet may throw", toChars()); + + int offend = blockexit & BE.BEfallthru; +} + if (type.nextOf().ty == TY.Tvoid) + { + if (offend && isMain()) + { // Add a return 0; statement + Statement s = new ReturnStatement(Loc(0), new IntegerExp(0)); + fbody = new CompoundStatement(Loc(0), fbody, s); + } + } + else + { + if (offend) + { + Expression e; +version (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"); +} + 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, "missing return expression") + ); + } + else + e = new HaltExp(endloc); + + e = new CommaExp(Loc(0), e, type.nextOf().defaultInit(Loc(0))); + e = e.semantic(sc2); + Statement s = new ExpStatement(Loc(0), e); + fbody = new CompoundStatement(Loc(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 = cast(VarDeclaration)parameters.data[i]; + if (v.storage_class & STC.STCout) + { + assert(v.init); + ExpInitializer ie = v.init.isExpInitializer(); + assert(ie); + a.push(cast(void*)new ExpStatement(Loc(0), ie.exp)); + } + } + } + + if (argptr) + { // Initialize _argptr to point past non-variadic arg +version (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; + uint offset; + + e1 = new VarExp(Loc(0), argptr); + if (parameters && parameters.dim) + p = cast(VarDeclaration)parameters.data[parameters.dim - 1]; + else + p = v_arguments; // last parameter is _arguments[] + offset = cast(uint)p.type.size(); /// + offset = (offset + 3) & ~3; // assume stack aligns on 4 + e = new SymOffExp(Loc(0), p, offset); + e = new AssignExp(Loc(0), e1, e); + e.type = t; + a.push(cast(void*)new ExpStatement(Loc(0), e)); +} + } + + if (_arguments) + { + /* Advance to elements[] member of TypeInfo_Tuple with: + * _arguments = v_arguments.elements; + */ + Expression e = new VarExp(Loc(0), v_arguments); + e = new DotIdExp(Loc(0), e, Id.elements); + Expression e1 = new VarExp(Loc(0), _arguments); + e = new AssignExp(Loc(0), e1, e); + e.op = TOK.TOKconstruct; + e = e.semantic(sc2); + a.push(cast(void*)new ExpStatement(Loc(0), e)); + } + + // Merge contracts together with body into one compound statement + +version (_DH) { + if (frequire && global.params.useIn) + { frequire.incontract = 1; + a.push(frequire); + } +} else { + if (frequire && global.params.useIn) + a.push(cast(void*)frequire); +} + + // 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(Loc(0), inv); + e = new CallExp(Loc(0), e); + e = e.semantic(sc2); + } + } + else + { // Call invariant virtually + Expression v = new ThisExp(Loc(0)); + v.type = vthis.type; +version (STRUCTTHISREF) { + if (ad.isStructDeclaration()) + v = v.addressOf(sc); +} + Expression se = new StringExp(Loc(0), "null this"); + se = se.semantic(sc); + se.type = Type.tchar.arrayOf(); + e = new AssertExp(loc, v, se); + } + if (e) + { + ExpStatement s = new ExpStatement(Loc(0), e); + a.push(cast(void*)s); + } + } + + if (fbody) + a.push(cast(void*)fbody); + + if (fensure) + { + a.push(cast(void*)returnLabel.statement); + + if (type.nextOf().ty != TY.Tvoid) + { + // Create: return vresult; + assert(vresult); + Expression e = new VarExp(Loc(0), vresult); + if (tintro) + { e = e.implicitCastTo(sc, tintro.nextOf()); + e = e.semantic(sc); + } + ReturnStatement s = new ReturnStatement(Loc(0), e); + a.push(cast(void*)s); + } + } + + fbody = new CompoundStatement(Loc(0), a); +version (DMDV2) { + /* Append destructor calls for parameters as finally blocks. + */ + if (parameters) + { for (size_t i = 0; i < parameters.dim; i++) + { + VarDeclaration v = cast(VarDeclaration)parameters.data[i]; + + if (v.storage_class & (STC.STCref | STC.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 == TY.Tsarray) + continue; + + Expression e = v.callAutoDtor(sc); + if (e) + { Statement s = new ExpStatement(Loc(0), e); + s = s.semantic(sc); + if (fbody.blockExit() == BE.BEfallthru) + fbody = new CompoundStatement(Loc(0), fbody, s); + else + fbody = new TryFinallyStatement(Loc(0), fbody, s); + } + } + } +} + +static if (true) { + if (isSynchronized()) + { /* Wrap the entire function body in a synchronized statement + */ + ClassDeclaration cd = parent.isClassDeclaration(); + if (cd) + { +///version (TARGET_WINDOS) { + if (/*config.flags2 & CFG2.CFG2seh &&*/ // always on for WINDOS + !isStatic() && !fbody.usesEH()) + { + /* 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(); + } + semanticRun = 4; + } + + // called from semantic3 + void varArgs(Scope sc, TypeFunction, ref VarDeclaration, ref VarDeclaration) + { + assert(false); + } + + void toCBuffer(OutBuffer buf, HdrGenState* hgs) + { + assert(false); + } + + void bodyToCBuffer(OutBuffer buf, HdrGenState* hgs) + { + assert(false); + } + + /**************************************************** + * Determine if 'this' overrides fd. + * Return true if it does. + */ + bool overrides(FuncDeclaration fd) + { + bool result = false; + + 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 = true; + } + } + 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 findVtblIndex(Array vtbl, int dim) + { + for (int vi = 0; vi < dim; vi++) + { + FuncDeclaration fdv = (cast(Dsymbol)vtbl.data[vi]).isFuncDeclaration(); + if (fdv && fdv.ident is 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 + } + } + } + return -1; + } + + /**************************************************** + * Overload this FuncDeclaration with the new one f. + * Return !=0 if successful; i.e. no conflict. + */ + bool overloadInsert(Dsymbol s) + { + FuncDeclaration f; + AliasDeclaration a; + + //writef("FuncDeclaration.overloadInsert(%s)\n", s.toChars()); + a = s.isAliasDeclaration(); + if (a) + { + if (overnext) + return overnext.overloadInsert(a); + + if (!a.aliassym && a.type.ty != TY.Tident && a.type.ty != TY.Tinstance) + { + //writef("\ta = '%s'\n", a.type.toChars()); + return false; + } + overnext = a; + //printf("\ttrue: no conflict\n"); + return true; + } + f = s.isFuncDeclaration(); + if (!f) + return false; + +static if (false) { + /* 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; + } +} + + if (overnext) + return overnext.overloadInsert(f); + overnext = f; + //printf("\ttrue: no conflict\n"); + return true; + } + + FuncDeclaration overloadExactMatch(Type t) + { + Param1 p; + p.t = t; + p.f = null; + overloadApply(this, &p.fp1); + return p.f; + } + + FuncDeclaration overloadResolve(Loc loc, Expression ethis, Expressions arguments, int flags = 0) + { + TypeFunction tf; + Match m; + +static if (false) { + printf("FuncDeclaration.overloadResolve('%s')\n", toChars()); + if (arguments) + { + int i; + + for (i = 0; i < arguments.dim; i++) + { + Expression arg; + + arg = cast(Expression)arguments.data[i]; + assert(arg.type); + printf("\t%s: ", arg.toChars()); + arg.type.print(); + } + } +} + + m.last = MATCH.MATCHnomatch; + overloadResolveX(&m, this, ethis, arguments); + + if (m.count == 1) // exactly one match + { + return m.lastf; + } + else + { + scope OutBuffer buf = new OutBuffer(); + + buf.writeByte('('); + if (arguments) + { + HdrGenState hgs; + + argExpTypesToCBuffer(buf, arguments, &hgs); + buf.writeByte(')'); + if (ethis) + ethis.type.modToBuffer(buf); + } + else + buf.writeByte(')'); + + if (m.last == MATCH.MATCHnomatch) + { + if (flags & 1) // if do not print error messages + return null; // no match + + tf = cast(TypeFunction)type; + + scope OutBuffer buf2 = new OutBuffer(); + tf.modToBuffer(buf2); + + //printf("tf = %s, args = %s\n", tf.deco, ((Expression *)arguments.data[0]).type.deco); + error(loc, "%s%s is not callable using argument types %s", + Argument.argsTypesToChars(tf.parameters, tf.varargs), + buf2.toChars(), + buf.toChars()); + return m.anyf; // as long as it's not a FuncAliasDeclaration + } + else + { +static if (true) { + TypeFunction t1 = cast(TypeFunction)m.lastf.type; + TypeFunction t2 = cast(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()); +} + 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 leastAsSpecialized(FuncDeclaration g) + { + version (LOG_LEASTAS) { + printf("%s.leastAsSpecialized(%s)\n", toChars(), g.toChars()); + } + + /* 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 = cast(TypeFunction)type; + TypeFunction tg = cast(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() + */ + scope Expressions args = new Expressions(); + 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(Loc(0), p.ident); + e.type = p.type; + } + else + e = p.type.defaultInit(Loc(0)); + + args.data[u] = cast(void*)e; + } + + MATCH m = cast(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 + + version (LOG_LEASTAS) { + printf(" matches %d, so is least as specialized\n", m); + } + return m; + } + L1: + version (LOG_LEASTAS) { + printf(" doesn't match, so is not as specialized\n"); + } + return MATCHnomatch; + } + + /******************************** + * Labels are in a separate scope, one per function. + */ + LabelDsymbol 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 cast(LabelDsymbol)s; + } + + /**************************************** + * If non-static member function that has a 'this' pointer, + * return the aggregate it is a member of. + * Otherwise, return null. + */ + AggregateDeclaration isThis() + { + AggregateDeclaration ad = null; + + //printf("+FuncDeclaration.isThis() '%s'\n", toChars()); + if ((storage_class & STC.STCstatic) == 0) + { + ad = isMember2(); + } + //printf("-FuncDeclaration.isThis() %p\n", ad); + return ad; + } + + AggregateDeclaration isMember2() + { + AggregateDeclaration ad = null; + + //printf("+FuncDeclaration.isMember2() '%s'\n", toChars()); + 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 getLevel(Loc loc, FuncDeclaration fd) // lexical nesting level difference + { + 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 + { + AggregateDeclaration thiscd = s.isAggregateDeclaration(); + 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 appendExp(Expression e) + { + assert(false); + } + + void appendState(Statement s) + { + assert(false); + } + + string mangle() + out (result) + { + assert(result.length > 0); + } + body + { + if (isMain()) { + return "_Dmain"; + } + + if (isWinMain() || isDllMain() || ident == Id.tls_get_addr) + return ident.toChars(); + + assert(this); + + return Declaration.mangle(); + } + + string toPrettyChars() + { + if (isMain()) + return "D main"; + else + return Dsymbol.toPrettyChars(); + } + + int isMain() + { + return ident is Id.main && linkage != LINK.LINKc && !isMember() && !isNested(); + } + + int isWinMain() + { + //printf("FuncDeclaration::isWinMain() %s\n", toChars()); +static if (false) { + int x = ident == Id.WinMain && + linkage != LINK.LINKc && !isMember(); + printf("%s\n", x ? "yes" : "no"); + return x; +} else { + return ident == Id.WinMain && linkage != LINK.LINKc && !isMember(); +} + } + + int isDllMain() + { + return ident == Id.DllMain && linkage != LINK.LINKc && !isMember(); + } + + /********************************** + * Determine if function is a builtin one that we can + * evaluate at compile time. + */ + BUILTIN isBuiltin() + { + static string FeZe = "FNaNbeZe"; // pure nothrow real function(real) + + //printf("FuncDeclaration::isBuiltin() %s\n", toChars()); + if (builtin == BUILTIN.BUILTINunknown) + { + builtin = BUILTIN.BUILTINnot; + if (parent && parent.isModule()) + { + // If it's in the std.math package + if (parent.ident == Id.math && parent.parent && parent.parent.ident == Id.std && !parent.parent.parent) + { + //printf("deco = %s\n", type.deco); + if (type.deco == FeZe) + { + if (ident == Id.sin) + builtin = BUILTIN.BUILTINsin; + else if (ident == Id.cos) + builtin = BUILTIN.BUILTINcos; + else if (ident == Id.tan) + builtin = BUILTIN.BUILTINtan; + else if (ident == Id._sqrt) + builtin = BUILTIN.BUILTINsqrt; + else if (ident == Id.fabs) + builtin = BUILTIN.BUILTINfabs; + //printf("builtin = %d\n", builtin); + } + // if float or double versions + else if (type.deco == "FNaNbdZd" || type.deco == "FNaNbfZf") + { + if (ident == Id._sqrt) + builtin = BUILTIN.BUILTINsqrt; + } + } + } + } + + return builtin; + } + + bool isExport() + { + return protection == PROT.PROTexport; + } + + bool isImportedSymbol() + { + //printf("isImportedSymbol()\n"); + //printf("protection = %d\n", protection); + return (protection == PROT.PROTexport) && !fbody; + } + + bool isAbstract() + { + return (storage_class & STC.STCabstract) != 0; + } + + bool isCodeseg() + { + assert(false); + } + + bool isOverloadable() + { + assert(false); + } + + bool isPure() + { + //printf("FuncDeclaration::isPure() '%s'\n", toChars()); + assert(type.ty == TY.Tfunction); + return (cast(TypeFunction)this.type).ispure; + } + + bool isNested() + { + //if (!toParent()) + //printf("FuncDeclaration.isNested('%s') parent=%p\n", toChars(), parent); + //printf("\ttoParent2() = '%s'\n", toParent2().toChars()); + return ((storage_class & STC.STCstatic) == 0) && + (toParent2().isFuncDeclaration() !is null); + } + + bool needThis() + { + //printf("FuncDeclaration.needThis() '%s'\n", toChars()); + bool needThis = isThis() !is null; + + //printf("\t%d\n", i); + if (!needThis) { + if (auto fa = isFuncAliasDeclaration()) { + needThis = fa.funcalias.needThis(); + } + } + + return needThis; + } + + bool isVirtual() + { +static if (false) { + printf("FuncDeclaration.isVirtual(%s)\n", toChars()); + printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROT.PROTprivate, isCtorDeclaration(), linkage != LINK.LINKd); + printf("result is %d\n", + isMember() && !(isStatic() || protection == PROT.PROTprivate || protection == PROT.PROTpackage) && toParent().isClassDeclaration()); +} + return isMember() && !(isStatic() || protection == PROT.PROTprivate || protection == PROT.PROTpackage) && toParent().isClassDeclaration(); + } + + int isFinal() + { + ClassDeclaration cd; +static if (false) { + printf("FuncDeclaration.isFinal(%s)\n", toChars()); + printf("%p %d %d %d %d\n", isMember(), isStatic(), protection == PROT.PROTprivate, isCtorDeclaration(), linkage != LINK.LINKd); + printf("result is %d\n", + isMember() && !(isStatic() || protection == PROT.PROTprivate || protection == PROT.PROTpackage) && (cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.STCfinal); +} + return isMember() && (Declaration.isFinal() || ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.STCfinal)); + } + + bool addPreInvariant() + { + AggregateDeclaration ad = isThis(); + return (ad && + //ad.isClassDeclaration() && + global.params.useInvariants && + (protection == PROT.PROTpublic || protection == PROT.PROTexport) && + !naked && + ident !is Id.cpctor); + } + + bool addPostInvariant() + { + AggregateDeclaration ad = isThis(); + return (ad && ad.inv && + //ad.isClassDeclaration() && + global.params.useInvariants && + (protection == PROT.PROTpublic || protection == PROT.PROTexport) && + !naked && + ident !is Id.cpctor); + } + + Expression interpret(InterState* istate, Expressions arguments, Expression thisexp = null) + { + assert(false); + } + + void inlineScan() + { + InlineScanState iss; + + version (LOG) { + printf("FuncDeclaration.inlineScan('%s')\n", toChars()); + } + ///memset(&iss, 0, sizeof(iss)); + iss.fd = this; + if (fbody) + { + inlineNest++; + fbody = fbody.inlineScan(&iss); + inlineNest--; + } + } + + int canInline(int hasthis, int hdrscan = 0) + { + int cost; + +// #define CANINLINE_LOG 0 + + version (CANINLINE_LOG) { + printf("FuncDeclaration.canInline(hasthis = %d, '%s')\n", hasthis, toChars()); + } + + if (needThis() && !hasthis) + return 0; + + if (inlineNest || (semanticRun < 3 && !hdrscan)) + { + version (CANINLINE_LOG) { + printf("\t1: no, inlineNest = %d, semanticRun = %d\n", inlineNest, semanticRun); + } + return 0; + } + + switch (inlineStatus) + { + case ILS.ILSyes: + version (CANINLINE_LOG) { + printf("\t1: yes %s\n", toChars()); + } + return 1; + + case ILS.ILSno: + version (CANINLINE_LOG) { + printf("\t1: no %s\n", toChars()); + } + return 0; + + case ILS.ILSuninitialized: + break; + + default: + assert(0); + } + + if (type) + { + assert(type.ty == Tfunction); + TypeFunction tf = cast(TypeFunction)type; + if (tf.varargs == 1) // no variadic parameter lists + goto Lno; + + /* Don't inline a function that returns non-void, but has + * no return expression. + */ + if (tf.next && tf.next.ty != Tvoid && + !(hasReturnExp & 1) && + !hdrscan) + goto Lno; + } + else + { + CtorDeclaration ctor = isCtorDeclaration(); + if (ctor && ctor.varargs == 1) + goto Lno; + } + + if ( + !fbody || + !hdrscan && + ( +/// static if (false) { +/// isCtorDeclaration() || // cannot because need to convert: +/// // return; +/// // to: +/// // return this; +/// } + isSynchronized() || + isImportedSymbol() || +/// version (DMDV2) { + closureVars.dim || // no nested references to this frame +/// } else { +/// nestedFrameRef || // no nested references to this frame +/// } + (isVirtual() && !isFinal()) + )) + { + goto Lno; + } + + /* If any parameters are Tsarray's (which are passed by reference) + * or out parameters (also passed by reference), don't do inlining. + */ + if (parameters) + { + for (int i = 0; i < parameters.dim; i++) + { + VarDeclaration v = cast(VarDeclaration)parameters.data[i]; + if (v.isOut() || v.isRef() || v.type.toBasetype().ty == Tsarray) + goto Lno; + } + } + + InlineCostState ics; + ///memset(&ics, 0, sizeof(ics)); + ics.hasthis = hasthis; + ics.fd = this; + ics.hdrscan = hdrscan; + cost = fbody.inlineCost(&ics); + version (CANINLINE_LOG) { + printf("cost = %d\n", cost); + } + if (cost >= COST_MAX) + goto Lno; + + if (!hdrscan) // Don't scan recursively for header content scan + inlineScan(); + + Lyes: + if (!hdrscan) // Don't modify inlineStatus for header content scan + inlineStatus = ILS.ILSyes; + version (CANINLINE_LOG) { + printf("\t2: yes %s\n", toChars()); + } + return 1; + + Lno: + if (!hdrscan) // Don't modify inlineStatus for header content scan + inlineStatus = ILS.ILSno; + version (CANINLINE_LOG) { + printf("\t2: no %s\n", toChars()); + } + return 0; + } + + Expression doInline(InlineScanState* iss, Expression ethis, Array arguments) + { + InlineDoState ids = new InlineDoState(); + DeclarationExp de; + Expression e = null; + + version (LOG) { + printf("FuncDeclaration.doInline('%s')\n", toChars()); + } + + ///memset(&ids, 0, sizeof(ids)); + ids.parent = iss.fd; + + // Set up vthis + if (ethis) + { + VarDeclaration vthis; + ExpInitializer ei; + VarExp ve; + + version (STRUCTTHISREF) { + if (ethis.type.ty == Tpointer) + { + Type t = ethis.type.nextOf(); + ethis = new PtrExp(ethis.loc, ethis); + ethis.type = t; + } + ei = new ExpInitializer(ethis.loc, ethis); + + vthis = new VarDeclaration(ethis.loc, ethis.type, Id.This, ei); + if (ethis.type.ty != Tclass) + vthis.storage_class = STCref; + else + vthis.storage_class = STCin; + } else { + if (ethis.type.ty != Tclass && ethis.type.ty != Tpointer) + { + ethis = ethis.addressOf(null); + } + + ei = new ExpInitializer(ethis.loc, ethis); + + vthis = new VarDeclaration(ethis.loc, ethis.type, Id.This, ei); + vthis.storage_class = STCin; + } + vthis.linkage = LINKd; + vthis.parent = iss.fd; + + ve = new VarExp(vthis.loc, vthis); + ve.type = vthis.type; + + ei.exp = new AssignExp(vthis.loc, ve, ethis); + ei.exp.type = ve.type; + version (STRUCTTHISREF) { + if (ethis.type.ty != Tclass) + { + /* This is a reference initialization, not a simple assignment. + */ + ei.exp.op = TOKconstruct; + } + } + + ids.vthis = vthis; + } + + // Set up parameters + if (ethis) + { + e = new DeclarationExp(Loc(0), ids.vthis); + e.type = Type.tvoid; + } + + if (arguments && arguments.dim) + { + assert(parameters.dim == arguments.dim); + + for (int i = 0; i < arguments.dim; i++) + { + VarDeclaration vfrom = cast(VarDeclaration)parameters.data[i]; + VarDeclaration vto; + Expression arg = cast(Expression)arguments.data[i]; + ExpInitializer ei; + VarExp ve; + + ei = new ExpInitializer(arg.loc, arg); + + vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei); + vto.storage_class |= vfrom.storage_class & (STCin | STCout | STClazy | STCref); + vto.linkage = vfrom.linkage; + vto.parent = iss.fd; + //printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class); + //printf("vto.parent = '%s'\n", iss.fd.toChars()); + + ve = new VarExp(vto.loc, vto); + //ve.type = vto.type; + ve.type = arg.type; + + ei.exp = new AssignExp(vto.loc, ve, arg); + ei.exp.type = ve.type; + //ve.type.print(); + //arg.type.print(); + //ei.exp.print(); + + ids.from.push(cast(void*)vfrom); + ids.to.push(cast(void*)vto); + + de = new DeclarationExp(Loc(0), vto); + de.type = Type.tvoid; + + e = Expression.combine(e, de); + } + } + + inlineNest++; + Expression eb = fbody.doInline(ids); + inlineNest--; + //eb.type.print(); + //eb.print(); + //eb.dump(0); + return Expression.combine(e, eb); + } + + string kind() + { + return "function"; + } + + void toDocBuffer(OutBuffer buf) + { + assert(false); + } + + FuncDeclaration isUnique() + { + assert(false); + } + + /******************************* + * 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. + */ + bool 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 = cast(VarDeclaration)closureVars.data[i]; + assert(v.isVarDeclaration()); + //printf("\tv = %s\n", v.toChars()); + + for (int j = 0; j < v.nestedrefs.dim; j++) + { FuncDeclaration f = cast(FuncDeclaration)v.nestedrefs.data[j]; + assert(f != this); + + //printf("\t\tf = %s, %d, %p, %d\n", f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf); + if (f.isThis() || f.tookAddressOf) + goto Lyes; // assume f escapes this function's scope + + // Look to see if any parents of f that are below this escape + for (Dsymbol s = f.parent; s && s !is this; s = s.parent) + { + f = s.isFuncDeclaration(); + if (f && (f.isThis() || f.tookAddressOf)) { + goto Lyes; + } + } + } + } + return false; + + Lyes: + //printf("\tneeds closure\n"); + return true; + } + + static FuncDeclaration genCfunc(Type treturn, string name) + { + return genCfunc(treturn, Lexer.idPool(name)); + } + + /********************************** + * Generate a FuncDeclaration for a runtime library function. + */ + static FuncDeclaration genCfunc(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(null, treturn, 0, LINK.LINKc); + fd = new FuncDeclaration(Loc(0), Loc(0), id, STCstatic, tf); + fd.protection = PROT.PROTpublic; + fd.linkage = LINK.LINKc; + + st.insert(fd); + } + return fd; + } + + Symbol* toSymbol() + { + if (!csym) + { + Symbol* s; + TYPE* t; + string id; + +static if (false) { + id = ident.toChars(); +} else { + id = mangle(); +} + //writef("FuncDeclaration.toSymbol(%s %s)\n", kind(), toChars()); + //writef("\tid = '%s'\n", id); + //writef("\ttype = %s\n", type.toChars()); + s = symbol_calloc(toStringz(id)); + slist_add(s); + + { + s.prettyIdent = toStringz(toPrettyChars()); + s.Sclass = SC.SCglobal; + symbol_func(s); + func_t* f = s.Sfunc; + if (isVirtual()) + f.Fflags |= F.Fvirtual; + else if (isMember2()) + f.Fflags |= F.Fstatic; + f.Fstartline.Slinnum = loc.linnum; + f.Fstartline.Sfilename = cast(char*)toStringz(loc.filename); + if (endloc.linnum) + { + f.Fendline.Slinnum = endloc.linnum; + f.Fendline.Sfilename = cast(char*)toStringz(endloc.filename); + } + else + { + f.Fendline.Slinnum = loc.linnum; + f.Fendline.Sfilename = cast(char*)toStringz(loc.filename); + } + t = type.toCtype(); + } + + mangle_t msave = t.Tmangle; + if (isMain()) + { + t.Tty = TYM.TYnfunc; + t.Tmangle = mTYman.mTYman_c; + } + else + { + switch (linkage) + { + case LINK.LINKwindows: + t.Tmangle = mTYman.mTYman_std; + break; + + case LINK.LINKpascal: + t.Tty = TYM.TYnpfunc; + t.Tmangle = mTYman.mTYman_pas; + break; + + case LINK.LINKc: + t.Tmangle = mTYman.mTYman_c; + break; + + case LINK.LINKd: + t.Tmangle = mTYman.mTYman_d; + break; + + case LINK.LINKcpp: + { t.Tmangle = mTYman.mTYman_cpp; + version (TARGET_WINDOS) { + if (isThis()) + t.Tty = TYM.TYmfunc; + } + s.Sflags |= SFL.SFLpublic; + Dsymbol parent = toParent(); + ClassDeclaration cd = parent.isClassDeclaration(); + if (cd) + { + .type* tt = cd.type.toCtype(); + s.Sscope = tt.Tnext.Ttag; + } + break; + } + default: + writef("linkage = %d\n", linkage); + assert(0); + } + } + if (msave) + assert(msave == t.Tmangle); + //printf("Tty = %x, mangle = x%x\n", t.Tty, t.Tmangle); + t.Tcount++; + s.Stype = t; + //s.Sfielddef = this; + + csym = s; + } + return csym; + } + + Symbol* toThunkSymbol(int offset) // thunk version + { + Symbol *sthunk; + + toSymbol(); + + static if (false) { + char *id; + char *n; + type *t; + + n = sym.Sident; + id = cast(char*) alloca(8 + 5 + strlen(n) + 1); + sprintf(id, "_thunk%d__%s", offset, n); + s = symbol_calloc(id); + slist_add(s); + s.Stype = csym.Stype; + s.Stype.Tcount++; + } + sthunk = symbol_generate(SCstatic, csym.Stype); + sthunk.Sflags |= SFLimplem; + cod3_thunk(sthunk, csym, 0, TYnptr, -offset, -1, 0); + return sthunk; + } + + void toObjFile(int multiobj) // compile to .obj file + { + Symbol* s; + func_t* f; + Symbol* senter; + Symbol* sexit; + + FuncDeclaration func = this; + ClassDeclaration cd = func.parent.isClassDeclaration(); + int reverse; + int i; + int has_arguments; + + //printf("FuncDeclaration.toObjFile(%p, %s.%s)\n", func, parent.toChars(), func.toChars()); +static if (false) { + //printf("line = %d\n",func.getWhere() / LINEINC); + EEcontext ee = env.getEEcontext(); + if (ee.EEcompile == 2) + { + if (ee.EElinnum < (func.getWhere() / LINEINC) || + ee.EElinnum > (func.endwhere / LINEINC) + ) + return; // don't compile this function + ee.EEfunc = func.toSymbol(); + } +} + + if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration()) + { + obj_append(this); + return; + } + + if (semanticRun >= 5) // if toObjFile() already run + return; + + semanticRun = 5; + + if (!func.fbody) + { + return; + } + + if (func.isUnitTestDeclaration() && !global.params.useUnitTests) + return; + + if (global.params.verbose) + writef("function %s\n",func.toChars()); + + s = func.toSymbol(); + f = s.Sfunc; + +version (TARGET_OSX) { + s.Sclass = SC.SCcomdat; +} else { + s.Sclass = SC.SCglobal; +} + + for (Dsymbol p = parent; p; p = p.parent) + { + if (p.isTemplateInstance()) + { + s.Sclass = SC.SCcomdat; + break; + } + } + + if (isNested()) + { + // if (!(config.flags3 & CFG3pic)) + // s.Sclass = SCstatic; + f.Fflags3 |= F3.Fnested; + } + else + { + const(char)* libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname; + + // Pull in RTL startup code + if (func.isMain()) + { objextdef("_main"); +version (XXX) { ///TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS + obj_ehsections(); // initialize exception handling sections +} else { + objextdef("__acrtused_con"); +} + obj_includelib(libname); + s.Sclass = SC.SCglobal; + } + else if (strcmp(s.Sident.ptr, "main".ptr) == 0 && linkage == LINK.LINKc) + s.Sclass = SC.SCglobal; + + else if (func.isWinMain()) + { + objextdef("__acrtused"); + obj_includelib(libname); + s.Sclass = SC.SCglobal; + } + + // Pull in RTL startup code + else if (func.isDllMain()) + { + objextdef("__acrtused_dll"); + obj_includelib(libname); + s.Sclass = SC.SCglobal; + } + } + + cstate.CSpsymtab = &f.Flocsym; + + // Find module m for this function + Module m = null; + for (Dsymbol p = parent; p; p = p.parent) + { + m = p.isModule(); + if (m) + break; + } + + IRState irs = IRState(m, func); + Array deferToObj = new Array(); // write these to OBJ file later + irs.deferToObj = deferToObj; + + TypeFunction tf; + RET retmethod; + Symbol* shidden = null; + Symbol* sthis = null; + tym_t tyf; + + tyf = tybasic(s.Stype.Tty); + //printf("linkage = %d, tyf = x%x\n", linkage, tyf); + reverse = tyrevfunc(s.Stype.Tty); + + assert(func.type.ty == TY.Tfunction); + tf = cast(TypeFunction)(func.type); + has_arguments = (tf.linkage == LINK.LINKd) && (tf.varargs == 1); + retmethod = tf.retStyle(); + if (retmethod == RET.RETstack) + { + // If function returns a struct, put a pointer to that + // as the first argument + .type* thidden = tf.next.pointerTo().toCtype(); + char hiddenparam[5+4+1]; + static int hiddenparami; // how many we've generated so far + + sprintf(hiddenparam.ptr, "__HID%d".ptr, ++hiddenparami); + shidden = symbol_name(hiddenparam.ptr, SC.SCparameter, thidden); + shidden.Sflags |= SFL.SFLtrue | SFL.SFLfree; + +version (DMDV1) { + bool nestedref = func.nrvo_can && func.nrvo_var && func.nrvo_var.nestedref; +} else { + bool nestedref = func.nrvo_can && func.nrvo_var && (func.nrvo_var.nestedrefs.dim != 0); +} + if (nestedref) { + type_setcv(&shidden.Stype, shidden.Stype.Tty | mTY.mTYvolatile); + } + + irs.shidden = shidden; + this.shidden = shidden; + } + + if (vthis) + { + assert(!vthis.csym); + sthis = vthis.toSymbol(); + irs.sthis = sthis; + if (!(f.Fflags3 & F3.Fnested)) + f.Fflags3 |= F3.Fmember; + } + + Symbol** params; + uint pi; + + // Estimate number of parameters, pi + pi = (v_arguments !is null); + if (parameters) + pi += parameters.dim; + + // Allow extra 2 for sthis and shidden + params = cast(Symbol**)alloca((pi + 2) * (Symbol*).sizeof); + + // Get the actual number of parameters, pi, and fill in the params[] + pi = 0; + if (v_arguments) + { + params[pi] = v_arguments.toSymbol(); + pi += 1; + } + if (parameters) + { + for (i = 0; i < parameters.dim; i++) + { + VarDeclaration v = cast(VarDeclaration)parameters.data[i]; +debug { + if (v.csym) + writef("parameter '%s'\n", v.toChars()); +} + assert(!v.csym); + params[pi + i] = v.toSymbol(); + } + pi += i; + } + + if (reverse) + { + // Reverse params[] entries + for (i = 0; i < pi/2; i++) + { + Symbol* sptmp = params[i]; + params[i] = params[pi - 1 - i]; + params[pi - 1 - i] = sptmp; + } + } + + if (shidden) + { +static if (false) { + // shidden becomes last parameter + params[pi] = shidden; +} else { + // shidden becomes first parameter + memmove(params + 1, params, pi * (*params).sizeof); + params[0] = shidden; +} + pi++; + } + + + if (sthis) + { +static if (false) { + // sthis becomes last parameter + params[pi] = sthis; +} else { + // sthis becomes first parameter + memmove(params + 1, params, pi * (*params).sizeof); + params[0] = sthis; +} + pi++; + } + + if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && + linkage != LINK.LINKd && shidden && sthis) + { + /* swap shidden and sthis + */ + Symbol* sp = params[0]; + params[0] = params[1]; + params[1] = sp; + } + + for (i = 0; i < pi; i++) + { + Symbol *sp = params[i]; + sp.Sclass = SC.SCparameter; + sp.Sflags &= ~SFL.SFLspill; + sp.Sfl = FL.FLpara; + symbol_add(sp); + } + + // First parameter goes in register + if (pi) + { + Symbol* sp = params[0]; + if ((tyf == TYM.TYjfunc || tyf == TYM.TYmfunc) && type_jparam(sp.Stype)) + { + sp.Sclass = SC.SCfastpar; + sp.Spreg = (tyf == TYM.TYjfunc) ? REG.AX : REG.CX; + sp.Sfl = FL.FLauto; + //printf("'%s' is SCfastpar\n",sp.Sident); + } + } + + if (func.fbody) + { + block* b; + Blockx bx; + Statement sbody; + + localgot = null; + + sbody = func.fbody; + ///memset(&bx, 0, (bx).sizeof); + bx.startblock = block_calloc(); + bx.curblock = bx.startblock; + bx.funcsym = s; + bx.scope_index = -1; + bx.classdec = cd; + bx.member = func; + bx.module_ = getModule(); + irs.blx = &bx; + + buildClosure(&irs); + +static if (false) { + if (func.isSynchronized()) + { + if (cd) + { + elem *esync; + if (func.isStatic()) + { // monitor is in ClassInfo + esync = el_ptr(cd.toSymbol()); + } + else + { // 'this' is the monitor + esync = el_var(sthis); + } + + if (func.isStatic() || sbody.usesEH() || + !(config.flags2 & CFG2.CFG2seh)) + { // BUG: what if frequire or fensure uses EH? + + sbody = new SynchronizedStatement(func.loc, esync, sbody); + } + else + { + version (TARGET_WINDOS) { + if (config.flags2 & CFG2.CFG2seh) + { + /* The "jmonitor" uses an optimized exception handling frame + * which is a little shorter than the more general EH frame. + * It isn't strictly necessary. + */ + s.Sfunc.Fflags3 |= Fjmonitor; + } + } + el_free(esync); + } + } + else + { + error("synchronized function %s must be a member of a class", func.toChars()); + } + } +} else version (TARGET_WINDOS) { + if (func.isSynchronized() && cd && config.flags2 & CFG2.CFG2seh && + !func.isStatic() && !sbody.usesEH()) + { + /* The "jmonitor" hack uses an optimized exception handling frame + * which is a little shorter than the more general EH frame. + */ + s.Sfunc.Fflags3 |= F3.Fjmonitor; + } +} + + sbody.toIR(&irs); + bx.curblock.BC = BC.BCret; + + f.Fstartblock = bx.startblock; + // einit = el_combine(einit,bx.init); + + if (isCtorDeclaration()) + { + assert(sthis); + for (b = f.Fstartblock; b; b = b.Bnext) + { + if (b.BC == BC.BCret) + { + b.BC = BC.BCretexp; + b.Belem = el_combine(b.Belem, el_var(sthis)); + } + } + } + } + + // If static constructor + if (isStaticConstructor()) + { + elem* e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s)); + ector = el_combine(ector, e); + } + + // If static destructor + if (isStaticDestructor()) + { + elem* e; + +version (STATICCTOR) { + e = el_bin(OPER.OPcall, TYM.TYvoid, el_var(rtlsym[RTLSYM.RTLSYM_FATEXIT]), el_ptr(s)); + ector = el_combine(ector, e); + dtorcount++; +} else { + StaticDtorDeclaration f2 = isStaticDtorDeclaration(); + assert(f2); + if (f2.vgate) + { + /* Increment destructor's vgate at construction time + */ + ectorgates.push(cast(void*)f2); + } + + e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s)); + edtor = el_combine(e, edtor); +} + } + + // If unit test + if (isUnitTestDeclaration()) + { + elem* e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s)); + etest = el_combine(etest, e); + } + + if (global.errors) + return; + + writefunc(s); + + if (isExport()) { + obj_export(s, Poffset); + } + + for (i = 0; i < irs.deferToObj.dim; i++) + { + Dsymbol ss = cast(Dsymbol)irs.deferToObj.data[i]; + ss.toObjFile(0); + } + +version (XXX) { ///TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS + // A hack to get a pointer to this function put in the .dtors segment + if (ident && ident.toChars() == "_STD") { + obj_staticdtor(s); + } +} +version (DMDV2) { + if (irs.startaddress) + { + writef("Setting start address\n"); + obj_startaddress(irs.startaddress); + } +} + } + + int cvMember(ubyte* p) + { + assert(false); + } + + /************************************* + * Closures are implemented by taking the local variables that + * need to survive the scope of the function, and copying them + * into a gc allocated chuck of memory. That chunk, called the + * closure here, is inserted into the linked list of stack + * frames instead of the usual stack frame. + * + * buildClosure() inserts code just after the function prolog + * is complete. It allocates memory for the closure, allocates + * a local variable (sclosure) to point to it, inserts into it + * the link to the enclosing frame, and copies into it the parameters + * that are referred to in nested functions. + * In VarExp.toElem and SymOffExp.toElem, when referring to a + * variable that is in a closure, takes the offset from sclosure rather + * than from the frame pointer. + * + * getEthis() and NewExp.toElem need to use sclosure, if set, rather + * than the current frame pointer. + */ + void buildClosure(IRState* irs) + { + if (needsClosure()) + { + // Generate closure on the heap + // BUG: doesn't capture variadic arguments passed to this function + + version (DMDV2) { + /* BUG: doesn't handle destructors for the local variables. + * The way to do it is to make the closure variables the fields + * of a class object: + * class Closure + * { vtbl[] + * monitor + * ptr to destructor + * sthis + * ... closure variables ... + * ~this() { call destructor } + * } + */ + } + //printf("FuncDeclaration.buildClosure()\n"); + Symbol* sclosure; + sclosure = symbol_name("__closptr".ptr, SC.SCauto, Type.tvoidptr.toCtype()); + sclosure.Sflags |= SFL.SFLtrue | SFL.SFLfree; + symbol_add(sclosure); + irs.sclosure = sclosure; + + uint offset = PTRSIZE; // leave room for previous sthis + for (int i = 0; i < closureVars.dim; i++) + { + VarDeclaration v = cast(VarDeclaration)closureVars.data[i]; + assert(v.isVarDeclaration()); + + version (DMDV2) { + if (v.needsAutoDtor()) + v.error("has scoped destruction, cannot build closure"); + } + /* Align and allocate space for v in the closure + * just like AggregateDeclaration.addField() does. + */ + uint memsize; + uint memalignsize; + uint xalign; +/// version (DMDV2) { + if (v.storage_class & STC.STClazy) + { + /* Lazy variables are really delegates, + * so give same answers that TypeDelegate would + */ + memsize = PTRSIZE * 2; + memalignsize = memsize; + xalign = global.structalign; + } + else +/// } + { + memsize = cast(uint)v.type.size(); + memalignsize = v.type.alignsize(); + xalign = v.type.memalign(global.structalign); + } + AggregateDeclaration.alignmember(xalign, memalignsize, &offset); + v.offset = offset; + offset += memsize; + + /* Can't do nrvo if the variable is put in a closure, since + * what the shidden points to may no longer exist. + */ + if (nrvo_can && nrvo_var == v) + { + nrvo_can = 0; + } + } + // offset is now the size of the closure + + // Allocate memory for the closure + elem* e; + e = el_long(TYM.TYint, offset); + e = el_bin(OPER.OPcall, TYM.TYnptr, el_var(rtlsym[RTLSYM.RTLSYM_ALLOCMEMORY]), e); + + // Assign block of memory to sclosure + // sclosure = allocmemory(sz); + e = el_bin(OPER.OPeq, TYM.TYvoid, el_var(sclosure), e); + + // Set the first element to sthis + // *(sclosure + 0) = sthis; + elem* ethis; + if (irs.sthis) + ethis = el_var(irs.sthis); + else + ethis = el_long(TYM.TYnptr, 0); + elem *ex = el_una(OPER.OPind, TYM.TYnptr, el_var(sclosure)); + ex = el_bin(OPER.OPeq, TYM.TYnptr, ex, ethis); + e = el_combine(e, ex); + + // Copy function parameters into closure + for (int i = 0; i < closureVars.dim; i++) + { VarDeclaration v = cast(VarDeclaration)closureVars.data[i]; + + if (!v.isParameter()) + continue; + TYM tym = v.type.totym(); + if (v.type.toBasetype().ty == TY.Tsarray || v.isOut() || v.isRef()) + tym = TYM.TYnptr; // reference parameters are just pointers +/// version (DMDV2) { + else if (v.storage_class & STC.STClazy) + tym = TYM.TYdelegate; +/// } + ex = el_bin(OPER.OPadd, TYM.TYnptr, el_var(sclosure), el_long(TYM.TYint, v.offset)); + ex = el_una(OPER.OPind, tym, ex); + if (ex.Ety == TYM.TYstruct) + { + ex.Enumbytes = cast(uint)v.type.size(); + ex = el_bin(OPER.OPstreq, tym, ex, el_var(v.toSymbol())); + ex.Enumbytes = cast(uint)v.type.size(); + } + else + { + ex = el_bin(OPER.OPeq, tym, ex, el_var(v.toSymbol())); + } + + e = el_combine(e, ex); + } + + block_appendexp(irs.blx.curblock, e); + } + } + + FuncDeclaration isFuncDeclaration() { return this; } +} \ No newline at end of file