0
|
1 module dmd.SwitchStatement;
|
|
2
|
|
3 import dmd.Statement;
|
|
4 import dmd.Expression;
|
|
5 import dmd.DefaultStatement;
|
|
6 import dmd.TryFinallyStatement;
|
|
7 import dmd.Array;
|
|
8 import dmd.Loc;
|
|
9 import dmd.Scope;
|
|
10 import dmd.OutBuffer;
|
|
11 import dmd.HdrGenState;
|
|
12 import dmd.InlineScanState;
|
|
13 import dmd.IRState;
|
|
14 import dmd.InterState;
|
|
15 import dmd.BE;
|
|
16 import dmd.TY;
|
|
17 import dmd.WANT;
|
|
18 import dmd.GotoCaseStatement;
|
|
19 import dmd.CaseStatement;
|
|
20 import dmd.ArrayTypes;
|
|
21 import dmd.CompoundStatement;
|
|
22 import dmd.Global;
|
|
23 import dmd.SwitchErrorStatement;
|
|
24 import dmd.Type;
|
|
25 import dmd.HaltExp;
|
|
26 import dmd.ExpStatement;
|
|
27 import dmd.BreakStatement;
|
|
28 import dmd.EnumDeclaration;
|
|
29 import dmd.TypeEnum;
|
|
30 import dmd.Dsymbol;
|
|
31 import dmd.EnumMember;
|
|
32 import dmd.TypeTypedef;
|
|
33 import dmd.TOK;
|
|
34 import dmd.StringExp;
|
|
35
|
|
36 import dmd.backend.Util;
|
|
37 import dmd.backend.block;
|
|
38 import dmd.backend.Blockx;
|
|
39 import dmd.backend.elem;
|
|
40 import dmd.backend.OPER;
|
|
41 import dmd.backend.TYM;
|
|
42 import dmd.backend.BC;
|
|
43 import dmd.backend.dt_t;
|
|
44 import dmd.backend.Symbol;
|
|
45 import dmd.backend.SC;
|
|
46 import dmd.backend.FL;
|
|
47 import dmd.backend.RTLSYM;
|
|
48 import dmd.backend.targ_types;
|
|
49
|
|
50 class SwitchStatement : Statement
|
|
51 {
|
|
52 Expression condition;
|
|
53 Statement body_;
|
|
54 bool isFinal;
|
|
55
|
|
56 DefaultStatement sdefault = null;
|
|
57 TryFinallyStatement tf = null;
|
|
58 Array gotoCases; // array of unresolved GotoCaseStatement's
|
|
59 Array cases; // array of CaseStatement's
|
|
60 int hasNoDefault = 0; // !=0 if no default statement
|
|
61 int hasVars = 0; // !=0 if has variable case values
|
|
62
|
|
63 this(Loc loc, Expression c, Statement b, bool isFinal)
|
|
64 {
|
|
65 super(loc);
|
|
66
|
|
67 this.condition = c;
|
|
68 this.body_ = b;
|
|
69 this.isFinal = isFinal;
|
|
70
|
|
71 gotoCases = new Array();
|
|
72 }
|
|
73
|
|
74 Statement syntaxCopy()
|
|
75 {
|
|
76 assert(false);
|
|
77 }
|
|
78
|
|
79 Statement semantic(Scope sc)
|
|
80 {
|
|
81 //printf("SwitchStatement.semantic(%p)\n", this);
|
|
82 tf = sc.tf;
|
|
83 assert(!cases); // ensure semantic() is only run once
|
|
84 condition = condition.semantic(sc);
|
|
85 condition = resolveProperties(sc, condition);
|
|
86 if (condition.type.isString())
|
|
87 {
|
|
88 // If it's not an array, cast it to one
|
|
89 if (condition.type.ty != Tarray)
|
|
90 {
|
|
91 condition = condition.implicitCastTo(sc, condition.type.nextOf().arrayOf());
|
|
92 }
|
|
93 condition.type = condition.type.constOf();
|
|
94 }
|
|
95 else
|
|
96 {
|
|
97 condition = condition.integralPromotions(sc);
|
|
98 condition.checkIntegral();
|
|
99 }
|
|
100 condition = condition.optimize(WANTvalue);
|
|
101
|
|
102 sc = sc.push();
|
|
103 sc.sbreak = this;
|
|
104 sc.sw = this;
|
|
105
|
|
106 cases = new Array();
|
|
107 sc.noctor++; // BUG: should use Scope.mergeCallSuper() for each case instead
|
|
108 body_ = body_.semantic(sc);
|
|
109 sc.noctor--;
|
|
110
|
|
111 // Resolve any goto case's with exp
|
|
112 for (int i = 0; i < gotoCases.dim; i++)
|
|
113 {
|
|
114 GotoCaseStatement gcs = cast(GotoCaseStatement)gotoCases.data[i];
|
|
115
|
|
116 if (!gcs.exp)
|
|
117 {
|
|
118 gcs.error("no case statement following goto case;");
|
|
119 break;
|
|
120 }
|
|
121
|
|
122 for (Scope scx = sc; scx; scx = scx.enclosing)
|
|
123 {
|
|
124 if (!scx.sw)
|
|
125 continue;
|
|
126 for (int j = 0; j < scx.sw.cases.dim; j++)
|
|
127 {
|
|
128 CaseStatement cs = cast(CaseStatement)scx.sw.cases.data[j];
|
|
129
|
|
130 if (cs.exp.equals(gcs.exp))
|
|
131 {
|
|
132 gcs.cs = cs;
|
|
133 goto Lfoundcase;
|
|
134 }
|
|
135 }
|
|
136 }
|
|
137 gcs.error("case %s not found", gcs.exp.toChars());
|
|
138
|
|
139 Lfoundcase:
|
|
140 ;
|
|
141 }
|
|
142
|
|
143 if (!sc.sw.sdefault && !isFinal)
|
|
144 {
|
|
145 hasNoDefault = 1;
|
|
146
|
|
147 warning("switch statement has no default");
|
|
148
|
|
149 // Generate runtime error if the default is hit
|
|
150 Statements a = new Statements();
|
|
151 CompoundStatement cs;
|
|
152 Statement s;
|
|
153
|
|
154 if (global.params.useSwitchError)
|
|
155 s = new SwitchErrorStatement(loc);
|
|
156 else
|
|
157 {
|
|
158 Expression e = new HaltExp(loc);
|
|
159 s = new ExpStatement(loc, e);
|
|
160 }
|
|
161
|
|
162 a.reserve(4);
|
|
163 a.push(cast(void*)body_);
|
|
164 a.push(cast(void*)new BreakStatement(loc, null));
|
|
165 sc.sw.sdefault = new DefaultStatement(loc, s);
|
|
166 a.push(cast(void*)sc.sw.sdefault);
|
|
167 cs = new CompoundStatement(loc, a);
|
|
168 body_ = cs;
|
|
169 }
|
|
170
|
|
171 version (DMDV2) {
|
|
172 if (isFinal)
|
|
173 {
|
|
174 Type t = condition.type;
|
|
175 while (t.ty == Ttypedef)
|
|
176 {
|
|
177 // Don't use toBasetype() because that will skip past enums
|
|
178 t = (cast(TypeTypedef)t).sym.basetype;
|
|
179 }
|
|
180 if (condition.type.ty == Tenum)
|
|
181 {
|
|
182 TypeEnum te = cast(TypeEnum)condition.type;
|
|
183 EnumDeclaration ed = te.toDsymbol(sc).isEnumDeclaration();
|
|
184 assert(ed);
|
|
185 size_t dim = ed.members.dim;
|
|
186 for (size_t i = 0; i < dim; i++)
|
|
187 {
|
|
188 EnumMember em = (cast(Dsymbol)ed.members.data[i]).isEnumMember();
|
|
189 if (em)
|
|
190 {
|
|
191 for (size_t j = 0; j < cases.dim; j++)
|
|
192 {
|
|
193 CaseStatement cs = cast(CaseStatement)cases.data[j];
|
|
194 if (cs.exp.equals(em.value))
|
|
195 goto L1;
|
|
196 }
|
|
197 error("enum member %s not represented in final switch", em.toChars());
|
|
198 }
|
|
199 L1:
|
|
200 ;
|
|
201 }
|
|
202 }
|
|
203 }
|
|
204 }
|
|
205
|
|
206 sc.pop();
|
|
207 return this;
|
|
208 }
|
|
209
|
|
210 bool hasBreak()
|
|
211 {
|
|
212 assert(false);
|
|
213 }
|
|
214
|
|
215 bool usesEH()
|
|
216 {
|
|
217 assert(false);
|
|
218 }
|
|
219
|
|
220 BE blockExit()
|
|
221 {
|
|
222 BE result = BE.BEnone;
|
|
223 if (condition.canThrow())
|
|
224 result |= BE.BEthrow;
|
|
225
|
|
226 if (body_)
|
|
227 { result |= body_.blockExit();
|
|
228 if (result & BE.BEbreak)
|
|
229 {
|
|
230 result |= BE.BEfallthru;
|
|
231 result &= ~BE.BEbreak;
|
|
232 }
|
|
233 }
|
|
234 else
|
|
235 result |= BE.BEfallthru;
|
|
236
|
|
237 return result;
|
|
238 }
|
|
239
|
|
240 Expression interpret(InterState* istate)
|
|
241 {
|
|
242 assert(false);
|
|
243 }
|
|
244
|
|
245 void toCBuffer(OutBuffer buf, HdrGenState* hgs)
|
|
246 {
|
|
247 assert(false);
|
|
248 }
|
|
249
|
|
250 Statement inlineScan(InlineScanState* iss)
|
|
251 {
|
|
252 //printf("SwitchStatement.inlineScan()\n");
|
|
253 condition = condition.inlineScan(iss);
|
|
254 body_ = body_ ? body_.inlineScan(iss) : null;
|
|
255 if (sdefault)
|
|
256 sdefault = cast(DefaultStatement)sdefault.inlineScan(iss);
|
|
257 if (cases)
|
|
258 {
|
|
259 for (int i = 0; i < cases.dim; i++)
|
|
260 {
|
|
261 Statement s = cast(Statement)cases.data[i];
|
|
262 cases.data[i] = cast(void*)s.inlineScan(iss);
|
|
263 }
|
|
264 }
|
|
265 return this;
|
|
266 }
|
|
267
|
|
268 void toIR(IRState* irs)
|
|
269 {
|
|
270 int string;
|
|
271 Blockx* blx = irs.blx;
|
|
272
|
|
273 //printf("SwitchStatement.toIR()\n");
|
|
274 IRState mystate = IRState(irs,this);
|
|
275
|
|
276 mystate.switchBlock = blx.curblock;
|
|
277
|
|
278 /* Block for where "break" goes to
|
|
279 */
|
|
280 mystate.breakBlock = block_calloc(blx);
|
|
281
|
|
282 /* Block for where "default" goes to.
|
|
283 * If there is a default statement, then that is where default goes.
|
|
284 * If not, then do:
|
|
285 * default: break;
|
|
286 * by making the default block the same as the break block.
|
|
287 */
|
|
288 mystate.defaultBlock = sdefault ? block_calloc(blx) : mystate.breakBlock;
|
|
289
|
|
290 int numcases = 0;
|
|
291 if (cases)
|
|
292 numcases = cases.dim;
|
|
293
|
|
294 incUsage(irs, loc);
|
|
295 elem* econd = condition.toElem(&mystate);
|
|
296
|
|
297 version (DMDV2) {
|
|
298 if (hasVars)
|
|
299 {
|
|
300 /* Generate a sequence of if-then-else blocks for the cases.
|
|
301 */
|
|
302 if (econd.Eoper != OPvar)
|
|
303 {
|
|
304 elem* e = exp2_copytotemp(econd);
|
|
305 block_appendexp(mystate.switchBlock, e);
|
|
306 econd = e.E2;
|
|
307 }
|
|
308
|
|
309 for (int i = 0; i < numcases; i++)
|
|
310 {
|
|
311 CaseStatement cs = cast(CaseStatement)cases.data[i];
|
|
312
|
|
313 elem* ecase = cs.exp.toElem(&mystate);
|
|
314 elem* e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase);
|
|
315 block* b = blx.curblock;
|
|
316 block_appendexp(b, e);
|
|
317 block* bcase = block_calloc(blx);
|
|
318 cs.cblock = bcase;
|
|
319 block_next(blx, BCiftrue, null);
|
|
320 list_append(&b.Bsucc, bcase);
|
|
321 list_append(&b.Bsucc, blx.curblock);
|
|
322 }
|
|
323
|
|
324 /* The final 'else' clause goes to the default
|
|
325 */
|
|
326 block* b = blx.curblock;
|
|
327 block_next(blx, BCgoto, null);
|
|
328 list_append(&b.Bsucc, mystate.defaultBlock);
|
|
329
|
|
330 body_.toIR(&mystate);
|
|
331
|
|
332 /* Have the end of the switch body fall through to the block
|
|
333 * following the switch statement.
|
|
334 */
|
|
335 block_goto(blx, BCgoto, mystate.breakBlock);
|
|
336 return;
|
|
337 }
|
|
338 }
|
|
339
|
|
340 if (condition.type.isString())
|
|
341 {
|
|
342 // Number the cases so we can unscramble things after the sort()
|
|
343 for (int i = 0; i < numcases; i++)
|
|
344 {
|
|
345 CaseStatement cs = cast(CaseStatement)cases.data[i];
|
|
346 cs.index = i;
|
|
347 }
|
|
348
|
|
349 cases.sort();
|
|
350
|
|
351 /* Create a sorted array of the case strings, and si
|
|
352 * will be the symbol for it.
|
|
353 */
|
|
354 dt_t* dt = null;
|
|
355 Symbol* si = symbol_generate(SCstatic,type_fake(TYullong));
|
|
356 version (MACHOBJ) {
|
|
357 si.Sseg = Segment.DATA;
|
|
358 }
|
|
359 dtdword(&dt, numcases);
|
|
360 dtxoff(&dt, si, 8, TYnptr);
|
|
361
|
|
362 for (int i = 0; i < numcases; i++)
|
|
363 {
|
|
364 CaseStatement cs = cast(CaseStatement)cases.data[i];
|
|
365
|
|
366 if (cs.exp.op != TOKstring)
|
|
367 {
|
|
368 error("case '%s' is not a string", cs.exp.toChars()); // BUG: this should be an assert
|
|
369 }
|
|
370 else
|
|
371 {
|
|
372 StringExp se = cast(StringExp)(cs.exp);
|
|
373 uint len = se.len;
|
|
374 dtdword(&dt, len);
|
|
375 dtabytes(&dt, TYnptr, 0, se.len * se.sz, cast(char*)se.string_);
|
|
376 }
|
|
377 }
|
|
378
|
|
379 si.Sdt = dt;
|
|
380 si.Sfl = FLdata;
|
|
381 outdata(si);
|
|
382
|
|
383 /* Call:
|
|
384 * _d_switch_string(string[] si, string econd)
|
|
385 */
|
|
386 elem* eparam = el_param(econd, el_var(si));
|
|
387 switch (condition.type.nextOf().ty)
|
|
388 {
|
|
389 case Tchar:
|
|
390 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_STRING]), eparam);
|
|
391 break;
|
|
392 case Twchar:
|
|
393 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_USTRING]), eparam);
|
|
394 break;
|
|
395 case Tdchar: // BUG: implement
|
|
396 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_DSTRING]), eparam);
|
|
397 break;
|
|
398 default:
|
|
399 assert(0);
|
|
400 }
|
|
401 elem_setLoc(econd, loc);
|
|
402 string = 1;
|
|
403 }
|
|
404 else
|
|
405 string = 0;
|
|
406 block_appendexp(mystate.switchBlock, econd);
|
|
407 block_next(blx,BCswitch,null);
|
|
408
|
|
409 targ_llong* pu = cast(targ_llong*) malloc(targ_llong.sizeof * (numcases + 1));
|
|
410 mystate.switchBlock.Bswitch = pu;
|
|
411 /* First pair is the number of cases, and the default block
|
|
412 */
|
|
413 *pu++ = numcases;
|
|
414 list_append(&mystate.switchBlock.Bsucc, mystate.defaultBlock);
|
|
415
|
|
416 /* Fill in the first entry in each pair, which is the case value.
|
|
417 * CaseStatement.toIR() will fill in
|
|
418 * the second entry for each pair with the block.
|
|
419 */
|
|
420 for (int i = 0; i < numcases; i++)
|
|
421 {
|
|
422 CaseStatement cs = cast(CaseStatement)cases.data[i];
|
|
423 if (string)
|
|
424 {
|
|
425 pu[cs.index] = i;
|
|
426 }
|
|
427 else
|
|
428 {
|
|
429 pu[i] = cs.exp.toInteger();
|
|
430 }
|
|
431 }
|
|
432
|
|
433 body_.toIR(&mystate);
|
|
434
|
|
435 /* Have the end of the switch body fall through to the block
|
|
436 * following the switch statement.
|
|
437 */
|
|
438 block_goto(blx, BCgoto, mystate.breakBlock);
|
|
439 }
|
|
440 } |