diff dmd/AssignExp.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 832f71e6f96c
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmd/AssignExp.d	Sat Oct 24 08:42:06 2009 +0400
@@ -0,0 +1,857 @@
+module dmd.AssignExp;
+
+import dmd.Expression;
+import dmd.Identifier;
+import dmd.backend.elem;
+import dmd.InterState;
+import dmd.IndexExp;
+import dmd.CallExp;
+import dmd.TypeSArray;
+import dmd.StructLiteralExp;
+import dmd.ArrayLengthExp;
+import dmd.TypeStruct;
+import dmd.StructDeclaration;
+import dmd.VarExp;
+import dmd.TY;
+import dmd.SliceExp;
+import dmd.CommaExp;
+import dmd.ArrayExp;
+import dmd.AggregateDeclaration;
+import dmd.CondExp;
+import dmd.DotVarExp;
+import dmd.WANT;
+import dmd.Id;
+import dmd.TypeClass;
+import dmd.OutBuffer;
+import dmd.Loc;
+import dmd.TupleExp;
+import dmd.VarDeclaration;
+import dmd.Scope;
+import dmd.IRState;
+import dmd.ArrayTypes;
+import dmd.BinExp;
+import dmd.TOK;
+import dmd.Global;
+import dmd.Declaration;
+import dmd.TypeFunction;
+import dmd.Type;
+import dmd.RET;
+import dmd.STC;
+import dmd.DotIdExp;
+
+import dmd.backend.Util;
+import dmd.backend.Symbol;
+import dmd.backend.OPER;
+import dmd.backend.TYM;
+import dmd.backend.RTLSYM;
+import dmd.codegen.Util;
+import dmd.expression.Util;
+
+class AssignExp : BinExp
+{
+	int ismemset = 0;
+
+	this(Loc loc, Expression e1, Expression e2)
+	{
+		super(loc, TOK.TOKassign, AssignExp.sizeof, e1, e2);
+	}
+
+	Expression semantic(Scope sc)
+	{
+		Expression e1old = e1;
+
+version (LOGSEMANTIC) {
+		printf("AssignExp.semantic('%s')\n", toChars());
+}
+		//printf("e1.op = %d, '%s'\n", e1.op, Token.toChars(e1.op));
+		//printf("e2.op = %d, '%s'\n", e2.op, Token.toChars(e2.op));
+
+		if (type)
+			return this;
+
+		if (e2.op == TOK.TOKcomma)
+		{	
+			/* Rewrite to get rid of the comma from rvalue
+			 */
+			AssignExp ea = new AssignExp(loc, e1, (cast(CommaExp)e2).e2);
+			ea.op = op;
+			Expression e = new CommaExp(loc, (cast(CommaExp)e2).e1, ea);
+			return e.semantic(sc);
+		}
+
+		/* Look for operator overloading of a[i]=value.
+		 * Do it before semantic() otherwise the a[i] will have been
+		 * converted to a.opIndex() already.
+		 */
+		if (e1.op == TOK.TOKarray)
+		{
+			ArrayExp ae = cast(ArrayExp)e1;
+			AggregateDeclaration ad;
+			Identifier id = Id.index;
+
+			ae.e1 = ae.e1.semantic(sc);
+			Type t1 = ae.e1.type.toBasetype();
+			if (t1.ty == TY.Tstruct)
+			{
+				ad = (cast(TypeStruct)t1).sym;
+				goto L1;
+			}
+			else if (t1.ty == TY.Tclass)
+			{
+				ad = (cast(TypeClass)t1).sym;
+			  L1:
+				// Rewrite (a[i] = value) to (a.opIndexAssign(value, i))
+				if (search_function(ad, Id.indexass))
+				{	
+					Expression e = new DotIdExp(loc, ae.e1, Id.indexass);
+					Expressions a = cast(Expressions)ae.arguments.copy();
+
+					a.insert(0, cast(void*)e2);
+					e = new CallExp(loc, e, a);
+					e = e.semantic(sc);
+					return e;
+				}
+				else
+				{
+					// Rewrite (a[i] = value) to (a.opIndex(i, value))
+					if (search_function(ad, id))
+					{   
+						Expression e = new DotIdExp(loc, ae.e1, id);
+
+						if (1 || !global.params.useDeprecated)
+							error("operator [] assignment overload with opIndex(i, value) illegal, use opIndexAssign(value, i)");
+
+						e = new CallExp(loc, e, cast(Expression)ae.arguments.data[0], e2);
+						e = e.semantic(sc);
+						return e;
+					}
+				}
+			}
+		}
+		/* Look for operator overloading of a[i..j]=value.
+		 * Do it before semantic() otherwise the a[i..j] will have been
+		 * converted to a.opSlice() already.
+		 */
+		if (e1.op == TOK.TOKslice)
+		{
+			Type t1;
+			SliceExp ae = cast(SliceExp)e1;
+			AggregateDeclaration ad;
+			Identifier id = Id.index;
+
+			ae.e1 = ae.e1.semantic(sc);
+			ae.e1 = resolveProperties(sc, ae.e1);
+			t1 = ae.e1.type.toBasetype();
+			if (t1.ty == TY.Tstruct)
+			{
+				ad = (cast(TypeStruct)t1).sym;
+				goto L2;
+			}
+			else if (t1.ty == TY.Tclass)
+			{
+				ad = (cast(TypeClass)t1).sym;
+			  L2:
+				// Rewrite (a[i..j] = value) to (a.opIndexAssign(value, i, j))
+				if (search_function(ad, Id.sliceass))
+				{	
+					Expression e = new DotIdExp(loc, ae.e1, Id.sliceass);
+					Expressions a = new Expressions();
+
+					a.push(cast(void*)e2);
+					if (ae.lwr)
+					{   
+						a.push(cast(void*)ae.lwr);
+						assert(ae.upr);
+						a.push(cast(void*)ae.upr);
+					}
+					else
+						assert(!ae.upr);
+
+					e = new CallExp(loc, e, a);
+					e = e.semantic(sc);
+					return e;
+				}
+			}
+		}
+
+		BinExp.semantic(sc);
+
+		if (e1.op == TOK.TOKdottd)
+		{	
+			// Rewrite a.b=e2, when b is a template, as a.b(e2)
+			Expression e = new CallExp(loc, e1, e2);
+			e = e.semantic(sc);
+			return e;
+		}
+
+		e2 = resolveProperties(sc, e2);
+		assert(e1.type);
+
+		/* Rewrite tuple assignment as a tuple of assignments.
+		 */
+		if (e1.op == TOK.TOKtuple && e2.op == TOK.TOKtuple)
+		{	
+			TupleExp tup1 = cast(TupleExp)e1;
+			TupleExp tup2 = cast(TupleExp)e2;
+			size_t dim = tup1.exps.dim;
+			if (dim != tup2.exps.dim)
+			{
+				error("mismatched tuple lengths, %d and %d", cast(int)dim, cast(int)tup2.exps.dim);
+			}
+			else
+			{   
+				Expressions exps = new Expressions;
+				exps.setDim(dim);
+
+				for (int i = 0; i < dim; i++)
+				{	
+					Expression ex1 = cast(Expression)tup1.exps.data[i];
+					Expression ex2 = cast(Expression)tup2.exps.data[i];
+					exps.data[i] = cast(void*) new AssignExp(loc, ex1, ex2);
+				}
+				Expression e = new TupleExp(loc, exps);
+				e = e.semantic(sc);
+				return e;
+			}
+		}
+
+		// Determine if this is an initialization of a reference
+		int refinit = 0;
+		if (op == TOK.TOKconstruct && e1.op == TOK.TOKvar)
+		{	
+			VarExp ve = cast(VarExp)e1;
+			VarDeclaration v = ve.var.isVarDeclaration();
+			if (v.storage_class & (STC.STCout | STC.STCref))
+				refinit = 1;
+		}
+
+		Type t1 = e1.type.toBasetype();
+
+		if (t1.ty == TY.Tfunction)
+		{	
+			// Rewrite f=value to f(value)
+			Expression e = new CallExp(loc, e1, e2);
+			e = e.semantic(sc);
+			return e;
+		}
+
+		/* If it is an assignment from a 'foreign' type,
+		 * check for operator overloading.
+		 */
+		if (t1.ty == TY.Tstruct)
+		{
+			StructDeclaration sd = (cast(TypeStruct)t1).sym;
+			if (op == TOK.TOKassign)
+			{
+				Expression e = op_overload(sc);
+				if (e)
+					return e;
+			}
+			else if (op == TOK.TOKconstruct && !refinit)
+			{   
+				Type t2 = e2.type.toBasetype();
+				if (t2.ty == TY.Tstruct && sd == (cast(TypeStruct)t2).sym && sd.cpctor)
+				{	
+					/* We have a copy constructor for this
+					 */
+					if (e2.op == TOK.TOKquestion)
+					{   /* Write as:
+						 *	a ? e1 = b : e1 = c;
+						 */
+						CondExp ec = cast(CondExp)e2;
+						AssignExp ea1 = new AssignExp(ec.e1.loc, e1, ec.e1);
+						ea1.op = op;
+						AssignExp ea2 = new AssignExp(ec.e1.loc, e1, ec.e2);
+						ea2.op = op;
+						Expression e = new CondExp(loc, ec.econd, ea1, ea2);
+						return e.semantic(sc);
+					}
+					else if (e2.op == TOK.TOKvar || e2.op == TOK.TOKdotvar || e2.op == TOK.TOKstar || e2.op == TOK.TOKindex)
+					{   /* Write as:
+						 *	e1.cpctor(e2);
+						 */
+						Expression e = new DotVarExp(loc, e1, sd.cpctor, 0);
+						e = new CallExp(loc, e, e2);
+						return e.semantic(sc);
+					}
+				}
+			}
+		}
+		else if (t1.ty == TY.Tclass)
+		{	
+			// Disallow assignment operator overloads for same type
+			if (!e2.type.implicitConvTo(e1.type))
+			{
+				Expression e = op_overload(sc);
+				if (e)
+					return e;
+			}
+		}
+
+		if (t1.ty == TY.Tsarray && !refinit)
+		{	
+			// Convert e1 to e1[]
+			Expression e = new SliceExp(e1.loc, e1, null, null);
+			e1 = e.semantic(sc);
+			t1 = e1.type.toBasetype();
+		}
+
+		e2.rvalue();
+
+		if (e1.op == TOK.TOKarraylength)
+		{
+			// e1 is not an lvalue, but we let code generator handle it
+			ArrayLengthExp ale = cast(ArrayLengthExp)e1;
+			ale.e1 = ale.e1.modifiableLvalue(sc, e1);
+		}
+		else if (e1.op == TOK.TOKslice)
+		{
+			Type tn = e1.type.nextOf();
+			if (tn && !tn.isMutable() && op != TOK.TOKconstruct)
+				error("slice %s is not mutable", e1.toChars());
+		}
+		else
+		{	
+			// Try to do a decent error message with the expression
+			// before it got constant folded
+			if (e1.op != TOK.TOKvar)
+				e1 = e1.optimize(WANT.WANTvalue);
+
+			if (op != TOK.TOKconstruct)
+				e1 = e1.modifiableLvalue(sc, e1old);
+		}
+
+		Type t2 = e2.type;
+		if (e1.op == TOK.TOKslice && t1.nextOf() && e2.implicitConvTo(t1.nextOf()))
+		{	
+			// memset
+			ismemset = 1;	// make it easy for back end to tell what this is
+			e2 = e2.implicitCastTo(sc, t1.nextOf());
+		}
+		else if (t1.ty == TY.Tsarray)
+		{
+			/* Should have already converted e1 => e1[]
+			 */
+			assert(op == TOK.TOKconstruct);
+			//error("cannot assign to static array %s", e1.toChars());
+		}
+		else if (e1.op == TOK.TOKslice)
+		{
+			e2 = e2.implicitCastTo(sc, e1.type.constOf());
+		}
+		else
+		{
+			e2 = e2.implicitCastTo(sc, e1.type);
+		}
+
+		/* Look for array operations
+		 */
+		if (e1.op == TOK.TOKslice && !ismemset &&
+			(e2.op == TOK.TOKadd || e2.op == TOK.TOKmin ||
+			 e2.op == TOK.TOKmul || e2.op == TOK.TOKdiv ||
+			 e2.op == TOK.TOKmod || e2.op == TOK.TOKxor ||
+			 e2.op == TOK.TOKand || e2.op == TOK.TOKor  ||
+			 e2.op == TOK.TOKtilde || e2.op == TOK.TOKneg))
+		{
+			type = e1.type;
+			return arrayOp(sc);
+		}
+
+		type = e1.type;
+		assert(type);
+		return this;
+	}
+
+	Expression checkToBoolean()
+	{
+		assert(false);
+	}
+
+	Expression interpret(InterState* istate)
+	{
+		assert(false);
+	}
+
+	Identifier opId()
+	{
+		return Id.assign;
+	}
+
+	void buildArrayIdent(OutBuffer buf, Expressions arguments)
+	{
+		assert(false);
+	}
+
+	Expression buildArrayLoop(Arguments fparams)
+	{
+		assert(false);
+	}
+
+	elem* toElem(IRState* irs)
+	{
+		elem* e;
+		IndexExp ae;
+		int r;
+		Type t1b;
+
+		//printf("AssignExp.toElem('%s')\n", toChars());
+		t1b = e1.type.toBasetype();
+
+		// Look for array.length = n
+		if (e1.op == TOK.TOKarraylength)
+		{
+			// Generate:
+			//	_d_arraysetlength(e2, sizeelem, &ale.e1);
+
+			ArrayLengthExp ale = cast(ArrayLengthExp)e1;
+			elem* p1;
+			elem* p2;
+			elem* p3;
+			elem* ep;
+			Type t1;
+
+			p1 = e2.toElem(irs);
+			p3 = ale.e1.toElem(irs);
+			p3 = addressElem(p3, null);
+			t1 = ale.e1.type.toBasetype();
+
+static if (true) {
+			// call _d_arraysetlengthT(ti, e2, &ale.e1);
+			p2 = t1.getTypeInfo(null).toElem(irs);
+			ep = el_params(p3, p1, p2, null);	// c function
+			r = t1.nextOf().isZeroInit(Loc(0)) ? RTLSYM.RTLSYM_ARRAYSETLENGTHT : RTLSYM.RTLSYM_ARRAYSETLENGTHIT;
+} else {
+			if (t1.next.isZeroInit())
+			{   
+				p2 = t1.getTypeInfo(null).toElem(irs);
+				ep = el_params(p3, p1, p2, null);	// c function
+				r = RTLSYM.RTLSYM_ARRAYSETLENGTHT;
+			}
+			else
+			{
+				p2 = el_long(TYM.TYint, t1.next.size());
+				ep = el_params(p3, p2, p1, null);	// c function
+				Expression init = t1.next.defaultInit();
+				ep = el_param(el_long(TYM.TYint, init.type.size()), ep);
+				elem* ei = init.toElem(irs);
+				ep = el_param(ei, ep);
+				r = RTLSYM.RTLSYM_ARRAYSETLENGTH3;
+			}
+}
+			e = el_bin(OPER.OPcall, type.totym(), el_var(rtlsym[r]), ep);
+			el_setLoc(e, loc);
+			return e;
+		}
+
+		// Look for array[]=n
+		if (e1.op == TOK.TOKslice)
+		{
+			Type t1 = t1b;
+			Type t2 = e2.type.toBasetype();
+
+			// which we do if the 'next' types match
+			if (ismemset)
+			{   
+				// Do a memset for array[]=v
+				//printf("Lpair %s\n", toChars());
+				SliceExp are = cast(SliceExp)e1;
+				elem* elwr;
+				elem* eupr;
+				elem* n1;
+				elem* evalue;
+				elem* enbytes;
+				elem* elength;
+				elem* einit;
+				long value;
+				Type ta = are.e1.type.toBasetype();
+				Type tb = ta.nextOf().toBasetype();
+				int sz = cast(uint)tb.size();
+				tym_t tym = type.totym();
+
+				n1 = are.e1.toElem(irs);
+				elwr = are.lwr ? are.lwr.toElem(irs) : null;
+				eupr = are.upr ? are.upr.toElem(irs) : null;
+
+				elem* n1x = n1;
+
+				// Look for array[]=n
+				if (ta.ty == TY.Tsarray)
+				{
+					TypeSArray ts = cast(TypeSArray)ta;
+					n1 = array_toPtr(ta, n1);
+					enbytes = ts.dim.toElem(irs);
+					n1x = n1;
+					n1 = el_same(&n1x);
+					einit = resolveLengthVar(are.lengthVar, &n1, ta);
+				}
+				else if (ta.ty == TY.Tarray)
+				{
+					n1 = el_same(&n1x);
+					einit = resolveLengthVar(are.lengthVar, &n1, ta);
+					enbytes = el_copytree(n1);
+					n1 = array_toPtr(ta, n1);
+					enbytes = el_una(OPER.OP64_32, TYM.TYint, enbytes);
+				}
+				else if (ta.ty == TY.Tpointer)
+				{
+					n1 = el_same(&n1x);
+					enbytes = el_long(TYM.TYint, -1);	// largest possible index
+					einit = null;
+				}
+
+				// Enforce order of evaluation of n1[elwr..eupr] as n1,elwr,eupr
+				elem* elwrx = elwr;
+				if (elwr) elwr = el_same(&elwrx);
+				elem* euprx = eupr;
+				if (eupr) eupr = el_same(&euprx);
+
+	static if (false) {
+				printf("sz = %d\n", sz);
+				printf("n1x\n");
+				elem_print(n1x);
+				printf("einit\n");
+				elem_print(einit);
+				printf("elwrx\n");
+				elem_print(elwrx);
+				printf("euprx\n");
+				elem_print(euprx);
+				printf("n1\n");
+				elem_print(n1);
+				printf("elwr\n");
+				elem_print(elwr);
+				printf("eupr\n");
+				elem_print(eupr);
+				printf("enbytes\n");
+				elem_print(enbytes);
+	}
+				einit = el_combine(n1x, einit);
+				einit = el_combine(einit, elwrx);
+				einit = el_combine(einit, euprx);
+
+				evalue = this.e2.toElem(irs);
+
+	static if (false) {
+				printf("n1\n");
+				elem_print(n1);
+				printf("enbytes\n");
+				elem_print(enbytes);
+	}
+
+				if (global.params.useArrayBounds && eupr && ta.ty != TY.Tpointer)
+				{
+					elem *c1;
+					elem *c2;
+					elem *ea;
+					elem *eb;
+					elem *enbytesx;
+
+					assert(elwr);
+					enbytesx = enbytes;
+					enbytes = el_same(&enbytesx);
+					c1 = el_bin(OPER.OPle, TYM.TYint, el_copytree(eupr), enbytesx);
+					c2 = el_bin(OPER.OPle, TYM.TYint, el_copytree(elwr), el_copytree(eupr));
+					c1 = el_bin(OPER.OPandand, TYM.TYint, c1, c2);
+
+					// Construct: (c1 || ModuleArray(line))
+					Symbol *sassert;
+
+					sassert = irs.blx.module_.toModuleArray();
+					ea = el_bin(OPER.OPcall,TYM.TYvoid,el_var(sassert), el_long(TYM.TYint, loc.linnum));
+					eb = el_bin(OPER.OPoror,TYM.TYvoid,c1,ea);
+					einit = el_combine(einit, eb);
+				}
+
+				if (elwr)
+				{   
+					elem *elwr2;
+
+					el_free(enbytes);
+					elwr2 = el_copytree(elwr);
+					elwr2 = el_bin(OPER.OPmul, TYM.TYint, elwr2, el_long(TYM.TYint, sz));
+					n1 = el_bin(OPER.OPadd, TYM.TYnptr, n1, elwr2);
+					enbytes = el_bin(OPER.OPmin, TYM.TYint, eupr, elwr);
+					elength = el_copytree(enbytes);
+				}
+				else
+					elength = el_copytree(enbytes);
+
+				e = setArray(n1, enbytes, tb, evalue, irs, op);
+			Lpair:
+				e = el_pair(TYM.TYullong, elength, e);
+			Lret2:
+				e = el_combine(einit, e);
+				//elem_print(e);
+				goto Lret;
+			}
+///static if (false) {
+///			else if (e2.op == TOK.TOKadd || e2.op == TOK.TOKmin)
+///			{
+///				/* It's ea[] = eb[] +- ec[]
+///				 */
+///				BinExp e2a = cast(BinExp)e2;
+///				Type t = e2.type.toBasetype().nextOf().toBasetype();
+///				if (t.ty != TY.Tfloat32 && t.ty != TY.Tfloat64 && t.ty != TY.Tfloat80)
+///				{
+///					e2.error("array add/min for %s not supported", t.toChars());
+///					return el_long(TYM.TYint, 0);
+///				}
+///				elem* ea = e1.toElem(irs);
+///				ea = array_toDarray(e1.type, ea);
+///				elem* eb = e2a.e1.toElem(irs);
+///				eb = array_toDarray(e2a.e1.type, eb);
+///				elem* ec = e2a.e2.toElem(irs);
+///				ec = array_toDarray(e2a.e2.type, ec);
+///
+///				int rtl = RTLSYM.RTLSYM_ARRAYASSADDFLOAT;
+///				if (t.ty == Tfloat64)
+///				rtl = RTLSYM.RTLSYM_ARRAYASSADDDOUBLE;
+///				else if (t.ty == Tfloat80)
+///				rtl = RTLSYM.RTLSYM_ARRAYASSADDREAL;
+///				if (e2.op == TOK.TOKmin)
+///				{
+///				rtl = RTLSYM.RTLSYM_ARRAYASSMINFLOAT;
+///				if (t.ty == Tfloat64)
+///					rtl = RTLSYM.RTLSYM_ARRAYASSMINDOUBLE;
+///				else if (t.ty == Tfloat80)
+///					rtl = RTLSYM.RTLSYM_ARRAYASSMINREAL;
+///				}
+///
+///				/* Set parameters so the order of evaluation is eb, ec, ea
+///				 */
+///				elem* ep = el_params(eb, ec, ea, null);
+///				e = el_bin(OPER.OPcall, type.totym(), el_var(rtlsym[rtl]), ep);
+///				goto Lret;
+///			}
+///}
+			else
+			{
+				/* It's array1[]=array2[]
+				 * which is a memcpy
+				 */
+				elem* eto;
+				elem* efrom;
+				elem* esize;
+				elem* ep;
+
+				eto = e1.toElem(irs);
+				efrom = e2.toElem(irs);
+
+				uint size = cast(uint)t1.nextOf().size();
+				esize = el_long(TYM.TYint, size);
+
+				/* Determine if we need to do postblit
+				 */
+				int postblit = 0;
+				if (needsPostblit(t1))
+					postblit = 1;
+
+				assert(e2.type.ty != TY.Tpointer);
+
+				if (!postblit && !global.params.useArrayBounds)
+				{	
+					elem* epto;
+					elem* epfr;
+					elem* elen;
+					elem* ex;
+
+					ex = el_same(&eto);
+
+					// Determine if elen is a constant
+					if (eto.Eoper == OPER.OPpair && eto.E1.Eoper == OPER.OPconst)
+					{
+						elen = el_copytree(eto.E1);
+					}
+					else
+					{
+						// It's not a constant, so pull it from the dynamic array
+						elen = el_una(OPER.OP64_32, TYM.TYint, el_copytree(ex));
+					}
+
+					esize = el_bin(OPER.OPmul, TYM.TYint, elen, esize);
+					epto = array_toPtr(e1.type, ex);
+					epfr = array_toPtr(e2.type, efrom);
+	static if (true) {
+					// memcpy() is faster, so if we can't beat 'em, join 'em
+					e = el_params(esize, epfr, epto, null);
+					e = el_bin(OPER.OPcall, TYM.TYnptr, el_var(rtlsym[RTLSYM.RTLSYM_MEMCPY]), e);
+	} else {
+					e = el_bin(OPER.OPmemcpy, TYM.TYnptr, epto, el_param(epfr, esize));
+	}
+					e = el_pair(eto.Ety, el_copytree(elen), e);
+					e = el_combine(eto, e);
+				}
+///version (DMDV2) {
+				else if (postblit && op != TOK.TOKblit)
+				{
+					/* Generate:
+					 *	_d_arrayassign(ti, efrom, eto)
+					 * or:
+					 *	_d_arrayctor(ti, efrom, eto)
+					 */
+					el_free(esize);
+					Expression ti = t1.nextOf().toBasetype().getTypeInfo(null);
+					ep = el_params(eto, efrom, ti.toElem(irs), null);
+					int rtl = (op == TOK.TOKconstruct) ? RTLSYM.RTLSYM_ARRAYCTOR : RTLSYM.RTLSYM_ARRAYASSIGN;
+					e = el_bin(OPER.OPcall, type.totym(), el_var(rtlsym[rtl]), ep);
+				}
+///}
+				else
+				{
+					// Generate:
+					//	_d_arraycopy(eto, efrom, esize)
+
+					ep = el_params(eto, efrom, esize, null);
+					e = el_bin(OPER.OPcall, type.totym(), el_var(rtlsym[RTLSYM.RTLSYM_ARRAYCOPY]), ep);
+				}
+				el_setLoc(e, loc);
+				return e;
+			}
+		}
+
+		if (e1.op == TOK.TOKindex)
+		{
+			elem* eb;
+			elem* ei;
+			elem* ev;
+			TY ty;
+			Type ta;
+
+			ae = cast(IndexExp)e1;
+			ta = ae.e1.type.toBasetype();
+			ty = ta.ty;
+		}
+
+version (DMDV2) {
+		/* Look for reference initializations
+		 */
+		if (op == TOK.TOKconstruct && e1.op == TOK.TOKvar)
+		{
+			VarExp ve = cast(VarExp)e1;
+			Declaration s = ve.var;
+			if (s.storage_class & STC.STCref)
+			{
+static if (false) {
+				Expression ae = e2.addressOf(null);
+				e = ae.toElem(irs);
+} else {
+				e = e2.toElem(irs);
+				e = addressElem(e, e2.type);
+}
+				elem* es = el_var(s.toSymbol());
+				es.Ety = TYM.TYnptr;
+				e = el_bin(OPER.OPeq, TYM.TYnptr, es, e);
+				// BUG: type is struct, and e2 is TOKint64
+				goto Lret;
+			}
+		}
+}
+
+static if (true) {
+		/* This will work if we can distinguish an assignment from
+		 * an initialization of the lvalue. It'll work if the latter.
+		 * If the former, because of aliasing of the return value with
+		 * function arguments, it'll fail.
+		 */
+		if (op == TOK.TOKconstruct && e2.op == TOK.TOKcall)
+		{	
+			CallExp ce = cast(CallExp)e2;
+
+			TypeFunction tf = cast(TypeFunction)ce.e1.type.toBasetype();
+			if (tf.ty == TY.Tfunction && tf.retStyle() == RET.RETstack)
+			{
+				elem* ehidden = e1.toElem(irs);
+				ehidden = el_una(OPER.OPaddr, TYM.TYnptr, ehidden);
+				assert(!irs.ehidden);
+				irs.ehidden = ehidden;
+				e = e2.toElem(irs);
+				goto Lret;
+			}
+		}
+}
+	//printf("test2 %d\n", op);
+	//if (op == TOK.TOKconstruct) printf("construct\n");
+		if (t1b.ty == TY.Tstruct)
+		{
+			elem* eleft = e1.toElem(irs);
+
+			if (e2.op == TOK.TOKint64)
+			{   
+				/* Implement:
+				 *	(struct = 0)
+				 * with:
+				 *	memset(&struct, 0, struct.sizeof)
+				 */
+				elem* ey = null;
+				int sz = cast(int)e1.type.size();
+				StructDeclaration sd = (cast(TypeStruct)t1b).sym;
+				if (sd.isnested && op == TOK.TOKconstruct)
+				{
+					ey = el_una(OPER.OPaddr, TYM.TYnptr, eleft);
+					eleft = el_same(&ey);
+					ey = setEthis(loc, irs, ey, sd);
+					sz = sd.vthis.offset;
+				}
+
+				elem *el = eleft;
+				elem *enbytes = el_long(TYM.TYint, sz);
+				elem *evalue = el_long(TYM.TYint, 0);
+
+				if (!(sd.isnested && op == TOK.TOKconstruct))
+					el = el_una(OPER.OPaddr, TYM.TYnptr, el);
+
+				e = el_param(enbytes, evalue);
+				e = el_bin(OPER.OPmemset, TYM.TYnptr,el,e);
+				e = el_combine(ey, e);
+				el_setLoc(e, loc);
+				//e = el_una(OPER.OPind, TYM.TYstruct, e);
+			}
+			else
+			{
+				//printf("toElemBin() '%s'\n", toChars());
+
+				tym_t tym = type.totym();
+
+				elem* e1 = eleft;
+				elem* ex = e1;
+				if (e1.Eoper == OPER.OPind)
+					ex = e1.E1;
+
+				if (this.e2.op == TOK.TOKstructliteral && ex.Eoper == OPER.OPvar && ex.EV.sp.Voffset == 0)
+				{	
+					StructLiteralExp se = cast(StructLiteralExp)this.e2;
+
+					Symbol* symSave = se.sym;
+					size_t soffsetSave = se.soffset;
+					int fillHolesSave = se.fillHoles;
+
+					se.sym = ex.EV.sp.Vsym;
+					se.soffset = 0;
+					se.fillHoles = (op == TOK.TOKconstruct || op == TOK.TOKblit) ? 1 : 0;
+
+					el_free(e1);
+					e = this.e2.toElem(irs);
+
+					se.sym = symSave;
+					se.soffset = soffsetSave;
+					se.fillHoles = fillHolesSave;
+				}
+				else
+				{
+					elem* e2 = this.e2.toElem(irs);
+					e = el_bin(OPER.OPstreq,tym,e1,e2);
+					e.Enumbytes = cast(uint)this.e1.type.size();
+				}
+				goto Lret;
+			}
+		}
+		else
+			e = toElemBin(irs,OPER.OPeq);
+
+		return e;
+
+	  Lret:
+		el_setLoc(e,loc);
+		return e;
+	}
+}
+