view dmd/FuncDeclaration.d @ 135:af1bebfd96a4 dmd2037

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

module dmd.FuncDeclaration;

import dmd.common;
import dmd.Declaration;
import dmd.DotIdExp;
import dmd.AddrExp;
import dmd.TryFinallyStatement;
import dmd.TryCatchStatement;
import dmd.Catch;
import dmd.DeclarationStatement;
import dmd.StaticDtorDeclaration;
import dmd.GlobalExpressions;
import dmd.PeelStatement;
import dmd.SynchronizedStatement;
import dmd.TOK;
import dmd.SymOffExp;
import dmd.AssignExp;
import dmd.ExpInitializer;
import dmd.BE;
import dmd.Id;
import dmd.StorageClassDeclaration;
import dmd.StringExp;
import dmd.DsymbolExp;
import dmd.HaltExp;
import dmd.CommaExp;
import dmd.ReturnStatement;
import dmd.IntegerExp;
import dmd.ExpStatement;
import dmd.CSX;
import dmd.CompoundStatement;
import dmd.LabelStatement;
import dmd.ThisExp;
import dmd.SuperExp;
import dmd.IdentifierExp;
import dmd.AssertExp;
import dmd.CallExp;
import dmd.RET;
import dmd.VarExp;
import dmd.TupleDeclaration;
import dmd.ThisDeclaration;
import dmd.TypeTuple;
import dmd.TemplateInstance;
import dmd.ScopeDsymbol;
import dmd.AliasDeclaration;
import dmd.MOD;
import dmd.PROT;
import dmd.Lexer;
import dmd.LINK;
import dmd.CtorDeclaration;
import dmd.Global;
import dmd.DtorDeclaration;
import dmd.InvariantDeclaration;
import dmd.TY;
import dmd.PtrExp;
import dmd.DeclarationExp;
import dmd.InlineDoState;
import dmd.Parameter;
import dmd.StructDeclaration;
import dmd.ClassDeclaration;
import dmd.InterfaceDeclaration;
import dmd.Array;
import dmd.Statement;
import dmd.Identifier;
import dmd.VarDeclaration;
import dmd.LabelDsymbol;
import dmd.DsymbolTable;
import dmd.ArrayTypes;
import dmd.Loc;
import dmd.ILS;
import dmd.ForeachStatement;
import dmd.Type;
import dmd.BUILTIN;
import dmd.TypeFunction;
import dmd.Expression;
import dmd.STC;
import dmd.TRUST;
import dmd.Dsymbol;
import dmd.Scope;
import dmd.OutBuffer;
import dmd.HdrGenState;
import dmd.MATCH;
import dmd.AggregateDeclaration;
import dmd.InterState;
import dmd.InlineScanState;
import dmd.IRState;
import dmd.Util;
import dmd.BaseClass;
import dmd.Module;
import dmd.InlineCostState;

import dmd.expression.Util;

import dmd.declaration.Match;

import dmd.backend.Symbol;
import dmd.backend.func_t;
import dmd.backend.Util;
import dmd.backend.glue;
import dmd.backend.SC;
import dmd.backend.F;
import dmd.backend.Cstate;
import dmd.backend.TYM;
import dmd.backend.OPER;
import dmd.backend.TYFL;
import dmd.backend.TYPE;
import dmd.backend.SFL;
import dmd.backend.mTY;
import dmd.backend.FL;
import dmd.backend.REG;
import dmd.backend.block;
import dmd.backend.Blockx;
import dmd.backend.Config;
import dmd.backend.BC;
import dmd.backend.elem;
import dmd.backend.targ_types;
import dmd.backend.mTYman;
import dmd.backend.RTLSYM;
import dmd.backend.LIST;

import core.stdc.stdio;
import core.stdc.string;
version (Bug4054) import core.memory;

import dmd.interpret.Util;

import std.string;

class FuncDeclaration : Declaration
{
    Array fthrows;			// Array of Type's of exceptions (not used)
    Statement frequire;
    Statement fensure;
    Statement fbody;
	
    FuncDeclarations foverrides;	// functions this function overrides
    FuncDeclaration fdrequire;		// function that does the in contract
    FuncDeclaration fdensure;		// function that does the out contract

    Identifier outId;			// identifier for out statement
    VarDeclaration vresult;		// variable corresponding to outId
    LabelDsymbol returnLabel;		// where the return goes

    DsymbolTable localsymtab;		// used to prevent symbols in different
					// scopes from having the same name
    VarDeclaration vthis;		// 'this' parameter (member and nested)
    VarDeclaration v_arguments;	// '_arguments' parameter
version (IN_GCC) {
    VarDeclaration v_argptr;	        // '_argptr' variable
}
    Dsymbols parameters;		// Array of VarDeclaration's for parameters
    DsymbolTable labtab;		// statement label symbol table
    Declaration overnext;		// next in overload list
    Loc endloc;				// location of closing curly bracket
    int vtblIndex;			// for member functions, index into vtbl[]
    int naked;				// !=0 if naked
    int inlineAsm;			// !=0 if has inline assembler
    ILS inlineStatus;
    int inlineNest;			// !=0 if nested inline
    int cantInterpret;			// !=0 if cannot interpret function
    int semanticRun;			// 1 semantic() run
					// 2 semantic2() run
					// 3 semantic3() started
					// 4 semantic3() done
					// 5 toObjFile() run
					// this function's frame ptr
    ForeachStatement fes;		// if foreach body, this is the foreach
    int introducing;			// !=0 if 'introducing' function
    Type tintro;			// if !=null, then this is the type
					// of the 'introducing' function
					// this one is overriding
    int inferRetType;			// !=0 if return type is to be inferred

    // Things that should really go into Scope
    int hasReturnExp;			// 1 if there's a return exp; statement
					// 2 if there's a throw statement
					// 4 if there's an assert(0)
					// 8 if there's inline asm

    // Support for NRVO (named return value optimization)
    bool nrvo_can = true;			// !=0 means we can do it
    VarDeclaration nrvo_var;		// variable to replace with shidden
    Symbol* shidden;			// hidden pointer passed to function

version (DMDV2) {
    BUILTIN builtin;		// set if this is a known, builtin
					// function we can evaluate at compile
					// time

    int tookAddressOf;			// set if someone took the address of
					// this function
    Dsymbols closureVars;		// local variables in this function
					// which are referenced by nested
					// functions
} else {
    int nestedFrameRef;			// !=0 if nested variables referenced
}

    this(Loc loc, Loc endloc, Identifier id, StorageClass storage_class, Type type)
	{
		super(id);

		//printf("FuncDeclaration(id = '%s', type = %p)\n", id.toChars(), type);
		//printf("storage_class = x%x\n", storage_class);
		this.storage_class = storage_class;
		this.type = type;
		this.loc = loc;
		this.endloc = endloc;
		fthrows = null;
		frequire = null;
		fdrequire = null;
		fdensure = null;
		outId = null;
		vresult = null;
		returnLabel = null;
		fensure = null;
		fbody = null;
		localsymtab = null;
		vthis = null;
		v_arguments = null;
version (IN_GCC) {
		v_argptr = null;
}
		parameters = null;
		labtab = null;
		overnext = null;
		vtblIndex = -1;
		hasReturnExp = 0;
		naked = 0;
		inlineStatus = ILS.ILSuninitialized;
		inlineNest = 0;
		inlineAsm = 0;
		cantInterpret = 0;
		semanticRun = 0;
version (DMDV1) {
		nestedFrameRef = 0;
}
		fes = null;
		introducing = 0;
		tintro = null;
		/* The type given for "infer the return type" is a TypeFunction with
		 * null for the return type.
		 */
		inferRetType = (type && type.nextOf() is null);
		hasReturnExp = 0;
		nrvo_can = 1;
		nrvo_var = null;
		shidden = null;
version (DMDV2) {
		builtin = BUILTINunknown;
		tookAddressOf = 0;
}
		foverrides = new FuncDeclarations();
		closureVars = new Dsymbols();
	}
	
    override Dsymbol syntaxCopy(Dsymbol s)
	{
		FuncDeclaration f;

		//printf("FuncDeclaration::syntaxCopy('%s')\n", toChars());
		if (s)
			f = cast(FuncDeclaration)s;
		else
			f = new FuncDeclaration(loc, endloc, ident, storage_class, type.syntaxCopy());

		f.outId = outId;
		f.frequire = frequire ? frequire.syntaxCopy() : null;
		f.fensure  = fensure  ? fensure.syntaxCopy()  : null;
		f.fbody    = fbody    ? fbody.syntaxCopy()    : null;
		assert(!fthrows); // deprecated

		return f;
	}
	
