view dmd/expression/Util.d @ 135:af1bebfd96a4 dmd2037

dmd 2.038
author Eldar Insafutdinov <e.insafutdinov@gmail.com>
date Mon, 13 Sep 2010 22:19:42 +0100
parents 60bb0fe4563e
children ea6325d0edd9
line wrap: on
line source

module dmd.expression.Util;

import dmd.common;
import dmd.Expression;
import dmd.Loc;
import dmd.RealExp;
import dmd.Scope;
import dmd.FuncExp;
import dmd.DelegateExp;
import dmd.LINK;
import dmd.NullExp;
import dmd.SymOffExp;
import dmd.ExpInitializer;
import dmd.Lexer;
import dmd.TypeSArray;
import dmd.TypeArray;
import dmd.VarDeclaration;
import dmd.VoidInitializer;
import dmd.DeclarationExp;
import dmd.VarExp;
import dmd.NewExp;
import dmd.STC;
import dmd.WANT;
import dmd.IndexExp;
import dmd.AssignExp;
import dmd.CommaExp;
import dmd.CondExp;
import dmd.Parameter;
import dmd.DefaultInitExp;
import dmd.Identifier;
import dmd.Dsymbol;
import dmd.Global;
import dmd.ScopeDsymbol;
import dmd.DotIdExp;
import dmd.DotVarExp;
import dmd.CallExp;
import dmd.TY;
import dmd.MATCH;
import dmd.BUILTIN;
import dmd.TypeFunction;
import dmd.declaration.Match;
import dmd.ArrayTypes;
import dmd.Declaration;
import dmd.FuncAliasDeclaration;
import dmd.AliasDeclaration;
import dmd.FuncDeclaration;
import dmd.TemplateDeclaration;
import dmd.AggregateDeclaration;
import dmd.IntegerExp;
import dmd.Type;
import dmd.TOK;
import dmd.TypeExp;
import dmd.TypeTuple;
import dmd.TupleExp;
import dmd.OutBuffer;
import dmd.HdrGenState;
import dmd.ClassDeclaration;
import dmd.TypeClass;
import dmd.StructDeclaration;
import dmd.TypeStruct;
import dmd.MOD;
import dmd.PROT;
import dmd.PREC;
import dmd.Util;
import dmd.TypeAArray;
import dmd.Id;
import dmd.PtrExp;
import dmd.ErrorExp;

import std.stdio : writef;

import core.stdc.math;
import core.stdc.string;

/***********************************
 * Utility to build a function call out of this reference and argument.
 */
Expression build_overload(Loc loc, Scope sc, Expression ethis, Expression earg, Identifier id)
{
    Expression e;

    //printf("build_overload(id = '%s')\n", id.toChars());
    //earg.print();
    //earg.type.print();
    e = new DotIdExp(loc, ethis, id);

    if (earg)
		e = new CallExp(loc, e, earg);
    else
		e = new CallExp(loc, e);

    e = e.semantic(sc);
    return e;
}

/***************************************
 * Search for function funcid in aggregate ad.
 */

Dsymbol search_function(ScopeDsymbol ad, Identifier funcid)
{
    Dsymbol s;
    FuncDeclaration fd;
    TemplateDeclaration td;

    s = ad.search(Loc(0), funcid, 0);
    if (s)
    {
		Dsymbol s2;

		//printf("search_function: s = '%s'\n", s.kind());
		s2 = s.toAlias();
		//printf("search_function: s2 = '%s'\n", s2.kind());
		fd = s2.isFuncDeclaration();
		if (fd && fd.type.ty == TY.Tfunction)
			return fd;

		td = s2.isTemplateDeclaration();
		if (td)
			return td;
    }

    return null;
}

/********************************************
 * Find function in overload list that exactly matches t.
 */

/***************************************************
 * Visit each overloaded function in turn, and call
 * dg(param, f) on it.
 * Exit when no more, or dg(param, f) returns 1.
 * Returns:
 *	0	continue
 *	1	done
 */

int overloadApply(FuncDeclaration fstart, int delegate(void*, FuncDeclaration) dg, void* param)
{
    FuncDeclaration f;
    Declaration d;
    Declaration next;

    for (d = fstart; d; d = next)
    {	
		FuncAliasDeclaration fa = d.isFuncAliasDeclaration();

		if (fa)
		{
			if (overloadApply(fa.funcalias, dg, param))
				return 1;
			next = fa.overnext;
		}
		else
		{
			AliasDeclaration a = d.isAliasDeclaration();

			if (a)
			{
				Dsymbol s = a.toAlias();
				next = s.isDeclaration();
				if (next is a)
					break;
				if (next is fstart)
					break;
			}
			else
			{
				f = d.isFuncDeclaration();
				if (f is null)
				{  
					d.error("is aliased to a function");
					break;		// BUG: should print error message?
				}
				if (dg(param, f))
					return 1;

				next = f.overnext;
			}
		}
    }
    return 0;
}

/********************************************
 * Decide which function matches the arguments best.
 */

struct Param2
{
    Match* m;
version(DMDV2) {
    Expression ethis;
    int property;	// 0: unintialized
			// 1: seen @property
			// 2: not @property
}
    Expressions arguments;
	
