Mercurial > projects > ddmd
view dmd/SwitchStatement.d @ 106:786ea1839396
ddoc branch
author | Trass3r |
---|---|
date | Tue, 31 Aug 2010 18:02:48 +0200 |
parents | ad4792a1cfd6 |
children | e28b18c23469 |
line wrap: on
line source
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; import core.memory; import core.stdc.stdlib; 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(); } override Statement syntaxCopy() { SwitchStatement s = new SwitchStatement(loc, condition.syntaxCopy(), body_.syntaxCopy(), isFinal); return s; } override 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; foreach (Dsymbol s; ed.members) { if (auto em = s.isEnumMember()) { 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; } override bool hasBreak() { assert(false); } override bool usesEH() { assert(false); } override 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; } override Expression interpret(InterState istate) { assert(false); } override void toCBuffer(OutBuffer buf, HdrGenState* hgs) { assert(false); } override 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; } override 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); } }