	// Do the semantic analysis on the external interface to the function.
    override void semantic(Scope sc)
	{
		TypeFunction f;
		StructDeclaration sd;
		ClassDeclaration cd;
		InterfaceDeclaration id;
		Dsymbol pd;

static if (false)
{
		printf("FuncDeclaration.semantic(sc = %p, this = %p, '%s', linkage = %d)\n", sc, this, toPrettyChars(), sc.linkage);
		if (isFuncLiteralDeclaration())
			printf("\tFuncLiteralDeclaration()\n");
		printf("sc.parent = %s, parent = %s\n", sc.parent.toChars(), parent ? parent.toChars() : "");
		printf("type: %p, %s\n", type, type.toChars());
}

		if (semanticRun && isFuncLiteralDeclaration())
		{
			/* Member functions that have return types that are
			 * forward references can have semantic() run more than
			 * once on them.
			 * See test\interface2.d, test20
			 */
			return;
		}
		assert(semanticRun <= 1);
		semanticRun = 1;

		storage_class |= sc.stc & ~STC.STCref;
		//printf("function storage_class = x%x\n", storage_class);

		if (!originalType)
			originalType = type;
		if (!type.deco)
		{
    	    sc = sc.push();
	        sc.stc |= storage_class & STC.STCref;	// forward refness to function type
	        type = type.semantic(loc, sc);
	        sc = sc.pop();
            
			/* Apply const, immutable and shared storage class
			 * to the function type
			 */
			StorageClass stc = storage_class;
			if (type.isImmutable())
				stc |= STC.STCimmutable;
			if (type.isConst())
				stc |= STC.STCconst;
			if (type.isShared() || storage_class & STC.STCsynchronized)
				stc |= STC.STCshared;
	        if (type.isWild())
	            stc |= STC.STCwild;
			switch (stc & STC.STC_TYPECTOR)
			{
				case STC.STCimmutable:
				case STC.STCimmutable | STC.STCconst:
				case STC.STCimmutable | STC.STCconst | STC.STCshared:
				case STC.STCimmutable | STC.STCshared:
                case STC.STCimmutable | STC.STCwild:
	            case STC.STCimmutable | STC.STCconst | STC.STCwild:
	            case STC.STCimmutable | STC.STCconst | STC.STCshared | STC.STCwild:
	            case STC.STCimmutable | STC.STCshared | STC.STCwild:
				// Don't use toInvariant(), as that will do a merge()
				type = type.makeInvariant();
				goto Lmerge;
	
				case STC.STCconst:
	            case STC.STCconst | STC.STCwild:
				type = type.makeConst();
				goto Lmerge;
	
				case STC.STCshared | STC.STCconst:
	            case STC.STCshared | STC.STCconst | STC.STCwild:
				type = type.makeSharedConst();
				goto Lmerge;
	
				case STC.STCshared:
				type = type.makeShared();
		        goto Lmerge;

	            case STC.STCwild:
		        type = type.makeWild();
		        goto Lmerge;

	            case STC.STCshared | STC.STCwild:
		        type = type.makeSharedWild();
		        goto Lmerge;

				Lmerge:
					if (!(type.ty == Tfunction && !type.nextOf()))
						/* Can't do merge if return type is not known yet
					     */
						type.deco = type.merge().deco;
				break;
	
				case STC.STCundefined:
				break;
	
				default:
				assert(0);
			}
		}
        storage_class &= ~STC.STCref;
		if (type.ty != TY.Tfunction)
		{
		error("%s must be a function", toChars());
		return;
		}
		f = cast(TypeFunction)type;
		size_t nparams = Parameter.dim(f.parameters);

		linkage = sc.linkage;
	//    if (!parent)
		{
		//parent = sc.scopesym;
		parent = sc.parent;
		}
		protection = sc.protection;
		Dsymbol parent = toParent();

		if (storage_class & STC.STCscope)
		error("functions cannot be scope");

		if (isAbstract() && !isVirtual())
		error("non-virtual functions cannot be abstract");

		if ((f.isConst() || f.isImmutable()) && !isThis())
		error("without 'this' cannot be const/immutable");

		if (isAbstract() && isFinal())
		error("cannot be both final and abstract");
static if (false) {
		if (isAbstract() && fbody)
		error("abstract functions cannot have bodies");
}

static if (false) {
		if (isStaticConstructor() || isStaticDestructor())
		{
		if (!isStatic() || type.nextOf().ty != Tvoid)
			error("static constructors / destructors must be static void");
		if (f.arguments && f.arguments.dim)
			error("static constructors / destructors must have empty parameter list");
		// BUG: check for invalid storage classes
		}
}

version (IN_GCC) {
		AggregateDeclaration ad;

		ad = parent.isAggregateDeclaration();
		if (ad)
			ad.methods.push(cast(void*)this);
}
		sd = parent.isStructDeclaration();
		if (sd)
		{
		if (isCtorDeclaration())
		{
			return;
		}
static if (false) {
		// Verify no constructors, destructors, etc.
		if (isCtorDeclaration()
			//||isDtorDeclaration()
			//|| isInvariantDeclaration()
			//|| isUnitTestDeclaration()
		   )
		{
			error("special member functions not allowed for %ss", sd.kind());
		}

		if (!sd.inv)
			sd.inv = isInvariantDeclaration();

		if (!sd.aggNew)
			sd.aggNew = isNewDeclaration();

		if (isDelete())
		{
			if (sd.aggDelete)
			error("multiple delete's for struct %s", sd.toChars());
			sd.aggDelete = cast(DeleteDeclaration)this;
		}
}
		}

		id = parent.isInterfaceDeclaration();
		if (id)
		{
		storage_class |= STC.STCabstract;

		if (isCtorDeclaration() ||
///static if (DMDV2) {
			isPostBlitDeclaration() ||
///}
			isDtorDeclaration() ||
			isInvariantDeclaration() ||
			isUnitTestDeclaration() || isNewDeclaration() || isDelete())
			error("special function not allowed in interface %s", id.toChars());
		if (fbody)
			error("function body is not abstract in interface %s", id.toChars());
		}

		/* Template member functions aren't virtual:
		 *   interface TestInterface { void tpl(T)(); }
		 * and so won't work in interfaces
		 */
		if ((pd = toParent()) !is null &&
		pd.isTemplateInstance() &&
		(pd = toParent2()) !is null &&
		(id = pd.isInterfaceDeclaration()) !is null)
		{
		error("template member function not allowed in interface %s", id.toChars());
		}

		cd = parent.isClassDeclaration();
		if (cd)
		{	int vi;
		CtorDeclaration ctor;
		DtorDeclaration dtor;
		InvariantDeclaration inv;

		if (isCtorDeclaration())
		{
	//	    ctor = cast(CtorDeclaration)this;
	//	    if (!cd.ctor)
	//		cd.ctor = ctor;
			return;
		}

static if (false) {
		dtor = isDtorDeclaration();
		if (dtor)
		{
			if (cd.dtor)
			error("multiple destructors for class %s", cd.toChars());
			cd.dtor = dtor;
		}

		inv = isInvariantDeclaration();
		if (inv)
		{
			cd.inv = inv;
		}

		if (isNewDeclaration())
		{
			if (!cd.aggNew)
				cd.aggNew = cast(NewDeclaration)this;
		}

		if (isDelete())
		{
			if (cd.aggDelete)
			error("multiple delete's for class %s", cd.toChars());
			cd.aggDelete = cast(DeleteDeclaration)this;
		}
}

		if (storage_class & STC.STCabstract)
			cd.isabstract = true;

		// if static function, do not put in vtbl[]
		if (!isVirtual())
		{
			//printf("\tnot virtual\n");
			goto Ldone;
		}

		// Find index of existing function in vtbl[] to override
		vi = findVtblIndex(cd.vtbl, cd.baseClass ? cd.baseClass.vtbl.dim : 0);
		switch (vi)
		{
			case -1:
			/* Didn't find one, so
			 * This is an 'introducing' function which gets a new
			 * slot in the vtbl[].
			 */

			// Verify this doesn't override previous final function
			if (cd.baseClass)
			{   
				Dsymbol s = cd.baseClass.search(loc, ident, 0);
				if (s)
				{
				FuncDeclaration ff = s.isFuncDeclaration();
				ff = ff.overloadExactMatch(type);
				if (ff && ff.isFinal() && ff.prot() != PROT.PROTprivate)
					error("cannot override final function %s", ff.toPrettyChars());
				}
			}

			if (isFinal())
			{
				if (isOverride())
				error("does not override any function");
				cd.vtblFinal.push(cast(void*)this);
			}
			else
			{
				// Append to end of vtbl[]
				//printf("\tintroducing function\n");
				introducing = 1;
				vi = cd.vtbl.dim;
				cd.vtbl.push(cast(void*)this);
				vtblIndex = vi;
			}
			break;

			case -2:	// can't determine because of fwd refs
			cd.sizeok = 2;	// can't finish due to forward reference
			return;

			default:
			{   
				FuncDeclaration fdv = cast(FuncDeclaration)cd.vtbl.data[vi];
			// This function is covariant with fdv
			if (fdv.isFinal())
				error("cannot override final function %s", fdv.toPrettyChars());

version (DMDV2) {
			if (!isOverride())
				warning(loc, "overrides base class function %s, but is not marked with 'override'", fdv.toPrettyChars());
}

			if (fdv.toParent() == parent)
			{
				// If both are mixins, then error.
				// If either is not, the one that is not overrides
				// the other.
				if (fdv.parent.isClassDeclaration())
				break;
				if (!this.parent.isClassDeclaration()
///static if (!BREAKABI) {
				&& !isDtorDeclaration()
///}
///version (DMDV2) {
				&& !isPostBlitDeclaration()
///}
				)
				error("multiple overrides of same function");
			}
			cd.vtbl.data[vi] = cast(void*)this;
			vtblIndex = vi;
			
			/* Remember which functions this overrides
			 */
			foverrides.push(fdv);

			/* This works by whenever this function is called,
			 * it actually returns tintro, which gets dynamically
			 * cast to type. But we know that tintro is a base
			 * of type, so we could optimize it by not doing a
			 * dynamic cast, but just subtracting the isBaseOf()
			 * offset if the value is != null.
			 */

			if (fdv.tintro)
				tintro = fdv.tintro;
			else if (!type.equals(fdv.type))
			{
				/* Only need to have a tintro if the vptr
				 * offsets differ
				 */
				int offset;
				if (fdv.type.nextOf().isBaseOf(type.nextOf(), &offset))
				{
				tintro = fdv.type;
				}
			}
			break;
			}
		}

		/* Go through all the interface bases.
		 * If this function is covariant with any members of those interface
		 * functions, set the tintro.
		 */
		for (int i = 0; i < cd.interfaces_dim; i++)
		{
			BaseClass b = cd.interfaces[i];
			vi = findVtblIndex(b.base.vtbl, b.base.vtbl.dim);
			switch (vi)
			{
			case -1:
				break;

			case -2:
				cd.sizeok = 2;	// can't finish due to forward reference
				return;

			default:
			{   FuncDeclaration fdv = cast(FuncDeclaration)b.base.vtbl.data[vi];
				Type ti = null;
				
				/* Remember which functions this overrides
				 */
				foverrides.push(fdv);

				if (fdv.tintro)
					ti = fdv.tintro;
				else if (!type.equals(fdv.type))
				{
				/* Only need to have a tintro if the vptr
				 * offsets differ
				 */
				int offset;
				if (fdv.type.nextOf().isBaseOf(type.nextOf(), &offset))
				{
					ti = fdv.type;
static if (false) {
					if (offset)
					ti = fdv.type;
					else if (type.nextOf().ty == Tclass)
					{   
						ClassDeclaration cdn = (cast(TypeClass)type.nextOf()).sym;
						if (cdn && cdn.sizeok != 1)
							ti = fdv.type;
						}
}
					}
				}
				if (ti)
				{
				if (tintro && !tintro.equals(ti))
				{
					error("incompatible covariant types %s and %s", tintro.toChars(), ti.toChars());
				}
				tintro = ti;
				}
				goto L2;
			}
			}
		}

		if (introducing && isOverride())
		{
			error("does not override any function");
		}

		L2: ;
		}
		else if (isOverride() && !parent.isTemplateInstance())
		error("override only applies to class member functions");

		/* Do not allow template instances to add virtual functions
		 * to a class.
		 */
		if (isVirtual())
		{
		TemplateInstance ti = parent.isTemplateInstance();
		if (ti)
		{
			// Take care of nested templates
			while (1)
			{
			TemplateInstance ti2 = ti.tempdecl.parent.isTemplateInstance();
			if (!ti2)
				break;
			ti = ti2;
			}

			// If it's a member template
			ClassDeclaration cdd = ti.tempdecl.isClassMember();
			if (cdd)
			{
				error("cannot use template to add virtual function to class '%s'", cdd.toChars());
			}
		}
		}

		if (isMain())
		{
		// Check parameters to see if they are either () or (char[][] args)
		switch (nparams)
		{
			case 0:
			break;

			case 1:
			{
			auto arg0 = Parameter.getNth(f.parameters, 0);
			if (arg0.type.ty != TY.Tarray ||
				arg0.type.nextOf().ty != TY.Tarray ||
				arg0.type.nextOf().nextOf().ty != TY.Tchar ||
				arg0.storageClass & (STC.STCout | STC.STCref | STC.STClazy))
				goto Lmainerr;
			break;
			}

			default:
			goto Lmainerr;
		}

		if (!f.nextOf())
			error("must return int or void");
		else if (f.nextOf().ty != TY.Tint32 && f.nextOf().ty != TY.Tvoid)
			error("must return int or void, not %s", f.nextOf().toChars());
		if (f.varargs)
		{
		Lmainerr:
			error("parameters must be main() or main(char[][] args)");
		}
		}

		if (ident == Id.assign && (sd || cd))
		{	// Disallow identity assignment operator.

		// opAssign(...)
		if (nparams == 0)
		{   if (f.varargs == 1)
			goto Lassignerr;
		}
		else
		{
			auto arg0 = Parameter.getNth(f.parameters, 0);
			Type t0 = arg0.type.toBasetype();
			Type tb = sd ? sd.type : cd.type;
			if (arg0.type.implicitConvTo(tb) ||
			(sd && t0.ty == TY.Tpointer && t0.nextOf().implicitConvTo(tb))
			   )
			{
			if (nparams == 1)
				goto Lassignerr;
			auto arg1 = Parameter.getNth(f.parameters, 1);
			if (arg1.defaultArg)
				goto Lassignerr;
			}
		}
		}
		
		if (isVirtual())
		{
			/* Rewrite contracts as nested functions, then call them.
			 * Doing it as nested functions means that overriding functions
			 * can call them.
			 */
			if (frequire)
			{   
				/*   in { ... }
				 * becomes:
				 *   void __require() { ... }
				 *   __require();
				 */
				Loc loc = frequire.loc;
				TypeFunction tf = new TypeFunction(null, Type.tvoid, 0, LINKd);
				FuncDeclaration fd = new FuncDeclaration(loc, loc, Id.require, STCundefined, tf);
				fd.fbody = frequire;
				Statement s1 = new DeclarationStatement(loc, fd);
				Expression e = new CallExp(loc, new VarExp(loc, fd, 0), cast(Expressions)null);
				Statement s2 = new ExpStatement(loc, e);
				frequire = new CompoundStatement(loc, s1, s2);
				fdrequire = fd;
			}

			if (fensure)
			{   /*   out (result) { ... }
				 * becomes:
				 *   tret __ensure(ref tret result) { ... }
				 *   __ensure(result);
				 */
				if (!outId && f.nextOf().toBasetype().ty != Tvoid)
					outId = Id.result;	// provide a default

				Loc loc = fensure.loc;
				auto arguments = new Parameters();
				Parameter a = null;
				if (outId)
				{	
					a = new Parameter(STCref, f.nextOf(), outId, null);
					arguments.push(a);
				}
				TypeFunction tf = new TypeFunction(arguments, Type.tvoid, 0, LINKd);
				FuncDeclaration fd = new FuncDeclaration(loc, loc, Id.ensure, STCundefined, tf);
				fd.fbody = fensure;
				Statement s1 = new DeclarationStatement(loc, fd);
				Expression eresult = null;
				if (outId)
					eresult = new IdentifierExp(loc, outId);
				Expression e = new CallExp(loc, new VarExp(loc, fd, 0), eresult);
				Statement s2 = new ExpStatement(loc, e);
				fensure = new CompoundStatement(loc, s1, s2);
				fdensure = fd;
			}
		}

	Ldone:
		/* Save scope for possible later use (if we need the
		 * function internals)
		 */
		scope_ = sc.clone();
		scope_.setNoFree();
		return;

	Lassignerr:
		if (sd)
		{
		sd.hasIdentityAssign = 1;	// don't need to generate it
		goto Ldone;
		}
		error("identity assignment operator overload is illegal");
	}
	
    override void semantic2(Scope sc)
	{
	}

