Mercurial > projects > ddmd
view dmd/TypeFunction.d @ 95:ae5b11064a9a
beginning of 2.036 branch
author | Trass3r |
---|---|
date | Mon, 30 Aug 2010 23:08:44 +0200 |
parents | 43073c7c7769 |
children | acd69f84627e |
line wrap: on
line source
module dmd.TypeFunction; import dmd.TypeNext; import dmd.TypeSArray; import dmd.TypeArray; import dmd.ArrayTypes; import dmd.LINK; import dmd.StructDeclaration; import dmd.TypeStruct; import dmd.Global; import dmd.STC; import dmd.MOD; import dmd.PROT; import dmd.Type; import dmd.Loc; import dmd.Scope; import dmd.Identifier; import dmd.OutBuffer; import dmd.HdrGenState; import dmd.CppMangleState; import dmd.TypeInfoDeclaration; import dmd.MATCH; import dmd.Argument; import dmd.Expression; import dmd.RET; import dmd.TY; import dmd.Util; import dmd.backend.TYPE; import dmd.backend.PARAM; import dmd.backend.Util; import dmd.backend.TYM; import dmd.backend.TF; import dmd.backend.mTY; import core.stdc.stdlib; import core.stdc.string; class TypeFunction : TypeNext { // .next is the return type Arguments parameters; // function parameters int varargs; // 1: T t, ...) style for variable number of arguments // 2: T t ...) style for variable number of arguments bool isnothrow; // true: nothrow bool ispure; // true: pure bool isproperty; // can be called without parentheses bool isref; // true: returns a reference LINK linkage; // calling convention int inuse; this(Arguments parameters, Type treturn, int varargs, LINK linkage) { super(TY.Tfunction, treturn); //if (!treturn) *(char*)0=0; // assert(treturn); assert(0 <= varargs && varargs <= 2); this.parameters = parameters; this.varargs = varargs; this.linkage = linkage; } override Type syntaxCopy() { Type treturn = next ? next.syntaxCopy() : null; Arguments params = Argument.arraySyntaxCopy(parameters); TypeFunction t = new TypeFunction(params, treturn, varargs, linkage); t.mod = mod; t.isnothrow = isnothrow; t.ispure = ispure; t.isproperty = isproperty; t.isref = isref; return t; } version (DumbClone) { } else { final TypeFunction cloneTo(TypeFunction t) { super.cloneTo(t); // these 3 should be set by ctor assert(t.parameters is null); assert(t.varargs == varargs); assert(t.linkage == linkage); t.isnothrow = isnothrow; t.ispure = ispure; t.isproperty = isproperty; t.isref = isref; t.inuse = inuse; if (parameters) { t.parameters = parameters.copy(); for (size_t i = 0; i < parameters.dim; i++) { Argument arg = cast(Argument)parameters.data[i]; Argument cpy = arg.clone(); t.parameters.data[i] = cast(void*)cpy; } } return t; } TypeFunction clone() { assert(this.classinfo == TypeFunction.classinfo); return cloneTo(new TypeFunction(null, next, varargs, linkage)); } } override Type semantic(Loc loc, Scope sc) { if (deco) // if semantic() already run { //printf("already done\n"); return this; } //printf("TypeFunction.semantic() this = %p\n", this); //printf("TypeFunction.semantic() %s, sc.stc = %x\n", toChars(), sc.stc); /* Copy in order to not mess up original. * This can produce redundant copies if inferring return type, * as semantic() will get called again on this. */ TypeFunction tf = cast(TypeFunction)clone(); if (sc.stc & STC.STCpure) tf.ispure = true; if (sc.stc & STC.STCnothrow) tf.isnothrow = true; if (sc.stc & STC.STCref) tf.isref = true; tf.linkage = sc.linkage; if (tf.next) { tf.next = tf.next.semantic(loc,sc); if (tf.next.toBasetype().ty == TY.Tsarray) { error(loc, "functions cannot return static array %s", tf.next.toChars()); tf.next = Type.terror; } if (tf.next.toBasetype().ty == TY.Tfunction) { error(loc, "functions cannot return a function"); tf.next = Type.terror; } if (tf.next.toBasetype().ty == TY.Ttuple) { error(loc, "functions cannot return a tuple"); tf.next = Type.terror; } if (tf.next.isauto() && !(sc.flags & SCOPE.SCOPEctor)) error(loc, "functions cannot return scope %s", tf.next.toChars()); } if (tf.parameters) { /* Create a scope for evaluating the default arguments for the parameters */ Scope argsc = sc.push(); argsc.stc = STCundefined; // don't inherit storage class argsc.protection = PROT.PROTpublic; size_t dim = Argument.dim(tf.parameters); for (size_t i = 0; i < dim; i++) { Argument arg = Argument.getNth(tf.parameters, i); tf.inuse++; arg.type = arg.type.semantic(loc, argsc); if (tf.inuse == 1) tf.inuse--; arg.type = arg.type.addStorageClass(arg.storageClass); if (arg.storageClass & (STC.STCauto | STC.STCalias | STC.STCstatic)) { if (!arg.type) continue; } Type t = arg.type.toBasetype(); if (arg.storageClass & (STC.STCout | STC.STCref | STC.STClazy)) { if (t.ty == TY.Tsarray) error(loc, "cannot have out or ref parameter of type %s", t.toChars()); if (arg.storageClass & STC.STCout && arg.type.mod) error(loc, "cannot have const/invariant out parameter of type %s", t.toChars()); } if (!(arg.storageClass & STC.STClazy) && t.ty == TY.Tvoid) error(loc, "cannot have parameter of type %s", arg.type.toChars()); if (arg.defaultArg) { arg.defaultArg = arg.defaultArg.semantic(argsc); arg.defaultArg = resolveProperties(argsc, arg.defaultArg); arg.defaultArg = arg.defaultArg.implicitCastTo(argsc, arg.type); } /* If arg turns out to be a tuple, the number of parameters may * change. */ if (t.ty == TY.Ttuple) { dim = Argument.dim(tf.parameters); i--; } } argsc.pop(); } if (tf.next) tf.deco = tf.merge().deco; if (tf.inuse) { error(loc, "recursive type"); tf.inuse = 0; return terror; } if (tf.varargs == 1 && tf.linkage != LINK.LINKd && Argument.dim(tf.parameters) == 0) error(loc, "variadic functions with non-D linkage must have at least one parameter"); /* Don't return merge(), because arg identifiers and default args * can be different * even though the types match */ return tf; } override void toDecoBuffer(OutBuffer buf, int flag) { ubyte mc; //printf("TypeFunction.toDecoBuffer() this = %p %s\n", this, toChars()); //static int nest; if (++nest == 50) *(char*)0=0; if (inuse) { inuse = 2; // flag error to caller return; } inuse++; static if (true) { if (mod & MOD.MODshared) buf.writeByte('O'); if (mod & MOD.MODconst) buf.writeByte('x'); else if (mod & MOD.MODinvariant) buf.writeByte('y'); } switch (linkage) { case LINK.LINKd: mc = 'F'; break; case LINK.LINKc: mc = 'U'; break; case LINK.LINKwindows: mc = 'W'; break; case LINK.LINKpascal: mc = 'V'; break; case LINK.LINKcpp: mc = 'R'; break; } buf.writeByte(mc); if (ispure || isnothrow || isproperty || isref) { if (ispure) buf.writestring("Na"); if (isnothrow) buf.writestring("Nb"); if (isref) buf.writestring("Nc"); if (isproperty) buf.writestring("Nd"); } // Write argument types Argument.argsToDecoBuffer(buf, parameters); //if (buf.data[buf.offset - 1] == '@') halt(); buf.writeByte('Z' - varargs); // mark end of arg list assert(next); next.toDecoBuffer(buf); inuse--; } override void toCBuffer(OutBuffer buf, Identifier ident, HdrGenState* hgs) { //printf("TypeFunction.toCBuffer() this = %p\n", this); string p = null; if (inuse) { inuse = 2; // flag error to caller return; } inuse++; /* Use 'storage class' style for attributes */ if (mod & MODconst) buf.writestring("const "); if (mod & MODinvariant) buf.writestring("immutable "); if (mod & MODshared) buf.writestring("shared "); if (ispure) buf.writestring("pure "); if (isnothrow) buf.writestring("nothrow "); if (isproperty) buf.writestring("@property "); if (isref) buf.writestring("ref "); if (next && (!ident || ident.toHChars2() == ident.toChars())) next.toCBuffer2(buf, hgs, MODundefined); if (hgs.ddoc != 1) { switch (linkage) { case LINKd: p = null; break; case LINKc: p = " C"; break; case LINKwindows: p = " Windows"; break; case LINKpascal: p = " Pascal"; break; case LINKcpp: p = " C++"; break; default: assert(0); } } if (!hgs.hdrgen && p) buf.writestring(p); if (ident) { buf.writeByte(' '); buf.writestring(ident.toHChars2()); } Argument.argsToCBuffer(buf, hgs, parameters, varargs); inuse--; } override void toCBuffer2(OutBuffer buf, HdrGenState* hgs, MOD mod) { //printf("TypeFunction::toCBuffer2() this = %p, ref = %d\n", this, isref); string p; if (inuse) { inuse = 2; // flag error to caller return; } inuse++; if (next) next.toCBuffer2(buf, hgs, MODundefined); if (hgs.ddoc != 1) { switch (linkage) { case LINKd: p = null; break; case LINKc: p = "C "; break; case LINKwindows: p = "Windows "; break; case LINKpascal: p = "Pascal "; break; case LINKcpp: p = "C++ "; break; default: assert(0); } } if (!hgs.hdrgen && p) buf.writestring(p); buf.writestring(" function"); Argument.argsToCBuffer(buf, hgs, parameters, varargs); /* Use postfix style for attributes */ if (mod != this.mod) { modToBuffer(buf); } if (ispure) buf.writestring(" pure"); if (isnothrow) buf.writestring(" nothrow"); if (isproperty) buf.writestring(" @property"); if (isref) buf.writestring(" ref"); inuse--; } override MATCH deduceType(Scope sc, Type tparam, TemplateParameters parameters, Objects dedtypes) { assert(false); } override TypeInfoDeclaration getTypeInfoDeclaration() { assert(false); } override Type reliesOnTident() { if (parameters) { for (size_t i = 0; i < parameters.dim; i++) { Argument arg = cast(Argument)parameters.data[i]; Type t = arg.type.reliesOnTident(); if (t) return t; } } return next.reliesOnTident(); } version (CPP_MANGLE) { void toCppMangle(OutBuffer buf, CppMangleState* cms) { assert(false); } } /*************************** * Examine function signature for parameter p and see if * p can 'escape' the scope of the function. */ bool parameterEscapes(Argument p) { /* Scope parameters do not escape. * Allow 'lazy' to imply 'scope' - * lazy parameters can be passed along * as lazy parameters to the next function, but that isn't * escaping. */ if (p.storageClass & (STC.STCscope | STC.STClazy)) return false; if (ispure) { /* With pure functions, we need only be concerned if p escapes * via any return statement. */ Type tret = nextOf().toBasetype(); if (!isref && !tret.hasPointers()) { /* The result has no references, so p could not be escaping * that way. */ return false; } } /* Assume it escapes in the absence of better information. */ return true; } /******************************** * 'args' are being matched to function 'this' * Determine match level. * Returns: * MATCHxxxx */ MATCH callMatch(Expression ethis, Expressions args) { //printf("TypeFunction.callMatch() %s\n", toChars()); MATCH match = MATCH.MATCHexact; // assume exact match if (ethis) { Type t = ethis.type; if (t.toBasetype().ty == TY.Tpointer) t = t.toBasetype().nextOf(); // change struct* to struct if (t.mod != mod) { if (mod == MOD.MODconst) match = MATCH.MATCHconst; else return MATCH.MATCHnomatch; } } size_t nparams = Argument.dim(parameters); size_t nargs = args ? args.dim : 0; if (nparams == nargs) { ; } else if (nargs > nparams) { if (varargs == 0) goto Nomatch; // too many args; no match match = MATCH.MATCHconvert; // match ... with a "conversion" match level } for (size_t u = 0; u < nparams; u++) { MATCH m; Expression arg; // BUG: what about out and ref? Argument p = Argument.getNth(parameters, u); assert(p); if (u >= nargs) { if (p.defaultArg) continue; if (varargs == 2 && u + 1 == nparams) goto L1; goto Nomatch; // not enough arguments } arg = cast(Expression)args.data[u]; assert(arg); // Non-lvalues do not match ref or out parameters if (p.storageClass & (STC.STCref | STC.STCout) && !arg.isLvalue()) goto Nomatch; if (p.storageClass & STC.STClazy && p.type.ty == TY.Tvoid && arg.type.ty != TY.Tvoid) m = MATCH.MATCHconvert; else m = arg.implicitConvTo(p.type); //printf("\tm = %d\n", m); if (m == MATCH.MATCHnomatch) // if no match { L1: if (varargs == 2 && u + 1 == nparams) // if last varargs param { Type tb = p.type.toBasetype(); TypeSArray tsa; long sz; switch (tb.ty) { case TY.Tsarray: tsa = cast(TypeSArray)tb; sz = tsa.dim.toInteger(); if (sz != nargs - u) goto Nomatch; case TY.Tarray: { TypeArray ta = cast(TypeArray)tb; for (; u < nargs; u++) { arg = cast(Expression)args.data[u]; assert(arg); static if (true) { /* If lazy array of delegates, * convert arg(s) to delegate(s) */ Type tret = p.isLazyArray(); if (tret) { if (ta.next.equals(arg.type)) { m = MATCH.MATCHexact; } else { m = arg.implicitConvTo(tret); if (m == MATCH.MATCHnomatch) { if (tret.toBasetype().ty == TY.Tvoid) m = MATCH.MATCHconvert; } } } else m = arg.implicitConvTo(ta.next); } else { m = arg.implicitConvTo(ta.next); } if (m == MATCH.MATCHnomatch) goto Nomatch; if (m < match) match = m; } goto Ldone; } case TY.Tclass: // Should see if there's a constructor match? // Or just leave it ambiguous? goto Ldone; default: goto Nomatch; } } goto Nomatch; } if (m < match) match = m; // pick worst match } Ldone: //printf("match = %d\n", match); return match; Nomatch: //printf("no match\n"); return MATCH.MATCHnomatch; } override type* toCtype() { if (ctype) { return ctype; } type* t; if (true) { param_t* paramtypes; tym_t tyf; type* tp; paramtypes = null; size_t nparams = Argument.dim(parameters); for (size_t i = 0; i < nparams; i++) { Argument arg = Argument.getNth(parameters, i); tp = arg.type.toCtype(); if (arg.storageClass & (STC.STCout | STC.STCref)) { // C doesn't have reference types, so it's really a pointer // to the parameter type tp = type_allocn(TYM.TYref, tp); } param_append_type(¶mtypes,tp); } tyf = totym(); t = type_alloc(tyf); t.Tflags |= TF.TFprototype; if (varargs != 1) t.Tflags |= TF.TFfixed; ctype = t; t.Tnext = next.toCtype(); t.Tnext.Tcount++; t.Tparamtypes = paramtypes; } ctype = t; return t; } /*************************** * Determine return style of function - whether in registers or * through a hidden pointer to the caller's stack. */ RET retStyle() { //printf("TypeFunction.retStyle() %s\n", toChars()); version (DMDV2) { if (isref) return RET.RETregs; // returns a pointer } Type tn = next.toBasetype(); Type tns = tn; ulong sz = tn.size(); version(SARRAYVALUE) { if (tn.ty == Tsarray) { do { tns = tns.nextOf().toBasetype(); } while (tns.ty == Tsarray); if (tns.ty != Tstruct) { if (global.params.isLinux && linkage != LINKd) {} else { switch (sz) { case 1: case 2: case 4: case 8: return RETregs; // return small structs in regs // (not 3 byte structs!) default: break; } } return RETstack; } } } if (tns.ty == TY.Tstruct) { StructDeclaration sd = (cast(TypeStruct)tn).sym; if (global.params.isLinux && linkage != LINK.LINKd) { ; } ///version (DMDV2) { // TODO: else if (sd.dtor || sd.cpctor) { } ///} else { switch (sz) { case 1: case 2: case 4: case 8: return RET.RETregs; // return small structs in regs // (not 3 byte structs!) default: break; } } return RET.RETstack; } else if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) && linkage == LINK.LINKc && tn.iscomplex()) { if (tn.ty == TY.Tcomplex32) return RET.RETregs; // in EDX:EAX, not ST1:ST0 else return RET.RETstack; } else return RET.RETregs; } override TYM totym() { TYM tyf; //printf("TypeFunction.totym(), linkage = %d\n", linkage); switch (linkage) { case LINK.LINKwindows: tyf = (varargs == 1) ? TYM.TYnfunc : TYM.TYnsfunc; break; case LINK.LINKpascal: tyf = (varargs == 1) ? TYM.TYnfunc : TYM.TYnpfunc; break; case LINK.LINKc: tyf = TYM.TYnfunc; version (POSIX) {///TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS if (retStyle() == RET.RETstack) tyf = TYM.TYhfunc; } break; case LINK.LINKd: tyf = (varargs == 1) ? TYM.TYnfunc : TYM.TYjfunc; break; case LINK.LINKcpp: tyf = TYM.TYnfunc; break; default: writef("linkage = %d\n", linkage); assert(0); } version (DMDV2) { if (isnothrow) tyf |= mTY.mTYnothrow; } return tyf; } }