	int fp2(void* param, FuncDeclaration f)
	{
        auto p = cast(Param2*)param;
		MATCH match;

		if (f != m.lastf)		// skip duplicates
		{
			m.anyf = f;
			TypeFunction tf = cast(TypeFunction)f.type;

	        int property = (tf.isproperty) ? 1 : 2;
	        if (p.property == 0)
	            p.property = property;
	        else if (p.property != property)
	            error(f.loc, "cannot overload both property and non-property functions");

			match = tf.callMatch(f.needThis() ? ethis : null, arguments);
			//printf("test: match = %d\n", match);
			if (match != MATCH.MATCHnomatch)
			{
				if (match > m.last)
					goto LfIsBetter;

				if (match < m.last)
					goto LlastIsBetter;

				/* See if one of the matches overrides the other.
				 */
				if (m.lastf.overrides(f))
					goto LlastIsBetter;
				else if (f.overrides(m.lastf))
					goto LfIsBetter;

version(DMDV2) {
				/* Try to disambiguate using template-style partial ordering rules.
				 * In essence, if f() and g() are ambiguous, if f() can call g(),
				 * but g() cannot call f(), then pick f().
				 * This is because f() is "more specialized."
				 */
				{
					MATCH c1 = f.leastAsSpecialized(m.lastf);
					MATCH c2 = m.lastf.leastAsSpecialized(f);
					//printf("c1 = %d, c2 = %d\n", c1, c2);
					if (c1 > c2)
						goto LfIsBetter;
					if (c1 < c2)
						goto LlastIsBetter;
				}
}

			Lambiguous:
				m.nextf = f;
				m.count++;
				return 0;

			LfIsBetter:
				m.last = match;
				m.lastf = f;
				m.count = 1;
				return 0;

			LlastIsBetter:
				return 0;
			}
		}
		return 0;
	}
}

struct Param1
{
    Type t;		// type to match
    FuncDeclaration f;	// return value
	
	int fp1(void*, FuncDeclaration f)
	{   
		if (t.equals(f.type))
		{	
			this.f = f;
			return 1;
		}

	version (DMDV2) {
        /* Allow covariant matches, as long as the return type
         * is just a const conversion.
         * This allows things like pure functions to match with an impure function type.
         */
		if (t.ty == Tfunction)
		{   
			TypeFunction tf = cast(TypeFunction)f.type;
			if (tf.covariant(t) == 1 &&
				tf.nextOf().implicitConvTo(t.nextOf()) >= MATCHconst)
			{
				this.f = f;
				return 1;
			}
		}
	}
		return 0;
	}
}

void overloadResolveX(Match* m, FuncDeclaration fstart, Expression ethis, Expressions arguments)
{
    Param2 p;
    p.m = m;
    p.ethis = ethis;
    p.property = 0;
    p.arguments = arguments;
    overloadApply(fstart, &p.fp2, &p);
}

void templateResolve(Match* m, TemplateDeclaration td, Scope sc, Loc loc, Objects targsi, Expression ethis, Expressions arguments)
{
    FuncDeclaration fd;

    assert(td);
    fd = td.deduceFunctionTemplate(sc, loc, targsi, ethis, arguments);
    if (!fd)
		return;
    m.anyf = fd;
    if (m.last >= MATCH.MATCHexact)
    {
		m.nextf = fd;
		m.count++;
    }
    else
    {
		m.last = MATCH.MATCHexact;
		m.lastf = fd;
		m.count = 1;
    }
}

/******************************
 * Perform semantic() on an array of Expressions.
 */

void arrayExpressionSemantic(Expressions exps, Scope sc)
{
    if (exps)
    {
		foreach (ref Expression e; exps)
		{   
			e = e.semantic(sc);
		}
    }
}

Expressions arrayExpressionToCommonType(Scope sc, Expressions exps, Type *pt)
{
//version(DMDV1) {
//    /* The first element sets the type
//     */
//    Type *t0 = NULL;
//    for (size_t i = 0; i < exps->dim; i++)
//    {	Expression *e = (Expression *)exps->data[i];
//
//	if (!e->type)
//	{   error("%s has no value", e->toChars());
//	    e = new ErrorExp();
//	}
//	e = resolveProperties(sc, e);
//
//	if (!t0)
//	    t0 = e->type;
//	else
//	    e = e->implicitCastTo(sc, t0);
//	exps->data[i] = (void *)e;
//    }
//
//    if (!t0)
//	t0 = Type::tvoid;
//    if (pt)
//	*pt = t0;
//
//    // Eventually, we want to make this copy-on-write
//    return exps;
//}
version(DMDV2) {
    /* The type is determined by applying ?: to each pair.
     */
    /* Still have a problem with:
     *	ubyte[][] = [ cast(ubyte[])"hello", [1]];
     * which works if the array literal is initialized top down with the ubyte[][]
     * type, but fails with this function doing bottom up typing.
     */
    //printf("arrayExpressionToCommonType()\n");
    scope integerexp = new IntegerExp(0);
    scope condexp = new CondExp(Loc(0), integerexp, null, null);

    Type t0;
    Expression e0;
    int j0;
    foreach (size_t i, Expression e; exps)
    {
		e = resolveProperties(sc, e);
		if (!e.type)
		{   error("%s has no value", e.toChars());
		    e = new ErrorExp();
		}

		if (t0)
		{ 
			if (t0 != e.type)
		    {
				/* This applies ?: to merge the types. It's backwards;
				* ?: should call this function to merge types.
				*/
				condexp.type = null;
				condexp.e1 = e0;
				condexp.e2 = e;
				condexp.semantic(sc);
				exps[j0] = condexp.e1;
				e = condexp.e2;
				j0 = i;
				e0 = e;
				t0 = e0.type;
			}
		}
		else
		{
			j0 = i;
			e0 = e;
			t0 = e.type;
		}
		exps[i] = e;
    }

    if (t0)
    {
		foreach (ref Expression e; exps)
		{
			e = e.implicitCastTo(sc, t0);
		}
    }
    else
		t0 = Type.tvoid;		// [] is typed as void[]
    if (pt)
		*pt = t0;

    // Eventually, we want to make this copy-on-write
    return exps;
}
}

/****************************************
 * Preprocess arguments to function.
 */

void preFunctionParameters(Loc loc, Scope sc, Expressions exps)
{
    if (exps)
    {
		expandTuples(exps);
		
		foreach (size_t i, ref Expression arg; exps)
		{   
			if (!arg.type)
			{
debug {
				if (!global.gag) {
					writef("1: \n");
				}
}
				arg.error("%s is not an expression", arg.toChars());
				arg = new IntegerExp(arg.loc, 0, Type.tint32);
			}

			arg = resolveProperties(sc, arg);

			//arg.rvalue();
static if (false) {
			if (arg.type.ty == TY.Tfunction)
			{
				arg = new AddrExp(arg.loc, arg);
				arg = arg.semantic(sc);
			}
}
		}
    }
}