	// Do the semantic analysis on the internals of the function.
    override void semantic3(Scope sc)
	{
		TypeFunction f;
		VarDeclaration argptr = null;
		VarDeclaration _arguments = null;

		if (!parent)
		{
		if (global.errors)
			return;
		//printf("FuncDeclaration.semantic3(%s '%s', sc = %p)\n", kind(), toChars(), sc);
		assert(0);
		}
		//printf("FuncDeclaration.semantic3('%s.%s', sc = %p, loc = %s)\n", parent.toChars(), toChars(), sc, loc.toChars());
		//fflush(stdout);
		//printf("storage class = x%x %x\n", sc.stc, storage_class);
		//{ static int x; if (++x == 2) *(char*)0=0; }
		//printf("\tlinkage = %d\n", sc.linkage);

		//printf(" sc.incontract = %d\n", sc.incontract);
		if (semanticRun >= 3)
		return;
		semanticRun = 3;

		if (!type || type.ty != TY.Tfunction)
		return;
		f = cast(TypeFunction)(type);

		// Check the 'throws' clause
		if (fthrows)
		{
		for (int i = 0; i < fthrows.dim; i++)
		{
			Type t = cast(Type)fthrows.data[i];

			t = t.semantic(loc, sc);
			if (!t.isClassHandle())
			error("can only throw classes, not %s", t.toChars());
		}
		}
		
	    frequire = mergeFrequire(frequire);
		fensure = mergeFensure(fensure);

		if (fbody || frequire)
		{
		/* Symbol table into which we place parameters and nested functions,
		 * solely to diagnose name collisions.
		 */
		localsymtab = new DsymbolTable();

		// Establish function scope
		ScopeDsymbol ss = new ScopeDsymbol();
		ss.parent = sc.scopesym;
		Scope sc2 = sc.push(ss);
		sc2.func = this;
		sc2.parent = this;
		sc2.callSuper = 0;
		sc2.sbreak = null;
		sc2.scontinue = null;
		sc2.sw = null;
		sc2.fes = fes;
		sc2.linkage = LINK.LINKd;
        sc2.stc &= ~(STC.STCauto | STC.STCscope | STC.STCstatic | STC.STCabstract | STC.STCdeprecated |
			        STC.STC_TYPECTOR | STC.STCfinal | STC.STCtls | STC.STCgshared | STC.STCref |
			        STCproperty | STCsafe | STCtrusted | STCsystem);
        sc2.protection = PROT.PROTpublic;
		sc2.explicitProtection = 0;
		sc2.structalign = 8;
		sc2.incontract = 0;
		sc2.tf = null;
		sc2.noctor = 0;

		// Declare 'this'
		AggregateDeclaration ad = isThis();
		if (ad)
		{   VarDeclaration v;

			if (isFuncLiteralDeclaration() && isNested())
			{
			error("literals cannot be class members");
			return;
			}
			else
			{
			assert(!isNested());	// can't be both member and nested
			assert(ad.handle);
			Type thandle = ad.handle;
version (STRUCTTHISREF) {
			thandle = thandle.addMod(type.mod);
			thandle = thandle.addStorageClass(storage_class);
			if (isPure())
				thandle = thandle.addMod(MOD.MODconst);
} else {
			if (storage_class & STC.STCconst || type.isConst())
			{
				assert(0); // BUG: shared not handled
				if (thandle.ty == TY.Tclass)
				thandle = thandle.constOf();
				else
				{	assert(thandle.ty == TY.Tpointer);
				thandle = thandle.nextOf().constOf().pointerTo();
				}
			}
			else if (storage_class & STC.STCimmutable || type.isImmutable())
			{
				if (thandle.ty == TY.Tclass)
				thandle = thandle.invariantOf();
				else
				{	assert(thandle.ty == TY.Tpointer);
				thandle = thandle.nextOf().invariantOf().pointerTo();
				}
			}
			else if (storage_class & STC.STCshared || type.isShared())
			{
				assert(0);  // not implemented
			}
}
			v = new ThisDeclaration(loc, thandle);
			v.storage_class |= STC.STCparameter;
version (STRUCTTHISREF) {
			if (thandle.ty == TY.Tstruct)
				v.storage_class |= STC.STCref;
}
			v.semantic(sc2);
			if (!sc2.insert(v))
				assert(0);
			v.parent = this;
			vthis = v;
			}
		}
		else if (isNested())
		{
			/* The 'this' for a nested function is the link to the
			 * enclosing function's stack frame.
			 * Note that nested functions and member functions are disjoint.
			 */
			VarDeclaration v = new ThisDeclaration(loc, Type.tvoid.pointerTo());
			v.storage_class |= STC.STCparameter;
			v.semantic(sc2);
			if (!sc2.insert(v))
			assert(0);
			v.parent = this;
			vthis = v;
		}

		// Declare hidden variable _arguments[] and _argptr
		if (f.varargs == 1)
		{
version (TARGET_NET) {
			varArgs(sc2, f, argptr, _arguments);
} else {
			Type t;

			if (f.linkage == LINK.LINKd)
			{	
				// Declare _arguments[]
version (BREAKABI) {
				v_arguments = new VarDeclaration(Loc(0), Type.typeinfotypelist.type, Id._arguments_typeinfo, null);
				v_arguments.storage_class = STCparameter;
				v_arguments.semantic(sc2);
				sc2.insert(v_arguments);
				v_arguments.parent = this;

				//t = Type.typeinfo.type.constOf().arrayOf();
				t = Type.typeinfo.type.arrayOf();
				_arguments = new VarDeclaration(Loc(0), t, Id._arguments, null);
				_arguments.semantic(sc2);
				sc2.insert(_arguments);
				_arguments.parent = this;
} else {
				t = Type.typeinfo.type.arrayOf();
				v_arguments = new VarDeclaration(Loc(0), t, Id._arguments, null);
				v_arguments.storage_class = STC.STCparameter | STC.STCin;
				v_arguments.semantic(sc2);
				sc2.insert(v_arguments);
				v_arguments.parent = this;
	}
				}
				if (f.linkage == LINK.LINKd || (parameters && parameters.dim))
				{	// Declare _argptr
version (IN_GCC) {
				t = d_gcc_builtin_va_list_d_type;
} else {
				t = Type.tvoid.pointerTo();
}
				argptr = new VarDeclaration(Loc(0), t, Id._argptr, null);
				argptr.semantic(sc2);
				sc2.insert(argptr);
				argptr.parent = this;
			}
}
		}
static if(false) {
		// Propagate storage class from tuple parameters to their element-parameters.
		if (f.parameters)
		{
			foreach (arg; f.parameters)
			{
			//printf("[%d] arg.type.ty = %d %s\n", i, arg.type.ty, arg.type.toChars());
			if (arg.type.ty == TY.Ttuple)
			{   auto t = cast(TypeTuple)arg.type;
				size_t dim = Parameter.dim(t.arguments);
				for (size_t j = 0; j < dim; j++)
				{
                    auto narg = Parameter.getNth(t.arguments, j);
				    narg.storageClass = arg.storageClass;
				}
			}
			}
		}
}
		/* Declare all the function parameters as variables
		 * and install them in parameters[]
		 */
		size_t nparams = Parameter.dim(f.parameters);
		if (nparams)
		{   /* parameters[] has all the tuples removed, as the back end
			 * doesn't know about tuples
			 */
			parameters = new Dsymbols();
			parameters.reserve(nparams);
			for (size_t i = 0; i < nparams; i++)
			{
			auto arg = Parameter.getNth(f.parameters, i);
			Identifier id = arg.ident;
			if (!id)
			{
				/* Generate identifier for un-named parameter,
				 * because we need it later on.
				 */
				arg.ident = id = Identifier.generateId("_param_", i);
			}
			Type vtype = arg.type;
			if (isPure())
				vtype = vtype.addMod(MOD.MODconst);
			VarDeclaration v = new VarDeclaration(loc, vtype, id, null);
			//printf("declaring parameter %s of type %s\n", v.toChars(), v.type.toChars());
			v.storage_class |= STC.STCparameter;
			if (f.varargs == 2 && i + 1 == nparams)
				v.storage_class |= STC.STCvariadic;
			v.storage_class |= arg.storageClass & (STC.STCin | STC.STCout | STC.STCref | STC.STClazy | STC.STCfinal | STC.STC_TYPECTOR | STC.STCnodtor);
			v.semantic(sc2);
			if (!sc2.insert(v))
				error("parameter %s.%s is already defined", toChars(), v.toChars());
			else
				parameters.push(v);
			localsymtab.insert(v);
			v.parent = this;
			}
		}

		// Declare the tuple symbols and put them in the symbol table,
		// but not in parameters[].
		if (f.parameters)
		{
			foreach (arg; f.parameters)
			{
			if (!arg.ident)
				continue;			// never used, so ignore
			if (arg.type.ty == TY.Ttuple)
			{   auto t = cast(TypeTuple)arg.type;
				size_t dim = Parameter.dim(t.arguments);
				Objects exps = new Objects();
				exps.setDim(dim);
				for (size_t j = 0; j < dim; j++)
				{	auto narg = Parameter.getNth(t.arguments, j);
				assert(narg.ident);
				VarDeclaration v = sc2.search(Loc(0), narg.ident, null).isVarDeclaration();
				assert(v);
				Expression e = new VarExp(v.loc, v);
				exps[j] = e;
				}
				assert(arg.ident);
				auto v = new TupleDeclaration(loc, arg.ident, exps);
				//printf("declaring tuple %s\n", v.toChars());
				v.isexp = 1;
				if (!sc2.insert(v))
				error("parameter %s.%s is already defined", toChars(), v.toChars());
				localsymtab.insert(v);
				v.parent = this;
			}
			}
		}

		/* Do the semantic analysis on the [in] preconditions and
		 * [out] postconditions.
		 */
		sc2.incontract++;

		if (frequire)
		{   /* frequire is composed of the [in] contracts
			 */
			// BUG: need to error if accessing out parameters
			// BUG: need to treat parameters as const
			// BUG: need to disallow returns and throws
			// BUG: verify that all in and ref parameters are read
			frequire = frequire.semantic(sc2);
			labtab = null;		// so body can't refer to labels
		}

		if (fensure || addPostInvariant())
		{
			/* fensure is composed of the [out] contracts
			 */
			if (!type.nextOf())		// if return type is inferred
		    {
				/* This case:
				 *   auto fp = function() out { } body { };
				 * Can fix by doing semantic() onf fbody first.
				 */
				error("post conditions are not supported if the return type is inferred");
				return;
		    }
			
			ScopeDsymbol sym = new ScopeDsymbol();
			sym.parent = sc2.scopesym;
			sc2 = sc2.push(sym);

			assert(type.nextOf());
			if (type.nextOf().ty == TY.Tvoid)
			{
			if (outId)
				error("void functions have no result");
			}
			else
			{
			if (!outId)
				outId = Id.result;		// provide a default
			}

			if (outId)
			{	// Declare result variable
			Loc loc = this.loc;

			if (fensure)
				loc = fensure.loc;

			auto v = new VarDeclaration(loc, type.nextOf(), outId, null);
			v.noauto = true;
version (DMDV2) {
		    if (!isVirtual())
		        v.storage_class |= STC.STCconst;
			if (f.isref)
			{
				v.storage_class |= STC.STCref | STC.STCforeach;
			}
}
			sc2.incontract--;
			v.semantic(sc2);
			sc2.incontract++;
			if (!sc2.insert(v))
				error("out result %s is already defined", v.toChars());
			v.parent = this;
			vresult = v;

			// vresult gets initialized with the function return value
			// in ReturnStatement.semantic()
			}

			// BUG: need to treat parameters as const
			// BUG: need to disallow returns and throws
			if (fensure)
			{	fensure = fensure.semantic(sc2);
			labtab = null;		// so body can't refer to labels
			}

			if (!global.params.useOut)
			{	fensure = null;		// discard
			vresult = null;
			}

			// Postcondition invariant
			if (addPostInvariant())
			{
			Expression e = null;
			if (isCtorDeclaration())
			{
				// Call invariant directly only if it exists
				InvariantDeclaration inv = ad.inv;
				ClassDeclaration cd = ad.isClassDeclaration();

				while (!inv && cd)
				{
				cd = cd.baseClass;
				if (!cd)
					break;
				inv = cd.inv;
				}
				if (inv)
				{
				e = new DsymbolExp(Loc(0), inv);
				e = new CallExp(Loc(0), e);
				e = e.semantic(sc2);
				}
			}
			else
			{   // Call invariant virtually
				Expression v = new ThisExp(Loc(0));
				v.type = vthis.type;
version (STRUCTTHISREF) {
				if (ad.isStructDeclaration())
				v = v.addressOf(sc);
}
				e = new AssertExp(Loc(0), v);
			}
			if (e)
			{
				ExpStatement s = new ExpStatement(Loc(0), e);
				if (fensure)
				fensure = new CompoundStatement(Loc(0), s, fensure);
				else
				fensure = s;
			}
			}

			if (fensure)
			{	returnLabel = new LabelDsymbol(Id.returnLabel);
			LabelStatement ls = new LabelStatement(Loc(0), Id.returnLabel, fensure);
			ls.isReturnLabel = 1;
			returnLabel.statement = ls;
			}
			sc2 = sc2.pop();
		}

		sc2.incontract--;
		
		if (fbody)
		{   ClassDeclaration cd = isClassMember();

			/* If this is a class constructor
			 */
			if (isCtorDeclaration() && cd)
			{
				for (int i = 0; i < cd.fields.dim; i++)
				{   VarDeclaration v = cast(VarDeclaration)cd.fields[i];
	
					v.ctorinit = 0;
				}
			}

			if (inferRetType || f.retStyle() != RET.RETstack)
			nrvo_can = 0;

			fbody = fbody.semantic(sc2);
			if (!fbody)
			fbody = new CompoundStatement(Loc(0), new Statements());

			if (inferRetType)
			{	// If no return type inferred yet, then infer a void
			if (!type.nextOf())
			{
				(cast(TypeFunction)type).next = Type.tvoid;
				type = type.semantic(loc, sc);
			}
			f = cast(TypeFunction)type;
			}
			
			if (isStaticCtorDeclaration())
			{	
				/* It's a static constructor. Ensure that all
				 * ctor consts were initialized.
				 */

			Dsymbol p = toParent();
			ScopeDsymbol add = p.isScopeDsymbol();
			if (!add)
			{
				error("static constructor can only be member of struct/class/module, not %s %s", p.kind(), p.toChars());
			}
			else
			{
				foreach (Dsymbol s; add.members)
				{
				s.checkCtorConstInit();
				}
			}
			}

			if (isCtorDeclaration() && cd)
			{
				//printf("callSuper = x%x\n", sc2.callSuper);

				// Verify that all the ctorinit fields got initialized
				if (!(sc2.callSuper & CSX.CSXthis_ctor))
				{
					for (int i = 0; i < cd.fields.dim; i++)
					{   VarDeclaration v = cast(VarDeclaration)cd.fields[i];

					if (v.ctorinit == 0 && v.isCtorinit())
						error("missing initializer for final field %s", v.toChars());
					}
				}

				if (!(sc2.callSuper & CSX.CSXany_ctor) &&
					cd.baseClass && cd.baseClass.ctor)
				{
					sc2.callSuper = 0;

					// Insert implicit super() at start of fbody
					Expression e1 = new SuperExp(Loc(0));
					Expression e = new CallExp(Loc(0), e1);

					e = e.trySemantic(sc2);
					if (!e)
					error("no match for implicit super() call in constructor");
					else
					{
					Statement s = new ExpStatement(Loc(0), e);
					fbody = new CompoundStatement(Loc(0), s, fbody);
					}
				}
				}
				else if (fes)
				{	// For foreach(){} body, append a return 0;
				Expression e = new IntegerExp(0);
				Statement s = new ReturnStatement(Loc(0), e);
				fbody = new CompoundStatement(Loc(0), fbody, s);
				assert(!returnLabel);
				}
				else if (!hasReturnExp && type.nextOf().ty != TY.Tvoid)
				error("expected to return a value of type %s", type.nextOf().toChars());
				else if (!inlineAsm)
				{
version (DMDV2) {
				BE blockexit = fbody ? fbody.blockExit() : BE.BEfallthru;
				if (f.isnothrow && blockexit & BE.BEthrow)
					error("'%s' is nothrow yet may throw", toChars());

				int offend = blockexit & BE.BEfallthru;
}
				if (type.nextOf().ty == TY.Tvoid)
				{
					if (offend && isMain())
					{	// Add a return 0; statement
					Statement s = new ReturnStatement(Loc(0), new IntegerExp(0));
					fbody = new CompoundStatement(Loc(0), fbody, s);
					}
				}
				else
				{
					if (offend)
					{   
						Expression e;
version (DMDV1) {
						warning(loc, "no return exp; or assert(0); at end of function");
} else {
						error("no return exp; or assert(0); at end of function");
}
						if (global.params.useAssert &&
							!global.params.useInline)
						{   /* Add an assert(0, msg); where the missing return
							 * should be.
							 */
							e = new AssertExp(
							  endloc,
							  new IntegerExp(0),
							  new StringExp(loc, "missing return expression")
							);
						}
						else
							e = new HaltExp(endloc);

						e = new CommaExp(Loc(0), e, type.nextOf().defaultInit(Loc(0)));
						e = e.semantic(sc2);
						Statement s = new ExpStatement(Loc(0), e);
						fbody = new CompoundStatement(Loc(0), fbody, s);
					}
				}
			}
		}

		{
			auto a = new Statements();

			// Merge in initialization of 'out' parameters
			if (parameters)
			{	foreach (Dsymbol s; parameters)
			{
				auto v = cast(VarDeclaration)s;
				if (v.storage_class & STC.STCout)
				{
				assert(v.init);
				ExpInitializer ie = v.init.isExpInitializer();
				assert(ie);
				a.push(new ExpStatement(Loc(0), ie.exp));
				}
			}
			}

			if (argptr)
			{	// Initialize _argptr to point past non-variadic arg
version (IN_GCC) {
				// Handled in FuncDeclaration.toObjFile
				v_argptr = argptr;
				v_argptr.init = new VoidInitializer(loc);
} else {
				Type t = argptr.type;
				VarDeclaration p;
				uint offset;

				Expression e1 = new VarExp(Loc(0), argptr);
				if (parameters && parameters.dim)
					p = cast(VarDeclaration)parameters[parameters.length - 1];
				else
					p = v_arguments;		// last parameter is _arguments[]
				if (p.storage_class & STClazy)
					// If the last parameter is lazy, it's the size of a delegate
					offset = PTRSIZE * 2;
				else
					offset = cast(size_t)p.type.size();
				offset = (offset + 3) & ~3;	// assume stack aligns on 4
				Expression e = new SymOffExp(Loc(0), p, offset);
				e = new AssignExp(Loc(0), e1, e);
				e.type = t;
				a.push(new ExpStatement(Loc(0), e));
				p.isargptr = true;
}
			}

			if (_arguments)
			{
			/* Advance to elements[] member of TypeInfo_Tuple with:
			 *  _arguments = v_arguments.elements;
			 */
			Expression e = new VarExp(Loc(0), v_arguments);
			e = new DotIdExp(Loc(0), e, Id.elements);
			Expression e1 = new VarExp(Loc(0), _arguments);
			e = new AssignExp(Loc(0), e1, e);
			e.op = TOK.TOKconstruct;
			e = e.semantic(sc2);
			a.push(new ExpStatement(Loc(0), e));
			}

			// Merge contracts together with body into one compound statement

version (_DH) {
			if (frequire && global.params.useIn)
			{	frequire.incontract = 1;
			a.push(frequire);
			}
} else {
			if (frequire && global.params.useIn)
			a.push(frequire);
}

			// Precondition invariant
			if (addPreInvariant())
			{
			Expression e = null;
			if (isDtorDeclaration())
			{
				// Call invariant directly only if it exists
				InvariantDeclaration inv = ad.inv;
				ClassDeclaration cd = ad.isClassDeclaration();

				while (!inv && cd)
				{
				cd = cd.baseClass;
				if (!cd)
					break;
				inv = cd.inv;
				}
				if (inv)
				{
				e = new DsymbolExp(Loc(0), inv);
				e = new CallExp(Loc(0), e);
				e = e.semantic(sc2);
				}
			}
			else
			{   // Call invariant virtually
				Expression v = new ThisExp(Loc(0));
				v.type = vthis.type;
version (STRUCTTHISREF) {
				if (ad.isStructDeclaration())
					v = v.addressOf(sc);
}
				Expression se = new StringExp(Loc(0), "null this");
				se = se.semantic(sc);
				se.type = Type.tchar.arrayOf();
				e = new AssertExp(loc, v, se);
			}
			if (e)
			{
				auto s = new ExpStatement(Loc(0), e);
				a.push(s);
			}
			}

			if (fbody)
			a.push(fbody);

			if (fensure)
			{
			a.push(returnLabel.statement);

			if (type.nextOf().ty != TY.Tvoid)
			{
				// Create: return vresult;
				assert(vresult);
				Expression e = new VarExp(Loc(0), vresult);
				if (tintro)
				{	e = e.implicitCastTo(sc, tintro.nextOf());
				e = e.semantic(sc);
				}
				auto s = new ReturnStatement(Loc(0), e);
				a.push(s);
			}
			}

			fbody = new CompoundStatement(Loc(0), a);
version (DMDV2) {
			/* Append destructor calls for parameters as finally blocks.
			 */
			if (parameters)
			{	foreach(Dsymbol symb; parameters)
			{
				auto v = cast(VarDeclaration)symb;

				if (v.storage_class & (STC.STCref | STC.STCout))
				continue;

				/* Don't do this for static arrays, since static
				 * arrays are called by reference. Remove this
				 * when we change them to call by value.
				 */
				if (v.type.toBasetype().ty == TY.Tsarray)
				continue;

				Expression e = v.callAutoDtor(sc);
				if (e)
				{	Statement s = new ExpStatement(Loc(0), e);
				s = s.semantic(sc);
				if (fbody.blockExit() == BE.BEfallthru)
					fbody = new CompoundStatement(Loc(0), fbody, s);
				else
					fbody = new TryFinallyStatement(Loc(0), fbody, s);
				}
			}
			}
}

static if (true) {
			if (isSynchronized())
			{	/* Wrap the entire function body in a synchronized statement
				 */
				ClassDeclaration cd = parent.isClassDeclaration();
				if (cd)
				{
///version (TARGET_WINDOS) {
					if (/*config.flags2 & CFG2.CFG2seh &&*/	// always on for WINDOS
					!isStatic() && !fbody.usesEH())
					{
						/* The back end uses the "jmonitor" hack for syncing;
						 * no need to do the sync at this level.
						 */
					}
					else
///}
					{
						Expression vsync;
						if (isStatic())
						{   
							// The monitor is in the ClassInfo
							vsync = new DotIdExp(loc, new DsymbolExp(loc, cd), Id.classinfo_);
						}
						else
						{   // 'this' is the monitor
							vsync = new VarExp(loc, vthis);
						}
						fbody = new PeelStatement(fbody);	// don't redo semantic()
						fbody = new SynchronizedStatement(loc, vsync, fbody);
						fbody = fbody.semantic(sc2);
					}
				}
				else
				{
					error("synchronized function %s must be a member of a class", toChars());
				}
			}
}
		}

		sc2.callSuper = 0;
		sc2.pop();
		}
		semanticRun = 4;
	}
	
