Mercurial > projects > ddmd
annotate dmd/ReturnStatement.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 | a43c65469219 |
children | b0d41ff5e0df |
rev | line source |
---|---|
0 | 1 module dmd.ReturnStatement; |
2 | |
114 | 3 import dmd.common; |
0 | 4 import dmd.Loc; |
5 import dmd.Statement; | |
6 import dmd.GotoStatement; | |
7 import dmd.STC; | |
8 import dmd.CompoundStatement; | |
9 import dmd.Id; | |
10 import dmd.AssignExp; | |
11 import dmd.ExpStatement; | |
12 import dmd.FuncDeclaration; | |
13 import dmd.IntegerExp; | |
14 import dmd.ThisExp; | |
15 import dmd.StructDeclaration; | |
16 import dmd.TypeFunction; | |
17 import dmd.CSX; | |
18 import dmd.RET; | |
19 import dmd.TOK; | |
20 import dmd.Type; | |
21 import dmd.Expression; | |
22 import dmd.StructLiteralExp; | |
23 import dmd.TypeStruct; | |
24 import dmd.Scope; | |
25 import dmd.OutBuffer; | |
26 import dmd.HdrGenState; | |
27 import dmd.InterState; | |
28 import dmd.InlineCostState; | |
29 import dmd.InlineDoState; | |
30 import dmd.InlineScanState; | |
31 import dmd.IRState; | |
32 import dmd.TY; | |
33 import dmd.WANT; | |
34 import dmd.VarExp; | |
35 import dmd.VarDeclaration; | |
63 | 36 import dmd.GlobalExpressions; |
0 | 37 import dmd.BE; |
135 | 38 import dmd.Global; |
67 | 39 |
0 | 40 import dmd.codegen.Util; |
41 | |
42 import dmd.backend.Blockx; | |
43 import dmd.backend.elem; | |
44 import dmd.backend.TYM; | |
45 import dmd.backend.Util; | |
46 import dmd.backend.OPER; | |
47 import dmd.backend.mTY; | |
48 import dmd.backend.BC; | |
49 | |
67 | 50 import core.stdc.string; |
51 | |
0 | 52 class ReturnStatement : Statement |
53 { | |
54 Expression exp; | |
55 | |
56 this(Loc loc, Expression exp) | |
57 { | |
178 | 58 register(); |
0 | 59 super(loc); |
60 this.exp = exp; | |
61 } | |
62 | |
72 | 63 override Statement syntaxCopy() |
0 | 64 { |
65 Expression e = exp ? exp.syntaxCopy() : null; | |
66 return new ReturnStatement(loc, e); | |
67 } | |
68 | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
69 override void toCBuffer(OutBuffer buf, HdrGenState* hgs) |
0 | 70 { |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
71 buf.printf("return "); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
72 if (exp) |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
73 exp.toCBuffer(buf, hgs); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
74 buf.writeByte(';'); |
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
75 buf.writenl(); |
0 | 76 } |
77 | |
93
df6d0f967680
implemented a whole bunch of methods to make phobos 2.035 compile
Trass3r
parents:
79
diff
changeset
|
78 override Statement semantic(Scope sc) |
0 | 79 { |
80 //printf("ReturnStatement.semantic() %s\n", toChars()); | |
81 | |
82 FuncDeclaration fd = sc.parent.isFuncDeclaration(); | |
83 Scope scx = sc; | |
84 int implicit0 = 0; | |
85 | |
86 if (sc.fes) | |
87 { | |
88 // Find scope of function foreach is in | |
89 for (; 1; scx = scx.enclosing) | |
90 { | |
91 assert(scx); | |
92 if (scx.func !is fd) | |
93 { | |
94 fd = scx.func; // fd is now function enclosing foreach | |
95 break; | |
96 } | |
97 } | |
98 } | |
99 | |
100 Type tret = fd.type.nextOf(); | |
101 if (fd.tintro) { | |
102 /* We'll be implicitly casting the return expression to tintro | |
103 */ | |
104 tret = fd.tintro.nextOf(); | |
105 } | |
106 | |
107 Type tbret = null; | |
108 | |
109 if (tret) { | |
110 tbret = tret.toBasetype(); | |
111 } | |
112 | |
113 // main() returns 0, even if it returns void | |
114 if (!exp && (!tbret || tbret.ty == TY.Tvoid) && fd.isMain()) | |
115 { | |
116 implicit0 = 1; | |
117 exp = new IntegerExp(0); | |
118 } | |
119 | |
120 if (sc.incontract || scx.incontract) | |
121 error("return statements cannot be in contracts"); | |
122 | |
123 if (sc.tf || scx.tf) | |
124 error("return statements cannot be in finally, scope(exit) or scope(success) bodies"); | |
125 | |
126 if (fd.isCtorDeclaration()) | |
127 { | |
128 // Constructors implicitly do: | |
129 // return this; | |
130 if (exp && exp.op != TOK.TOKthis) { | |
131 error("cannot return expression from constructor"); | |
132 } | |
133 | |
134 exp = new ThisExp(Loc(0)); | |
135 } | |
136 | |
137 if (!exp) { | |
138 fd.nrvo_can = 0; | |
139 } | |
140 | |
141 if (exp) | |
142 { | |
143 fd.hasReturnExp |= 1; | |
144 | |
145 exp = exp.semantic(sc); | |
146 exp = resolveProperties(sc, exp); | |
147 exp = exp.optimize(WANT.WANTvalue); | |
148 | |
149 if (fd.nrvo_can && exp.op == TOK.TOKvar) { | |
150 VarExp ve = cast(VarExp)exp; | |
151 VarDeclaration v = ve.var.isVarDeclaration(); | |
152 | |
153 if ((cast(TypeFunction)fd.type).isref) { | |
154 // Function returns a reference | |
155 fd.nrvo_can = 0; | |
156 } else if (!v || v.isOut() || v.isRef()) { | |
157 fd.nrvo_can = 0; | |
158 } else if (tbret.ty == TY.Tstruct && (cast(TypeStruct)tbret).sym.dtor) { | |
159 // Struct being returned has destructors | |
160 fd.nrvo_can = 0; | |
161 } else if (fd.nrvo_var is null) { | |
162 if (!v.isDataseg() && !v.isParameter() && v.toParent2() == fd) { | |
163 //printf("Setting nrvo to %s\n", v.toChars()); | |
164 fd.nrvo_var = v; | |
165 } else { | |
166 fd.nrvo_can = 0; | |
167 } | |
168 } else if (fd.nrvo_var != v) { | |
169 fd.nrvo_can = 0; | |
170 } | |
171 } else { | |
172 fd.nrvo_can = 0; | |
173 } | |
174 | |
175 if (fd.returnLabel && tbret.ty != TY.Tvoid) { | |
176 ; | |
177 } else if (fd.inferRetType) { | |
135 | 178 auto tf = cast(TypeFunction)fd.type; |
179 assert(tf.ty == TY.Tfunction); | |
180 Type tfret = tf.nextOf(); | |
181 if (tfret) | |
182 { | |
183 if (!exp.type.equals(tfret)) | |
184 error("mismatched function return type inference of %s and %s", exp.type.toChars(), tfret.toChars()); | |
185 /* The "refness" is determined by the first return statement, | |
186 * not all of them. This means: | |
187 * return 3; return x; // ok, x can be a value | |
188 * return x; return 3; // error, 3 is not an lvalue | |
189 */ | |
0 | 190 } |
191 else | |
192 { | |
135 | 193 if (tf.isref) |
194 { /* Determine "refness" of function return: | |
195 * if it's an lvalue, return by ref, else return by value | |
196 */ | |
197 if (exp.isLvalue()) | |
198 { | |
199 /* Return by ref | |
200 * (but first ensure it doesn't fail the "check for | |
201 * escaping reference" test) | |
202 */ | |
203 uint errors = global.errors; | |
204 global.gag++; | |
205 exp.checkEscapeRef(); | |
206 global.gag--; | |
207 if (errors != global.errors) | |
208 { tf.isref = false; // return by value | |
209 global.errors = errors; | |
210 } | |
211 } | |
212 else | |
213 tf.isref = false; // return by value | |
214 } | |
215 tf.next = exp.type; | |
216 fd.type = tf.semantic(loc, sc); | |
217 | |
0 | 218 if (!fd.tintro) |
219 { | |
220 tret = fd.type.nextOf(); | |
221 tbret = tret.toBasetype(); | |
222 } | |
223 } | |
224 } else if (tbret.ty != TY.Tvoid) | |
225 { | |
226 exp = exp.implicitCastTo(sc, tret); | |
227 exp = exp.optimize(WANT.WANTvalue); | |
228 } | |
229 } else if (fd.inferRetType) { | |
230 if (fd.type.nextOf()) | |
231 { | |
232 if (fd.type.nextOf().ty != TY.Tvoid) { | |
233 error("mismatched function return type inference of void and %s", fd.type.nextOf().toChars()); | |
234 } | |
235 } | |
236 else | |
237 { | |
238 (cast(TypeFunction*)fd.type).next = Type.tvoid; | |
239 fd.type = fd.type.semantic(loc, sc); | |
240 if (!fd.tintro) | |
241 { | |
242 tret = Type.tvoid; | |
243 tbret = tret; | |
244 } | |
245 } | |
246 } | |
247 else if (tbret.ty != TY.Tvoid) {// if non-void return | |
248 error("return expression expected"); | |
249 } | |
250 | |
251 if (sc.fes) | |
252 { | |
253 Statement s; | |
254 | |
255 if (exp && !implicit0) | |
256 { | |
257 exp = exp.implicitCastTo(sc, tret); | |
258 } | |
259 if (!exp || exp.op == TOK.TOKint64 || exp.op == TOK.TOKfloat64 || | |
260 exp.op == TOK.TOKimaginary80 || exp.op == TOK.TOKcomplex80 || | |
261 exp.op == TOK.TOKthis || exp.op == TOK.TOKsuper || exp.op == TOK.TOKnull || | |
262 exp.op == TOK.TOKstring) | |
263 { | |
264 sc.fes.cases.push(cast(void*)this); | |
265 // Construct: return cases.dim+1; | |
266 s = new ReturnStatement(Loc(0), new IntegerExp(sc.fes.cases.dim + 1)); | |
267 } | |
268 else if (fd.type.nextOf().toBasetype() == Type.tvoid) | |
269 { | |
270 s = new ReturnStatement(Loc(0), null); | |
271 sc.fes.cases.push(cast(void*)s); | |
272 | |
273 // Construct: { exp; return cases.dim + 1; } | |
274 Statement s1 = new ExpStatement(loc, exp); | |
275 Statement s2 = new ReturnStatement(Loc(0), new IntegerExp(sc.fes.cases.dim + 1)); | |
276 s = new CompoundStatement(loc, s1, s2); | |
277 } | |
278 else | |
279 { | |
280 // Construct: return vresult; | |
281 if (!fd.vresult) | |
282 { | |
283 // Declare vresult | |
284 VarDeclaration v = new VarDeclaration(loc, tret, Id.result, null); | |
285 v.noauto = true; | |
286 v.semantic(scx); | |
287 if (!scx.insert(v)) { | |
288 assert(0); | |
289 } | |
290 v.parent = fd; | |
291 fd.vresult = v; | |
292 } | |
293 | |
294 s = new ReturnStatement(Loc(0), new VarExp(Loc(0), fd.vresult)); | |
295 sc.fes.cases.push(cast(void*)s); | |
296 | |
297 // Construct: { vresult = exp; return cases.dim + 1; } | |
298 exp = new AssignExp(loc, new VarExp(Loc(0), fd.vresult), exp); | |
299 exp.op = TOK.TOKconstruct; | |
300 exp = exp.semantic(sc); | |
301 Statement s1 = new ExpStatement(loc, exp); | |
302 Statement s2 = new ReturnStatement(Loc(0), new IntegerExp(sc.fes.cases.dim + 1)); | |
303 s = new CompoundStatement(loc, s1, s2); | |
304 } | |
305 return s; | |
306 } | |
307 | |
308 if (exp) | |
309 { | |
310 if (fd.returnLabel && tbret.ty != TY.Tvoid) | |
311 { | |
312 assert(fd.vresult); | |
313 VarExp v = new VarExp(Loc(0), fd.vresult); | |
314 | |
315 exp = new AssignExp(loc, v, exp); | |
316 exp.op = TOK.TOKconstruct; | |
317 exp = exp.semantic(sc); | |
318 } | |
319 | |
320 if ((cast(TypeFunction)fd.type).isref && !fd.isCtorDeclaration()) | |
321 { // Function returns a reference | |
322 if (tbret.isMutable()) | |
323 exp = exp.modifiableLvalue(sc, exp); | |
324 else | |
325 exp = exp.toLvalue(sc, exp); | |
326 | |
135 | 327 exp.checkEscapeRef(); |
0 | 328 } |
135 | 329 else |
330 { | |
331 //exp.dump(0); | |
332 //exp.print(); | |
333 exp.checkEscape(); | |
334 } | |
0 | 335 } |
336 | |
337 /* BUG: need to issue an error on: | |
338 * this | |
339 * { if (x) return; | |
340 * super(); | |
341 * } | |
342 */ | |
343 | |
344 if (sc.callSuper & CSX.CSXany_ctor && !(sc.callSuper & (CSX.CSXthis_ctor | CSX.CSXsuper_ctor))) { | |
345 error("return without calling constructor"); | |
346 } | |
347 | |
348 sc.callSuper |= CSX.CSXreturn; | |
349 | |
350 // See if all returns are instead to be replaced with a goto returnLabel; | |
351 if (fd.returnLabel) | |
352 { | |
353 GotoStatement gs = new GotoStatement(loc, Id.returnLabel); | |
354 | |
355 gs.label = fd.returnLabel; | |
356 if (exp) | |
357 { | |
358 /* Replace: return exp; | |
359 * with: exp; goto returnLabel; | |
360 */ | |
361 Statement s = new ExpStatement(Loc(0), exp); | |
362 return new CompoundStatement(loc, s, gs); | |
363 } | |
364 return gs; | |
365 } | |
366 | |
73 | 367 if (exp && tbret.ty == Tvoid && !implicit0) |
0 | 368 { |
369 /* Replace: | |
370 * return exp; | |
371 * with: | |
372 * exp; return; | |
373 */ | |
374 Statement s = new ExpStatement(loc, exp); | |
79 | 375 exp = null; |
376 s = s.semantic(sc); | |
377 loc = Loc(); | |
0 | 378 return new CompoundStatement(loc, s, this); |
379 } | |
380 | |
381 return this; | |
382 } | |
383 | |
72 | 384 override BE blockExit() |
0 | 385 { |
386 BE result = BE.BEreturn; | |
387 if (exp && exp.canThrow()) | |
388 result |= BE.BEthrow; | |
389 | |
390 return result; | |
391 } | |
392 | |
72 | 393 override Expression interpret(InterState istate) |
0 | 394 { |
63 | 395 version (LOG) { |
396 printf("ReturnStatement.interpret(%s)\n", exp ? exp.toChars() : ""); | |
397 } | |
155 | 398 mixin(START); |
63 | 399 if (!exp) |
400 return EXP_VOID_INTERPRET; | |
401 version (LOG) { | |
402 Expression e = exp.interpret(istate); | |
403 printf("e = %p\n", e); | |
404 return e; | |
405 } else { | |
406 return exp.interpret(istate); | |
407 } | |
0 | 408 } |
409 | |
72 | 410 override int inlineCost(InlineCostState* ics) |
0 | 411 { |
412 // Can't handle return statements nested in if's | |
413 if (ics.nested) | |
414 return COST_MAX; | |
415 return exp ? exp.inlineCost(ics) : 0; | |
416 } | |
417 | |
72 | 418 override Expression doInline(InlineDoState ids) |
0 | 419 { |
420 //printf("ReturnStatement.doInline() '%s'\n", exp ? exp.toChars() : ""); | |
421 return exp ? exp.doInline(ids) : null; | |
422 } | |
423 | |
72 | 424 override Statement inlineScan(InlineScanState* iss) |
0 | 425 { |
426 //printf("ReturnStatement.inlineScan()\n"); | |
427 if (exp) | |
428 { | |
429 exp = exp.inlineScan(iss); | |
430 } | |
431 return this; | |
432 } | |
433 | |
72 | 434 override void toIR(IRState* irs) |
0 | 435 { |
436 Blockx* blx = irs.blx; | |
68
ee3a9f34dc48
final bits of codegen implementation to compile Phobos
korDen
parents:
67
diff
changeset
|
437 |
0 | 438 incUsage(irs, loc); |
439 if (exp) | |
440 { | |
68
ee3a9f34dc48
final bits of codegen implementation to compile Phobos
korDen
parents:
67
diff
changeset
|
441 //printf("%.*s %.*s\n", exp.classinfo.name, exp.toChars()); |
0 | 442 elem *e; |
443 | |
444 FuncDeclaration func = irs.getFunc(); | |
445 assert(func); | |
446 assert(func.type.ty == TY.Tfunction); | |
447 TypeFunction tf = cast(TypeFunction)(func.type); | |
448 | |
449 RET retmethod = tf.retStyle(); | |
450 if (retmethod == RET.RETstack) | |
451 { | |
452 elem* es; | |
453 | |
454 /* If returning struct literal, write result | |
455 * directly into return value | |
456 */ | |
457 if (exp.op == TOK.TOKstructliteral) | |
458 { | |
68
ee3a9f34dc48
final bits of codegen implementation to compile Phobos
korDen
parents:
67
diff
changeset
|
459 StructLiteralExp se = cast(StructLiteralExp)exp; |
67 | 460 enum objectSize = __traits(classInstanceSize, StructLiteralExp); |
461 ubyte save[objectSize]; | |
462 memcpy(save.ptr, cast(void*)se, objectSize); | |
0 | 463 se.sym = irs.shidden; |
464 se.soffset = 0; | |
465 se.fillHoles = 1; | |
466 e = exp.toElem(irs); | |
67 | 467 memcpy(cast(void*)se, save.ptr, objectSize); |
0 | 468 } |
469 else | |
470 e = exp.toElem(irs); | |
471 | |
472 assert(e); | |
473 | |
474 if (exp.op == TOK.TOKstructliteral || (func.nrvo_can && func.nrvo_var)) | |
475 { | |
476 // Return value via hidden pointer passed as parameter | |
477 // Write exp; return shidden; | |
478 es = e; | |
479 } | |
480 else | |
481 { | |
482 // Return value via hidden pointer passed as parameter | |
483 // Write *shidden=exp; return shidden; | |
484 int op; | |
485 tym_t ety; | |
486 | |
487 ety = e.Ety; | |
488 es = el_una(OPER.OPind,ety,el_var(irs.shidden)); | |
489 op = (tybasic(ety) == TYM.TYstruct) ? OPER.OPstreq : OPER.OPeq; | |
490 es = el_bin(op, ety, es, e); | |
491 if (op == OPER.OPstreq) | |
492 es.Enumbytes = cast(uint)exp.type.size(); | |
493 version (DMDV2) { | |
494 /* Call postBlit() on *shidden | |
495 */ | |
496 Type tb = exp.type.toBasetype(); | |
497 //if (tb.ty == TY.Tstruct) exp.dump(0); | |
498 if ((exp.op == TOK.TOKvar || exp.op == TOK.TOKdotvar || exp.op == TOK.TOKstar) && | |
499 tb.ty == TY.Tstruct) | |
68
ee3a9f34dc48
final bits of codegen implementation to compile Phobos
korDen
parents:
67
diff
changeset
|
500 { |
ee3a9f34dc48
final bits of codegen implementation to compile Phobos
korDen
parents:
67
diff
changeset
|
501 StructDeclaration sd = (cast(TypeStruct)tb).sym; |
0 | 502 if (sd.postblit) |
503 { | |
504 FuncDeclaration fd = sd.postblit; | |
505 elem* ec = el_var(irs.shidden); | |
506 ec = callfunc(loc, irs, 1, Type.tvoid, ec, tb.pointerTo(), fd, fd.type, null, null); | |
507 es = el_bin(OPER.OPcomma, ec.Ety, es, ec); | |
508 } | |
509 | |
510 static if (false) { | |
511 /* It has been moved, so disable destructor | |
512 */ | |
513 if (exp.op == TOK.TOKvar) | |
514 { | |
515 VarExp ve = cast(VarExp)exp; | |
516 VarDeclaration v = ve.var.isVarDeclaration(); | |
517 if (v && v.rundtor) | |
518 { | |
519 elem* er = el_var(v.rundtor.toSymbol()); | |
520 er = el_bin(OPER.OPeq, TYM.TYint, er, el_long(TYM.TYint, 0)); | |
521 es = el_bin(OPER.OPcomma, TYM.TYint, es, er); | |
522 } | |
523 } | |
524 } | |
525 } | |
526 } | |
527 } | |
528 e = el_var(irs.shidden); | |
529 e = el_bin(OPER.OPcomma, e.Ety, es, e); | |
530 } | |
531 ///version (DMDV2) { | |
532 else if (tf.isref) | |
533 { // Reference return, so convert to a pointer | |
534 Expression ae = exp.addressOf(null); | |
535 e = ae.toElem(irs); | |
536 } | |
537 ///} | |
538 else | |
539 { | |
540 e = exp.toElem(irs); | |
541 assert(e); | |
542 } | |
543 | |
544 block_appendexp(blx.curblock, e); | |
545 block_next(blx, BC.BCretexp, null); | |
546 } | |
547 else | |
548 block_next(blx, BC.BCret, null); | |
549 } | |
550 | |
72 | 551 override ReturnStatement isReturnStatement() { return this; } |
552 } |