Mercurial > projects > ddmd
diff dmd/TypeFunction.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children | fd4acc376c45 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dmd/TypeFunction.d Sat Oct 24 08:42:06 2009 +0400 @@ -0,0 +1,734 @@ +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.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; + } + + 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)); + } +} + 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) + { 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,sc); + 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(sc); + arg.defaultArg = resolveProperties(sc, arg.defaultArg); + arg.defaultArg = arg.defaultArg.implicitCastTo(sc, 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--; + } + } + } + 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; + } + + 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 + next.toDecoBuffer(buf); + inuse--; + } + + 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--; + } + + 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--; + } + + MATCH deduceType(Scope sc, Type tparam, TemplateParameters parameters, Objects dedtypes) + { + assert(false); + } + + TypeInfoDeclaration getTypeInfoDeclaration() + { + assert(false); + } + + 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; + } + + 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(); + + if (tn.ty == TY.Tstruct) + { + StructDeclaration sd = (cast(TypeStruct)tn).sym; + if (global.params.isLinux && linkage != LINK.LINKd) { + ; + } +///version (DMDV2) { + else if (sd.dtor || sd.cpctor) { + ; + } +///} + else + { + switch (cast(int)tn.size()) + { + 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; + } + + 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 (XXX) {///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; + } +} \ No newline at end of file