    // called from semantic3
    void varArgs(Scope sc, TypeFunction, ref VarDeclaration, ref VarDeclaration)
	{
		assert(false);
	}

	override void toCBuffer(OutBuffer buf, HdrGenState* hgs)
	{
//		writef("FuncDeclaration.toCBuffer() '%s'\n", toChars());

		StorageClassDeclaration.stcToCBuffer(buf, storage_class);
		type.toCBuffer(buf, ident, hgs);
		bodyToCBuffer(buf, hgs);
	}
	
	void bodyToCBuffer(OutBuffer buf, HdrGenState* hgs)
	{
		if (fbody &&
			(!hgs.hdrgen || hgs.tpltMember || canInline(1,1))
		   )
		{
			buf.writenl();

			// in{}
			if (frequire)
			{
				buf.writestring("in");
				buf.writenl();
				frequire.toCBuffer(buf, hgs);
			}

			// out{}
			if (fensure)
			{
				buf.writestring("out");
				if (outId)
				{
					buf.writebyte('(');
					buf.writestring(outId.toChars());
					buf.writebyte(')');
				}
				buf.writenl();
				fensure.toCBuffer(buf, hgs);
			}

			if (frequire || fensure)
			{
				buf.writestring("body");
				buf.writenl();
			}

			buf.writebyte('{');
			buf.writenl();
			fbody.toCBuffer(buf, hgs);
			buf.writebyte('}');
			buf.writenl();
		}
		else
		{   buf.writeByte(';');
			buf.writenl();
		}
	}
	
	/****************************************************
	 * Determine if 'this' overrides fd.
	 * Return true if it does.
	 */
    bool overrides(FuncDeclaration fd)
	{
		bool result = false;

		if (fd.ident == ident)
		{
			int cov = type.covariant(fd.type);
			if (cov)
			{   
				ClassDeclaration cd1 = toParent().isClassDeclaration();
				ClassDeclaration cd2 = fd.toParent().isClassDeclaration();

				if (cd1 && cd2 && cd2.isBaseOf(cd1, null))
					result = true;
			}
		}
		return result;
	}
	
	/*************************************************
	 * Find index of function in vtbl[0..dim] that
	 * this function overrides.
	 * Returns:
	 *	-1	didn't find one
	 *	-2	can't determine because of forward references
	 */
    int findVtblIndex(Array vtbl, int dim)
	{
		for (int vi = 0; vi < dim; vi++)
		{
			FuncDeclaration fdv = (cast(Dsymbol)vtbl.data[vi]).isFuncDeclaration();
			if (fdv && fdv.ident is ident)
			{
				int cov = type.covariant(fdv.type);
				//printf("\tbaseclass cov = %d\n", cov);
				switch (cov)
				{
					case 0:		// types are distinct
						break;

					case 1:
						return vi;

					case 2:
						//type.print();
						//fdv.type.print();
						//printf("%s %s\n", type.deco, fdv.type.deco);
						error("of type %s overrides but is not covariant with %s of type %s",
						type.toChars(), fdv.toPrettyChars(), fdv.type.toChars());
						break;

					case 3:
						return -2;	// forward references
				}
			}
		}
		return -1;
	}

	/****************************************************
	 * Overload this FuncDeclaration with the new one f.
	 * Return !=0 if successful; i.e. no conflict.
	 */
    override bool overloadInsert(Dsymbol s)
	{
		FuncDeclaration f;
		AliasDeclaration a;

		//writef("FuncDeclaration.overloadInsert(%s)\n", s.toChars());
		a = s.isAliasDeclaration();
		if (a)
		{
			if (overnext)
				return overnext.overloadInsert(a);

			if (!a.aliassym && a.type.ty != TY.Tident && a.type.ty != TY.Tinstance)
			{
				//writef("\ta = '%s'\n", a.type.toChars());
				return false;
			}
			overnext = a;
			//printf("\ttrue: no conflict\n");
			return true;
		}
		f = s.isFuncDeclaration();
		if (!f)
			return false;

static if (false) {
		/* Disable this check because:
		 *	const void foo();
		 * semantic() isn't run yet on foo(), so the const hasn't been
		 * applied yet.
		 */
		if (type)
		{   
			printf("type = %s\n", type.toChars());
			printf("f.type = %s\n", f.type.toChars());
		}
		if (type && f.type &&	// can be null for overloaded constructors
		f.type.covariant(type) &&
		f.type.mod == type.mod &&
		!isFuncAliasDeclaration())
		{
			//printf("\tfalse: conflict %s\n", kind());
			return false;
		}
}

		if (overnext)
			return overnext.overloadInsert(f);
		overnext = f;
		//printf("\ttrue: no conflict\n");
		return true;
	}
	
    FuncDeclaration overloadExactMatch(Type t)
	{
		Param1 p;
		p.t = t;
		p.f = null;
		overloadApply(this, &p.fp1, &p);
		return p.f;
	}
	
    FuncDeclaration overloadResolve(Loc loc, Expression ethis, Expressions arguments, int flags = 0)
	{
		TypeFunction tf;
		Match m;

static if (false) {
		printf("FuncDeclaration.overloadResolve('%s')\n", toChars());
		if (arguments)
		{   
			int i;

			for (i = 0; i < arguments.dim; i++)
			{   
				Expression arg;

				arg = cast(Expression)arguments.data[i];
				assert(arg.type);
				printf("\t%s: ", arg.toChars());
				arg.type.print();
			}
		}
}

		m.last = MATCH.MATCHnomatch;
		overloadResolveX(&m, this, ethis, arguments);

		if (m.count == 1)		// exactly one match
		{
			return m.lastf;
		}
		else
		{
			scope OutBuffer buf = new OutBuffer();

			buf.writeByte('(');
			if (arguments)
			{
				HdrGenState hgs;

				argExpTypesToCBuffer(buf, arguments, &hgs);
				buf.writeByte(')');
				if (ethis)
					ethis.type.modToBuffer(buf);
			}
			else
				buf.writeByte(')');

			if (m.last == MATCH.MATCHnomatch)
			{
				if (flags & 1)		// if do not print error messages
					return null;		// no match

				tf = cast(TypeFunction)type;

				scope OutBuffer buf2 = new OutBuffer();
				tf.modToBuffer(buf2);

				//printf("tf = %s, args = %s\n", tf.deco, ((Expression *)arguments.data[0]).type.deco);
				error(loc, "%s%s is not callable using argument types %s",
				Parameter.argsTypesToChars(tf.parameters, tf.varargs),
				buf2.toChars(),
				buf.toChars());
				return m.anyf;		// as long as it's not a FuncAliasDeclaration
			}
			else
			{
static if (true) {
				TypeFunction t1 = cast(TypeFunction)m.lastf.type;
				TypeFunction t2 = cast(TypeFunction)m.nextf.type;

				error(loc, "called with argument types:\n\t(%s)\nmatches both:\n\t%s%s\nand:\n\t%s%s",
					buf.toChars(),
					m.lastf.toPrettyChars(), Parameter.argsTypesToChars(t1.parameters, t1.varargs),
					m.nextf.toPrettyChars(), Parameter.argsTypesToChars(t2.parameters, t2.varargs));
} else {
				error(loc, "overloads %s and %s both match argument list for %s",
					m.lastf.type.toChars(),
					m.nextf.type.toChars(),
					m.lastf.toChars());
}
				return m.lastf;
			}
		}
	}
	