/*************************************************************
 * Given var, we need to get the
 * right 'this' pointer if var is in an outer class, but our
 * existing 'this' pointer is in an inner class.
 * Input:
 *	e1	existing 'this'
 *	ad	struct or class we need the correct 'this' for
 *	var	the specific member of ad we're accessing
 */

Expression getRightThis(Loc loc, Scope sc, AggregateDeclaration ad, Expression e1, Declaration var)
{
	//printf("\ngetRightThis(e1 = %s, ad = %s, var = %s)\n", e1.toChars(), ad.toChars(), var.toChars());
 L1:
    Type t = e1.type.toBasetype();
    //printf("e1.type = %s, var.type = %s\n", e1.type.toChars(), var.type.toChars());

    /* If e1 is not the 'this' pointer for ad
     */
    if (ad && !(t.ty == TY.Tpointer && t.nextOf().ty == TY.Tstruct && (cast(TypeStruct)t.nextOf()).sym == ad) && !(t.ty == TY.Tstruct && (cast(TypeStruct)t).sym == ad))
    {
		ClassDeclaration cd = ad.isClassDeclaration();
		ClassDeclaration tcd = t.isClassHandle();

		/* e1 is the right this if ad is a base class of e1
		 */
		if (!cd || !tcd || !(tcd == cd || cd.isBaseOf(tcd, null)))
		{
			/* Only classes can be inner classes with an 'outer'
			 * member pointing to the enclosing class instance
			 */
			if (tcd && tcd.isNested())
			{   
				/* e1 is the 'this' pointer for an inner class: tcd.
				 * Rewrite it as the 'this' pointer for the outer class.
				 */

				e1 = new DotVarExp(loc, e1, tcd.vthis);
				e1.type = tcd.vthis.type;
				// Do not call checkNestedRef()
				//e1 = e1.semantic(sc);

				// Skip up over nested functions, and get the enclosing
				// class type.
				int n = 0;
				Dsymbol s;
				for (s = tcd.toParent(); s && s.isFuncDeclaration(); s = s.toParent())
				{   
					FuncDeclaration f = s.isFuncDeclaration();
					if (f.vthis)
					{
						//printf("rewriting e1 to %s's this\n", f.toChars());
						n++;
						e1 = new VarExp(loc, f.vthis);
					}
				}
				if (s && s.isClassDeclaration())
				{   
					e1.type = s.isClassDeclaration().type;
					if (n > 1)
						e1 = e1.semantic(sc);
				}
				else
					e1 = e1.semantic(sc);
				goto L1;
			}
			/* Can't find a path from e1 to ad
			 */
			e1.error("this for %s needs to be type %s not type %s", var.toChars(), ad.toChars(), t.toChars());
		}
    }
    return e1;
}

/*******************************************
 * Given a symbol that could be either a FuncDeclaration or
 * a function template, resolve it to a function symbol.
 *	sc		instantiation scope
 *	loc		instantiation location
 *	targsi		initial list of template arguments
 *	ethis		if !null, the 'this' pointer argument
 *	fargs		arguments to function
 *	flags		1: do not issue error message on no match, just return null
 */

FuncDeclaration resolveFuncCall(Scope sc, Loc loc, Dsymbol s,
	Objects tiargs,
	Expression ethis,
	Expressions arguments,
	int flags)
{
	if (!s)
		return null;			// no match
    FuncDeclaration f = s.isFuncDeclaration();
    if (f)
		f = f.overloadResolve(loc, ethis, arguments);
    else
    {	
		TemplateDeclaration td = s.isTemplateDeclaration();
		assert(td);
		f = td.deduceFunctionTemplate(sc, loc, tiargs, null, arguments, flags);
    }
    return f;
}

/****************************************
 * Now that we know the exact type of the function we're calling,
 * the arguments[] need to be adjusted:
 *	1. implicitly convert argument to the corresponding parameter type
 *	2. add default arguments for any missing arguments
 *	3. do default promotions on arguments corresponding to ...
 *	4. add hidden _arguments[] argument
 *	5. call copy constructor for struct value arguments
 * Returns:
 *	return type from function
 */

