Mercurial > projects > ddmd
annotate dmd/SwitchStatement.d @ 192:eb38fdcb3e62 default tip
updated to compile with dmd2.062
author | korDen |
---|---|
date | Sat, 02 Mar 2013 01:25:52 -0800 |
parents | b0d41ff5e0df |
children |
rev | line source |
---|---|
0 | 1 module dmd.SwitchStatement; |
2 | |
114 | 3 import dmd.common; |
0 | 4 import dmd.Statement; |
5 import dmd.Expression; | |
162 | 6 import dmd.GlobalExpressions; |
0 | 7 import dmd.DefaultStatement; |
8 import dmd.TryFinallyStatement; | |
9 import dmd.Array; | |
10 import dmd.Loc; | |
11 import dmd.Scope; | |
12 import dmd.OutBuffer; | |
13 import dmd.HdrGenState; | |
14 import dmd.InlineScanState; | |
15 import dmd.IRState; | |
16 import dmd.InterState; | |
17 import dmd.BE; | |
18 import dmd.TY; | |
19 import dmd.WANT; | |
20 import dmd.GotoCaseStatement; | |
21 import dmd.CaseStatement; | |
22 import dmd.ArrayTypes; | |
23 import dmd.CompoundStatement; | |
24 import dmd.Global; | |
25 import dmd.SwitchErrorStatement; | |
26 import dmd.Type; | |
27 import dmd.HaltExp; | |
28 import dmd.ExpStatement; | |
29 import dmd.BreakStatement; | |
30 import dmd.EnumDeclaration; | |
31 import dmd.TypeEnum; | |
32 import dmd.Dsymbol; | |
33 import dmd.EnumMember; | |
34 import dmd.TypeTypedef; | |
35 import dmd.TOK; | |
36 import dmd.StringExp; | |
162 | 37 import dmd.expression.Equal; |
0 | 38 |
39 import dmd.backend.Util; | |
40 import dmd.backend.block; | |
41 import dmd.backend.Blockx; | |
42 import dmd.backend.elem; | |
43 import dmd.backend.OPER; | |
44 import dmd.backend.TYM; | |
45 import dmd.backend.BC; | |
46 import dmd.backend.dt_t; | |
47 import dmd.backend.Symbol; | |
48 import dmd.backend.SC; | |
49 import dmd.backend.FL; | |
50 import dmd.backend.RTLSYM; | |
51 import dmd.backend.targ_types; | |
52 | |
183
190ba98276b3
Several changes to make it build on posix systems.
Jacob Carlborg <doob@me.com>
parents:
180
diff
changeset
|
53 version (MACHOBJ) |
190ba98276b3
Several changes to make it build on posix systems.
Jacob Carlborg <doob@me.com>
parents:
180
diff
changeset
|
54 import dmd.Module; |
190ba98276b3
Several changes to make it build on posix systems.
Jacob Carlborg <doob@me.com>
parents:
180
diff
changeset
|
55 |
4 | 56 import core.memory; |
2 | 57 |
5
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
58 import core.stdc.stdlib; |
63623152e82a
Fixed memory corruption bug which was introduced when attempting to restore GC functionality
dkoroskin <>
parents:
4
diff
changeset
|
59 |
187
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
183
diff
changeset
|
60 import dmd.DDMDExtensions; |
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
183
diff
changeset
|
61 |
0 | 62 class SwitchStatement : Statement |
63 { | |
187
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
183
diff
changeset
|
64 mixin insertMemberExtension!(typeof(this)); |
b0d41ff5e0df
Added expandability scheme outlined in http://www.dsource.org/forums/viewtopic.php?t=5659&sid=6f2150ff5b0bffcd47512a6a7608d218
Abscissa
parents:
183
diff
changeset
|
65 |
0 | 66 Expression condition; |
67 Statement body_; | |
68 bool isFinal; | |
69 | |
70 DefaultStatement sdefault = null; | |
71 TryFinallyStatement tf = null; | |
72 Array gotoCases; // array of unresolved GotoCaseStatement's | |
73 Array cases; // array of CaseStatement's | |
74 int hasNoDefault = 0; // !=0 if no default statement | |
75 int hasVars = 0; // !=0 if has variable case values | |
76 | |
77 this(Loc loc, Expression c, Statement b, bool isFinal) | |
78 { | |
178 | 79 register(); |
0 | 80 super(loc); |
81 | |
82 this.condition = c; | |
83 this.body_ = b; | |
84 this.isFinal = isFinal; | |
85 | |
86 gotoCases = new Array(); | |
87 } | |
88 | |
72 | 89 override Statement syntaxCopy() |
0 | 90 { |
53 | 91 SwitchStatement s = new SwitchStatement(loc, |
92 condition.syntaxCopy(), body_.syntaxCopy(), isFinal); | |
93 return s; | |
0 | 94 } |
95 | |
72 | 96 override Statement semantic(Scope sc) |
0 | 97 { |
98 //printf("SwitchStatement.semantic(%p)\n", this); | |
99 tf = sc.tf; | |
100 assert(!cases); // ensure semantic() is only run once | |
101 condition = condition.semantic(sc); | |
102 condition = resolveProperties(sc, condition); | |
103 if (condition.type.isString()) | |
104 { | |
105 // If it's not an array, cast it to one | |
106 if (condition.type.ty != Tarray) | |
107 { | |
108 condition = condition.implicitCastTo(sc, condition.type.nextOf().arrayOf()); | |
109 } | |
110 condition.type = condition.type.constOf(); | |
111 } | |
112 else | |
113 { | |
114 condition = condition.integralPromotions(sc); | |
115 condition.checkIntegral(); | |
116 } | |
117 condition = condition.optimize(WANTvalue); | |
118 | |
119 sc = sc.push(); | |
120 sc.sbreak = this; | |
121 sc.sw = this; | |
122 | |
123 cases = new Array(); | |
124 sc.noctor++; // BUG: should use Scope.mergeCallSuper() for each case instead | |
125 body_ = body_.semantic(sc); | |
126 sc.noctor--; | |
127 | |
128 // Resolve any goto case's with exp | |
129 for (int i = 0; i < gotoCases.dim; i++) | |
130 { | |
131 GotoCaseStatement gcs = cast(GotoCaseStatement)gotoCases.data[i]; | |
132 | |
133 if (!gcs.exp) | |
134 { | |
135 gcs.error("no case statement following goto case;"); | |
136 break; | |
137 } | |
138 | |
139 for (Scope scx = sc; scx; scx = scx.enclosing) | |
140 { | |
141 if (!scx.sw) | |
142 continue; | |
143 for (int j = 0; j < scx.sw.cases.dim; j++) | |
144 { | |
145 CaseStatement cs = cast(CaseStatement)scx.sw.cases.data[j]; | |
146 | |
147 if (cs.exp.equals(gcs.exp)) | |
148 { | |
149 gcs.cs = cs; | |
150 goto Lfoundcase; | |
151 } | |
152 } | |
153 } | |
154 gcs.error("case %s not found", gcs.exp.toChars()); | |
155 | |
156 Lfoundcase: | |
157 ; | |
158 } | |
159 | |
160 if (!sc.sw.sdefault && !isFinal) | |
161 { | |
162 hasNoDefault = 1; | |
163 | |
164 warning("switch statement has no default"); | |
165 | |
166 // Generate runtime error if the default is hit | |
167 Statements a = new Statements(); | |
168 CompoundStatement cs; | |
169 Statement s; | |
170 | |
171 if (global.params.useSwitchError) | |
172 s = new SwitchErrorStatement(loc); | |
173 else | |
174 { | |
175 Expression e = new HaltExp(loc); | |
176 s = new ExpStatement(loc, e); | |
177 } | |
178 | |
179 a.reserve(4); | |
122
c77e9f4f1793
Statements -> Vector
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
114
diff
changeset
|
180 a.push(body_); |
c77e9f4f1793
Statements -> Vector
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
114
diff
changeset
|
181 a.push(new BreakStatement(loc, null)); |
0 | 182 sc.sw.sdefault = new DefaultStatement(loc, s); |
122
c77e9f4f1793
Statements -> Vector
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
114
diff
changeset
|
183 a.push(sc.sw.sdefault); |
0 | 184 cs = new CompoundStatement(loc, a); |
185 body_ = cs; | |
186 } | |
187 | |
188 version (DMDV2) { | |
189 if (isFinal) | |
190 { | |
191 Type t = condition.type; | |
192 while (t.ty == Ttypedef) | |
193 { | |
194 // Don't use toBasetype() because that will skip past enums | |
195 t = (cast(TypeTypedef)t).sym.basetype; | |
196 } | |
197 if (condition.type.ty == Tenum) | |
198 { | |
199 TypeEnum te = cast(TypeEnum)condition.type; | |
200 EnumDeclaration ed = te.toDsymbol(sc).isEnumDeclaration(); | |
201 assert(ed); | |
202 size_t dim = ed.members.dim; | |
77
ad4792a1cfd6
more D-ification container accessing
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
203 foreach (Dsymbol s; ed.members) |
0 | 204 { |
77
ad4792a1cfd6
more D-ification container accessing
Eldar Insafutdinov <e.insafutdinov@gmail.com>
parents:
72
diff
changeset
|
205 if (auto em = s.isEnumMember()) |
0 | 206 { |
207 for (size_t j = 0; j < cases.dim; j++) | |
208 { | |
209 CaseStatement cs = cast(CaseStatement)cases.data[j]; | |
210 if (cs.exp.equals(em.value)) | |
211 goto L1; | |
212 } | |
213 error("enum member %s not represented in final switch", em.toChars()); | |
214 } | |
215 L1: | |
216 ; | |
217 } | |
218 } | |
219 } | |
220 } | |
221 | |
222 sc.pop(); | |
223 return this; | |
224 } | |
225 | |
72 | 226 override bool hasBreak() |
0 | 227 { |
228 assert(false); | |
229 } | |
230 | |
72 | 231 override bool usesEH() |
0 | 232 { |
233 assert(false); | |
234 } | |
235 | |
72 | 236 override BE blockExit() |
0 | 237 { |
238 BE result = BE.BEnone; | |
239 if (condition.canThrow()) | |
240 result |= BE.BEthrow; | |
241 | |
242 if (body_) | |
243 { result |= body_.blockExit(); | |
244 if (result & BE.BEbreak) | |
245 { | |
246 result |= BE.BEfallthru; | |
247 result &= ~BE.BEbreak; | |
248 } | |
249 } | |
250 else | |
251 result |= BE.BEfallthru; | |
252 | |
253 return result; | |
254 } | |
255 | |
72 | 256 override Expression interpret(InterState istate) |
0 | 257 { |
162 | 258 version (LOG) { |
259 printf("SwitchStatement.interpret()\n"); | |
260 } | |
261 if (istate.start == this) | |
262 istate.start = null; | |
263 Expression e = null; | |
264 | |
265 if (istate.start) | |
266 { | |
267 e = body_ ? body_.interpret(istate) : null; | |
268 if (istate.start) | |
269 return null; | |
270 if (e is EXP_CANT_INTERPRET) | |
271 return e; | |
272 if (e is EXP_BREAK_INTERPRET) | |
273 return null; | |
274 return e; | |
275 } | |
276 | |
277 | |
278 Expression econdition = condition.interpret(istate); | |
279 if (econdition is EXP_CANT_INTERPRET) | |
280 return EXP_CANT_INTERPRET; | |
281 | |
282 Statement s = null; | |
283 if (cases) | |
284 { | |
285 for (size_t i = 0; i < cases.dim; i++) | |
286 { | |
287 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
288 e = Equal(TOKequal, Type.tint32, econdition, cs.exp); | |
289 if (e is EXP_CANT_INTERPRET) | |
290 return EXP_CANT_INTERPRET; | |
291 if (e.isBool(true)) | |
292 { | |
293 s = cs; | |
294 break; | |
295 } | |
296 } | |
297 } | |
298 if (!s) | |
299 { | |
300 if (hasNoDefault) | |
301 error("no default or case for %s in switch statement", econdition.toChars()); | |
302 s = sdefault; | |
303 } | |
304 | |
305 assert(s); | |
306 istate.start = s; | |
307 e = body_ ? body_.interpret(istate) : null; | |
308 assert(!istate.start); | |
309 if (e is EXP_BREAK_INTERPRET) | |
310 return null; | |
311 return e; | |
0 | 312 } |
313 | |
72 | 314 override void toCBuffer(OutBuffer buf, HdrGenState* hgs) |
0 | 315 { |
174 | 316 buf.writestring("switch ("); |
317 condition.toCBuffer(buf, hgs); | |
318 buf.writebyte(')'); | |
319 buf.writenl(); | |
320 if (body_) | |
321 { | |
322 if (!body_.isScopeStatement()) | |
323 { | |
324 buf.writebyte('{'); | |
325 buf.writenl(); | |
326 body_.toCBuffer(buf, hgs); | |
327 buf.writebyte('}'); | |
328 buf.writenl(); | |
329 } | |
330 else | |
331 { | |
332 body_.toCBuffer(buf, hgs); | |
333 } | |
334 } | |
0 | 335 } |
336 | |
72 | 337 override Statement inlineScan(InlineScanState* iss) |
0 | 338 { |
339 //printf("SwitchStatement.inlineScan()\n"); | |
340 condition = condition.inlineScan(iss); | |
341 body_ = body_ ? body_.inlineScan(iss) : null; | |
342 if (sdefault) | |
343 sdefault = cast(DefaultStatement)sdefault.inlineScan(iss); | |
344 if (cases) | |
345 { | |
346 for (int i = 0; i < cases.dim; i++) | |
347 { | |
348 Statement s = cast(Statement)cases.data[i]; | |
349 cases.data[i] = cast(void*)s.inlineScan(iss); | |
350 } | |
351 } | |
352 return this; | |
353 } | |
354 | |
72 | 355 override void toIR(IRState* irs) |
0 | 356 { |
357 int string; | |
358 Blockx* blx = irs.blx; | |
359 | |
360 //printf("SwitchStatement.toIR()\n"); | |
361 IRState mystate = IRState(irs,this); | |
362 | |
363 mystate.switchBlock = blx.curblock; | |
364 | |
365 /* Block for where "break" goes to | |
366 */ | |
367 mystate.breakBlock = block_calloc(blx); | |
368 | |
369 /* Block for where "default" goes to. | |
370 * If there is a default statement, then that is where default goes. | |
371 * If not, then do: | |
372 * default: break; | |
373 * by making the default block the same as the break block. | |
374 */ | |
375 mystate.defaultBlock = sdefault ? block_calloc(blx) : mystate.breakBlock; | |
376 | |
377 int numcases = 0; | |
378 if (cases) | |
379 numcases = cases.dim; | |
380 | |
381 incUsage(irs, loc); | |
382 elem* econd = condition.toElem(&mystate); | |
383 | |
384 version (DMDV2) { | |
385 if (hasVars) | |
386 { | |
387 /* Generate a sequence of if-then-else blocks for the cases. | |
388 */ | |
389 if (econd.Eoper != OPvar) | |
390 { | |
391 elem* e = exp2_copytotemp(econd); | |
392 block_appendexp(mystate.switchBlock, e); | |
393 econd = e.E2; | |
394 } | |
395 | |
396 for (int i = 0; i < numcases; i++) | |
397 { | |
398 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
399 | |
400 elem* ecase = cs.exp.toElem(&mystate); | |
401 elem* e = el_bin(OPeqeq, TYbool, el_copytree(econd), ecase); | |
402 block* b = blx.curblock; | |
403 block_appendexp(b, e); | |
404 block* bcase = block_calloc(blx); | |
405 cs.cblock = bcase; | |
406 block_next(blx, BCiftrue, null); | |
407 list_append(&b.Bsucc, bcase); | |
408 list_append(&b.Bsucc, blx.curblock); | |
409 } | |
410 | |
411 /* The final 'else' clause goes to the default | |
412 */ | |
413 block* b = blx.curblock; | |
414 block_next(blx, BCgoto, null); | |
415 list_append(&b.Bsucc, mystate.defaultBlock); | |
416 | |
417 body_.toIR(&mystate); | |
418 | |
419 /* Have the end of the switch body fall through to the block | |
420 * following the switch statement. | |
421 */ | |
422 block_goto(blx, BCgoto, mystate.breakBlock); | |
423 return; | |
424 } | |
425 } | |
426 | |
427 if (condition.type.isString()) | |
428 { | |
429 // Number the cases so we can unscramble things after the sort() | |
430 for (int i = 0; i < numcases; i++) | |
431 { | |
432 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
433 cs.index = i; | |
434 } | |
435 | |
436 cases.sort(); | |
437 | |
438 /* Create a sorted array of the case strings, and si | |
439 * will be the symbol for it. | |
440 */ | |
441 dt_t* dt = null; | |
442 Symbol* si = symbol_generate(SCstatic,type_fake(TYullong)); | |
443 version (MACHOBJ) { | |
444 si.Sseg = Segment.DATA; | |
445 } | |
446 dtdword(&dt, numcases); | |
447 dtxoff(&dt, si, 8, TYnptr); | |
448 | |
449 for (int i = 0; i < numcases; i++) | |
450 { | |
451 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
452 | |
453 if (cs.exp.op != TOKstring) | |
454 { | |
455 error("case '%s' is not a string", cs.exp.toChars()); // BUG: this should be an assert | |
456 } | |
457 else | |
458 { | |
459 StringExp se = cast(StringExp)(cs.exp); | |
460 uint len = se.len; | |
461 dtdword(&dt, len); | |
462 dtabytes(&dt, TYnptr, 0, se.len * se.sz, cast(char*)se.string_); | |
463 } | |
464 } | |
465 | |
466 si.Sdt = dt; | |
467 si.Sfl = FLdata; | |
468 outdata(si); | |
469 | |
470 /* Call: | |
471 * _d_switch_string(string[] si, string econd) | |
472 */ | |
473 elem* eparam = el_param(econd, el_var(si)); | |
474 switch (condition.type.nextOf().ty) | |
475 { | |
476 case Tchar: | |
477 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_STRING]), eparam); | |
478 break; | |
479 case Twchar: | |
480 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_USTRING]), eparam); | |
481 break; | |
482 case Tdchar: // BUG: implement | |
483 econd = el_bin(OPcall, TYint, el_var(rtlsym[RTLSYM_SWITCH_DSTRING]), eparam); | |
484 break; | |
485 default: | |
486 assert(0); | |
487 } | |
488 elem_setLoc(econd, loc); | |
489 string = 1; | |
490 } | |
491 else | |
492 string = 0; | |
493 block_appendexp(mystate.switchBlock, econd); | |
494 block_next(blx,BCswitch,null); | |
495 | |
135 | 496 // Corresponding free is in block_free |
180 | 497 targ_llong* pu = cast(targ_llong*) malloc(targ_llong.sizeof * (numcases + 1)); |
0 | 498 mystate.switchBlock.Bswitch = pu; |
499 /* First pair is the number of cases, and the default block | |
500 */ | |
501 *pu++ = numcases; | |
502 list_append(&mystate.switchBlock.Bsucc, mystate.defaultBlock); | |
503 | |
504 /* Fill in the first entry in each pair, which is the case value. | |
505 * CaseStatement.toIR() will fill in | |
506 * the second entry for each pair with the block. | |
507 */ | |
508 for (int i = 0; i < numcases; i++) | |
509 { | |
510 CaseStatement cs = cast(CaseStatement)cases.data[i]; | |
511 if (string) | |
512 { | |
513 pu[cs.index] = i; | |
514 } | |
515 else | |
516 { | |
517 pu[i] = cs.exp.toInteger(); | |
518 } | |
519 } | |
520 | |
521 body_.toIR(&mystate); | |
522 | |
523 /* Have the end of the switch body fall through to the block | |
524 * following the switch statement. | |
525 */ | |
526 block_goto(blx, BCgoto, mystate.breakBlock); | |
527 } | |
72 | 528 } |