Mercurial > projects > ddmd
view dmd/SwitchStatement.d @ 178:e3afd1303184
Many small bugs fixed
Made all classes derive from TObject to detect memory leaks (functionality is disabled for now)
Began work on overriding backend memory allocations (to avoid memory leaks)
author | korDen |
---|---|
date | Sun, 17 Oct 2010 07:42:00 +0400 |
parents | af724d3510d7 |
children | 0622fff7810a |
line wrap: on
line source
module dmd.SwitchStatement; import dmd.common; import dmd.Statement; import dmd.Expression; import dmd.GlobalExpressions; 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.expression.Equal; 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) { register(); 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(body_); a.push(new BreakStatement(loc, null)); sc.sw.sdefault = new DefaultStatement(loc, s); a.push(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) { version (LOG) { printf("SwitchStatement.interpret()\n"); } if (istate.start == this) istate.start = null; Expression e = null; if (istate.start) { e = body_ ? body_.interpret(istate) : null; if (istate.start) return null; if (e is EXP_CANT_INTERPRET) return e; if (e is EXP_BREAK_INTERPRET) return null; return e; } Expression econdition = condition.interpret(istate); if (econdition is EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; Statement s = null; if (cases) { for (size_t i = 0; i < cases.dim; i++) { CaseStatement cs = cast(CaseStatement)cases.data[i]; e = Equal(TOKequal, Type.tint32, econdition, cs.exp); if (e is EXP_CANT_INTERPRET) return EXP_CANT_INTERPRET; if (e.isBool(true)) { s = cs; break; } } } if (!s) { if (hasNoDefault) error("no default or case for %s in switch statement", econdition.toChars()); s = sdefault; } assert(s); istate.start = s; e = body_ ? body_.interpret(istate) : null; assert(!istate.start); if (e is EXP_BREAK_INTERPRET) return null; return e; } override void toCBuffer(OutBuffer buf, HdrGenState* hgs) { buf.writestring("switch ("); condition.toCBuffer(buf, hgs); buf.writebyte(')'); buf.writenl(); if (body_) { if (!body_.isScopeStatement()) { buf.writebyte('{'); buf.writenl(); body_.toCBuffer(buf, hgs); buf.writebyte('}'); buf.writenl(); } else { body_.toCBuffer(buf, hgs); } } } 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); // Corresponding free is in block_free targ_llong* pu = cast(targ_llong*) GC.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); } }