Type functionParameters(Loc loc, Scope sc, TypeFunction tf, Expressions arguments)
{
    //printf("functionParameters()\n");
    assert(arguments);
    size_t nargs = arguments ? arguments.dim : 0;
    size_t nparams = Parameter.dim(tf.parameters);

    if (nargs > nparams && tf.varargs == 0)
	error(loc, "expected %zu arguments, not %zu for non-variadic function type %s", nparams, nargs, tf.toChars());

    uint n = (nargs > nparams) ? nargs : nparams;	// n = max(nargs, nparams)

    uint wildmatch = 0;
    
    int done = 0;
    for (size_t i = 0; i < n; i++)
    {
		Expression arg;

		if (i < nargs)
			arg = arguments[i];
		else
			arg = null;

		Type tb;

		if (i < nparams)
		{
			auto p = Parameter.getNth(tf.parameters, i);

			if (!arg)
			{
				if (!p.defaultArg)
				{
					if (tf.varargs == 2 && i + 1 == nparams)
						goto L2;

					error(loc, "expected %d function arguments, not %d", nparams, nargs);
					return tf.next;
				}
				arg = p.defaultArg;
				arg = arg.copy();
version (DMDV2)
{
				arg = arg.resolveLoc(loc, sc);		// __FILE__ and __LINE__
}
				arguments.push(arg);
				nargs++;
			}

			if (tf.varargs == 2 && i + 1 == nparams)
			{
				//printf("\t\tvarargs == 2, p.type = '%s'\n", p.type.toChars());
				if (arg.implicitConvTo(p.type))
				{
					if (nargs != nparams)
					{
						error(loc, "expected %zu function arguments, not %zu", nparams, nargs);
						return tf.next;
					}
					goto L1;
				}
				 L2:
				tb = p.type.toBasetype();		///
				Type tret = p.isLazyArray();
				switch (tb.ty)
				{
					case TY.Tsarray:
					case TY.Tarray:
					{	// Create a static array variable v of type arg.type
version (IN_GCC) {
						/* GCC 4.0 does not like zero length arrays used like
						   this; pass a null array value instead. Could also
						   just make a one-element array. */
						if (nargs - i == 0)
						{
							arg = new NullExp(loc);
							break;
						}
}
						Identifier id = Lexer.uniqueId("__arrayArg");
						Type t = new TypeSArray((cast(TypeArray)tb).next, new IntegerExp(nargs - i));
						t = t.semantic(loc, sc);
						VarDeclaration v = new VarDeclaration(loc, t, id, new VoidInitializer(loc));
			            v.storage_class |= STCctfe;
						v.semantic(sc);
						v.parent = sc.parent;
						//sc.insert(v);

						Expression c = new DeclarationExp(Loc(0), v);
						c.type = v.type;

						for (size_t u = i; u < nargs; u++)
						{   
							auto a = arguments[u];
							if (tret && !(cast(TypeArray)tb).next.equals(a.type))
								a = a.toDelegate(sc, tret);

							Expression e = new VarExp(loc, v);
							e = new IndexExp(loc, e, new IntegerExp(u + 1 - nparams));
							auto ae = new AssignExp(loc, e, a);

			version (DMDV2) {
							ae.op = TOK.TOKconstruct;
			}

							if (c)
								c = new CommaExp(loc, c, ae);
							else
								c = ae;
						}

						arg = new VarExp(loc, v);
						if (c)
							arg = new CommaExp(loc, c, arg);
						break;
					}

					case TY.Tclass:
					{	/* Set arg to be:
						 *	new Tclass(arg0, arg1, ..., argn)
						 */
						Expressions args = new Expressions();
						args.setDim(nargs - i);
						for (size_t u = i; u < nargs; u++)
							args[u - i] = arguments[u];
						arg = new NewExp(loc, null, null, p.type, args);
						break;
					}

					default:
						if (!arg)
						{   
							error(loc, "not enough arguments");
							return tf.next;
						}
						break;
				}

				arg = arg.semantic(sc);
				//printf("\targ = '%s'\n", arg.toChars());
				arguments.setDim(i + 1);
				done = 1;
			}

		L1:
			if (!(p.storageClass & STC.STClazy && p.type.ty == TY.Tvoid))
			{
				if (p.type != arg.type)
				{
					//printf("arg.type = %s, p.type = %s\n", arg.type.toChars(), p.type.toChars());
					if (arg.op == TOKtype)
						arg.error("cannot pass type %s as function argument", arg.toChars());
		            if (p.type.isWild() && tf.next.isWild())
		            {	
                        Type t = p.type;
			            MATCH m = arg.implicitConvTo(t);
			            if (m == MATCH.MATCHnomatch)
			            {
                            t = t.constOf();
			                m = arg.implicitConvTo(t);
			                if (m == MATCHnomatch)
			                {
                                t = t.sharedConstOf();
				                m = arg.implicitConvTo(t);
			                }
			                wildmatch |= p.type.wildMatch(arg.type);
			            }
			            arg = arg.implicitCastTo(sc, t);
		            }
		            else
    					arg = arg.implicitCastTo(sc, p.type);
					arg = arg.optimize(WANT.WANTvalue);
				}
			}
			if (p.storageClass & STC.STCref)
			{
				arg = arg.toLvalue(sc, arg);
			}
			else if (p.storageClass & STC.STCout)
			{
				arg = arg.modifiableLvalue(sc, arg);
			}

			tb = arg.type.toBasetype();
version(SARRAYVALUE) {} else
{
			// Convert static arrays to pointers
			if (tb.ty == TY.Tsarray)
			{
				arg = arg.checkToPointer();
			}
}
version (DMDV2) {
			if (tb.ty == TY.Tstruct && !(p.storageClass & (STC.STCref | STC.STCout)))
			{
				arg = callCpCtor(loc, sc, arg);
			}
}

			// Convert lazy argument to a delegate
			if (p.storageClass & STC.STClazy)
			{
				arg = arg.toDelegate(sc, p.type);
			}
version (DMDV2) {
			/* Look for arguments that cannot 'escape' from the called
			 * function.
			 */
			if (!tf.parameterEscapes(p))
			{
				/* Function literals can only appear once, so if this
				 * appearance was scoped, there cannot be any others.
				 */
				if (arg.op == TOK.TOKfunction)
				{   
					FuncExp fe = cast(FuncExp)arg;
					fe.fd.tookAddressOf = 0;
				}

				/* For passing a delegate to a scoped parameter,
				 * this doesn't count as taking the address of it.
				 * We only worry about 'escaping' references to the function.
				 */
				else if (arg.op == TOK.TOKdelegate)
				{   
					DelegateExp de = cast(DelegateExp)arg;
					if (de.e1.op == TOK.TOKvar)
					{	
						VarExp ve = cast(VarExp)de.e1;
						FuncDeclaration f = ve.var.isFuncDeclaration();
						if (f)
						{   
							f.tookAddressOf--;
							//printf("tookAddressOf = %d\n", f.tookAddressOf);
						}
					}
				}
			}
}
		}
		else
		{
			// If not D linkage, do promotions
			if (tf.linkage != LINK.LINKd)
			{
				// Promote bytes, words, etc., to ints
				arg = arg.integralPromotions(sc);

				// Promote floats to doubles
				switch (arg.type.ty)
				{
					case TY.Tfloat32:
						arg = arg.castTo(sc, Type.tfloat64);
						break;

					case TY.Timaginary32:
						arg = arg.castTo(sc, Type.timaginary64);
						break;
					default:
						break;
				}
			}

			// Convert static arrays to dynamic arrays
			tb = arg.type.toBasetype();
			if (tb.ty == TY.Tsarray)
			{	
				TypeSArray ts = cast(TypeSArray)tb;
				Type ta = ts.next.arrayOf();
				if (ts.size(arg.loc) == 0)
					arg = new NullExp(arg.loc, ta);
				else
					arg = arg.castTo(sc, ta);
			}
version (DMDV2) {
			if (tb.ty == Tstruct)
			{
				arg = callCpCtor(loc, sc, arg);
			}
}

	    // Give error for overloaded function addresses
	    if (arg.op == TOKsymoff)
	    {	
			SymOffExp se = cast(SymOffExp)arg;
version (DMDV2) {
			bool aux = (se.hasOverloads != 0);
} else {
			bool aux = true;
}
		if (aux && !se.var.isFuncDeclaration().isUnique())
		    arg.error("function %s is overloaded", arg.toChars());
	    }

	    arg.rvalue();
	}

		arg = arg.optimize(WANT.WANTvalue);
		arguments[i] = arg;
		if (done)
			break;
    }

    // If D linkage and variadic, add _arguments[] as first argument
    if (tf.linkage == LINK.LINKd && tf.varargs == 1)
    {
		assert(arguments.dim >= nparams);
		auto e = createTypeInfoArray(sc, &arguments[nparams], arguments.dim - nparams);
		arguments.insert(0, e);
    }
    
    Type tret = tf.next;
    if (wildmatch)
    {	/* Adjust function return type based on wildmatch
	 */
	    //printf("wildmatch = x%x\n", wildmatch);
	    assert(tret.isWild());
	    if (wildmatch & MOD.MODconst || wildmatch & (wildmatch - 1))
	        tret = tret.constOf();
	    else if (wildmatch & MOD.MODimmutable)
	        tret = tret.invariantOf();
	    else
	    {
            assert(wildmatch & MOD.MODmutable);
	        tret = tret.mutableOf();
	    }
    }
    return tret;
}

