diff dmd/FuncDeclaration.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 2cc604139636
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmd/FuncDeclaration.d	Sat Oct 24 08:42:06 2009 +0400
@@ -0,0 +1,3248 @@
+module dmd.FuncDeclaration;
+
+import dmd.Declaration;
+import dmd.DotIdExp;
+import dmd.TryFinallyStatement;
+import dmd.StaticDtorDeclaration;
+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.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.Argument;
+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.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.ILS;
+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;
+
+import std.string;
+
+class FuncDeclaration : Declaration
+{
+    Array fthrows;			// Array of Type's of exceptions (not used)
+    Statement frequire;
+    Statement fensure;
+    Statement fbody;
+
+    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 = -1;			// for member functions, index into vtbl[]
+    int naked;				// !=0 if naked
+    int inlineAsm;			// !=0 if has inline assembler
+    ILS inlineStatus = ILS.ILSuninitialized;
+    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, STC 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;
+
+		/* The type given for "infer the return type" is a TypeFunction with
+		 * null for the return type.
+		 */
+		inferRetType = (type && type.nextOf() is null);
+
+		closureVars = new Dsymbols();
+
+version (DMDV2) {
+		builtin = BUILTIN.BUILTINunknown;
+}
+	}
+	
+    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.
+    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)
+		{
+		/* Apply const and invariant storage class
+		 * to the function type
+		 */
+		type = type.semantic(loc, sc);
+		STC stc = storage_class;
+		if (type.isInvariant())
+			stc |= STC.STCimmutable;
+		if (type.isConst())
+			stc |= STC.STCconst;
+		if (type.isShared() || storage_class & STC.STCsynchronized)
+			stc |= STC.STCshared;
+		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:
+			// Don't use toInvariant(), as that will do a merge()
+			type = type.makeInvariant();
+			type.deco = type.merge().deco;
+			break;
+
+			case STC.STCconst:
+			type = type.makeConst();
+			type.deco = type.merge().deco;
+			break;
+
+			case STC.STCshared | STC.STCconst:
+			type = type.makeSharedConst();
+			type.deco = type.merge().deco;
+			break;
+
+			case STC.STCshared:
+			type = type.makeShared();
+			type.deco = type.merge().deco;
+			break;
+
+			case STC.STCundefined:
+			break;
+
+			default:
+			assert(0);
+		}
+		}
+		//type.print();
+		if (type.ty != TY.Tfunction)
+		{
+		error("%s must be a function", toChars());
+		return;
+		}
+		f = cast(TypeFunction)type;
+		size_t nparams = Argument.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.isInvariant()) && !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;
+
+			/* 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;
+
+				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:
+			{
+			Argument arg0 = Argument.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().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
+		{
+			Argument arg0 = Argument.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;
+			Argument arg1 = Argument.getNth(f.parameters, 1);
+			if (arg1.defaultArg)
+				goto Lassignerr;
+			}
+		}
+		}
+
+	Ldone:
+		/* Save scope for possible later use (if we need the
+		 * function internals)
+		 */
+		scope_ = new Scope(sc);
+		scope_.setNoFree();
+		return;
+
+	Lassignerr:
+		if (sd)
+		{
+		sd.hasIdentityAssign = 1;	// don't need to generate it
+		goto Ldone;
+		}
+		error("identity assignment operator overload is illegal");
+	}
+	
+    void semantic2(Scope sc)
+	{
+	}
+
+	// Do the semantic analysis on the internals of the function.
+    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());
+		}
+		}
+
+		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);
+		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.isInvariant())
+			{
+				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;
+			}
+}
+		}
+
+		// Propagate storage class from tuple parameters to their element-parameters.
+		if (f.parameters)
+		{
+			for (size_t i = 0; i < f.parameters.dim; i++)
+			{	Argument arg = cast(Argument)f.parameters.data[i];
+
+			//printf("[%d] arg.type.ty = %d %s\n", i, arg.type.ty, arg.type.toChars());
+			if (arg.type.ty == TY.Ttuple)
+			{   TypeTuple t = cast(TypeTuple)arg.type;
+				size_t dim = Argument.dim(t.arguments);
+				for (size_t j = 0; j < dim; j++)
+				{	Argument narg = Argument.getNth(t.arguments, j);
+				narg.storageClass = arg.storageClass;
+				}
+			}
+			}
+		}
+
+		/* Declare all the function parameters as variables
+		 * and install them in parameters[]
+		 */
+		size_t nparams = Argument.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++)
+			{
+			Argument arg = Argument.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(cast(void*)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)
+		{
+			for (size_t i = 0; i < f.parameters.dim; i++)
+			{	Argument arg = cast(Argument)f.parameters.data[i];
+
+			if (!arg.ident)
+				continue;			// never used, so ignore
+			if (arg.type.ty == TY.Ttuple)
+			{   TypeTuple t = cast(TypeTuple)arg.type;
+				size_t dim = Argument.dim(t.arguments);
+				Objects exps = new Objects();
+				exps.setDim(dim);
+				for (size_t j = 0; j < dim; j++)
+				{	Argument narg = Argument.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.data[j] = cast(void*)e;
+				}
+				assert(arg.ident);
+				TupleDeclaration 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
+			 */
+			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
+			VarDeclaration v;
+			Loc loc = this.loc;
+
+			if (fensure)
+				loc = fensure.loc;
+
+			v = new VarDeclaration(loc, type.nextOf(), outId, null);
+			v.noauto = true;
+version (DMDV2) {
+			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.data[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
+			{
+				for (int i = 0; i < add.members.dim; i++)
+				{   Dsymbol s = cast(Dsymbol)add.members.data[i];
+
+				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.data[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);
+					}
+				}
+			}
+		}
+
+		{
+			Statements a = new Statements();
+
+			// Merge in initialization of 'out' parameters
+			if (parameters)
+			{	for (size_t i = 0; i < parameters.dim; i++)
+			{
+				VarDeclaration v = cast(VarDeclaration)parameters.data[i];
+				if (v.storage_class & STC.STCout)
+				{
+				assert(v.init);
+				ExpInitializer ie = v.init.isExpInitializer();
+				assert(ie);
+				a.push(cast(void*)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 {
+			Expression e1;
+			Expression e;
+			Type t = argptr.type;
+			VarDeclaration p;
+			uint offset;
+
+			e1 = new VarExp(Loc(0), argptr);
+			if (parameters && parameters.dim)
+				p = cast(VarDeclaration)parameters.data[parameters.dim - 1];
+			else
+				p = v_arguments;		// last parameter is _arguments[]
+			offset = cast(uint)p.type.size();	///
+			offset = (offset + 3) & ~3;	// assume stack aligns on 4
+			e = new SymOffExp(Loc(0), p, offset);
+			e = new AssignExp(Loc(0), e1, e);
+			e.type = t;
+			a.push(cast(void*)new ExpStatement(Loc(0), e));
+}
+			}
+
+			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(cast(void*)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(cast(void*)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)
+			{
+				ExpStatement s = new ExpStatement(Loc(0), e);
+				a.push(cast(void*)s);
+			}
+			}
+
+			if (fbody)
+			a.push(cast(void*)fbody);
+
+			if (fensure)
+			{
+			a.push(cast(void*)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);
+				}
+				ReturnStatement s = new ReturnStatement(Loc(0), e);
+				a.push(cast(void*)s);
+			}
+			}
+
+			fbody = new CompoundStatement(Loc(0), a);
+version (DMDV2) {
+			/* Append destructor calls for parameters as finally blocks.
+			 */
+			if (parameters)
+			{	for (size_t i = 0; i < parameters.dim; i++)
+			{
+				VarDeclaration v = cast(VarDeclaration)parameters.data[i];
+
+				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);
+	}
+	
+    void toCBuffer(OutBuffer buf, HdrGenState* hgs)
+	{
+		assert(false);
+	}
+	
+    void bodyToCBuffer(OutBuffer buf, HdrGenState* hgs)
+	{
+		assert(false);
+	}
+	
+	/****************************************************
+	 * 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.
+	 */
+    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);
+		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",
+				Argument.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(), Argument.argsTypesToChars(t1.parameters, t1.varargs),
+					m.nextf.toPrettyChars(), Argument.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 = Argument.dim(tf.parameters);
+		size_t ngparams = Argument.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++)
+		{
+			Argument p = Argument.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.data[u] = cast(void*)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.
+	 */
+    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);
+	}
+
+    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();
+	}
+	
+    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;
+	}
+	
+    bool isExport()
+	{
+		return protection == PROT.PROTexport;
+	}
+	
+    bool isImportedSymbol()
+	{
+		//printf("isImportedSymbol()\n");
+		//printf("protection = %d\n", protection);
+		return (protection == PROT.PROTexport) && !fbody;
+	}
+	
+    bool isAbstract()
+	{
+		return (storage_class & STC.STCabstract) != 0;
+	}
+	
+    bool isCodeseg()
+	{
+		assert(false);
+	}
+	
+    bool isOverloadable()
+	{
+		assert(false);
+	}
+	
+    bool isPure()
+	{
+		//printf("FuncDeclaration::isPure() '%s'\n", toChars());
+		assert(type.ty == TY.Tfunction);
+		return (cast(TypeFunction)this.type).ispure;
+	}
+	
+    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);
+	}
+	
+    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();
+	}
+	
+    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);
+	}
+	
+    Expression interpret(InterState* istate, Expressions arguments, Expression thisexp = null)
+	{
+		assert(false);
+	}
+	
+    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)
+		{
+			for (int i = 0; i < parameters.dim; i++)
+			{
+				VarDeclaration v = cast(VarDeclaration)parameters.data[i];
+				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, Array 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++)
+			{
+				VarDeclaration vfrom = cast(VarDeclaration)parameters.data[i];
+				VarDeclaration vto;
+				Expression arg = cast(Expression)arguments.data[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);
+	}
+
+    string kind()
+	{
+		return "function";
+	}
+
+    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());
+		for (int i = 0; i < closureVars.dim; i++)
+		{	
+			VarDeclaration v = cast(VarDeclaration)closureVars.data[i];
+			assert(v.isVarDeclaration());
+			//printf("\tv = %s\n", v.toChars());
+
+			for (int j = 0; j < v.nestedrefs.dim; j++)
+			{   FuncDeclaration f = cast(FuncDeclaration)v.nestedrefs.data[j];
+				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;
+	}
+	
+    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;
+	}
+	
+    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;
+		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;
+	}
+	
+    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 i;
+		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_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 (XXX) { ///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
+		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)
+		{
+			for (i = 0; i < parameters.dim; i++)
+			{   
+				VarDeclaration v = cast(VarDeclaration)parameters.data[i];
+debug {
+				if (v.csym)
+					writef("parameter '%s'\n", v.toChars());
+}
+				assert(!v.csym);
+				params[pi + i] = v.toSymbol();
+			}
+			pi += i;
+		}
+
+		if (reverse)
+		{	
+			// Reverse params[] entries
+			for (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 (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 (i = 0; i < irs.deferToObj.dim; i++)
+		{
+			Dsymbol ss = cast(Dsymbol)irs.deferToObj.data[i];
+			ss.toObjFile(0);
+		}
+
+version (XXX) { ///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);
+		}
+}
+	}
+	
+    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
+			for (int i = 0; i < closureVars.dim; i++)
+			{   
+				VarDeclaration v = cast(VarDeclaration)closureVars.data[i];
+				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
+			for (int i = 0; i < closureVars.dim; i++)
+			{   VarDeclaration v = cast(VarDeclaration)closureVars.data[i];
+
+				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);
+		}
+	}
+
+    FuncDeclaration isFuncDeclaration() { return this; }
+}
\ No newline at end of file