view dmd/TypeFunction.d @ 79:43073c7c7769

updated to 2.035 also implemented a few missing functions still crashes in Import.importAll though
author Trass3r
date Mon, 30 Aug 2010 03:57:51 +0200
parents ef02e2e203c2
children ae5b11064a9a
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(&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;
	}

    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;
	}
}