/******************************
 * Perform canThrow() on an array of Expressions.
 */

version (DMDV2) {
bool arrayExpressionCanThrow(Expressions exps)
{
    if (exps)
    {
	foreach (e; exps)
	{
	    if (e && e.canThrow())
		return true;
	}
    }
    return false;
}
}

/****************************************
 * Expand tuples.
 */

void expandTuples(Expressions exps)
{
    //printf("expandTuples()\n");
    if (exps)
    {
		for (size_t i = 0; i < exps.dim; i++)
		{   
			auto arg = exps[i];
			if (!arg)
				continue;

			// Look for tuple with 0 members
			if (arg.op == TOK.TOKtype)
			{
				auto e = cast(TypeExp)arg;
				if (e.type.toBasetype().ty == TY.Ttuple)
				{   
					auto tt = cast(TypeTuple)e.type.toBasetype();

					if (!tt.arguments || tt.arguments.dim == 0)
					{
						exps.remove(i);
						if (i == exps.dim)
							return;
						i--;
						continue;
					}
				}
			}

			// Inline expand all the tuples
			while (arg.op == TOK.TOKtuple)
			{	
				auto te = cast(TupleExp)arg;

				exps.remove(i);		// remove arg
				exps.insert(i, te.exps);	// replace with tuple contents

				if (i == exps.dim)
					return;		// empty tuple, no more arguments

				arg = exps[i];
			}
		}
    }
}

/**************************************************
 * Write out argument types to buf.
 */

void argExpTypesToCBuffer(OutBuffer buf, Expressions arguments, HdrGenState* hgs)
{
    if (arguments)
    {	
		scope OutBuffer argbuf = new OutBuffer();

		foreach (size_t i, Expression arg; arguments)
		{   
			if (i)
				buf.writeByte(',');

			argbuf.reset();
			arg.type.toCBuffer2(argbuf, hgs, MOD.MODundefined);
			buf.write(argbuf);
		}
    }
}

/****************************************
 * Determine if scope sc has package level access to s.
 */

bool hasPackageAccess(Scope sc, Dsymbol s)
{
version (LOG) {
    printf("hasPackageAccess(s = '%s', sc = '%p')\n", s.toChars(), sc);
}

    for (; s; s = s.parent)
    {
		if (s.isPackage() && !s.isModule())
			break;
    }
version (LOG) {
    if (s)
		printf("\tthis is in package '%s'\n", s.toChars());
}

    if (s && s == sc.module_.parent)
    {
version (LOG) {
		printf("\ts is in same package as sc\n");
}
		return true;
    }


version (LOG) {
    printf("\tno package access\n");
}

    return false;
}

/*********************************************
 * Call copy constructor for struct value argument.
 */
version (DMDV2) {
	Expression callCpCtor(Loc loc, Scope sc, Expression e)
	{
		Type tb = e.type.toBasetype();
		assert(tb.ty == Tstruct);
		StructDeclaration sd = (cast(TypeStruct)tb).sym;
		if (sd.cpctor)
		{
			/* Create a variable tmp, and replace the argument e with:
			 *	(tmp = e),tmp
			 * and let AssignExp() handle the construction.
			 * This is not the most efficent, ideally tmp would be constructed
			 * directly onto the stack.
			 */
			Identifier idtmp = Lexer.uniqueId("__tmp");
			VarDeclaration tmp = new VarDeclaration(loc, tb, idtmp, new ExpInitializer(Loc(0), e));
			Expression ae = new DeclarationExp(loc, tmp);
			e = new CommaExp(loc, ae, new VarExp(loc, tmp));
			e = e.semantic(sc);
		}
		return e;
	}
}

/***************************************
 * Create a static array of TypeInfo references
 * corresponding to an array of Expression's.
 * Used to supply hidden _arguments[] value for variadic D functions.
 */

