1
2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/escape.c
9 */
10
11 #include "mars.h"
12 #include "init.h"
13 #include "expression.h"
14 #include "scope.h"
15 #include "aggregate.h"
16 #include "declaration.h"
17 #include "module.h"
18
19 /************************************
20 * Aggregate the data collected by the escapeBy??() functions.
21 */
22 struct EscapeByResults
23 {
24 VarDeclarations byref; // array into which variables being returned by ref are inserted
25 VarDeclarations byvalue; // array into which variables with values containing pointers are inserted
26 FuncDeclarations byfunc; // nested functions that are turned into delegates
27 Expressions byexp; // array into which temporaries being returned by ref are inserted
28 };
29
30 static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag);
31 static void inferReturn(FuncDeclaration *fd, VarDeclaration *v);
32 static void escapeByValue(Expression *e, EscapeByResults *er);
33 static void escapeByRef(Expression *e, EscapeByResults *er);
34 static void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars);
35
36 /* 'v' is assigned unsafely to 'par'
37 */
unsafeAssign(Scope * sc,FuncDeclaration * fdc,Identifier * par,Expression * arg,bool gag,bool & result,VarDeclaration * v,const char * desc)38 static void unsafeAssign(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag,
39 bool &result, VarDeclaration *v, const char *desc)
40 {
41 if (global.params.vsafe && sc->func->setUnsafe())
42 {
43 if (!gag)
44 error(arg->loc, "%s %s assigned to non-scope parameter %s calling %s",
45 desc, v->toChars(),
46 par ? par->toChars() : "unnamed",
47 fdc ? fdc->toPrettyChars() : "indirectly");
48 result = true;
49 }
50 }
51
52 /****************************************
53 * Function parameter par is being initialized to arg,
54 * and par may escape.
55 * Detect if scoped values can escape this way.
56 * Print error messages when these are detected.
57 * Params:
58 * sc = used to determine current function and module
59 * par = identifier of function parameter
60 * arg = initializer for param
61 * gag = do not print error messages
62 * Returns:
63 * true if pointers to the stack can escape via assignment
64 */
checkParamArgumentEscape(Scope * sc,FuncDeclaration * fdc,Identifier * par,Expression * arg,bool gag)65 bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag)
66 {
67 //printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg->toChars(), par->toChars());
68 //printf("type = %s, %d\n", arg->type->toChars(), arg->type->hasPointers());
69
70 if (!arg->type->hasPointers())
71 return false;
72
73 EscapeByResults er;
74
75 escapeByValue(arg, &er);
76
77 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
78 return false;
79
80 bool result = false;
81
82 for (size_t i = 0; i < er.byvalue.dim; i++)
83 {
84 //printf("byvalue %s\n", v->toChars());
85 VarDeclaration *v = er.byvalue[i];
86 if (v->isDataseg())
87 continue;
88
89 Dsymbol *p = v->toParent2();
90
91 v->storage_class &= ~STCmaybescope;
92
93 if (v->isScope())
94 {
95 unsafeAssign(sc, fdc, par, arg, gag, result, v, "scope variable");
96 }
97 else if (v->storage_class & STCvariadic && p == sc->func)
98 {
99 Type *tb = v->type->toBasetype();
100 if (tb->ty == Tarray || tb->ty == Tsarray)
101 {
102 unsafeAssign(sc, fdc, par, arg, gag, result, v, "variadic variable");
103 }
104 }
105 else
106 {
107 /* v is not 'scope', and is assigned to a parameter that may escape.
108 * Therefore, v can never be 'scope'.
109 */
110 v->doNotInferScope = true;
111 }
112 }
113
114 for (size_t i = 0; i < er.byref.dim; i++)
115 {
116 VarDeclaration *v = er.byref[i];
117 if (v->isDataseg())
118 continue;
119
120 Dsymbol *p = v->toParent2();
121
122 v->storage_class &= ~STCmaybescope;
123
124 if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
125 {
126 unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local variable");
127 continue;
128 }
129 }
130
131 for (size_t i = 0; i < er.byfunc.dim; i++)
132 {
133 FuncDeclaration *fd = er.byfunc[i];
134 //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
135 VarDeclarations vars;
136 findAllOuterAccessedVariables(fd, &vars);
137
138 for (size_t j = 0; j < vars.dim; j++)
139 {
140 VarDeclaration *v = vars[j];
141 //printf("v = %s\n", v->toChars());
142 assert(!v->isDataseg()); // these are not put in the closureVars[]
143
144 Dsymbol *p = v->toParent2();
145
146 v->storage_class &= ~STCmaybescope;
147
148 if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
149 {
150 unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local");
151 continue;
152 }
153 }
154 }
155
156 for (size_t i = 0; i < er.byexp.dim; i++)
157 {
158 Expression *ee = er.byexp[i];
159 if (sc->func->setUnsafe())
160 {
161 if (!gag)
162 error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope parameter %s",
163 ee->toChars(),
164 par ? par->toChars() : "unnamed");
165 result = true;
166 }
167 }
168
169 return result;
170 }
171
172 /****************************************
173 * Given an AssignExp, determine if the lvalue will cause
174 * the contents of the rvalue to escape.
175 * Print error messages when these are detected.
176 * Infer 'scope' for the lvalue where possible, in order
177 * to eliminate the error.
178 * Params:
179 * sc = used to determine current function and module
180 * ae = AssignExp to check for any pointers to the stack
181 * gag = do not print error messages
182 * Returns:
183 * true if pointers to the stack can escape via assignment
184 */
checkAssignEscape(Scope * sc,Expression * e,bool gag)185 bool checkAssignEscape(Scope *sc, Expression *e, bool gag)
186 {
187 //printf("checkAssignEscape(e: %s)\n", e->toChars());
188 if (e->op != TOKassign && e->op != TOKblit && e->op != TOKconstruct)
189 return false;
190 AssignExp *ae = (AssignExp *)e;
191 Expression *e1 = ae->e1;
192 Expression *e2 = ae->e2;
193 //printf("type = %s, %d\n", e1->type->toChars(), e1->type->hasPointers());
194
195 if (!e1->type->hasPointers())
196 return false;
197
198 if (e1->op == TOKslice)
199 return false;
200
201 EscapeByResults er;
202
203 escapeByValue(e2, &er);
204
205 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
206 return false;
207
208 VarDeclaration *va = NULL;
209 while (e1->op == TOKdotvar)
210 e1 = ((DotVarExp *)e1)->e1;
211
212 if (e1->op == TOKvar)
213 va = ((VarExp *)e1)->var->isVarDeclaration();
214 else if (e1->op == TOKthis)
215 va = ((ThisExp *)e1)->var->isVarDeclaration();
216 else if (e1->op == TOKindex)
217 {
218 IndexExp *ie = (IndexExp *)e1;
219 if (ie->e1->op == TOKvar && ie->e1->type->toBasetype()->ty == Tsarray)
220 va = ((VarExp *)ie->e1)->var->isVarDeclaration();
221 }
222
223 // Try to infer 'scope' for va if in a function not marked @system
224 bool inferScope = false;
225 if (va && sc->func && sc->func->type && sc->func->type->ty == Tfunction)
226 inferScope = ((TypeFunction *)sc->func->type)->trust != TRUSTsystem;
227
228 bool result = false;
229 for (size_t i = 0; i < er.byvalue.dim; i++)
230 {
231 VarDeclaration *v = er.byvalue[i];
232 //printf("byvalue: %s\n", v->toChars());
233 if (v->isDataseg())
234 continue;
235
236 Dsymbol *p = v->toParent2();
237
238 if (!(va && va->isScope()))
239 v->storage_class &= ~STCmaybescope;
240
241 if (v->isScope())
242 {
243 if (va && va->isScope() && va->storage_class & STCreturn && !(v->storage_class & STCreturn) &&
244 sc->func->setUnsafe())
245 {
246 if (!gag)
247 error(ae->loc, "scope variable %s assigned to return scope %s", v->toChars(), va->toChars());
248 result = true;
249 continue;
250 }
251
252 // If va's lifetime encloses v's, then error
253 if (va &&
254 ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) ||
255 // va is class reference
256 (ae->e1->op == TOKdotvar && va->type->toBasetype()->ty == Tclass && (va->enclosesLifetimeOf(v) || !va->isScope())) ||
257 va->storage_class & STCref) &&
258 sc->func->setUnsafe())
259 {
260 if (!gag)
261 error(ae->loc, "scope variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
262 result = true;
263 continue;
264 }
265
266 if (va && !va->isDataseg() && !va->doNotInferScope)
267 {
268 if (!va->isScope() && inferScope)
269 { //printf("inferring scope for %s\n", va->toChars());
270 va->storage_class |= STCscope | STCscopeinferred;
271 va->storage_class |= v->storage_class & STCreturn;
272 }
273 continue;
274 }
275 if (sc->func->setUnsafe())
276 {
277 if (!gag)
278 error(ae->loc, "scope variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
279 result = true;
280 }
281 }
282 else if (v->storage_class & STCvariadic && p == sc->func)
283 {
284 Type *tb = v->type->toBasetype();
285 if (tb->ty == Tarray || tb->ty == Tsarray)
286 {
287 if (va && !va->isDataseg() && !va->doNotInferScope)
288 {
289 if (!va->isScope() && inferScope)
290 { //printf("inferring scope for %s\n", va->toChars());
291 va->storage_class |= STCscope | STCscopeinferred;
292 }
293 continue;
294 }
295 if (sc->func->setUnsafe())
296 {
297 if (!gag)
298 error(ae->loc, "variadic variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
299 result = true;
300 }
301 }
302 }
303 else
304 {
305 /* v is not 'scope', and we didn't check the scope of where we assigned it to.
306 * It may escape via that assignment, therefore, v can never be 'scope'.
307 */
308 v->doNotInferScope = true;
309 }
310 }
311
312 for (size_t i = 0; i < er.byref.dim; i++)
313 {
314 VarDeclaration *v = er.byref[i];
315 //printf("byref: %s\n", v->toChars());
316 if (v->isDataseg())
317 continue;
318
319 Dsymbol *p = v->toParent2();
320
321 // If va's lifetime encloses v's, then error
322 if (va &&
323 ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) || va->storage_class & STCref) &&
324 sc->func->setUnsafe())
325 {
326 if (!gag)
327 error(ae->loc, "address of variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
328 result = true;
329 continue;
330 }
331
332 if (!(va && va->isScope()))
333 v->storage_class &= ~STCmaybescope;
334
335 if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
336 {
337 if (va && !va->isDataseg() && !va->doNotInferScope)
338 {
339 if (!va->isScope() && inferScope)
340 { //printf("inferring scope for %s\n", va->toChars());
341 va->storage_class |= STCscope | STCscopeinferred;
342 }
343 continue;
344 }
345 if (sc->func->setUnsafe())
346 {
347 if (!gag)
348 error(ae->loc, "reference to local variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
349 result = true;
350 }
351 continue;
352 }
353 }
354
355 for (size_t i = 0; i < er.byfunc.dim; i++)
356 {
357 FuncDeclaration *fd = er.byfunc[i];
358 //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
359 VarDeclarations vars;
360 findAllOuterAccessedVariables(fd, &vars);
361
362 for (size_t j = 0; j < vars.dim; j++)
363 {
364 VarDeclaration *v = vars[j];
365 //printf("v = %s\n", v->toChars());
366 assert(!v->isDataseg()); // these are not put in the closureVars[]
367
368 Dsymbol *p = v->toParent2();
369
370 if (!(va && va->isScope()))
371 v->storage_class &= ~STCmaybescope;
372
373 if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
374 {
375 if (va && !va->isDataseg() && !va->doNotInferScope)
376 {
377 /* Don't infer STCscope for va, because then a closure
378 * won't be generated for sc->func.
379 */
380 //if (!va->isScope() && inferScope)
381 //va->storage_class |= STCscope | STCscopeinferred;
382 continue;
383 }
384 if (sc->func->setUnsafe())
385 {
386 if (!gag)
387 error(ae->loc, "reference to local %s assigned to non-scope %s in @safe code", v->toChars(), e1->toChars());
388 result = true;
389 }
390 continue;
391 }
392 }
393 }
394
395 for (size_t i = 0; i < er.byexp.dim; i++)
396 {
397 Expression *ee = er.byexp[i];
398 if (va && !va->isDataseg() && !va->doNotInferScope)
399 {
400 if (!va->isScope() && inferScope)
401 { //printf("inferring scope for %s\n", va->toChars());
402 va->storage_class |= STCscope | STCscopeinferred;
403 }
404 continue;
405 }
406 if (sc->func->setUnsafe())
407 {
408 if (!gag)
409 error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope %s",
410 ee->toChars(), e1->toChars());
411 result = true;
412 }
413 }
414
415 return result;
416 }
417
418 /************************************
419 * Detect cases where pointers to the stack can 'escape' the
420 * lifetime of the stack frame when throwing `e`.
421 * Print error messages when these are detected.
422 * Params:
423 * sc = used to determine current function and module
424 * e = expression to check for any pointers to the stack
425 * gag = do not print error messages
426 * Returns:
427 * true if pointers to the stack can escape
428 */
checkThrowEscape(Scope * sc,Expression * e,bool gag)429 bool checkThrowEscape(Scope *sc, Expression *e, bool gag)
430 {
431 //printf("[%s] checkThrowEscape, e = %s\n", e->loc->toChars(), e->toChars());
432 EscapeByResults er;
433
434 escapeByValue(e, &er);
435
436 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
437 return false;
438
439 bool result = false;
440 for (size_t i = 0; i < er.byvalue.dim; i++)
441 {
442 VarDeclaration *v = er.byvalue[i];
443 //printf("byvalue %s\n", v->toChars());
444 if (v->isDataseg())
445 continue;
446
447 if (v->isScope())
448 {
449 if (sc->_module && sc->_module->isRoot())
450 {
451 // Only look for errors if in module listed on command line
452 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
453 {
454 if (!gag)
455 error(e->loc, "scope variable %s may not be thrown", v->toChars());
456 result = true;
457 }
458 continue;
459 }
460 }
461 else
462 {
463 //printf("no infer for %s\n", v->toChars());
464 v->doNotInferScope = true;
465 }
466 }
467 return result;
468 }
469
470 /************************************
471 * Detect cases where pointers to the stack can 'escape' the
472 * lifetime of the stack frame by returning 'e' by value.
473 * Params:
474 * sc = used to determine current function and module
475 * e = expression to check for any pointers to the stack
476 * gag = do not print error messages
477 * Returns:
478 * true if pointers to the stack can escape
479 */
480
checkReturnEscape(Scope * sc,Expression * e,bool gag)481 bool checkReturnEscape(Scope *sc, Expression *e, bool gag)
482 {
483 //printf("[%s] checkReturnEscape, e = %s\n", e->loc->toChars(), e->toChars());
484 return checkReturnEscapeImpl(sc, e, false, gag);
485 }
486
487 /************************************
488 * Detect cases where returning 'e' by ref can result in a reference to the stack
489 * being returned.
490 * Print error messages when these are detected.
491 * Params:
492 * sc = used to determine current function and module
493 * e = expression to check
494 * gag = do not print error messages
495 * Returns:
496 * true if references to the stack can escape
497 */
checkReturnEscapeRef(Scope * sc,Expression * e,bool gag)498 bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag)
499 {
500 //printf("[%s] checkReturnEscapeRef, e = %s\n", e->loc.toChars(), e->toChars());
501 //printf("current function %s\n", sc->func->toChars());
502 //printf("parent2 function %s\n", sc->func->toParent2()->toChars());
503
504 return checkReturnEscapeImpl(sc, e, true, gag);
505 }
506
escapingRef(VarDeclaration * v,Expression * e,bool & result,bool gag)507 static void escapingRef(VarDeclaration *v, Expression *e, bool &result, bool gag)
508 {
509 if (!gag)
510 {
511 const char *msg;
512 if (v->storage_class & STCparameter)
513 msg = "returning `%s` escapes a reference to parameter `%s`, perhaps annotate with `return`";
514 else
515 msg = "returning `%s` escapes a reference to local variable `%s`";
516 error(e->loc, msg, e->toChars(), v->toChars());
517 }
518 result = true;
519 }
520
checkReturnEscapeImpl(Scope * sc,Expression * e,bool refs,bool gag)521 static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag)
522 {
523 //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars());
524 EscapeByResults er;
525
526 if (refs)
527 escapeByRef(e, &er);
528 else
529 escapeByValue(e, &er);
530
531 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
532 return false;
533
534 bool result = false;
535 for (size_t i = 0; i < er.byvalue.dim; i++)
536 {
537 VarDeclaration *v = er.byvalue[i];
538 //printf("byvalue %s\n", v->toChars());
539 if (v->isDataseg())
540 continue;
541
542 Dsymbol *p = v->toParent2();
543
544 if ((v->isScope() || (v->storage_class & STCmaybescope)) &&
545 !(v->storage_class & STCreturn) &&
546 v->isParameter() &&
547 sc->func->flags & FUNCFLAGreturnInprocess &&
548 p == sc->func)
549 {
550 inferReturn(sc->func, v); // infer addition of 'return'
551 continue;
552 }
553
554 if (v->isScope())
555 {
556 if (v->storage_class & STCreturn)
557 continue;
558
559 if (sc->_module && sc->_module->isRoot() &&
560 /* This case comes up when the ReturnStatement of a __foreachbody is
561 * checked for escapes by the caller of __foreachbody. Skip it.
562 *
563 * struct S { static int opApply(int delegate(S*) dg); }
564 * S* foo() {
565 * foreach (S* s; S) // create __foreachbody for body of foreach
566 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
567 * return null; }
568 */
569 !(!refs && p->parent == sc->func))
570 {
571 // Only look for errors if in module listed on command line
572 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
573 {
574 if (!gag)
575 error(e->loc, "scope variable %s may not be returned", v->toChars());
576 result = true;
577 }
578 continue;
579 }
580 }
581 else if (v->storage_class & STCvariadic && p == sc->func)
582 {
583 Type *tb = v->type->toBasetype();
584 if (tb->ty == Tarray || tb->ty == Tsarray)
585 {
586 if (!gag)
587 error(e->loc, "returning `%s` escapes a reference to variadic parameter `%s`", e->toChars(), v->toChars());
588 result = false;
589 }
590 }
591 else
592 {
593 //printf("no infer for %s\n", v->toChars());
594 v->doNotInferScope = true;
595 }
596 }
597
598 for (size_t i = 0; i < er.byref.dim; i++)
599 {
600 VarDeclaration *v = er.byref[i];
601 //printf("byref %s\n", v->toChars());
602 if (v->isDataseg())
603 continue;
604
605 Dsymbol *p = v->toParent2();
606
607 if ((v->storage_class & (STCref | STCout)) == 0)
608 {
609 if (p == sc->func)
610 {
611 escapingRef(v, e, result, gag);
612 continue;
613 }
614 FuncDeclaration *fd = p->isFuncDeclaration();
615 if (fd && sc->func->flags & FUNCFLAGreturnInprocess)
616 {
617 /* Code like:
618 * int x;
619 * auto dg = () { return &x; }
620 * Making it:
621 * auto dg = () return { return &x; }
622 * Because dg.ptr points to x, this is returning dt.ptr+offset
623 */
624 if (global.params.vsafe)
625 sc->func->storage_class |= STCreturn;
626 }
627 }
628
629 /* Check for returning a ref variable by 'ref', but should be 'return ref'
630 * Infer the addition of 'return', or set result to be the offending expression.
631 */
632 if ( (v->storage_class & (STCref | STCout)) &&
633 !(v->storage_class & (STCreturn | STCforeach)))
634 {
635 if ((sc->func->flags & FUNCFLAGreturnInprocess) && p == sc->func)
636 {
637 inferReturn(sc->func, v); // infer addition of 'return'
638 }
639 else if (global.params.useDIP25 &&
640 sc->_module && sc->_module->isRoot())
641 {
642 // Only look for errors if in module listed on command line
643
644 if (p == sc->func)
645 {
646 //printf("escaping reference to local ref variable %s\n", v->toChars());
647 //printf("storage class = x%llx\n", v->storage_class);
648 escapingRef(v, e, result, gag);
649 continue;
650 }
651 // Don't need to be concerned if v's parent does not return a ref
652 FuncDeclaration *fd = p->isFuncDeclaration();
653 if (fd && fd->type && fd->type->ty == Tfunction)
654 {
655 TypeFunction *tf = (TypeFunction *)fd->type;
656 if (tf->isref)
657 {
658 if (!gag)
659 error(e->loc, "escaping reference to outer local variable %s", v->toChars());
660 result = true;
661 continue;
662 }
663 }
664 }
665 }
666 }
667
668 for (size_t i = 0; i < er.byexp.dim; i++)
669 {
670 Expression *ee = er.byexp[i];
671 //printf("byexp %s\n", ee->toChars());
672 if (!gag)
673 error(ee->loc, "escaping reference to stack allocated value returned by %s", ee->toChars());
674 result = true;
675 }
676
677 return result;
678 }
679
680
681 /*************************************
682 * Variable v needs to have 'return' inferred for it.
683 * Params:
684 * fd = function that v is a parameter to
685 * v = parameter that needs to be STCreturn
686 */
687
inferReturn(FuncDeclaration * fd,VarDeclaration * v)688 static void inferReturn(FuncDeclaration *fd, VarDeclaration *v)
689 {
690 // v is a local in the current function
691
692 //printf("for function '%s' inferring 'return' for variable '%s'\n", fd->toChars(), v->toChars());
693 v->storage_class |= STCreturn;
694
695 TypeFunction *tf = (TypeFunction *)fd->type;
696 if (v == fd->vthis)
697 {
698 /* v is the 'this' reference, so mark the function
699 */
700 fd->storage_class |= STCreturn;
701 if (tf->ty == Tfunction)
702 {
703 //printf("'this' too %p %s\n", tf, sc->func->toChars());
704 tf->isreturn = true;
705 }
706 }
707 else
708 {
709 // Perform 'return' inference on parameter
710 if (tf->ty == Tfunction && tf->parameters)
711 {
712 const size_t dim = Parameter::dim(tf->parameters);
713 for (size_t i = 0; i < dim; i++)
714 {
715 Parameter *p = Parameter::getNth(tf->parameters, i);
716 if (p->ident == v->ident)
717 {
718 p->storageClass |= STCreturn;
719 break; // there can be only one
720 }
721 }
722 }
723 }
724 }
725
726
727 /****************************************
728 * e is an expression to be returned by value, and that value contains pointers.
729 * Walk e to determine which variables are possibly being
730 * returned by value, such as:
731 * int* function(int* p) { return p; }
732 * If e is a form of &p, determine which variables have content
733 * which is being returned as ref, such as:
734 * int* function(int i) { return &i; }
735 * Multiple variables can be inserted, because of expressions like this:
736 * int function(bool b, int i, int* p) { return b ? &i : p; }
737 *
738 * No side effects.
739 *
740 * Params:
741 * e = expression to be returned by value
742 * er = where to place collected data
743 */
escapeByValue(Expression * e,EscapeByResults * er)744 static void escapeByValue(Expression *e, EscapeByResults *er)
745 {
746 //printf("[%s] escapeByValue, e: %s\n", e->loc.toChars(), e->toChars());
747
748 class EscapeVisitor : public Visitor
749 {
750 public:
751 EscapeByResults *er;
752
753 EscapeVisitor(EscapeByResults *er)
754 : er(er)
755 {
756 }
757
758 void visit(Expression *)
759 {
760 }
761
762 void visit(AddrExp *e)
763 {
764 escapeByRef(e->e1, er);
765 }
766
767 void visit(SymOffExp *e)
768 {
769 VarDeclaration *v = e->var->isVarDeclaration();
770 if (v)
771 er->byref.push(v);
772 }
773
774 void visit(VarExp *e)
775 {
776 VarDeclaration *v = e->var->isVarDeclaration();
777 if (v)
778 er->byvalue.push(v);
779 }
780
781 void visit(ThisExp *e)
782 {
783 if (e->var)
784 er->byvalue.push(e->var);
785 }
786
787 void visit(DotVarExp *e)
788 {
789 Type *t = e->e1->type->toBasetype();
790 if (t->ty == Tstruct)
791 e->e1->accept(this);
792 }
793
794 void visit(DelegateExp *e)
795 {
796 Type *t = e->e1->type->toBasetype();
797 if (t->ty == Tclass || t->ty == Tpointer)
798 escapeByValue(e->e1, er);
799 else
800 escapeByRef(e->e1, er);
801 er->byfunc.push(e->func);
802 }
803
804 void visit(FuncExp *e)
805 {
806 if (e->fd->tok == TOKdelegate)
807 er->byfunc.push(e->fd);
808 }
809
810 void visit(TupleExp *)
811 {
812 assert(0); // should have been lowered by now
813 }
814
815 void visit(ArrayLiteralExp *e)
816 {
817 Type *tb = e->type->toBasetype();
818 if (tb->ty == Tsarray || tb->ty == Tarray)
819 {
820 if (e->basis)
821 e->basis->accept(this);
822 for (size_t i = 0; i < e->elements->dim; i++)
823 {
824 Expression *el = (*e->elements)[i];
825 if (el)
826 el->accept(this);
827 }
828 }
829 }
830
831 void visit(StructLiteralExp *e)
832 {
833 if (e->elements)
834 {
835 for (size_t i = 0; i < e->elements->dim; i++)
836 {
837 Expression *ex = (*e->elements)[i];
838 if (ex)
839 ex->accept(this);
840 }
841 }
842 }
843
844 void visit(NewExp *e)
845 {
846 Type *tb = e->newtype->toBasetype();
847 if (tb->ty == Tstruct && !e->member && e->arguments)
848 {
849 for (size_t i = 0; i < e->arguments->dim; i++)
850 {
851 Expression *ex = (*e->arguments)[i];
852 if (ex)
853 ex->accept(this);
854 }
855 }
856 }
857
858 void visit(CastExp *e)
859 {
860 Type *tb = e->type->toBasetype();
861 if (tb->ty == Tarray &&
862 e->e1->type->toBasetype()->ty == Tsarray)
863 {
864 escapeByRef(e->e1, er);
865 }
866 else
867 e->e1->accept(this);
868 }
869
870 void visit(SliceExp *e)
871 {
872 if (e->e1->op == TOKvar)
873 {
874 VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
875 Type *tb = e->type->toBasetype();
876 if (v)
877 {
878 if (tb->ty == Tsarray)
879 return;
880 if (v->storage_class & STCvariadic)
881 {
882 er->byvalue.push(v);
883 return;
884 }
885 }
886 }
887 Type *t1b = e->e1->type->toBasetype();
888 if (t1b->ty == Tsarray)
889 {
890 Type *tb = e->type->toBasetype();
891 if (tb->ty != Tsarray)
892 escapeByRef(e->e1, er);
893 }
894 else
895 e->e1->accept(this);
896 }
897
898 void visit(BinExp *e)
899 {
900 Type *tb = e->type->toBasetype();
901 if (tb->ty == Tpointer)
902 {
903 e->e1->accept(this);
904 e->e2->accept(this);
905 }
906 }
907
908 void visit(BinAssignExp *e)
909 {
910 e->e1->accept(this);
911 }
912
913 void visit(AssignExp *e)
914 {
915 e->e1->accept(this);
916 }
917
918 void visit(CommaExp *e)
919 {
920 e->e2->accept(this);
921 }
922
923 void visit(CondExp *e)
924 {
925 e->e1->accept(this);
926 e->e2->accept(this);
927 }
928
929 void visit(CallExp *e)
930 {
931 //printf("CallExp(): %s\n", e->toChars());
932 /* Check each argument that is
933 * passed as 'return scope'.
934 */
935 Type *t1 = e->e1->type->toBasetype();
936 TypeFunction *tf = NULL;
937 TypeDelegate *dg = NULL;
938 if (t1->ty == Tdelegate)
939 {
940 dg = (TypeDelegate *)t1;
941 tf = (TypeFunction *)dg->next;
942 }
943 else if (t1->ty == Tfunction)
944 tf = (TypeFunction *)t1;
945 else
946 return;
947
948 if (e->arguments && e->arguments->dim)
949 {
950 /* j=1 if _arguments[] is first argument,
951 * skip it because it is not passed by ref
952 */
953 size_t j = (tf->linkage == LINKd && tf->varargs == 1);
954 for (size_t i = j; i < e->arguments->dim; ++i)
955 {
956 Expression *arg = (*e->arguments)[i];
957 size_t nparams = Parameter::dim(tf->parameters);
958 if (i - j < nparams && i >= j)
959 {
960 Parameter *p = Parameter::getNth(tf->parameters, i - j);
961 const StorageClass stc = tf->parameterStorageClass(p);
962 if ((stc & (STCscope)) && (stc & STCreturn))
963 arg->accept(this);
964 else if ((stc & (STCref)) && (stc & STCreturn))
965 escapeByRef(arg, er);
966 }
967 }
968 }
969 // If 'this' is returned, check it too
970 if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
971 {
972 DotVarExp *dve = (DotVarExp *)e->e1;
973 FuncDeclaration *fd = dve->var->isFuncDeclaration();
974 AggregateDeclaration *ad = NULL;
975 if (global.params.vsafe && tf->isreturn && fd && (ad = fd->isThis()) != NULL)
976 {
977 if (ad->isClassDeclaration() || tf->isscope) // this is 'return scope'
978 dve->e1->accept(this);
979 else if (ad->isStructDeclaration()) // this is 'return ref'
980 escapeByRef(dve->e1, er);
981 }
982 else if (dve->var->storage_class & STCreturn || tf->isreturn)
983 {
984 if (dve->var->storage_class & STCscope)
985 dve->e1->accept(this);
986 else if (dve->var->storage_class & STCref)
987 escapeByRef(dve->e1, er);
988 }
989 }
990
991 /* If returning the result of a delegate call, the .ptr
992 * field of the delegate must be checked.
993 */
994 if (dg)
995 {
996 if (tf->isreturn)
997 e->e1->accept(this);
998 }
999 }
1000 };
1001
1002 EscapeVisitor v(er);
1003 e->accept(&v);
1004 }
1005
1006 /****************************************
1007 * e is an expression to be returned by 'ref'.
1008 * Walk e to determine which variables are possibly being
1009 * returned by ref, such as:
1010 * ref int function(int i) { return i; }
1011 * If e is a form of *p, determine which variables have content
1012 * which is being returned as ref, such as:
1013 * ref int function(int* p) { return *p; }
1014 * Multiple variables can be inserted, because of expressions like this:
1015 * ref int function(bool b, int i, int* p) { return b ? i : *p; }
1016 *
1017 * No side effects.
1018 *
1019 * Params:
1020 * e = expression to be returned by 'ref'
1021 * er = where to place collected data
1022 */
escapeByRef(Expression * e,EscapeByResults * er)1023 static void escapeByRef(Expression *e, EscapeByResults *er)
1024 {
1025 //printf("[%s] escapeByRef, e: %s\n", e->loc->toChars(), e->toChars());
1026 class EscapeRefVisitor : public Visitor
1027 {
1028 public:
1029 EscapeByResults *er;
1030
1031 EscapeRefVisitor(EscapeByResults *er)
1032 : er(er)
1033 {
1034 }
1035
1036 void visit(Expression *)
1037 {
1038 }
1039
1040 void visit(VarExp *e)
1041 {
1042 VarDeclaration *v = e->var->isVarDeclaration();
1043 if (v)
1044 {
1045 if (v->storage_class & STCref && v->storage_class & (STCforeach | STCtemp) && v->_init)
1046 {
1047 /* If compiler generated ref temporary
1048 * (ref v = ex; ex)
1049 * look at the initializer instead
1050 */
1051 if (ExpInitializer *ez = v->_init->isExpInitializer())
1052 {
1053 assert(ez->exp && ez->exp->op == TOKconstruct);
1054 Expression *ex = ((ConstructExp *)ez->exp)->e2;
1055 ex->accept(this);
1056 }
1057 }
1058 else
1059 er->byref.push(v);
1060 }
1061 }
1062
1063 void visit(ThisExp *e)
1064 {
1065 if (e->var)
1066 er->byref.push(e->var);
1067 }
1068
1069 void visit(PtrExp *e)
1070 {
1071 escapeByValue(e->e1, er);
1072 }
1073
1074 void visit(IndexExp *e)
1075 {
1076 Type *tb = e->e1->type->toBasetype();
1077 if (e->e1->op == TOKvar)
1078 {
1079 VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
1080 if (tb->ty == Tarray || tb->ty == Tsarray)
1081 {
1082 if (v->storage_class & STCvariadic)
1083 {
1084 er->byref.push(v);
1085 return;
1086 }
1087 }
1088 }
1089 if (tb->ty == Tsarray)
1090 {
1091 e->e1->accept(this);
1092 }
1093 else if (tb->ty == Tarray)
1094 {
1095 escapeByValue(e->e1, er);
1096 }
1097 }
1098
1099 void visit(DotVarExp *e)
1100 {
1101 Type *t1b = e->e1->type->toBasetype();
1102 if (t1b->ty == Tclass)
1103 escapeByValue(e->e1, er);
1104 else
1105 e->e1->accept(this);
1106 }
1107
1108 void visit(BinAssignExp *e)
1109 {
1110 e->e1->accept(this);
1111 }
1112
1113 void visit(AssignExp *e)
1114 {
1115 e->e1->accept(this);
1116 }
1117
1118 void visit(CommaExp *e)
1119 {
1120 e->e2->accept(this);
1121 }
1122
1123 void visit(CondExp *e)
1124 {
1125 e->e1->accept(this);
1126 e->e2->accept(this);
1127 }
1128
1129 void visit(CallExp *e)
1130 {
1131 /* If the function returns by ref, check each argument that is
1132 * passed as 'return ref'.
1133 */
1134 Type *t1 = e->e1->type->toBasetype();
1135 TypeFunction *tf;
1136 if (t1->ty == Tdelegate)
1137 tf = (TypeFunction *)((TypeDelegate *)t1)->next;
1138 else if (t1->ty == Tfunction)
1139 tf = (TypeFunction *)t1;
1140 else
1141 return;
1142 if (tf->isref)
1143 {
1144 if (e->arguments && e->arguments->dim)
1145 {
1146 /* j=1 if _arguments[] is first argument,
1147 * skip it because it is not passed by ref
1148 */
1149 size_t j = (tf->linkage == LINKd && tf->varargs == 1);
1150
1151 for (size_t i = j; i < e->arguments->dim; ++i)
1152 {
1153 Expression *arg = (*e->arguments)[i];
1154 size_t nparams = Parameter::dim(tf->parameters);
1155 if (i - j < nparams && i >= j)
1156 {
1157 Parameter *p = Parameter::getNth(tf->parameters, i - j);
1158 const StorageClass stc = tf->parameterStorageClass(p);
1159 if ((stc & (STCout | STCref)) && (stc & STCreturn))
1160 arg->accept(this);
1161 else if ((stc & STCscope) && (stc & STCreturn))
1162 {
1163 if (arg->op == TOKdelegate)
1164 {
1165 DelegateExp *de = (DelegateExp *)arg;
1166 if (de->func->isNested())
1167 er->byexp.push(de);
1168 }
1169 else
1170 escapeByValue(arg, er);
1171 }
1172 }
1173 }
1174 }
1175
1176 // If 'this' is returned by ref, check it too
1177 if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
1178 {
1179 DotVarExp *dve = (DotVarExp *)e->e1;
1180 if (dve->var->storage_class & STCreturn || tf->isreturn)
1181 {
1182 if ((dve->var->storage_class & STCscope) || tf->isscope)
1183 escapeByValue(dve->e1, er);
1184 else if ((dve->var->storage_class & STCref) || tf->isref)
1185 dve->e1->accept(this);
1186 }
1187
1188 }
1189 // If it's a delegate, check it too
1190 if (e->e1->op == TOKvar && t1->ty == Tdelegate)
1191 {
1192 escapeByValue(e->e1, er);
1193 }
1194 }
1195 else
1196 er->byexp.push(e);
1197 }
1198 };
1199
1200 EscapeRefVisitor v(er);
1201 e->accept(&v);
1202 }
1203
1204 /*************************
1205 * Find all variables accessed by this delegate that are
1206 * in functions enclosing it.
1207 * Params:
1208 * fd = function
1209 * vars = array to append found variables to
1210 */
findAllOuterAccessedVariables(FuncDeclaration * fd,VarDeclarations * vars)1211 void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars)
1212 {
1213 //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
1214 for (Dsymbol *p = fd->parent; p; p = p->parent)
1215 {
1216 FuncDeclaration *fdp = p->isFuncDeclaration();
1217 if (fdp)
1218 {
1219 for (size_t i = 0; i < fdp->closureVars.dim; i++)
1220 {
1221 VarDeclaration *v = fdp->closureVars[i];
1222 for (size_t j = 0; j < v->nestedrefs.dim; j++)
1223 {
1224 FuncDeclaration *fdv = v->nestedrefs[j];
1225 if (fdv == fd)
1226 {
1227 //printf("accessed: %s, type %s\n", v->toChars(), v->type->toChars());
1228 vars->push(v);
1229 }
1230 }
1231 }
1232 }
1233 }
1234 }
1235