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