Expression createTypeInfoArray(Scope sc, Expression* exps, int dim)
{
static if (true)
{
	/* Get the corresponding TypeInfo_Tuple and
	 * point at its elements[].
	 */

	/* Create the TypeTuple corresponding to the types of args[]
	 */
	auto args = new Parameters;
	args.setDim(dim);
	for (size_t i = 0; i < dim; i++)
	{	
		auto arg = new Parameter(STCin, exps[i].type, null, null);
		args[i] = arg;
	}
	TypeTuple tup = new TypeTuple(args);
	Expression e = tup.getTypeInfo(sc);
	e = e.optimize(WANTvalue);
	assert(e.op == TOKsymoff);		// should be SymOffExp

version (BREAKABI)
{
	/*
	 * Should just pass a reference to TypeInfo_Tuple instead,
	 * but that would require existing code to be recompiled.
	 * Source compatibility can be maintained by computing _arguments[]
	 * at the start of the called function by offseting into the
	 * TypeInfo_Tuple reference.
	 */

}
else
{
	// Advance to elements[] member of TypeInfo_Tuple
	SymOffExp se = cast(SymOffExp)e;
	se.offset += PTRSIZE + PTRSIZE;

	// Set type to TypeInfo[]*
	se.type = Type.typeinfo.type.arrayOf().pointerTo();

	// Indirect to get the _arguments[] value
	e = new PtrExp(Loc(0), se);
	e.type = se.type.next;
}
	return e;
} // of static if (true)
else
{
	/* Improvements:
	 * 1) create an array literal instead,
	 * as it would eliminate the extra dereference of loading the
	 * static variable.
	 */

	ArrayInitializer ai = new ArrayInitializer(Loc(0));
	VarDeclaration v;
	Type t;
	Expression e;
	scope OutBuffer buf = new OutBuffer();
	Identifier id;
	string name;

	// Generate identifier for _arguments[]
	buf.writestring("_arguments_");
	for (int i = 0; i < dim; i++)
	{	
		t = exps[i].type;
		t.toDecoBuffer(buf);
	}
	buf.writeByte(0);
	id = Lexer.idPool(buf.extractString());

	Module m = sc.module_;
	Dsymbol s = m.symtab.lookup(id);

	if (s && s.parent == m)
	{	
		// Use existing one
		v = s.isVarDeclaration();
		assert(v);
	}
	else
	{	
		// Generate new one

		for (int i = 0; i < dim; i++)
		{   
			t = exps[i].type;
			e = t.getTypeInfo(sc);
			ai.addInit(new IntegerExp(i), new ExpInitializer(0, e));
		}

		t = Type.typeinfo.type.arrayOf();
		ai.type = t;
		v = new VarDeclaration(0, t, id, ai);
		m.members.push(v);
		m.symtabInsert(v);
		sc = sc.push();
		sc.linkage = LINKc;
		sc.stc = STCstatic | STCcomdat;
		ai.semantic(sc, t);
		v.semantic(sc);
		v.parent = m;
		sc = sc.pop();
	}
	e = new VarExp(0, v);
	e = e.semantic(sc);
	return e;
}
}

/**************************************
 * Evaluate builtin function.
 * Return result: null if cannot evaluate it.
 */

extern(C) extern real sinl(real);
extern(C) extern real cosl(real);
extern(C) extern real tanl(real);
extern(C) extern real sqrtl(real);
extern(C) extern real fabsl(real);
 
Expression eval_builtin(BUILTIN builtin, Expressions arguments)
{
	assert(arguments && arguments.dim);
    auto arg0 = arguments[0];
    Expression e = null;
    switch (builtin)
    {
	case BUILTINsin:
	    if (arg0.op == TOKfloat64)
		e = new RealExp(Loc(0), sinl(arg0.toReal()), arg0.type);
	    break;

	case BUILTINcos:
	    if (arg0.op == TOKfloat64)
		e = new RealExp(Loc(0), cosl(arg0.toReal()), arg0.type);
	    break;

	case BUILTINtan:
	    if (arg0.op == TOKfloat64)
		e = new RealExp(Loc(0), tanl(arg0.toReal()), arg0.type);
	    break;

	case BUILTINsqrt:
	    if (arg0.op == TOKfloat64)
		e = new RealExp(Loc(0), sqrtl(arg0.toReal()), arg0.type);
	    break;

	case BUILTINfabs:
	    if (arg0.op == TOKfloat64)
		e = new RealExp(Loc(0), fabsl(arg0.toReal()), arg0.type);
	    break;
    }
    return e;
}

Expression fromConstInitializer(int result, Expression e1)
{
    //printf("fromConstInitializer(result = %x, %s)\n", result, e1.toChars());
    //static int xx; if (xx++ == 10) assert(0);
    Expression e = e1;
    if (e1.op == TOK.TOKvar)
    {	
		VarExp ve = cast(VarExp)e1;
		VarDeclaration v = ve.var.isVarDeclaration();
		e = expandVar(result, v);
		if (e)
		{   
			if (e.type != e1.type)
			{   
				// Type 'paint' operation
				e = e.copy();
				e.type = e1.type;
			}
			e.loc = e1.loc;
		}
		else
		{
			e = e1;
		}
    }
    return e;
}

/*************************************
 * If variable has a const initializer,
 * return that initializer.
 */

