diff dmd/SwitchStatement.d @ 0:10317f0c89a5

Initial commit
author korDen
date Sat, 24 Oct 2009 08:42:06 +0400
parents
children 7427ded8caf7
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/dmd/SwitchStatement.d	Sat Oct 24 08:42:06 2009 +0400
@@ -0,0 +1,440 @@
+module dmd.SwitchStatement;
+
+import dmd.Statement;
+import dmd.Expression;
+import dmd.DefaultStatement;
+import dmd.TryFinallyStatement;
+import dmd.Array;
+import dmd.Loc;
+import dmd.Scope;
+import dmd.OutBuffer;
+import dmd.HdrGenState;
+import dmd.InlineScanState;
+import dmd.IRState;
+import dmd.InterState;
+import dmd.BE;
+import dmd.TY;
+import dmd.WANT;
+import dmd.GotoCaseStatement;
+import dmd.CaseStatement;
+import dmd.ArrayTypes;
+import dmd.CompoundStatement;
+import dmd.Global;
+import dmd.SwitchErrorStatement;
+import dmd.Type;
+import dmd.HaltExp;
+import dmd.ExpStatement;
+import dmd.BreakStatement;
+import dmd.EnumDeclaration;
+import dmd.TypeEnum;
+import dmd.Dsymbol;
+import dmd.EnumMember;
+import dmd.TypeTypedef;
+import dmd.TOK;
+import dmd.StringExp;
+
+import dmd.backend.Util;
+import dmd.backend.block;
+import dmd.backend.Blockx;
+import dmd.backend.elem;
+import dmd.backend.OPER;
+import dmd.backend.TYM;
+import dmd.backend.BC;
+import dmd.backend.dt_t;
+import dmd.backend.Symbol;
+import dmd.backend.SC;
+import dmd.backend.FL;
+import dmd.backend.RTLSYM;
+import dmd.backend.targ_types;
+
+class SwitchStatement : Statement
+{
+    Expression condition;
+    Statement body_;
+    bool isFinal;
+
+    DefaultStatement sdefault = null;
+    TryFinallyStatement tf = null;
+    Array gotoCases;		// array of unresolved GotoCaseStatement's
+    Array cases;		// array of CaseStatement's
+    int hasNoDefault = 0;	// !=0 if no default statement
+    int hasVars = 0;		// !=0 if has variable case values
+
+    this(Loc loc, Expression c, Statement b, bool isFinal)
+	{
+		super(loc);
+		
+		this.condition = c;
+		this.body_ = b;
+		this.isFinal = isFinal;
+		
+		gotoCases = new Array();
+	}
+	
+    Statement syntaxCopy()
+	{
+		assert(false);
+	}
+	
+    Statement semantic(Scope sc)
+	{
+		//printf("SwitchStatement.semantic(%p)\n", this);
+		tf = sc.tf;
+		assert(!cases);		// ensure semantic() is only run once
+		condition = condition.semantic(sc);
+		condition = resolveProperties(sc, condition);
+		if (condition.type.isString())
+		{
+			// If it's not an array, cast it to one
+			if (condition.type.ty != Tarray)
+			{
+				condition = condition.implicitCastTo(sc, condition.type.nextOf().arrayOf());
+			}
+			condition.type = condition.type.constOf();
+		}
+		else
+		{	
+			condition = condition.integralPromotions(sc);
+			condition.checkIntegral();
+		}
+		condition = condition.optimize(WANTvalue);
+
+		sc = sc.push();
+		sc.sbreak = this;
+		sc.sw = this;
+
+		cases = new Array();
+		sc.noctor++;	// BUG: should use Scope.mergeCallSuper() for each case instead
+		body_ = body_.semantic(sc);
+		sc.noctor--;
+
+		// Resolve any goto case's with exp
+		for (int i = 0; i < gotoCases.dim; i++)
+		{
+			GotoCaseStatement gcs = cast(GotoCaseStatement)gotoCases.data[i];
+
+			if (!gcs.exp)
+			{
+				gcs.error("no case statement following goto case;");
+				break;
+			}
+
+			for (Scope scx = sc; scx; scx = scx.enclosing)
+			{
+				if (!scx.sw)
+					continue;
+				for (int j = 0; j < scx.sw.cases.dim; j++)
+				{
+					CaseStatement cs = cast(CaseStatement)scx.sw.cases.data[j];
+
+					if (cs.exp.equals(gcs.exp))
+					{
+						gcs.cs = cs;
+						goto Lfoundcase;
+					}
+				}
+			}
+			gcs.error("case %s not found", gcs.exp.toChars());
+
+		Lfoundcase:
+			;
+		}
+
+		if (!sc.sw.sdefault && !isFinal)
+		{	
+			hasNoDefault = 1;
+
+			warning("switch statement has no default");
+
+			// Generate runtime error if the default is hit
+			Statements a = new Statements();
+			CompoundStatement cs;
+			Statement s;
+
+			if (global.params.useSwitchError)
+				s = new SwitchErrorStatement(loc);
+			else
+			{   
+				Expression e = new HaltExp(loc);
+				s = new ExpStatement(loc, e);
+			}
+
+			a.reserve(4);
+			a.push(cast(void*)body_);
+			a.push(cast(void*)new BreakStatement(loc, null));
+			sc.sw.sdefault = new DefaultStatement(loc, s);
+			a.push(cast(void*)sc.sw.sdefault);
+			cs = new CompoundStatement(loc, a);
+			body_ = cs;
+		}
+
+	version (DMDV2) {
+		if (isFinal)
+		{	
+			Type t = condition.type;
+			while (t.ty == Ttypedef)
+			{   
+				// Don't use toBasetype() because that will skip past enums
+				t = (cast(TypeTypedef)t).sym.basetype;
+			}
+			if (condition.type.ty == Tenum)
+			{   
+				TypeEnum te = cast(TypeEnum)condition.type;
+				EnumDeclaration ed = te.toDsymbol(sc).isEnumDeclaration();
+				assert(ed);
+				size_t dim = ed.members.dim;
+				for (size_t i = 0; i < dim; i++)
+				{
+					EnumMember em = (cast(Dsymbol)ed.members.data[i]).isEnumMember();
+					if (em)
+					{
+						for (size_t j = 0; j < cases.dim; j++)
+						{   
+							CaseStatement cs = cast(CaseStatement)cases.data[j];
+							if (cs.exp.equals(em.value))
+								goto L1;
+						}
+						error("enum member %s not represented in final switch", em.toChars());
+					}
+				L1:
+					;
+				}
+			}
+		}
+	}
+
+		sc.pop();
+		return this;
+	}
+	
+    bool hasBreak()
+	{
+		assert(false);
+	}
+	
+    bool usesEH()
+	{
+		assert(false);
+	}
+	
+    BE blockExit()
+	{
+		BE result = BE.BEnone;
+		if (condition.canThrow())
+			result |= BE.BEthrow;
+
+		if (body_)
+		{	result |= body_.blockExit();
+			if (result & BE.BEbreak)
+			{   
+				result |= BE.BEfallthru;
+				result &= ~BE.BEbreak;
+			}
+		}
+		else
+			result |= BE.BEfallthru;
+
+		return result;
+	}
+
+    Expression interpret(InterState* istate)
+	{
+		assert(false);
+	}
+	
+    void toCBuffer(OutBuffer buf, HdrGenState* hgs)
+	{
+		assert(false);
+	}
+
+    Statement inlineScan(InlineScanState* iss)
+	{
+		//printf("SwitchStatement.inlineScan()\n");
+		condition = condition.inlineScan(iss);
+		body_ = body_ ? body_.inlineScan(iss) : null;
+		if (sdefault)
+			sdefault = cast(DefaultStatement)sdefault.inlineScan(iss);
+		if (cases)
+		{
+			for (int i = 0; i < cases.dim; i++)
+			{   
+				Statement s = cast(Statement)cases.data[i];
+				cases.data[i] = cast(void*)s.inlineScan(iss);
+			}
+		}
+		return this;
+	}
+
+    void toIR(IRState* irs)
+	{
+		int string;
+		Blockx* blx = irs.blx;
+
+		//printf("SwitchStatement.toIR()\n");
+		IRState mystate = IRState(irs,this);
+
+		mystate.switchBlock = blx.curblock;
+
+		/* Block for where "break" goes to
+		 */
+		mystate.breakBlock = block_calloc(blx);
+
+		/* Block for where "default" goes to.
+		 * If there is a default statement, then that is where default goes.
+		 * If not, then do:
+		 *   default: break;
+		 * by making the default block the same as the break block.
+		 */
+		mystate.defaultBlock = sdefault ? block_calloc(blx) : mystate.breakBlock;
+
+		int numcases = 0;
+		if (cases)
+			numcases = cases.dim;
+
+		incUsage(irs, loc);
+		elem* econd = condition.toElem(&mystate);
+
+	version (DMDV2) {
+		if (hasVars)
+		{	
+			/* Generate a sequence of if-then-else blocks for the cases.
+			 */
+			if (econd.Eoper != OPvar)
+			{
+				elem* e = exp2_copytotemp(econd);
+				block_appendexp(mystate.switchBlock, e);
+				econd = e.E2;
+			}
+
+			for (int i = 0; i < numcases; i++)
+			{   
+				CaseStatement cs = cast(CaseStatement)cases.data[i];
+
+				elem* ecase = cs.exp.toElem(&mystate);
+				elem* e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase);
+				block* b = blx.curblock;
+				block_appendexp(b, e);
+				block* bcase = block_calloc(blx);
+				cs.cblock = bcase;
+				block_next(blx, BCiftrue, null);
+				list_append(&b.Bsucc, bcase);
+				list_append(&b.Bsucc, blx.curblock);
+			}
+
+			/* The final 'else' clause goes to the default
+			 */
+			block* b = blx.curblock;
+			block_next(blx, BCgoto, null);
+			list_append(&b.Bsucc, mystate.defaultBlock);
+
+			body_.toIR(&mystate);
+
+			/* Have the end of the switch body fall through to the block
+			 * following the switch statement.
+			 */
+			block_goto(blx, BCgoto, mystate.breakBlock);
+			return;
+		}
+	}
+
+		if (condition.type.isString())
+		{
+			// Number the cases so we can unscramble things after the sort()
+			for (int i = 0; i < numcases; i++)
+			{   
+				CaseStatement cs = cast(CaseStatement)cases.data[i];
+				cs.index = i;
+			}
+
+			cases.sort();
+
+			/* Create a sorted array of the case strings, and si
+			 * will be the symbol for it.
+			 */
+			dt_t* dt = null;
+			Symbol* si = symbol_generate(SCstatic,type_fake(TYullong));
+		version (MACHOBJ) {
+			si.Sseg = Segment.DATA;
+		}
+			dtdword(&dt, numcases);
+			dtxoff(&dt, si, 8, TYnptr);
+
+			for (int i = 0; i < numcases; i++)
+			{   
+				CaseStatement cs = cast(CaseStatement)cases.data[i];
+
+				if (cs.exp.op != TOKstring)
+				{	
+					error("case '%s' is not a string", cs.exp.toChars());	// BUG: this should be an assert
+				}
+				else
+				{
+					StringExp se = cast(StringExp)(cs.exp);
+					uint len = se.len;
+					dtdword(&dt, len);
+					dtabytes(&dt, TYnptr, 0, se.len * se.sz, cast(char*)se.string_);
+				}
+			}
+
+			si.Sdt = dt;
+			si.Sfl = FLdata;
+			outdata(si);
+
+			/* Call:
+			 *	_d_switch_string(string[] si, string econd)
+			 */
+			elem* eparam = el_param(econd, el_var(si));
+			switch (condition.type.nextOf().ty)
+			{
+				case Tchar:
+					econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_STRING]), eparam);
+					break;
+				case Twchar:
+					econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_USTRING]), eparam);
+					break;
+				case Tdchar:	// BUG: implement
+					econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_DSTRING]), eparam);
+					break;
+				default:
+					assert(0);
+			}
+			elem_setLoc(econd, loc);
+			string = 1;
+		}
+		else
+			string = 0;
+		block_appendexp(mystate.switchBlock, econd);
+		block_next(blx,BCswitch,null);
+
+		targ_llong* pu = cast(targ_llong*) malloc(targ_llong.sizeof * (numcases + 1));
+		mystate.switchBlock.Bswitch = pu;
+		/* First pair is the number of cases, and the default block
+		 */
+		*pu++ = numcases;
+		list_append(&mystate.switchBlock.Bsucc, mystate.defaultBlock);
+
+		/* Fill in the first entry in each pair, which is the case value.
+		 * CaseStatement.toIR() will fill in
+		 * the second entry for each pair with the block.
+		 */
+		for (int i = 0; i < numcases; i++)
+		{
+			CaseStatement cs = cast(CaseStatement)cases.data[i];
+			if (string)
+			{
+				pu[cs.index] = i;
+			}
+			else
+			{
+				pu[i] = cs.exp.toInteger();
+			}
+		}
+
+		body_.toIR(&mystate);
+
+		/* Have the end of the switch body fall through to the block
+		 * following the switch statement.
+		 */
+		block_goto(blx, BCgoto, mystate.breakBlock);
+	}
+}
\ No newline at end of file