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