Expression expandVar(int result, VarDeclaration v)
{
	//printf("expandVar(result = %d, v = %p, %s)\n", result, v, v ? v.toChars() : "null");

    Expression e = null;
    if (!v)
		return e;

    if (v.isConst() || v.isImmutable() || v.storage_class & STC.STCmanifest)
    {
		if (!v.type)
		{
			//error("ICE");
			return e;
		}

		Type tb = v.type.toBasetype();
		if (result & WANT.WANTinterpret || v.storage_class & STC.STCmanifest || (tb.ty != TY.Tsarray && tb.ty != TY.Tstruct))
		{
			if (v.init)
			{
				if (v.inuse)
				{   
					if (v.storage_class & STC.STCmanifest)
						v.error("recursive initialization of constant");
					goto L1;
				}
				Expression ei = v.init.toExpression();
				if (!ei)
					goto L1;
				if (ei.op == TOK.TOKconstruct || ei.op == TOK.TOKblit)
				{   
					AssignExp ae = cast(AssignExp)ei;
					ei = ae.e2;
					if (ei.isConst() != 1 && ei.op != TOK.TOKstring)
						goto L1;
					if (ei.type != v.type)
						goto L1;
				}
				if (v.scope_)
				{
					v.inuse++;
					e = ei.syntaxCopy();
					e = e.semantic(v.scope_);
					e = e.implicitCastTo(v.scope_, v.type);
					// enabling this line causes test22 in test suite to fail
					//ei.type = e.type;
					v.scope_ = null;
					v.inuse--;
				}
				else if (!ei.type)
				{
					goto L1;
				}
				else
					// Should remove the copy() operation by
					// making all mods to expressions copy-on-write
					e = ei.copy();
			}
			else
			{
static if (true) {
			goto L1;
} else {
			// BUG: what if const is initialized in constructor?
			e = v.type.defaultInit();
			e.loc = e1.loc;
}
			}
			if (e.type != v.type)
			{
				e = e.castTo(null, v.type);
			}
			v.inuse++;
			e = e.optimize(result);
			v.inuse--;
		}
    }
L1:
    //if (e) printf("\te = %s, e.type = %s\n", e.toChars(), e.type.toChars());
    return e;
}

/****************************************
 * Check access to d for expression e.d
 */

void accessCheck(Loc loc, Scope sc, Expression e, Declaration d)
{
version (LOG) {
    if (e)
    {	
		printf("accessCheck(%s . %s)\n", e.toChars(), d.toChars());
		printf("\te.type = %s\n", e.type.toChars());
    }
    else
    {
		//printf("accessCheck(%s)\n", d.toChars());
    }
}
    if (!e)
    {
		if (d.prot() == PROT.PROTprivate && d.getModule() != sc.module_ ||
			d.prot() == PROT.PROTpackage && !hasPackageAccess(sc, d))

			error(loc, "%s %s.%s is not accessible from %s",
			d.kind(), d.getModule().toChars(), d.toChars(), sc.module_.toChars());
    }
    else if (e.type.ty == TY.Tclass)
    {   
		// Do access check
		ClassDeclaration cd;

		cd = cast(ClassDeclaration)((cast(TypeClass)e.type).sym);
static if (true) {
		if (e.op == TOK.TOKsuper)
		{   
			ClassDeclaration cd2 = sc.func.toParent().isClassDeclaration();
			if (cd2)
				cd = cd2;
		}
}
		cd.accessCheck(loc, sc, d);
    }
    else if (e.type.ty == TY.Tstruct)
    {   
		// Do access check
		StructDeclaration cd = cast(StructDeclaration)((cast(TypeStruct)e.type).sym);
		cd.accessCheck(loc, sc, d);
    }
}

/*****************************************
 * Given array of arguments and an aggregate type,
 * if any of the argument types are missing, attempt to infer
 * them from the aggregate type.
 */

void inferApplyArgTypes(TOK op, Parameters arguments, Expression aggr)
{
    if (!arguments || !arguments.dim)
		return;

    /* Return if no arguments need types.
     */
    for (size_t u = 0; 1; u++)
    {	
		if (u == arguments.dim)
			return;

		auto arg = arguments[u];
		if (!arg.type)
			break;
    }

    Dsymbol s;
    AggregateDeclaration ad;

    auto arg = arguments[0];
    Type taggr = aggr.type;
    if (!taggr)
		return;
    Type tab = taggr.toBasetype();
    switch (tab.ty)
    {
		case TY.Tarray:
		case TY.Tsarray:
		case TY.Ttuple:
			if (arguments.dim == 2)
			{
				if (!arg.type)
					arg.type = Type.tsize_t;	// key type
				arg = arguments[1];
			}
			if (!arg.type && tab.ty != TY.Ttuple)
				arg.type = tab.nextOf();	// value type
			break;

		case TY.Taarray:
		{   
			auto taa = cast(TypeAArray)tab;

			if (arguments.dim == 2)
			{
				if (!arg.type)
					arg.type = taa.index;	// key type
				arg = arguments[1];
			}
			if (!arg.type)
				arg.type = taa.next;		// value type
			break;
		}

		case TY.Tclass:
			ad = (cast(TypeClass)tab).sym;
			goto Laggr;

		case TY.Tstruct:
			ad = (cast(TypeStruct)tab).sym;
			goto Laggr;

		Laggr:
	        s = search_function(ad, (op == TOKforeach_reverse) ? Id.applyReverse : Id.apply);
	        if (s)
		        goto Lapply;			// prefer opApply

			if (arguments.dim == 1)
			{
				if (!arg.type)
				{
					/* Look for a head() or rear() overload
					 */
					Identifier id = (op == TOK.TOKforeach) ? Id.Fhead : Id.Ftoe;
					Dsymbol s1 = search_function(ad, id);
					FuncDeclaration fd = s1 ? s1.isFuncDeclaration() : null;
					if (!fd)
					{	
						if (s1 && s1.isTemplateDeclaration())
							break;
						goto Lapply;
					}
					arg.type = fd.type.nextOf();
				}
				break;
			}

		Lapply:
		{   /* Look for an
			 *	int opApply(int delegate(ref Type [, ...]) dg);
			 * overload
			 */
			if (s)
			{
				FuncDeclaration fd = s.isFuncDeclaration();
				if (fd) 
				{   
					inferApplyArgTypesX(fd, arguments);
					break;
				}
static if (false) {
				TemplateDeclaration td = s.isTemplateDeclaration();
				if (td)
				{   
					inferApplyArgTypesZ(td, arguments);
					break;
				}
}
			}
			break;
		}

		case TY.Tdelegate:
		{
			if (0 && aggr.op == TOK.TOKdelegate)
			{	
				DelegateExp de = cast(DelegateExp)aggr;

				FuncDeclaration fd = de.func.isFuncDeclaration();
				if (fd)
					inferApplyArgTypesX(fd, arguments);
				}
			else
			{
				inferApplyArgTypesY(cast(TypeFunction)tab.nextOf(), arguments);
			}
			break;
		}

		default:
			break;		// ignore error, caught later
    }
}