	/*************************************
	 * Determine partial specialization order of 'this' vs g.
	 * This is very similar to TemplateDeclaration.leastAsSpecialized().
	 * Returns:
	 *	match	'this' is at least as specialized as g
	 *	0	g is more specialized than 'this'
	 */
    MATCH leastAsSpecialized(FuncDeclaration g)
	{
	version (LOG_LEASTAS) {
		printf("%s.leastAsSpecialized(%s)\n", toChars(), g.toChars());
	}

		/* This works by calling g() with f()'s parameters, and
		 * if that is possible, then f() is at least as specialized
		 * as g() is.
		 */

		TypeFunction tf = cast(TypeFunction)type;
		TypeFunction tg = cast(TypeFunction)g.type;
		size_t nfparams = Parameter.dim(tf.parameters);
		size_t ngparams = Parameter.dim(tg.parameters);
		MATCH match = MATCHexact;

		/* If both functions have a 'this' pointer, and the mods are not
		 * the same and g's is not const, then this is less specialized.
		 */
		if (needThis() && g.needThis())
		{
			if (tf.mod != tg.mod)
			{
				if (tg.mod == MODconst)
					match = MATCHconst;
				else
					return MATCHnomatch;
			}
		}

		/* Create a dummy array of arguments out of the parameters to f()
		 */
		scope Expressions args = new Expressions();
		args.setDim(nfparams);
		for (int u = 0; u < nfparams; u++)
		{
			auto p = Parameter.getNth(tf.parameters, u);
			Expression e;
			if (p.storageClass & (STCref | STCout))
			{
				e = new IdentifierExp(Loc(0), p.ident);
				e.type = p.type;
			}
			else
				e = p.type.defaultInit(Loc(0));

			args[u] = e;
		}

		MATCH m = cast(MATCH) tg.callMatch(null, args);
		if (m)
		{
			/* A variadic parameter list is less specialized than a
			 * non-variadic one.
			 */
			if (tf.varargs && !tg.varargs)
				goto L1;	// less specialized

	version (LOG_LEASTAS) {
			printf("  matches %d, so is least as specialized\n", m);
	}
			return m;
		}
	  L1:
	version (LOG_LEASTAS) {
		printf("  doesn't match, so is not as specialized\n");
	}
		return MATCHnomatch;
	}
	
	/********************************
	 * Labels are in a separate scope, one per function.
	 */
    LabelDsymbol searchLabel(Identifier ident)
	{
		Dsymbol s;

		if (!labtab)
			labtab = new DsymbolTable();	// guess we need one

		s = labtab.lookup(ident);
		if (!s)
		{
			s = new LabelDsymbol(ident);
			labtab.insert(s);
		}

		return cast(LabelDsymbol)s;
	}
	
	/****************************************
	 * If non-static member function that has a 'this' pointer,
	 * return the aggregate it is a member of.
	 * Otherwise, return null.
	 */
    override AggregateDeclaration isThis()
	{
		AggregateDeclaration ad = null;

		//printf("+FuncDeclaration.isThis() '%s'\n", toChars());
		if ((storage_class & STC.STCstatic) == 0)
		{
			ad = isMember2();
		}
		//printf("-FuncDeclaration.isThis() %p\n", ad);
		return ad;
	}
	
    AggregateDeclaration isMember2()
	{
		AggregateDeclaration ad = null;

		//printf("+FuncDeclaration.isMember2() '%s'\n", toChars());
		for (Dsymbol s = this; s; s = s.parent)
		{
		//printf("\ts = '%s', parent = '%s', kind = %s\n", s.toChars(), s.parent.toChars(), s.parent.kind());
			ad = s.isMember();
			if (ad)
			{   //printf("test4\n");
					break;
			}
			if (!s.parent || (!s.parent.isTemplateInstance()))
			{   //printf("test5\n");
					break;
			}
		}
		//printf("-FuncDeclaration.isMember2() %p\n", ad);
		return ad;
	}
	
	/*****************************************
	 * Determine lexical level difference from 'this' to nested function 'fd'.
	 * Error if this cannot call fd.
	 * Returns:
	 *	0	same level
	 *	-1	increase nesting by 1 (fd is nested within 'this')
	 *	>0	decrease nesting by number
	 */
    int getLevel(Loc loc, FuncDeclaration fd)	// lexical nesting level difference
	{
		int level;
		Dsymbol s;
		Dsymbol fdparent;

		//printf("FuncDeclaration.getLevel(fd = '%s')\n", fd.toChars());
		fdparent = fd.toParent2();
		if (fdparent == this)
			return -1;
		s = this;
		level = 0;
		while (fd != s && fdparent != s.toParent2())
		{
			//printf("\ts = '%s'\n", s.toChars());
			FuncDeclaration thisfd = s.isFuncDeclaration();
			if (thisfd)
			{   
				if (!thisfd.isNested() && !thisfd.vthis)
					goto Lerr;
			}
			else
			{
				AggregateDeclaration thiscd = s.isAggregateDeclaration();
				if (thiscd)
				{	
					if (!thiscd.isNested())
						goto Lerr;
				}
				else
					goto Lerr;
			}

			s = s.toParent2();
			assert(s);
			level++;
		}
		return level;

	Lerr:
		error(loc, "cannot access frame of function %s", fd.toChars());
		return 1;
	}

    void appendExp(Expression e)
	{
		assert(false);
	}

    void appendState(Statement s)
	{
		assert(false);
	}

    override string mangle()
	out (result)
	{
		assert(result.length > 0);
	}
	body
	{
		if (isMain()) {
			return "_Dmain";
		}

		if (isWinMain() || isDllMain() || ident == Id.tls_get_addr)
			return ident.toChars();

		assert(this);

		return Declaration.mangle();
	}
	
    override string toPrettyChars()
	{
		if (isMain())
			return "D main";
		else
			return Dsymbol.toPrettyChars();
	}
	
    int isMain()
	{
		return ident is Id.main && linkage != LINK.LINKc && !isMember() && !isNested();
	}
	
    int isWinMain()
	{
		//printf("FuncDeclaration::isWinMain() %s\n", toChars());
static if (false) {
		int x = ident == Id.WinMain &&
		linkage != LINK.LINKc && !isMember();
		printf("%s\n", x ? "yes" : "no");
		return x;
} else {
		return ident == Id.WinMain && linkage != LINK.LINKc && !isMember();
}
	}

    int isDllMain()
	{
		return ident == Id.DllMain && linkage != LINK.LINKc && !isMember();
	}

	/**********************************
	 * Determine if function is a builtin one that we can
	 * evaluate at compile time.
	 */
    BUILTIN isBuiltin()
	{
		static string FeZe = "FNaNbeZe";	// pure nothrow real function(real)

		//printf("FuncDeclaration::isBuiltin() %s\n", toChars());
		if (builtin == BUILTIN.BUILTINunknown)
		{
			builtin = BUILTIN.BUILTINnot;
			if (parent && parent.isModule())
			{
				// If it's in the std.math package
				if (parent.ident == Id.math && parent.parent && parent.parent.ident == Id.std && !parent.parent.parent)
				{
					//printf("deco = %s\n", type.deco);
					if (type.deco == FeZe)
					{
						if (ident == Id.sin)
							builtin = BUILTIN.BUILTINsin;
						else if (ident == Id.cos)
							builtin = BUILTIN.BUILTINcos;
						else if (ident == Id.tan)
							builtin = BUILTIN.BUILTINtan;
						else if (ident == Id._sqrt)
							builtin = BUILTIN.BUILTINsqrt;
						else if (ident == Id.fabs)
							builtin = BUILTIN.BUILTINfabs;
						//printf("builtin = %d\n", builtin);
					}
					// if float or double versions
					else if (type.deco == "FNaNbdZd" || type.deco == "FNaNbfZf")
					{
						if (ident == Id._sqrt)
							builtin = BUILTIN.BUILTINsqrt;
					}
				}
			}
		}

		return builtin;
	}
	
    override bool isExport()
	{
		return protection == PROT.PROTexport;
	}
	
    override bool isImportedSymbol()
	{
		//printf("isImportedSymbol()\n");
		//printf("protection = %d\n", protection);
		return (protection == PROT.PROTexport) && !fbody;
	}
	
    override bool isAbstract()
	{
		return (storage_class & STC.STCabstract) != 0;
	}
	
    override bool isCodeseg()
	{
		return true;		// functions are always in the code segment
	}
	
    override bool isOverloadable()
	{
		return 1;			// functions can be overloaded
	}
	
    bool isPure()
	{
		//printf("FuncDeclaration::isPure() '%s'\n", toChars());
		assert(type.ty == TY.Tfunction);
		return (cast(TypeFunction)this.type).ispure;
	}
	
	int isSafe()
	{
		assert(type.ty == TY.Tfunction);
		return (cast(TypeFunction)this.type).trust == TRUST.TRUSTsafe;
	}

	int isTrusted()
	{
		assert(type.ty == TY.Tfunction);
		return (cast(TypeFunction)this.type).trust == TRUST.TRUSTtrusted;
	}
	
    bool isNested()
	{
		//if (!toParent())
		//printf("FuncDeclaration.isNested('%s') parent=%p\n", toChars(), parent);
		//printf("\ttoParent2() = '%s'\n", toParent2().toChars());
		return ((storage_class & STC.STCstatic) == 0) &&
		   (toParent2().isFuncDeclaration() !is null);
	}
	
    override bool needThis()
	{
		//printf("FuncDeclaration.needThis() '%s'\n", toChars());
		bool needThis = isThis() !is null;

		//printf("\t%d\n", i);
		if (!needThis) {
			if (auto fa = isFuncAliasDeclaration()) {
				needThis = fa.funcalias.needThis();
			}
		}

		return needThis;
	}
	
    bool isVirtual()
	{
static if (false) {
		printf("FuncDeclaration.isVirtual(%s)\n", toChars());
		printf("isMember:%p isStatic:%d private:%d ctor:%d !Dlinkage:%d\n", isMember(), isStatic(), protection == PROT.PROTprivate, isCtorDeclaration(), linkage != LINK.LINKd);
		printf("result is %d\n",
		isMember() && !(isStatic() || protection == PROT.PROTprivate || protection == PROT.PROTpackage) && toParent().isClassDeclaration());
}
		return isMember() && !(isStatic() || protection == PROT.PROTprivate || protection == PROT.PROTpackage) && toParent().isClassDeclaration();
	}
	
