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(&paramtypes,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