struct Param3
{
	/********************************
	 * Recursive helper function,
	 * analogous to func.overloadResolveX().
	 */

	int fp3(void*, FuncDeclaration f)
	{
		auto tf = cast(TypeFunction)f.type;
		if (inferApplyArgTypesY(tf, arguments) == 1)
			return 0;

		if (arguments.dim == 0)
			return 1;

		return 0;
	}
	
	Parameters arguments;
}

void inferApplyArgTypesX(FuncDeclaration fstart, Parameters arguments)
{
	Param3 p3;
	p3.arguments = arguments;
    overloadApply(fstart, &p3.fp3, cast(void*)arguments);
}

/******************************
 * Infer arguments from type of function.
 * Returns:
 *	0 match for this function
 *	1 no match for this function
 */

int inferApplyArgTypesY(TypeFunction tf, Parameters arguments)
{   
	size_t nparams;
    Parameter p;

    if (Parameter.dim(tf.parameters) != 1)
		goto Lnomatch;

    p = Parameter.getNth(tf.parameters, 0);
    if (p.type.ty != TY.Tdelegate)
		goto Lnomatch;

    tf = cast(TypeFunction)p.type.nextOf();
    assert(tf.ty == TY.Tfunction);

    /* We now have tf, the type of the delegate. Match it against
     * the arguments, filling in missing argument types.
     */
    nparams = Parameter.dim(tf.parameters);
    if (nparams == 0 || tf.varargs)
		goto Lnomatch;		// not enough parameters
    if (arguments.dim != nparams)
		goto Lnomatch;		// not enough parameters

    for (size_t u = 0; u < nparams; u++)
    {
		auto arg = arguments[u];
		auto param = Parameter.getNth(tf.parameters, u);
		if (arg.type)
		{   
			if (!arg.type.equals(param.type))
			{
				/* Cannot resolve argument types. Indicate an
				 * error by setting the number of arguments to 0.
				 */
				arguments.dim = 0;
				goto Lmatch;
			}
			continue;
		}
		arg.type = param.type;
    }

  Lmatch:
    return 0;

  Lnomatch:
    return 1;
}

/**************************************************
 * Write expression out to buf, but wrap it
 * in ( ) if its precedence is less than pr.
 */

void expToCBuffer(OutBuffer buf, HdrGenState* hgs, Expression e, PREC pr)
{
    //if (precedence[e.op] == 0) e.dump(0);
    if (precedence[e.op] < pr ||
	/* Despite precedence, we don't allow a<b<c expressions.
	 * They must be parenthesized.
	 */
	(pr == PREC.PREC_rel && precedence[e.op] == pr))
    {
		buf.writeByte('(');
		e.toCBuffer(buf, hgs);
		buf.writeByte(')');
    }
    else
		e.toCBuffer(buf, hgs);
}

/**************************************************
 * Write out argument list to buf.
 */

void argsToCBuffer(OutBuffer buf, Expressions arguments, HdrGenState* hgs)
{
    if (arguments)
    {
		foreach (size_t i, Expression arg; arguments)
		{   
			if (arg)
			{	
				if (i)
					buf.writeByte(',');
				expToCBuffer(buf, hgs, arg, PREC.PREC_assign);
			}
		}
    }
}

ulong getMask(ulong v)
{
    ulong u = 0;
    if (v >= 0x80)
		u = 0xFF;
    while (u < v)
		u = (u << 1) | 1;
    return u;
}

/******************************
 * Perform scanForNestedRef() on an array of Expressions.
 */

void arrayExpressionScanForNestedRef(Scope sc, Expressions a)
{
    //printf("arrayExpressionScanForNestedRef(%p)\n", a);
    if (a)
    {
		foreach (e; a)
		{   
			if (e)
			{
				e.scanForNestedRef(sc);
			}
		}
    }
}

void realToMangleBuffer(OutBuffer buf, real value)
{
    /* Rely on %A to get portable mangling.
     * Must munge result to get only identifier characters.
     *
     * Possible values from %A	=> mangled result
     * NAN			=> NAN
     * -INF			=> NINF
     * INF			=> INF
     * -0X1.1BC18BA997B95P+79	=> N11BC18BA997B95P79
     * 0X1.9P+2			=> 19P2
     */

    if (isnan(value))
		buf.writestring("NAN");	// no -NAN bugs
    else
    {
		char buffer[32];
		int n = sprintf(buffer.ptr, "%LA", value);
		assert(n > 0 && n < buffer.sizeof);
		for (int i = 0; i < n; i++)
		{   char c = buffer[i];

			switch (c)
			{
			case '-':
				buf.writeByte('N');
				break;

			case '+':
			case 'X':
			case '.':
				break;

			case '0':
				if (i < 2)
				break;		// skip leading 0X
			default:
				buf.writeByte(c);
				break;
			}
		}
    }
}

/********************************
 * Test to see if two reals are the same.
 * Regard NaN's as equivalent.
 * Regard +0 and -0 as different.
 */

int RealEquals(real x1, real x2)
{
    return (isnan(x1) && isnan(x2)) || 
		/* In some cases, the REALPAD bytes get garbage in them,
		 * so be sure and ignore them.
		 */
		memcmp(&x1, &x2, REALSIZE - REALPAD) == 0;
}