    override int isFinal()
	{
		ClassDeclaration cd;
static if (false) {
		printf("FuncDeclaration.isFinal(%s)\n", toChars());
		printf("%p %d %d %d %d\n", isMember(), isStatic(), protection == PROT.PROTprivate, isCtorDeclaration(), linkage != LINK.LINKd);
		printf("result is %d\n",
		isMember() && !(isStatic() || protection == PROT.PROTprivate || protection == PROT.PROTpackage) && (cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.STCfinal);
}
		return isMember() && (Declaration.isFinal() || ((cd = toParent().isClassDeclaration()) !is null && cd.storage_class & STC.STCfinal));
	}
	
    bool addPreInvariant()
	{
		AggregateDeclaration ad = isThis();
		return (ad &&
			//ad.isClassDeclaration() &&
			global.params.useInvariants &&
			(protection == PROT.PROTpublic || protection == PROT.PROTexport) &&
			!naked &&
			ident !is Id.cpctor);
	}
	
    bool addPostInvariant()
	{
		AggregateDeclaration ad = isThis();
		return (ad && ad.inv && 
			//ad.isClassDeclaration() &&
			global.params.useInvariants &&
			(protection == PROT.PROTpublic || protection == PROT.PROTexport) &&
			!naked &&
			ident !is Id.cpctor);
	}
	
	/*************************************
	 * Attempt to interpret a function given the arguments.
	 * Input:
	 *	istate     state for calling function (null if none)
	 *      arguments  function arguments
	 *      thisarg    'this', if a needThis() function, null if not.	
	 *
	 * Return result expression if successful, null if not.
	 */
    Expression interpret(InterState istate, Expressions arguments, Expression thisarg = null)
	{
version (LOG) {
		printf("\n********\nFuncDeclaration.interpret(istate = %p) %s\n", istate, toChars());
		printf("cantInterpret = %d, semanticRun = %d\n", cantInterpret, semanticRun);
}
		if (global.errors)
			return null;
version(DMDV1)
{
		if (ident == Id.aaLen)
			return interpret_aaLen(istate, arguments);
		else if (ident == Id.aaKeys)
			return interpret_aaKeys(istate, arguments);
		else if (ident == Id.aaValues)
			return interpret_aaValues(istate, arguments);
}
else version(DMDV2)
{
		if (thisarg && (!arguments || arguments.dim == 0))
		{
			if (ident == Id.length)
				return interpret_length(istate, thisarg);
			else if (ident == Id.keys)
				return interpret_keys(istate, thisarg, this);
			else if (ident == Id.values)
				return interpret_values(istate, thisarg, this);
		}
}
	
		if (cantInterpret || semanticRun == 3)
			return null;

		if (!fbody)
		{	
			cantInterpret = 1;
			return null;
		}

		if (semanticRun < 3 && scope_)
		{
			semantic3(scope_);
			if (global.errors)	// if errors compiling this function
				return null;
		}
		if (semanticRun < 4)
			return null;

		Type tb = type.toBasetype();
		assert(tb.ty == Tfunction);
		TypeFunction tf = cast(TypeFunction)tb;
		Type tret = tf.next.toBasetype();
		if (tf.varargs && arguments && parameters && arguments.dim != parameters.dim)
		{	
			cantInterpret = 1;
			error("C-style variadic functions are not yet implemented in CTFE");
			return null;
		}
		
		// Ensure there are no lazy parameters
		if (tf.parameters)
		{	
			size_t dim = Parameter.dim(tf.parameters);
			for (size_t i = 0; i < dim; i++)
			{   
				auto arg = Parameter.getNth(tf.parameters, i);
				if (arg.storageClass & STClazy)
				{   
					cantInterpret = 1;
					return null;
				}
			}
		}

		scope InterState istatex = new InterState();
		istatex.caller = istate;
		istatex.fd = this;
		istatex.localThis = thisarg;

		scope Expressions vsave = new Expressions();		// place to save previous parameter values
		size_t dim = 0;
		if (needThis() && !thisarg)
		{	
			cantInterpret = 1;
			// error, no this. Prevent segfault.
			error("need 'this' to access member %s", toChars());
			return null;
		}
		if (arguments)
		{
			dim = arguments.dim;
			assert(!dim || (parameters && (parameters.dim == dim)));
			vsave.setDim(dim);

			/* Evaluate all the arguments to the function,
			 * store the results in eargs[]
			 */
			scope Expressions eargs = new Expressions();
			eargs.setDim(dim);

			for (size_t i = 0; i < dim; i++)
			{   
				Expression earg = arguments[i];
				auto arg = Parameter.getNth(tf.parameters, i);

				if (arg.storageClass & (STCout | STCref))
				{
				}
				else
				{	/* Value parameters
				 */
					Type ta = arg.type.toBasetype();
					if (ta.ty == Tsarray && earg.op == TOKaddress)
					{
						/* Static arrays are passed by a simple pointer.
						 * Skip past this to get at the actual arg.
						 */
						earg = (cast(AddrExp)earg).e1;
					}
					earg = earg.interpret(istate ? istate : istatex);
					if (earg is EXP_CANT_INTERPRET)
					{   
						cantInterpret = 1;
						return null;
					}
				}
				eargs[i] = earg;
			}

			for (size_t i = 0; i < dim; i++)
			{   
				auto earg = eargs[i];
				auto arg = Parameter.getNth(tf.parameters, i);
				auto v = cast(VarDeclaration)parameters[i];
				vsave[i] = v.value;
version (LOG) {
				printf("arg[%d] = %s\n", i, earg.toChars());
}
				if (arg.storageClass & (STCout | STCref) && earg.op==TOKvar)
				{
					/* Bind out or ref parameter to the corresponding
					 * variable v2
					 */
					if (!istate)
					{   
						cantInterpret = 1;
						error("%s cannot be by passed by reference at compile time", earg.toChars());
						return null;	// can't bind to non-interpreted vars
					}		
					// We need to chase down all of the the passed parameters until
					// we find something that isn't a TOKvar, then create a variable
					// containg that expression.
					VarDeclaration v2;
					while (1)
					{
						VarExp ve = cast(VarExp)earg;
						v2 = ve.var.isVarDeclaration();
						if (!v2)
						{   
							cantInterpret = 1;
							return null;
						}
						if (!v2.value || v2.value.op != TOKvar)
							break;
						if ((cast(VarExp)v2.value).var.isSymbolDeclaration())		   
						{	
							// This can happen if v is a struct initialized to
							// 0 using an __initZ SymbolDeclaration from
							// TypeStruct.defaultInit()
							break; // eg default-initialized variable
						}
						earg = v2.value;
					}

					v.value = new VarExp(earg.loc, v2);

					/* Don't restore the value of v2 upon function return
					 */
					assert(istate);
					foreach(size_t j, Dsymbol s2; istate.vars)// (size_t j = 0; j < istate.vars.dim; j++)
					{   
						auto vd = cast(VarDeclaration)s2;
						if (vd == v2)
						{	
							istate.vars[j] = null;
							break;
						}
					}
				}
				else
				{	
					// Value parameters and non-trivial references
					v.value = earg;
				}
version (LOG) {
				printf("interpreted arg[%d] = %s\n", i, earg.toChars());
}
			}
		}
		// Don't restore the value of 'this' upon function return
		if (needThis() && thisarg.op == TOKvar && istate) {
			VarDeclaration thisvar = (cast(VarExp)thisarg).var.isVarDeclaration();
 			foreach (size_t i, Dsymbol s; istate.vars)
			{   
				auto v = cast(VarDeclaration)s;
				if (v == thisvar)
				{	
					istate.vars[i] = null;
					break;
				}
			}
		}

		/* Save the values of the local variables used
		 */
		scope valueSaves = new Expressions();
		if (istate && !isNested())
		{
			//printf("saving local variables...\n");
			valueSaves.setDim(istate.vars.dim);
			foreach (size_t i, Dsymbol s3; istate.vars)
			{   
				if (auto v = cast(VarDeclaration)s3)
				{
					//printf("\tsaving [%d] %s = %s\n", i, v.toChars(), v.value ? v.value.toChars() : "");
					valueSaves[i] = v.value;
					v.value = null;
				}
			}
		}

		Expression e = null;
		while (1)
		{
			e = fbody.interpret(istatex);
			if (e is EXP_CANT_INTERPRET)
			{
version (LOG) {
				printf("function body failed to interpret\n");
}
				e = null;
			}

			/* This is how we deal with a recursive statement AST
			 * that has arbitrary goto statements in it.
			 * Bubble up a 'result' which is the target of the goto
			 * statement, then go recursively down the AST looking
			 * for that statement, then execute starting there.
			 */
			if (e is EXP_GOTO_INTERPRET)
			{
				istatex.start = istatex.gotoTarget;	// set starting statement
				istatex.gotoTarget = null;
			}
			else
				break;
		}
		/* Restore the parameter values
		 */
		for (size_t i = 0; i < dim; i++)
		{
			auto v = cast(VarDeclaration)parameters[i];
			v.value = vsave[i];
		}

		if (istate && !isNested())
		{
			/* Restore the variable values
			 */
			//printf("restoring local variables...\n");
			foreach (size_t i , Dsymbol s3; istate.vars)
			{   
				if (auto v = cast(VarDeclaration)s3)
				{	
					v.value = valueSaves[i];
					//printf("\trestoring [%d] %s = %s\n", i, v.toChars(), v.value ? v.value.toChars() : "");
				}
			}
		}
		return e;
	}
	
    override void inlineScan()
	{
		InlineScanState iss;

	version (LOG) {
		printf("FuncDeclaration.inlineScan('%s')\n", toChars());
	}
		///memset(&iss, 0, sizeof(iss));
		iss.fd = this;
		if (fbody)
		{
			inlineNest++;
			fbody = fbody.inlineScan(&iss);
			inlineNest--;
		}
	}
	
    int canInline(int hasthis, int hdrscan = 0)
	{
		int cost;

//	#define CANINLINE_LOG 0

	version (CANINLINE_LOG) {
		printf("FuncDeclaration.canInline(hasthis = %d, '%s')\n", hasthis, toChars());
	}

		if (needThis() && !hasthis)
			return 0;

		if (inlineNest || (semanticRun < 3 && !hdrscan))
		{
	version (CANINLINE_LOG) {
			printf("\t1: no, inlineNest = %d, semanticRun = %d\n", inlineNest, semanticRun);
	}
			return 0;
		}

		switch (inlineStatus)
		{
			case ILS.ILSyes:
		version (CANINLINE_LOG) {
				printf("\t1: yes %s\n", toChars());
		}
				return 1;

			case ILS.ILSno:
		version (CANINLINE_LOG) {
				printf("\t1: no %s\n", toChars());
		}
				return 0;

			case ILS.ILSuninitialized:
				break;

			default:
				assert(0);
		}

		if (type)
		{	
			assert(type.ty == Tfunction);
			TypeFunction tf = cast(TypeFunction)type;
			if (tf.varargs == 1)	// no variadic parameter lists
				goto Lno;

			/* Don't inline a function that returns non-void, but has
			 * no return expression.
			 */
			if (tf.next && tf.next.ty != Tvoid &&
				!(hasReturnExp & 1) &&
				!hdrscan)
					goto Lno;
		}
		else
		{	
			CtorDeclaration ctor = isCtorDeclaration();
			if (ctor && ctor.varargs == 1)
				goto Lno;
		}

		if (
			!fbody ||
			!hdrscan &&
		(
///	static if (false) {
///		isCtorDeclaration() ||	// cannot because need to convert:
///					//	return;
///					// to:
///					//	return this;
///	}
		isSynchronized() ||
		isImportedSymbol() ||
///	version (DMDV2) {
		closureVars.dim ||	// no nested references to this frame
///	} else {
///		nestedFrameRef ||	// no nested references to this frame
///	}
		(isVirtual() && !isFinal())
		   ))
		{
			goto Lno;
		}

		/* If any parameters are Tsarray's (which are passed by reference)
		 * or out parameters (also passed by reference), don't do inlining.
		 */
		if (parameters)
		{
			foreach (Dsymbol s3; parameters)
			{
				auto v = cast(VarDeclaration)s3;
				if (v.isOut() || v.isRef() || v.type.toBasetype().ty == Tsarray)
					goto Lno;
			}
		}

		InlineCostState ics;
		///memset(&ics, 0, sizeof(ics));
		ics.hasthis = hasthis;
		ics.fd = this;
		ics.hdrscan = hdrscan;
		cost = fbody.inlineCost(&ics);
	version (CANINLINE_LOG) {
		printf("cost = %d\n", cost);
	}
		if (cost >= COST_MAX)
			goto Lno;

		if (!hdrscan)    // Don't scan recursively for header content scan
			inlineScan();

	Lyes:
		if (!hdrscan)    // Don't modify inlineStatus for header content scan
			inlineStatus = ILS.ILSyes;
	version (CANINLINE_LOG) {
		printf("\t2: yes %s\n", toChars());
	}
		return 1;

	Lno:
		if (!hdrscan)    // Don't modify inlineStatus for header content scan
			inlineStatus = ILS.ILSno;
	version (CANINLINE_LOG) {
		printf("\t2: no %s\n", toChars());
	}
		return 0;
	}

    Expression doInline(InlineScanState* iss, Expression ethis, Expressions arguments)
	{
		InlineDoState ids = new InlineDoState();
		DeclarationExp de;
		Expression e = null;

	version (LOG) {
		printf("FuncDeclaration.doInline('%s')\n", toChars());
	}

		///memset(&ids, 0, sizeof(ids));
		ids.parent = iss.fd;

		// Set up vthis
		if (ethis)
		{
			VarDeclaration vthis;
			ExpInitializer ei;
			VarExp ve;

		version (STRUCTTHISREF) {
			if (ethis.type.ty == Tpointer)
			{   
				Type t = ethis.type.nextOf();
				ethis = new PtrExp(ethis.loc, ethis);
				ethis.type = t;
			}
			ei = new ExpInitializer(ethis.loc, ethis);

			vthis = new VarDeclaration(ethis.loc, ethis.type, Id.This, ei);
			if (ethis.type.ty != Tclass)
				vthis.storage_class = STCref;
			else
				vthis.storage_class = STCin;
		} else {
			if (ethis.type.ty != Tclass && ethis.type.ty != Tpointer)
			{
				ethis = ethis.addressOf(null);
			}

			ei = new ExpInitializer(ethis.loc, ethis);

			vthis = new VarDeclaration(ethis.loc, ethis.type, Id.This, ei);
			vthis.storage_class = STCin;
		}
			vthis.linkage = LINKd;
			vthis.parent = iss.fd;

			ve = new VarExp(vthis.loc, vthis);
			ve.type = vthis.type;

			ei.exp = new AssignExp(vthis.loc, ve, ethis);
			ei.exp.type = ve.type;
		version (STRUCTTHISREF) {
			if (ethis.type.ty != Tclass)
			{   
				/* This is a reference initialization, not a simple assignment.
				 */
				ei.exp.op = TOKconstruct;
			}
		}

			ids.vthis = vthis;
		}

		// Set up parameters
		if (ethis)
		{
			e = new DeclarationExp(Loc(0), ids.vthis);
			e.type = Type.tvoid;
		}

		if (arguments && arguments.dim)
		{
			assert(parameters.dim == arguments.dim);

			for (int i = 0; i < arguments.dim; i++)
			{
				auto vfrom = cast(VarDeclaration)parameters[i];
				VarDeclaration vto;
				Expression arg = arguments[i];
				ExpInitializer ei;
				VarExp ve;

				ei = new ExpInitializer(arg.loc, arg);

				vto = new VarDeclaration(vfrom.loc, vfrom.type, vfrom.ident, ei);
				vto.storage_class |= vfrom.storage_class & (STCin | STCout | STClazy | STCref);
				vto.linkage = vfrom.linkage;
				vto.parent = iss.fd;
				//printf("vto = '%s', vto.storage_class = x%x\n", vto.toChars(), vto.storage_class);
				//printf("vto.parent = '%s'\n", iss.fd.toChars());

				ve = new VarExp(vto.loc, vto);
				//ve.type = vto.type;
				ve.type = arg.type;

				ei.exp = new AssignExp(vto.loc, ve, arg);
				ei.exp.type = ve.type;
		//ve.type.print();
		//arg.type.print();
		//ei.exp.print();

				ids.from.push(cast(void*)vfrom);
				ids.to.push(cast(void*)vto);

				de = new DeclarationExp(Loc(0), vto);
				de.type = Type.tvoid;

				e = Expression.combine(e, de);
			}
		}

		inlineNest++;
		Expression eb = fbody.doInline(ids);
		inlineNest--;
	//eb.type.print();
	//eb.print();
	//eb.dump(0);
		return Expression.combine(e, eb);
	}

    override string kind()
	{
		return "function";
	}

    override void toDocBuffer(OutBuffer buf)
	{
		assert(false);
	}
	
    FuncDeclaration isUnique()
	{
		assert(false);
	}
	
	/*******************************
	 * Look at all the variables in this function that are referenced
	 * by nested functions, and determine if a closure needs to be
	 * created for them.
	 */
    bool needsClosure()
	{
		/* Need a closure for all the closureVars[] if any of the
		 * closureVars[] are accessed by a
		 * function that escapes the scope of this function.
		 * We take the conservative approach and decide that any function that:
		 * 1) is a virtual function
		 * 2) has its address taken
		 * 3) has a parent that escapes
		 *
		 * Note that since a non-virtual function can be called by
		 * a virtual one, if that non-virtual function accesses a closure
		 * var, the closure still has to be taken. Hence, we check for isThis()
		 * instead of isVirtual(). (thanks to David Friedman)
		 */

		//printf("FuncDeclaration.needsClosure() %s\n", toChars());
		foreach (Dsymbol s3; closureVars)
		{	
			auto v = cast(VarDeclaration)s3;
			assert(v.isVarDeclaration());
			//printf("\tv = %s\n", v.toChars());

			foreach(FuncDeclaration f; v.nestedrefs)
			{
				assert(f != this);

				//printf("\t\tf = %s, %d, %p, %d\n", f.toChars(), f.isVirtual(), f.isThis(), f.tookAddressOf);
				if (f.isThis() || f.tookAddressOf)
					goto Lyes;	// assume f escapes this function's scope

				// Look to see if any parents of f that are below this escape
				for (Dsymbol s = f.parent; s && s !is this; s = s.parent)
				{
					f = s.isFuncDeclaration();
					if (f && (f.isThis() || f.tookAddressOf)) {
						goto Lyes;
					}
				}
			}
		}
		return false;

	Lyes:
		//printf("\tneeds closure\n");
		return true;
	}
	
    /****************************************************
	 * Merge into this function the 'in' contracts of all it overrides.
	 * 'in's are OR'd together, i.e. only one of them needs to pass.
	 */

	Statement mergeFrequire(Statement sf)
	{
		/* Implementing this is done by having the overriding function call
		 * nested functions (the fdrequire functions) nested inside the overridden
		 * function. This requires that the stack layout of the calling function's
		 * parameters and 'this' pointer be in the same place (as the nested
		 * function refers to them).
		 * This is easy for the parameters, as they are all on the stack in the same
		 * place by definition, since it's an overriding function. The problem is
		 * getting the 'this' pointer in the same place, since it is a local variable.
		 * We did some hacks in the code generator to make this happen:
		 *	1. always generate exception handler frame, or at least leave space for it
		 *     in the frame (Windows 32 SEH only)
		 *	2. always generate an EBP style frame
		 *  3. since 'this' is passed in a register that is subsequently copied into
		 *     a stack local, allocate that local immediately following the exception
		 *     handler block, so it is always at the same offset from EBP.
		 */
		foreach(FuncDeclaration fdv; foverrides) //(int i = 0; i < foverrides.dim; i++)
		{
			sf = fdv.mergeFrequire(sf);
			if (fdv.fdrequire)
			{
				//printf("fdv.frequire: %s\n", fdv.frequire.toChars());
				/* Make the call:
				 *   try { __require(); }
				 *   catch { frequire; }
				 */
				Expression eresult = null;
				Expression e = new CallExp(loc, new VarExp(loc, fdv.fdrequire, 0), eresult);
				Statement s2 = new ExpStatement(loc, e);

				if (sf)
				{	
					Catch c = new Catch(loc, null, null, sf);
					Array catches = new Array();
					catches.push(cast(void*)c);
					sf = new TryCatchStatement(loc, s2, catches);
				}
				else
					sf = s2;
			}
		}
		return sf;
	}

    /****************************************************
	 * Merge into this function the 'out' contracts of all it overrides.
	 * 'out's are AND'd together, i.e. all of them need to pass.
	 */

	Statement mergeFensure(Statement sf)
	{
		/* Same comments as for mergeFrequire(), except that we take care
		 * of generating a consistent reference to the 'result' local by
		 * explicitly passing 'result' to the nested function as a reference
		 * argument.
		 * This won't work for the 'this' parameter as it would require changing
		 * the semantic code for the nested function so that it looks on the parameter
		 * list for the 'this' pointer, something that would need an unknown amount
		 * of tweaking of various parts of the compiler that I'd rather leave alone.
		 */
		foreach (FuncDeclaration fdv; foverrides)
		{
			sf = fdv.mergeFensure(sf);
			if (fdv.fdensure)
			{
				//printf("fdv.fensure: %s\n", fdv.fensure.toChars());
				// Make the call: __ensure(result)
				Expression eresult = null;
				if (outId)
					eresult = new IdentifierExp(loc, outId);
				Expression e = new CallExp(loc, new VarExp(loc, fdv.fdensure, 0), eresult);
				Statement s2 = new ExpStatement(loc, e);

				if (sf)
				{
					sf = new CompoundStatement(fensure.loc, s2, sf);
				}
				else
					sf = s2;
			}
		}
		return sf;
	}
	
    static FuncDeclaration genCfunc(Type treturn, string name)
	{
		return genCfunc(treturn, Lexer.idPool(name));
	}
	
	/**********************************
	 * Generate a FuncDeclaration for a runtime library function.
	 */
    static FuncDeclaration genCfunc(Type treturn, Identifier id)
	{
		FuncDeclaration fd;
		TypeFunction tf;
		Dsymbol s;
		static DsymbolTable st = null;

		//printf("genCfunc(name = '%s')\n", id.toChars());
		//printf("treturn\n\t"); treturn.print();

		// See if already in table
		if (!st)
			st = new DsymbolTable();

		s = st.lookup(id);
		if (s)
		{
			fd = s.isFuncDeclaration();
			assert(fd);
			assert(fd.type.nextOf().equals(treturn));
		}
		else
		{
			tf = new TypeFunction(null, treturn, 0, LINK.LINKc);
			fd = new FuncDeclaration(Loc(0), Loc(0), id, STCstatic, tf);
			fd.protection = PROT.PROTpublic;
			fd.linkage = LINK.LINKc;

			st.insert(fd);
		}
		return fd;
	}
	
    override Symbol* toSymbol()
	{
		if (!csym)
		{
			Symbol* s;
			TYPE* t;
			string id;

static if (false) {
			id = ident.toChars();
} else {
			id = mangle();
}
			//writef("FuncDeclaration.toSymbol(%s %s)\n", kind(), toChars());
			//writef("\tid = '%s'\n", id);
			//writef("\ttype = %s\n", type.toChars());
			s = symbol_calloc(toStringz(id));
			slist_add(s);

			{
				s.prettyIdent = toStringz(toPrettyChars());
				s.Sclass = SC.SCglobal;
				symbol_func(s);
				func_t* f = s.Sfunc;
				if (isVirtual())
					f.Fflags |= F.Fvirtual;
				else if (isMember2())
					f.Fflags |= F.Fstatic;
				f.Fstartline.Slinnum = loc.linnum;
				f.Fstartline.Sfilename = cast(char*)toStringz(loc.filename);
				if (endloc.linnum)
				{	
					f.Fendline.Slinnum = endloc.linnum;
					f.Fendline.Sfilename = cast(char*)toStringz(endloc.filename);
				}
				else
				{	
					f.Fendline.Slinnum = loc.linnum;
					f.Fendline.Sfilename = cast(char*)toStringz(loc.filename);
				}
				t = type.toCtype();
			}

			mangle_t msave = t.Tmangle;
			if (isMain())
			{
				t.Tty = TYM.TYnfunc;
				t.Tmangle = mTYman.mTYman_c;
			}
			else
			{
				switch (linkage)
				{
				case LINK.LINKwindows:
					t.Tmangle = mTYman.mTYman_std;
					break;

				case LINK.LINKpascal:
					t.Tty = TYM.TYnpfunc;
					t.Tmangle = mTYman.mTYman_pas;
					break;

				case LINK.LINKc:
					t.Tmangle = mTYman.mTYman_c;
					break;

				case LINK.LINKd:
					t.Tmangle = mTYman.mTYman_d;
					break;

				case LINK.LINKcpp:
				{   t.Tmangle = mTYman.mTYman_cpp;
		version (TARGET_WINDOS) {
					if (isThis())
						t.Tty = TYM.TYmfunc;
		}
					s.Sflags |= SFL.SFLpublic;
					Dsymbol parent = toParent();
					ClassDeclaration cd = parent.isClassDeclaration();
					if (cd)
					{
						.type* tt = cd.type.toCtype();
						s.Sscope = tt.Tnext.Ttag;
					}
					break;
				}
				default:
					writef("linkage = %d\n", linkage);
					assert(0);
				}
			}
			if (msave)
				assert(msave == t.Tmangle);
			//printf("Tty = %x, mangle = x%x\n", t.Tty, t.Tmangle);
			t.Tcount++;
			s.Stype = t;
				//s.Sfielddef = this;

			csym = s;
		}
		return csym;
	}
	
    Symbol* toThunkSymbol(int offset)	// thunk version
	{
		Symbol *sthunk;

		toSymbol();

	static if (false) {
		char *id;
		char *n;
		type *t;

		n = sym.Sident;
		version (Bug4054) {
			id = cast(char*) GC.malloc(8 + 5 + strlen(n) + 1);
		} else {
			id = cast(char*) alloca(8 + 5 + strlen(n) + 1);
		}
		sprintf(id, "_thunk%d__%s", offset, n);
		s = symbol_calloc(id);
		slist_add(s);
		s.Stype = csym.Stype;
		s.Stype.Tcount++;
	}
		sthunk = symbol_generate(SCstatic, csym.Stype);
		sthunk.Sflags |= SFLimplem;
		cod3_thunk(sthunk, csym, 0, TYnptr, -offset, -1, 0);
		return sthunk;
	}
	
    override void toObjFile(int multiobj)			// compile to .obj file
	{
		Symbol* s;
		func_t* f;
		Symbol* senter;
		Symbol* sexit;
		
		FuncDeclaration func = this;
		ClassDeclaration cd = func.parent.isClassDeclaration();
		int reverse;

		int has_arguments;

		//printf("FuncDeclaration.toObjFile(%p, %s.%s)\n", func, parent.toChars(), func.toChars());
static if (false)
{
		//printf("line = %d\n",func.getWhere() / LINEINC);
		EEcontext ee = env.getEEcontext();
		if (ee.EEcompile == 2)
		{
			if (ee.EElinnum < (func.getWhere() / LINEINC) ||
				ee.EElinnum > (func.endwhere / LINEINC)
			)
			return;		// don't compile this function
			ee.EEfunc = func.toSymbol();
		}
}

		if (multiobj && !isStaticDtorDeclaration() && !isStaticCtorDeclaration())
		{	
			obj_append(this);
			return;
		}

		if (semanticRun >= 5)	// if toObjFile() already run
			return;

		semanticRun = 5;

		if (!func.fbody)
		{
			return;
		}

		if (func.isUnitTestDeclaration() && !global.params.useUnitTests)
			return;

		if (global.params.verbose)
			writef("function  %s\n",func.toChars());

		s = func.toSymbol();
		f = s.Sfunc;
		
version (TARGET_WINDOS)
{
    /* This is done so that the 'this' pointer on the stack is the same
     * distance away from the function parameters, so that an overriding
     * function can call the nested fdensure or fdrequire of its overridden function
     * and the stack offsets are the same.
     */
    if (isVirtual() && (fensure || frequire))
		f.Fflags3 |= F3.Ffakeeh;
}

version (TARGET_OSX) {
		s.Sclass = SC.SCcomdat;
} else {
		s.Sclass = SC.SCglobal;
}

		for (Dsymbol p = parent; p; p = p.parent)
		{
			if (p.isTemplateInstance())
			{
				s.Sclass = SC.SCcomdat;
				break;
			}
		}

		if (isNested())
		{
		//	if (!(config.flags3 & CFG3pic))
		//	    s.Sclass = SCstatic;
			f.Fflags3 |= F3.Fnested;
		}
		else
		{
			const(char)* libname = (global.params.symdebug) ? global.params.debuglibname : global.params.defaultlibname;

			// Pull in RTL startup code
			if (func.isMain())
			{   objextdef("_main");
version (POSIX) { ///TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS
				obj_ehsections();	// initialize exception handling sections
} else {
				objextdef("__acrtused_con");
}
				obj_includelib(libname);
				s.Sclass = SC.SCglobal;
			}
			else if (strcmp(s.Sident.ptr, "main".ptr) == 0 && linkage == LINK.LINKc)
				s.Sclass = SC.SCglobal;

			else if (func.isWinMain())
			{
				objextdef("__acrtused");
				obj_includelib(libname);
				s.Sclass = SC.SCglobal;
			}

			// Pull in RTL startup code
			else if (func.isDllMain())
			{
				objextdef("__acrtused_dll");
				obj_includelib(libname);
				s.Sclass = SC.SCglobal;
			}
		}

		cstate.CSpsymtab = &f.Flocsym;

		// Find module m for this function
		Module m = null;
		for (Dsymbol p = parent; p; p = p.parent)
		{
			m = p.isModule();
			if (m)
				break;
		}

		IRState irs = IRState(m, func);
		Array deferToObj = new Array();			// write these to OBJ file later
		irs.deferToObj = deferToObj;

		TypeFunction tf;
		RET retmethod;
		Symbol* shidden = null;
		Symbol* sthis = null;
		tym_t tyf;

		tyf = tybasic(s.Stype.Tty);
		//printf("linkage = %d, tyf = x%x\n", linkage, tyf);
		reverse = tyrevfunc(s.Stype.Tty);

		assert(func.type.ty == TY.Tfunction);
		tf = cast(TypeFunction)(func.type);
		has_arguments = (tf.linkage == LINK.LINKd) && (tf.varargs == 1);
		retmethod = tf.retStyle();
		if (retmethod == RET.RETstack)
		{
			// If function returns a struct, put a pointer to that
			// as the first argument
			.type* thidden = tf.next.pointerTo().toCtype();
			char hiddenparam[5+4+1];
			static int hiddenparami;    // how many we've generated so far

			sprintf(hiddenparam.ptr, "__HID%d".ptr, ++hiddenparami);
			shidden = symbol_name(hiddenparam.ptr, SC.SCparameter, thidden);
			shidden.Sflags |= SFL.SFLtrue | SFL.SFLfree;
			
version (DMDV1) {
			bool nestedref = func.nrvo_can && func.nrvo_var && func.nrvo_var.nestedref;
} else {
			bool nestedref = func.nrvo_can && func.nrvo_var && (func.nrvo_var.nestedrefs.dim != 0);
}
			if (nestedref) {
				type_setcv(&shidden.Stype, shidden.Stype.Tty | mTY.mTYvolatile);
			}

			irs.shidden = shidden;
			this.shidden = shidden;
		}

		if (vthis)
		{
			assert(!vthis.csym);
			sthis = vthis.toSymbol();
			irs.sthis = sthis;
			if (!(f.Fflags3 & F3.Fnested))
				f.Fflags3 |= F3.Fmember;
		}

		Symbol** params;
		uint pi;

		// Estimate number of parameters, pi
		pi = (v_arguments !is null);
		if (parameters)
			pi += parameters.dim;

		// Allow extra 2 for sthis and shidden
		version (Bug4054)
		params = cast(Symbol**)GC.malloc((pi + 2) * (Symbol*).sizeof);
		else
		params = cast(Symbol**)alloca((pi + 2) * (Symbol*).sizeof);

		// Get the actual number of parameters, pi, and fill in the params[]
		pi = 0;
		if (v_arguments)
		{
			params[pi] = v_arguments.toSymbol();
			pi += 1;
		}
		if (parameters)
		{
			size_t i = 0;
			for ( ; i < parameters.dim; ++i)
			{   
				auto v = cast(VarDeclaration)parameters[i];

				if (v.csym)
				{
					error("compiler error, parameter '%s', bugzilla 2962?", v.toChars());
					assert(false);
				}
				params[pi + i] = v.toSymbol();
			}
			pi += i;
		}

		if (reverse)
		{	
			// Reverse params[] entries
			for (size_t i = 0; i < pi/2; i++)
			{   
				Symbol* sptmp = params[i];
				params[i] = params[pi - 1 - i];
				params[pi - 1 - i] = sptmp;
			}
		}

		if (shidden)
		{
static if (false) {
			// shidden becomes last parameter
			params[pi] = shidden;
} else {
			// shidden becomes first parameter
			memmove(params + 1, params, pi * (*params).sizeof);
			params[0] = shidden;
}
			pi++;
		}


		if (sthis)
		{
static if (false) {
			// sthis becomes last parameter
			params[pi] = sthis;
} else {
			// sthis becomes first parameter
			memmove(params + 1, params, pi * (*params).sizeof);
			params[0] = sthis;
}
			pi++;
		}

		if ((global.params.isLinux || global.params.isOSX || global.params.isFreeBSD || global.params.isSolaris) &&
			linkage != LINK.LINKd && shidden && sthis)
		{
			/* swap shidden and sthis
			 */
			Symbol* sp = params[0];
			params[0] = params[1];
			params[1] = sp;
		}

		for (size_t i = 0; i < pi; i++)
		{	
			Symbol *sp = params[i];
			sp.Sclass = SC.SCparameter;
			sp.Sflags &= ~SFL.SFLspill;
			sp.Sfl = FL.FLpara;
			symbol_add(sp);
		}

		// First parameter goes in register
		if (pi)
		{
			Symbol* sp = params[0];
			if ((tyf == TYM.TYjfunc || tyf == TYM.TYmfunc) && type_jparam(sp.Stype))
			{   
				sp.Sclass = SC.SCfastpar;
				sp.Spreg = (tyf == TYM.TYjfunc) ? REG.AX : REG.CX;
				sp.Sfl = FL.FLauto;
				//printf("'%s' is SCfastpar\n",sp.Sident);
			}
		}

		if (func.fbody)
		{  
			block* b;
			Blockx bx;
			Statement sbody;

			localgot = null;

			sbody = func.fbody;
			///memset(&bx, 0, (bx).sizeof);
			bx.startblock = block_calloc();
			bx.curblock = bx.startblock;
			bx.funcsym = s;
			bx.scope_index = -1;
			bx.classdec = cd;
			bx.member = func;
			bx.module_ = getModule();
			irs.blx = &bx;

			buildClosure(&irs);

static if (false) {
			if (func.isSynchronized())
			{
				if (cd)
				{	
					elem *esync;
					if (func.isStatic())
					{   // monitor is in ClassInfo
						esync = el_ptr(cd.toSymbol());
					}
					else
					{   // 'this' is the monitor
						esync = el_var(sthis);
					}

					if (func.isStatic() || sbody.usesEH() ||
						!(config.flags2 & CFG2.CFG2seh))
					{   // BUG: what if frequire or fensure uses EH?

						sbody = new SynchronizedStatement(func.loc, esync, sbody);
					}
					else
					{
		version (TARGET_WINDOS) {
						if (config.flags2 & CFG2.CFG2seh)
						{
							/* The "jmonitor" uses an optimized exception handling frame
							 * which is a little shorter than the more general EH frame.
							 * It isn't strictly necessary.
							 */
							s.Sfunc.Fflags3 |= Fjmonitor;
						}
			}
						el_free(esync);
					}
				}
				else
				{
					error("synchronized function %s must be a member of a class", func.toChars());
				}
			}
} else version (TARGET_WINDOS) {
			if (func.isSynchronized() && cd && config.flags2 & CFG2.CFG2seh &&
				!func.isStatic() && !sbody.usesEH())
			{
				/* The "jmonitor" hack uses an optimized exception handling frame
				 * which is a little shorter than the more general EH frame.
				 */
				s.Sfunc.Fflags3 |= F3.Fjmonitor;
			}
}

			sbody.toIR(&irs);
			bx.curblock.BC = BC.BCret;

			f.Fstartblock = bx.startblock;
		//	einit = el_combine(einit,bx.init);

			if (isCtorDeclaration())
			{
				assert(sthis);
				for (b = f.Fstartblock; b; b = b.Bnext)
				{
					if (b.BC == BC.BCret)
					{
						b.BC = BC.BCretexp;
						b.Belem = el_combine(b.Belem, el_var(sthis));
					}
				}
			} 
		}

		// If static constructor
		if (isStaticConstructor())
		{
			elem* e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s));
			ector = el_combine(ector, e);
		}

