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 */
22struct 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
30static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag);
31static void inferReturn(FuncDeclaration *fd, VarDeclaration *v);
32static void escapeByValue(Expression *e, EscapeByResults *er);
33static void escapeByRef(Expression *e, EscapeByResults *er);
34static void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars);
35
36/* 'v' is assigned unsafely to 'par'
37*/
38static 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 */
65bool 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 */
185bool 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 */
429bool 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
481bool 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 */
498bool 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
507static 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
521static 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
688static 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 */
744static 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 */
1023static 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 */
1211void 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