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