		// If static destructor
		if (isStaticDestructor())
		{
			elem* e;

version (STATICCTOR) {
			e = el_bin(OPER.OPcall, TYM.TYvoid, el_var(rtlsym[RTLSYM.RTLSYM_FATEXIT]), el_ptr(s));
			ector = el_combine(ector, e);
			dtorcount++;
} else {
			StaticDtorDeclaration f2 = isStaticDtorDeclaration();
			assert(f2);
			if (f2.vgate)
			{   
				/* Increment destructor's vgate at construction time
				 */
				ectorgates.push(cast(void*)f2);
			}

			e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s));
			edtor = el_combine(e, edtor);
}
		}

		// If unit test
		if (isUnitTestDeclaration())
		{
			elem* e = el_una(OPER.OPucall, TYM.TYvoid, el_var(s));
			etest = el_combine(etest, e);
		}

		if (global.errors)
			return;

		writefunc(s);
		
		if (isExport()) {
			obj_export(s, Poffset);
		}

		for (size_t i = 0; i < irs.deferToObj.dim; i++)
		{
			Dsymbol ss = cast(Dsymbol)irs.deferToObj.data[i];
			ss.toObjFile(0);
		}

version (POSIX) { ///TARGET_LINUX || TARGET_OSX || TARGET_FREEBSD || TARGET_SOLARIS
		// A hack to get a pointer to this function put in the .dtors segment
		if (ident && ident.toChars() == "_STD") {
			obj_staticdtor(s);
		}
}
version (DMDV2) {
		if (irs.startaddress) 
		{
			writef("Setting start address\n");
			obj_startaddress(irs.startaddress);
		}
}
	}
	
    override int cvMember(ubyte* p)
	{
		assert(false);
	}
	
	/*************************************
	 * Closures are implemented by taking the local variables that
	 * need to survive the scope of the function, and copying them
	 * into a gc allocated chuck of memory. That chunk, called the
	 * closure here, is inserted into the linked list of stack
	 * frames instead of the usual stack frame.
	 *
	 * buildClosure() inserts code just after the function prolog
	 * is complete. It allocates memory for the closure, allocates
	 * a local variable (sclosure) to point to it, inserts into it
	 * the link to the enclosing frame, and copies into it the parameters
	 * that are referred to in nested functions.
	 * In VarExp.toElem and SymOffExp.toElem, when referring to a
	 * variable that is in a closure, takes the offset from sclosure rather
	 * than from the frame pointer.
	 *
	 * getEthis() and NewExp.toElem need to use sclosure, if set, rather
	 * than the current frame pointer.
	 */
    void buildClosure(IRState* irs)
	{
		if (needsClosure())
		{   
			// Generate closure on the heap
			// BUG: doesn't capture variadic arguments passed to this function

		version (DMDV2) {
			/* BUG: doesn't handle destructors for the local variables.
			 * The way to do it is to make the closure variables the fields
			 * of a class object:
			 *    class Closure
			 *    {   vtbl[]
			 *	  monitor
			 *	  ptr to destructor
			 *	  sthis
			 *	  ... closure variables ...
			 *	  ~this() { call destructor }
			 *    }
			 */
		}
			//printf("FuncDeclaration.buildClosure()\n");
			Symbol* sclosure;
			sclosure = symbol_name("__closptr".ptr, SC.SCauto, Type.tvoidptr.toCtype());
			sclosure.Sflags |= SFL.SFLtrue | SFL.SFLfree;
			symbol_add(sclosure);
			irs.sclosure = sclosure;

			uint offset = PTRSIZE;	// leave room for previous sthis
			foreach (Dsymbol s3; closureVars)
			{   
				auto v = cast(VarDeclaration)s3;
				assert(v.isVarDeclaration());

		version (DMDV2) {
				if (v.needsAutoDtor())
					v.error("has scoped destruction, cannot build closure");
		}
				/* Align and allocate space for v in the closure
				 * just like AggregateDeclaration.addField() does.
				 */
				uint memsize;
				uint memalignsize;
				uint xalign;
///		version (DMDV2) {
				if (v.storage_class & STC.STClazy)
				{
					/* Lazy variables are really delegates,
					 * so give same answers that TypeDelegate would
					 */
					memsize = PTRSIZE * 2;
					memalignsize = memsize;
					xalign = global.structalign;
				}
				else
///		}
				{
					memsize = cast(uint)v.type.size();
					memalignsize = v.type.alignsize();
					xalign = v.type.memalign(global.structalign);
				}
				AggregateDeclaration.alignmember(xalign, memalignsize, &offset);
				v.offset = offset;
				offset += memsize;

				/* Can't do nrvo if the variable is put in a closure, since
				 * what the shidden points to may no longer exist.
				 */
				if (nrvo_can && nrvo_var == v)
				{
					nrvo_can = 0;
				}
			}
			// offset is now the size of the closure

			// Allocate memory for the closure
			elem* e;
			e = el_long(TYM.TYint, offset);
			e = el_bin(OPER.OPcall, TYM.TYnptr, el_var(rtlsym[RTLSYM.RTLSYM_ALLOCMEMORY]), e);

			// Assign block of memory to sclosure
			//    sclosure = allocmemory(sz);
			e = el_bin(OPER.OPeq, TYM.TYvoid, el_var(sclosure), e);

			// Set the first element to sthis
			//    *(sclosure + 0) = sthis;
			elem* ethis;
			if (irs.sthis)
				ethis = el_var(irs.sthis);
			else
				ethis = el_long(TYM.TYnptr, 0);
			elem *ex = el_una(OPER.OPind, TYM.TYnptr, el_var(sclosure));
			ex = el_bin(OPER.OPeq, TYM.TYnptr, ex, ethis);
			e = el_combine(e, ex);

			// Copy function parameters into closure
			foreach (Dsymbol s3; closureVars)
			{   auto v = cast(VarDeclaration)s3;

				if (!v.isParameter())
					continue;
				TYM tym = v.type.totym();
				if (v.type.toBasetype().ty == TY.Tsarray || v.isOut() || v.isRef())
					tym = TYM.TYnptr;	// reference parameters are just pointers
///		version (DMDV2) {
				else if (v.storage_class & STC.STClazy)
					tym = TYM.TYdelegate;
///		}
				ex = el_bin(OPER.OPadd, TYM.TYnptr, el_var(sclosure), el_long(TYM.TYint, v.offset));
				ex = el_una(OPER.OPind, tym, ex);
				if (ex.Ety == TYM.TYstruct)
				{   
					ex.Enumbytes = cast(uint)v.type.size();
					ex = el_bin(OPER.OPstreq, tym, ex, el_var(v.toSymbol()));
					ex.Enumbytes = cast(uint)v.type.size();
				}
				else
				{
					ex = el_bin(OPER.OPeq, tym, ex, el_var(v.toSymbol()));
				}

				e = el_combine(e, ex);
			}

			block_appendexp(irs.blx.curblock, e);
		}
	}

    /*********************************************
     * Return the function's parameter list, and whether
     * it is variadic or not.
     */

    Parameters getParameters(int *pvarargs)
    {
        Parameters fparameters;
        int fvarargs;

        if (type)
        {
	        assert(type.ty == Tfunction);
	        auto fdtype = cast(TypeFunction)type;
	        fparameters = fdtype.parameters;
	        fvarargs = fdtype.varargs;
        }
        else // Constructors don't have type's
        {
            CtorDeclaration fctor = isCtorDeclaration();
	        assert(fctor);
	        fparameters = fctor.arguments;
	        fvarargs = fctor.varargs;
        }
        if (pvarargs)
	    *pvarargs = fvarargs;
        return fparameters;
    }

    override FuncDeclaration isFuncDeclaration() { return this; }
}

alias Vector!FuncDeclaration FuncDeclarations;