Mercurial > projects > ddmd
view dmd/FuncDeclaration.d @ 178:e3afd1303184
Many small bugs fixed
Made all classes derive from TObject to detect memory leaks (functionality is disabled for now)
Began work on overriding backend memory allocations (to avoid memory leaks)
author | korDen |
---|---|
date | Sun, 17 Oct 2010 07:42:00 +0400 |
parents | 1475fd394c9e |
children | cd48cb899aee |
line wrap: on
line source
module dmd.FuncDeclaration; import dmd.common; import dmd.Declaration; import dmd.DotIdExp; import dmd.AddrExp; import dmd.TryFinallyStatement; import dmd.TryCatchStatement; import dmd.Catch; import dmd.DeclarationStatement; import dmd.StaticDtorDeclaration; import dmd.GlobalExpressions; 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.StorageClassDeclaration; 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.Parameter; 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.TRUST; 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.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; version (Bug4054) import core.memory; import dmd.interpret.Util; import std.string; class FuncDeclaration : Declaration { Array fthrows; // Array of Type's of exceptions (not used) Statement frequire; Statement fensure; Statement fbody; FuncDeclarations foverrides; // functions this function overrides FuncDeclaration fdrequire; // function that does the in contract FuncDeclaration fdensure; // function that does the out contract 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; // for member functions, index into vtbl[] int naked; // !=0 if naked int inlineAsm; // !=0 if has inline assembler ILS inlineStatus; 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, StorageClass storage_class, Type type) { register(); 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; fthrows = null; frequire = null; fdrequire = null; fdensure = null; outId = null; vresult = null; returnLabel = null; fensure = null; fbody = null; localsymtab = null; vthis = null; v_arguments = null; version (IN_GCC) { v_argptr = null; } parameters = null; labtab = null; overnext = null; vtblIndex = -1; hasReturnExp = 0; naked = 0; inlineStatus = ILS.ILSuninitialized; inlineNest = 0; inlineAsm = 0; cantInterpret = 0; semanticRun = 0; version (DMDV1) { nestedFrameRef = 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() is null); hasReturnExp = 0; nrvo_can = 1; nrvo_var = null; shidden = null; version (DMDV2) { builtin = BUILTINunknown; tookAddressOf = 0; } foverrides = new FuncDeclarations(); closureVars = new Dsymbols(); } override 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. override 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) { sc = sc.push(); sc.stc |= storage_class & STC.STCref; // forward refness to function type type = type.semantic(loc, sc); sc = sc.pop(); /* Apply const, immutable and shared storage class * to the function type */ StorageClass stc = storage_class; if (type.isImmutable()) stc |= STC.STCimmutable; if (type.isConst()) stc |= STC.STCconst; if (type.isShared() || storage_class & STC.STCsynchronized) stc |= STC.STCshared; if (type.isWild()) stc |= STC.STCwild; 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: case STC.STCimmutable | STC.STCwild: case STC.STCimmutable | STC.STCconst | STC.STCwild: case STC.STCimmutable | STC.STCconst | STC.STCshared | STC.STCwild: case STC.STCimmutable | STC.STCshared | STC.STCwild: // Don't use toInvariant(), as that will do a merge() type = type.makeInvariant(); goto Lmerge; case STC.STCconst: case STC.STCconst | STC.STCwild: type = type.makeConst(); goto Lmerge; case STC.STCshared | STC.STCconst: case STC.STCshared | STC.STCconst | STC.STCwild: type = type.makeSharedConst(); goto Lmerge; case STC.STCshared: type = type.makeShared(); goto Lmerge; case STC.STCwild: type = type.makeWild(); goto Lmerge; case STC.STCshared | STC.STCwild: type = type.makeSharedWild(); goto Lmerge; Lmerge: if (!(type.ty == Tfunction && !type.nextOf())) /* Can't do merge if return type is not known yet */ type.deco = type.merge().deco; break; case STC.STCundefined: break; default: assert(0); } } storage_class &= ~STC.STCref; if (type.ty != TY.Tfunction) { error("%s must be a function", toChars()); return; } f = cast(TypeFunction)type; size_t nparams = Parameter.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.isImmutable()) && !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; /* Remember which functions this overrides */ foverrides.push(fdv); /* This works by whenever this function is called, * it actually returns tintro, which gets dynamically * cast to type. But we know that tintro is a base * of type, so we could optimize it by not doing a * dynamic cast, but just subtracting the isBaseOf() * offset if the value is != null. */ if (fdv.tintro) tintro = fdv.tintro; else if (!type.equals(fdv.type)) { /* Only need to have a tintro if the vptr * offsets differ */ int offset; if (fdv.type.nextOf().isBaseOf(type.nextOf(), &offset)) { tintro = fdv.type; } } break; } } /* Go through all the interface bases. * If this function is covariant with any members of those interface * functions, set the tintro. */ for (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; /* Remember which functions this overrides */ foverrides.push(fdv); 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: { auto arg0 = Parameter.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()) error("must return int or void"); else 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 { auto arg0 = Parameter.getNth(f.parameters, 0); Type t0 = arg0.type.toBasetype(); Type tb = sd ? sd.type : cd.type; if (arg0.type.implicitConvTo(tb) || (sd && t0.ty == TY.Tpointer && t0.nextOf().implicitConvTo(tb)) ) { if (nparams == 1) goto Lassignerr; auto arg1 = Parameter.getNth(f.parameters, 1); if (arg1.defaultArg) goto Lassignerr; } } } if (isVirtual()) { /* Rewrite contracts as nested functions, then call them. * Doing it as nested functions means that overriding functions * can call them. */ if (frequire) { /* in { ... } * becomes: * void __require() { ... } * __require(); */ Loc loc = frequire.loc; TypeFunction tf = new TypeFunction(null, Type.tvoid, 0, LINKd); FuncDeclaration fd = new FuncDeclaration(loc, loc, Id.require, STCundefined, tf); fd.fbody = frequire; Statement s1 = new DeclarationStatement(loc, fd); Expression e = new CallExp(loc, new VarExp(loc, fd, 0), cast(Expressions)null); Statement s2 = new ExpStatement(loc, e); frequire = new CompoundStatement(loc, s1, s2); fdrequire = fd; } if (fensure) { /* out (result) { ... } * becomes: * tret __ensure(ref tret result) { ... } * __ensure(result); */ if (!outId && f.nextOf().toBasetype().ty != Tvoid) outId = Id.result; // provide a default Loc loc = fensure.loc; auto arguments = new Parameters(); Parameter a = null; if (outId) { a = new Parameter(STCref, f.nextOf(), outId, null); arguments.push(a); } TypeFunction tf = new TypeFunction(arguments, Type.tvoid, 0, LINKd); FuncDeclaration fd = new FuncDeclaration(loc, loc, Id.ensure, STCundefined, tf); fd.fbody = fensure; Statement s1 = new DeclarationStatement(loc, fd); Expression eresult = null; if (outId) eresult = new IdentifierExp(loc, outId); Expression e = new CallExp(loc, new VarExp(loc, fd, 0), eresult); Statement s2 = new ExpStatement(loc, e); fensure = new CompoundStatement(loc, s1, s2); fdensure = fd; } } Ldone: /* Save scope for possible later use (if we need the * function internals) */ scope_ = sc.clone(); scope_.setNoFree(); return; Lassignerr: if (sd) { sd.hasIdentityAssign = 1; // don't need to generate it goto Ldone; } error("identity assignment operator overload is illegal"); } override void semantic2(Scope sc) { } // Do the semantic analysis on the internals of the function. override 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()); } } frequire = mergeFrequire(frequire); fensure = mergeFensure(fensure); 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 | STCproperty | STCsafe | STCtrusted | STCsystem); 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.isImmutable()) { 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), global.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 = global.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; } } } static if(false) { // Propagate storage class from tuple parameters to their element-parameters. if (f.parameters) { foreach (arg; f.parameters) { //printf("[%d] arg.type.ty = %d %s\n", i, arg.type.ty, arg.type.toChars()); if (arg.type.ty == TY.Ttuple) { auto t = cast(TypeTuple)arg.type; size_t dim = Parameter.dim(t.arguments); for (size_t j = 0; j < dim; j++) { auto narg = Parameter.getNth(t.arguments, j); narg.storageClass = arg.storageClass; } } } } } /* Declare all the function parameters as variables * and install them in parameters[] */ size_t nparams = Parameter.dim(f.parameters); if (nparams) { /* parameters[] has all the tuples removed, as the back end * doesn't know about tuples */ parameters = new Dsymbols(); parameters.reserve(nparams); for (size_t i = 0; i < nparams; i++) { auto arg = Parameter.getNth(f.parameters, i); Identifier id = arg.ident; if (!id) { /* Generate identifier for un-named parameter, * because we need it later on. */ arg.ident = id = Identifier.generateId("_param_", i); } Type vtype = arg.type; 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(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) { foreach (arg; f.parameters) { if (!arg.ident) continue; // never used, so ignore if (arg.type.ty == TY.Ttuple) { auto t = cast(TypeTuple)arg.type; size_t dim = Parameter.dim(t.arguments); Objects exps = new Objects(); exps.setDim(dim); for (size_t j = 0; j < dim; j++) { auto narg = Parameter.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[j] = e; } assert(arg.ident); auto 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 */ if (!type.nextOf()) // if return type is inferred { /* This case: * auto fp = function() out { } body { }; * Can fix by doing semantic() onf fbody first. */ error("post conditions are not supported if the return type is inferred"); return; } 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 Loc loc = this.loc; if (fensure) loc = fensure.loc; auto v = new VarDeclaration(loc, type.nextOf(), outId, null); v.noauto = true; version (DMDV2) { if (!isVirtual()) v.storage_class |= STC.STCconst; 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[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 { foreach (Dsymbol s; add.members) { 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[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); } } } } { auto a = new Statements(); // Merge in initialization of 'out' parameters if (parameters) { foreach (Dsymbol s; parameters) { auto v = cast(VarDeclaration)s; if (v.storage_class & STC.STCout) { assert(v.init); ExpInitializer ie = v.init.isExpInitializer(); assert(ie); a.push(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 { Type t = argptr.type; VarDeclaration p; uint offset; Expression e1 = new VarExp(Loc(0), argptr); if (parameters && parameters.dim) p = cast(VarDeclaration)parameters[parameters.length - 1]; else p = v_arguments; // last parameter is _arguments[] if (p.storage_class & STClazy) // If the last parameter is lazy, it's the size of a delegate offset = PTRSIZE * 2; else offset = cast(size_t)p.type.size(); offset = (offset + 3) & ~3; // assume stack aligns on 4 Expression e = new SymOffExp(Loc(0), p, offset); e = new AssignExp(Loc(0), e1, e); e.type = t; a.push(new ExpStatement(Loc(0), e)); p.isargptr = true; } } 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(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(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) { auto s = new ExpStatement(Loc(0), e); a.push(s); } } if (fbody) a.push(fbody); if (fensure) { a.push(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); } auto s = new ReturnStatement(Loc(0), e); a.push(s); } } fbody = new CompoundStatement(Loc(0), a); version (DMDV2) { /* Append destructor calls for parameters as finally blocks. */ if (parameters) { foreach(Dsymbol symb; parameters) { auto v = cast(VarDeclaration)symb; 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); } override void toCBuffer(OutBuffer buf, HdrGenState* hgs) { // writef("FuncDeclaration.toCBuffer() '%s'\n", toChars()); StorageClassDeclaration.stcToCBuffer(buf, storage_class); type.toCBuffer(buf, ident, hgs); bodyToCBuffer(buf, hgs); } void 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 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 default: writef("cov = %d\n", cov); assert(false); } } } return -1; } /**************************************************** * Overload this FuncDeclaration with the new one f. * Return !=0 if successful; i.e. no conflict. */ override 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); 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", Parameter.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(), Parameter.argsTypesToChars(t1.parameters, t1.varargs), m.nextf.toPrettyChars(), Parameter.argsTypesToChars(t2.parameters, t2.varargs)); } else { error(loc, "overloads %s and %s both match argument list for %s", m.lastf.type.toChars(), 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 = Parameter.dim(tf.parameters); size_t ngparams = Parameter.dim(tg.parameters); MATCH match = MATCHexact; /* If both functions have a 'this' pointer, and the mods are not * the same and g's is not const, then this is less specialized. */ if (needThis() && g.needThis()) { 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++) { auto p = Parameter.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[u] = 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. */ override 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); } override 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(); } override 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() { enum 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; } override bool isExport() { return protection == PROT.PROTexport; } override bool isImportedSymbol() { //printf("isImportedSymbol()\n"); //printf("protection = %d\n", protection); return (protection == PROT.PROTexport) && !fbody; } override bool isAbstract() { return (storage_class & STC.STCabstract) != 0; } override bool isCodeseg() { return true; // functions are always in the code segment } override bool isOverloadable() { return 1; // functions can be overloaded } bool isPure() { //printf("FuncDeclaration::isPure() '%s'\n", toChars()); assert(type.ty == TY.Tfunction); return (cast(TypeFunction)this.type).ispure; } int isSafe() { assert(type.ty == TY.Tfunction); return (cast(TypeFunction)this.type).trust == TRUST.TRUSTsafe; } int isTrusted() { assert(type.ty == TY.Tfunction); return (cast(TypeFunction)this.type).trust == TRUST.TRUSTtrusted; } 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); } override 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(); } override bool 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); } /************************************* * Attempt to interpret a function given the arguments. * Input: * istate state for calling function (null if none) * arguments function arguments * thisarg 'this', if a needThis() function, null if not. * * Return result expression if successful, null if not. */ Expression interpret(InterState istate, Expressions arguments, Expression thisarg = null) { version (LOG) { printf("\n********\nFuncDeclaration.interpret(istate = %p) %s\n", istate, toChars()); printf("cantInterpret = %d, semanticRun = %d\n", cantInterpret, semanticRun); } if (global.errors) return null; version(DMDV1) { if (ident == Id.aaLen) return interpret_aaLen(istate, arguments); else if (ident == Id.aaKeys) return interpret_aaKeys(istate, arguments); else if (ident == Id.aaValues) return interpret_aaValues(istate, arguments); } else version(DMDV2) { if (thisarg && (!arguments || arguments.dim == 0)) { if (ident == Id.length) return interpret_length(istate, thisarg); else if (ident == Id.keys) return interpret_keys(istate, thisarg, this); else if (ident == Id.values) return interpret_values(istate, thisarg, this); } } if (cantInterpret || semanticRun == 3) return null; if (!fbody) { cantInterpret = 1; return null; } if (semanticRun < 3 && scope_) { semantic3(scope_); if (global.errors) // if errors compiling this function return null; } if (semanticRun < 4) return null; Type tb = type.toBasetype(); assert(tb.ty == Tfunction); TypeFunction tf = cast(TypeFunction)tb; Type tret = tf.next.toBasetype(); if (tf.varargs && arguments && parameters && arguments.dim != parameters.dim) { cantInterpret = 1; error("C-style variadic functions are not yet implemented in CTFE"); return null; } // Ensure there are no lazy parameters if (tf.parameters) { size_t dim = Parameter.dim(tf.parameters); for (size_t i = 0; i < dim; i++) { auto arg = Parameter.getNth(tf.parameters, i); if (arg.storageClass & STClazy) { cantInterpret = 1; return null; } } } scope InterState istatex = new InterState(); istatex.caller = istate; istatex.fd = this; istatex.localThis = thisarg; scope Expressions vsave = new Expressions(); // place to save previous parameter values size_t dim = 0; if (needThis() && !thisarg) { cantInterpret = 1; // error, no this. Prevent segfault. error("need 'this' to access member %s", toChars()); return null; } if (arguments) { dim = arguments.dim; assert(!dim || (parameters && (parameters.dim == dim))); vsave.setDim(dim); /* Evaluate all the arguments to the function, * store the results in eargs[] */ scope Expressions eargs = new Expressions(); eargs.setDim(dim); for (size_t i = 0; i < dim; i++) { Expression earg = arguments[i]; auto arg = Parameter.getNth(tf.parameters, i); if (arg.storageClass & (STCout | STCref)) { } else { /* Value parameters */ Type ta = arg.type.toBasetype(); if (ta.ty == Tsarray && earg.op == TOKaddress) { /* Static arrays are passed by a simple pointer. * Skip past this to get at the actual arg. */ earg = (cast(AddrExp)earg).e1; } earg = earg.interpret(istate ? istate : istatex); if (earg is EXP_CANT_INTERPRET) { cantInterpret = 1; return null; } } eargs[i] = earg; } for (size_t i = 0; i < dim; i++) { auto earg = eargs[i]; auto arg = Parameter.getNth(tf.parameters, i); auto v = cast(VarDeclaration)parameters[i]; vsave[i] = v.value; version (LOG) { printf("arg[%d] = %s\n", i, earg.toChars()); } if (arg.storageClass & (STCout | STCref) && earg.op==TOKvar) { /* Bind out or ref parameter to the corresponding * variable v2 */ if (!istate) { cantInterpret = 1; error("%s cannot be by passed by reference at compile time", earg.toChars()); return null; // can't bind to non-interpreted vars } // We need to chase down all of the the passed parameters until // we find something that isn't a TOKvar, then create a variable // containg that expression. VarDeclaration v2; while (1) { VarExp ve = cast(VarExp)earg; v2 = ve.var.isVarDeclaration(); if (!v2) { cantInterpret = 1; return null; } if (!v2.value || v2.value.op != TOKvar) break; if ((cast(VarExp)v2.value).var.isSymbolDeclaration()) { // This can happen if v is a struct initialized to // 0 using an __initZ SymbolDeclaration from // TypeStruct.defaultInit() break; // eg default-initialized variable } earg = v2.value; } v.value = new VarExp(earg.loc, v2); /* Don't restore the value of v2 upon function return */ assert(istate); foreach(size_t j, Dsymbol s2; istate.vars)// (size_t j = 0; j < istate.vars.dim; j++) { auto vd = cast(VarDeclaration)s2; if (vd == v2) { istate.vars[j] = null; break; } } } else { // Value parameters and non-trivial references v.value = earg; } version (LOG) { printf("interpreted arg[%d] = %s\n", i, earg.toChars()); } } } // Don't restore the value of 'this' upon function return if (needThis() && thisarg.op == TOKvar && istate) { VarDeclaration thisvar = (cast(VarExp)thisarg).var.isVarDeclaration(); foreach (size_t i, Dsymbol s; istate.vars) { auto v = cast(VarDeclaration)s; if (v == thisvar) { istate.vars[i] = null; break; } } } /* Save the values of the local variables used */ scope valueSaves = new Expressions(); if (istate && !isNested()) { //printf("saving local variables...\n"); valueSaves.setDim(istate.vars.dim); foreach (size_t i, Dsymbol s3; istate.vars) { if (auto v = cast(VarDeclaration)s3) { //printf("\tsaving [%d] %s = %s\n", i, v.toChars(), v.value ? v.value.toChars() : ""); valueSaves[i] = v.value; v.value = null; } } } Expression e = null; while (1) { e = fbody.interpret(istatex); if (e is EXP_CANT_INTERPRET) { version (LOG) { printf("function body failed to interpret\n"); } e = null; } /* This is how we deal with a recursive statement AST * that has arbitrary goto statements in it. * Bubble up a 'result' which is the target of the goto * statement, then go recursively down the AST looking * for that statement, then execute starting there. */ if (e is EXP_GOTO_INTERPRET) { istatex.start = istatex.gotoTarget; // set starting statement istatex.gotoTarget = null; } else break; } /* Restore the parameter values */ for (size_t i = 0; i < dim; i++) { auto v = cast(VarDeclaration)parameters[i]; v.value = vsave[i]; } if (istate && !isNested()) { /* Restore the variable values */ //printf("restoring local variables...\n"); foreach (size_t i , Dsymbol s3; istate.vars) { if (auto v = cast(VarDeclaration)s3) { v.value = valueSaves[i]; //printf("\trestoring [%d] %s = %s\n", i, v.toChars(), v.value ? v.value.toChars() : ""); } } } return e; } override 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) { foreach (Dsymbol s3; parameters) { auto v = cast(VarDeclaration)s3; 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, Expressions 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++) { auto vfrom = cast(VarDeclaration)parameters[i]; VarDeclaration vto; Expression arg = arguments[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); } override string kind() { return "function"; } override void toDocBuffer(OutBuffer buf) { assert(false); } FuncDeclaration isUnique() { Unique unique; overloadApply(this, unique); return unique.f; } /******************************* * 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()); foreach (Dsymbol s3; closureVars) { auto v = cast(VarDeclaration)s3; assert(v.isVarDeclaration()); //printf("\tv = %s\n", v.toChars()); foreach(FuncDeclaration f; v.nestedrefs) { 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; } /**************************************************** * Merge into this function the 'in' contracts of all it overrides. * 'in's are OR'd together, i.e. only one of them needs to pass. */ Statement mergeFrequire(Statement sf) { /* Implementing this is done by having the overriding function call * nested functions (the fdrequire functions) nested inside the overridden * function. This requires that the stack layout of the calling function's * parameters and 'this' pointer be in the same place (as the nested * function refers to them). * This is easy for the parameters, as they are all on the stack in the same * place by definition, since it's an overriding function. The problem is * getting the 'this' pointer in the same place, since it is a local variable. * We did some hacks in the code generator to make this happen: * 1. always generate exception handler frame, or at least leave space for it * in the frame (Windows 32 SEH only) * 2. always generate an EBP style frame * 3. since 'this' is passed in a register that is subsequently copied into * a stack local, allocate that local immediately following the exception * handler block, so it is always at the same offset from EBP. */ foreach(FuncDeclaration fdv; foverrides) //(int i = 0; i < foverrides.dim; i++) { sf = fdv.mergeFrequire(sf); if (fdv.fdrequire) { //printf("fdv.frequire: %s\n", fdv.frequire.toChars()); /* Make the call: * try { __require(); } * catch { frequire; } */ Expression eresult = null; Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, 0), eresult); Statement s2 = new ExpStatement(loc, e); if (sf) { Catch c = new Catch(loc, null, null, sf); Array catches = new Array(); catches.push(cast(void*)c); sf = new TryCatchStatement(loc, s2, catches); } else sf = s2; } } return sf; } /**************************************************** * Merge into this function the 'out' contracts of all it overrides. * 'out's are AND'd together, i.e. all of them need to pass. */ Statement mergeFensure(Statement sf) { /* Same comments as for mergeFrequire(), except that we take care * of generating a consistent reference to the 'result' local by * explicitly passing 'result' to the nested function as a reference * argument. * This won't work for the 'this' parameter as it would require changing * the semantic code for the nested function so that it looks on the parameter * list for the 'this' pointer, something that would need an unknown amount * of tweaking of various parts of the compiler that I'd rather leave alone. */ foreach (FuncDeclaration fdv; foverrides) { sf = fdv.mergeFensure(sf); if (fdv.fdensure) { //printf("fdv.fensure: %s\n", fdv.fensure.toChars()); // Make the call: __ensure(result) Expression eresult = null; if (outId) eresult = new IdentifierExp(loc, outId); Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, 0), eresult); Statement s2 = new ExpStatement(loc, e); if (sf) { sf = new CompoundStatement(fensure.loc, s2, sf); } else sf = s2; } } return sf; } 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; //printf("genCfunc(name = '%s')\n", id.toChars()); //printf("treturn\n\t"); treturn.print(); // See if already in table s = global.st.lookup(id); if (s) { debug fd = s.isFuncDeclaration(); debug assert(fd); debug 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; global.st.insert(fd); } return fd; } override 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; version (Bug4054) { id = cast(char*) GC.malloc(8 + 5 + strlen(n) + 1); } else { 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; } override 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 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_WINDOS) { /* This is done so that the 'this' pointer on the stack is the same * distance away from the function parameters, so that an overriding * function can call the nested fdensure or fdrequire of its overridden function * and the stack offsets are the same. */ if (isVirtual() && (fensure || frequire)) f.Fflags3 |= F3.Ffakeeh; } 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 (POSIX) { ///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]; sprintf(hiddenparam.ptr, "__HID%d".ptr, ++global.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 version (Bug4054) params = cast(Symbol**)GC.malloc((pi + 2) * (Symbol*).sizeof); else 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) { size_t i = 0; for ( ; i < parameters.dim; ++i) { auto v = cast(VarDeclaration)parameters[i]; if (v.csym) { error("compiler error, parameter '%s', bugzilla 2962?", v.toChars()); assert(false); } params[pi + i] = v.toSymbol(); } pi += i; } if (reverse) { // Reverse params[] entries for (size_t 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 (size_t 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; global.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)); global.ector = el_combine(global.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 */ global.ectorgates.push(cast(void*)f2); } e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s)); global.edtor = el_combine(e, global.edtor); } } // If unit test if (isUnitTestDeclaration()) { elem* e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s)); global.etest = el_combine(global.etest, e); } if (global.errors) return; writefunc(s); if (isExport()) { obj_export(s, Poffset); } for (size_t i = 0; i < irs.deferToObj.dim; i++) { Dsymbol ss = cast(Dsymbol)irs.deferToObj.data[i]; ss.toObjFile(0); } version (POSIX) { ///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); } } } override 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, global.tvoidptr.toCtype()); sclosure.Sflags |= SFL.SFLtrue | SFL.SFLfree; symbol_add(sclosure); irs.sclosure = sclosure; uint offset = PTRSIZE; // leave room for previous sthis foreach (Dsymbol s3; closureVars) { auto v = cast(VarDeclaration)s3; 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 foreach (Dsymbol s3; closureVars) { auto v = cast(VarDeclaration)s3; 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); } } /********************************************* * Return the function's parameter list, and whether * it is variadic or not. */ Parameters getParameters(int *pvarargs) { Parameters fparameters; int fvarargs; if (type) { assert(type.ty == Tfunction); auto fdtype = cast(TypeFunction)type; fparameters = fdtype.parameters; fvarargs = fdtype.varargs; } else // Constructors don't have type's { CtorDeclaration fctor = isCtorDeclaration(); assert(fctor); fparameters = fctor.arguments; fvarargs = fctor.varargs; } if (pvarargs) *pvarargs = fvarargs; return fparameters; } override FuncDeclaration isFuncDeclaration() { return this; } } alias Vector!FuncDeclaration FuncDeclarations;