1 /**
2  * Most of the logic to implement scoped pointers and scoped references is here.
3  *
4  * Copyright:   Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/escape.d, _escape.d)
8  * Documentation:  https://dlang.org/phobos/dmd_escape.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/escape.d
10  */
11 
12 module dmd.escape;
13 
14 import core.stdc.stdio : printf;
15 import core.stdc.stdlib;
16 import core.stdc.string;
17 
18 import dmd.root.rmem;
19 
20 import dmd.aggregate;
21 import dmd.declaration;
22 import dmd.dscope;
23 import dmd.dsymbol;
24 import dmd.errors;
25 import dmd.expression;
26 import dmd.func;
27 import dmd.globals;
28 import dmd.id;
29 import dmd.identifier;
30 import dmd.init;
31 import dmd.mtype;
32 import dmd.printast;
33 import dmd.root.rootobject;
34 import dmd.tokens;
35 import dmd.visitor;
36 import dmd.arraytypes;
37 
38 /******************************************************
39  * Checks memory objects passed to a function.
40  * Checks that if a memory object is passed by ref or by pointer,
41  * all of the refs or pointers are const, or there is only one mutable
42  * ref or pointer to it.
43  * References:
44  *      DIP 1021
45  * Params:
46  *      sc = used to determine current function and module
47  *      fd = function being called
48  *      tf = fd's type
49  *      ethis = if not null, the `this` pointer
50  *      arguments = actual arguments to function
51  *      gag = do not print error messages
52  * Returns:
53  *      `true` if error
54  */
55 bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf,
56     Expression ethis, Expressions* arguments, bool gag)
57 {
58     enum log = false;
59     if (log) printf("[%s] checkMutableArguments, fd: `%s`\n", fd.loc.toChars(), fd.toChars());
60     if (log && ethis) printf("ethis: `%s`\n", ethis.toChars());
61     bool errors = false;
62 
63     /* Outer variable references are treated as if they are extra arguments
64      * passed by ref to the function (which they essentially are via the static link).
65      */
66     VarDeclaration[] outerVars = fd ? fd.outerVars[] : null;
67 
68     const len = arguments.length + (ethis !is null) + outerVars.length;
69     if (len <= 1)
70         return errors;
71 
72     struct EscapeBy
73     {
74         EscapeByResults er;
75         Parameter param;        // null if no Parameter for this argument
76         bool isMutable;         // true if reference to mutable
77     }
78 
79     /* Store escapeBy as static data escapeByStorage so we can keep reusing the same
80      * arrays rather than reallocating them.
81      */
82     __gshared EscapeBy[] escapeByStorage;
83     auto escapeBy = escapeByStorage;
84     if (escapeBy.length < len)
85     {
86         auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof);
87         // Clear the new section
88         memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof);
89         escapeBy = newPtr[0 .. len];
90         escapeByStorage = escapeBy;
91     }
92     else
93         escapeBy = escapeBy[0 .. len];
94 
95     const paramLength = tf.parameterList.length;
96 
97     // Fill in escapeBy[] with arguments[], ethis, and outerVars[]
98     foreach (const i, ref eb; escapeBy)
99     {
100         bool refs;
101         Expression arg;
102         if (i < arguments.length)
103         {
104             arg = (*arguments)[i];
105             if (i < paramLength)
106             {
107                 eb.param = tf.parameterList[i];
108                 refs = eb.param.isReference();
109                 eb.isMutable = eb.param.isReferenceToMutable(arg.type);
110             }
111             else
112             {
113                 eb.param = null;
114                 refs = false;
115                 eb.isMutable = arg.type.isReferenceToMutable();
116             }
117         }
118         else if (ethis)
119         {
120             /* ethis is passed by value if a class reference,
121              * by ref if a struct value
122              */
123             eb.param = null;
124             arg = ethis;
125             auto ad = fd.isThis();
126             assert(ad);
127             assert(ethis);
128             if (ad.isClassDeclaration())
129             {
130                 refs = false;
131                 eb.isMutable = arg.type.isReferenceToMutable();
132             }
133             else
134             {
135                 assert(ad.isStructDeclaration());
136                 refs = true;
137                 eb.isMutable = arg.type.isMutable();
138             }
139         }
140         else
141         {
142             // outer variables are passed by ref
143             eb.param = null;
144             refs = true;
145             auto var = outerVars[i - (len - outerVars.length)];
146             eb.isMutable = var.type.isMutable();
147             eb.er.byref.push(var);
148             continue;
149         }
150 
151         if (refs)
152             escapeByRef(arg, &eb.er);
153         else
154             escapeByValue(arg, &eb.er);
155     }
156 
157     void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2,
158                       VarDeclaration v, VarDeclaration v2, bool of)
159     {
160         if (log) printf("v2: `%s`\n", v2.toChars());
161         if (v2 != v)
162             return;
163         //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable);
164         if (!(eb.isMutable || eb2.isMutable))
165             return;
166 
167         if (!(global.params.vsafe && sc.func.setUnsafe()))
168             return;
169 
170         if (!gag)
171         {
172             // int i; funcThatEscapes(ref int i);
173             // funcThatEscapes(i); // error escaping reference _to_ `i`
174             // int* j; funcThatEscapes2(int* j);
175             // funcThatEscapes2(j); // error escaping reference _of_ `i`
176             const(char)* referenceVerb = of ? "of" : "to";
177             const(char)* msg = eb.isMutable && eb2.isMutable
178                                 ? "more than one mutable reference %s `%s` in arguments to `%s()`"
179                                 : "mutable and const references %s `%s` in arguments to `%s()`";
180             error((*arguments)[i].loc, msg,
181                   referenceVerb,
182                   v.toChars(),
183                   fd ? fd.toPrettyChars() : "indirectly");
184         }
185         errors = true;
186     }
187 
188     void escape(size_t i, ref EscapeBy eb, bool byval)
189     {
190         foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref)
191         {
192             if (log)
193             {
194                 const(char)* by = byval ? "byval" : "byref";
195                 printf("%s %s\n", by, v.toChars());
196             }
197             if (byval && !v.type.hasPointers())
198                 continue;
199             foreach (ref eb2; escapeBy[i + 1 .. $])
200             {
201                 foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref)
202                 {
203                     checkOnePair(i, eb, eb2, v, v2, byval);
204                 }
205             }
206         }
207     }
208     foreach (const i, ref eb; escapeBy[0 .. $ - 1])
209     {
210         escape(i, eb, true);
211         escape(i, eb, false);
212     }
213 
214     /* Reset the arrays in escapeBy[] so we can reuse them next time through
215      */
216     foreach (ref eb; escapeBy)
217     {
218         eb.er.reset();
219     }
220 
221     return errors;
222 }
223 
224 /******************************************
225  * Array literal is going to be allocated on the GC heap.
226  * Check its elements to see if any would escape by going on the heap.
227  * Params:
228  *      sc = used to determine current function and module
229  *      ae = array literal expression
230  *      gag = do not print error messages
231  * Returns:
232  *      `true` if any elements escaped
233  */
234 bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag)
235 {
236     bool errors;
237     if (ae.basis)
238         errors = checkNewEscape(sc, ae.basis, gag);
239     foreach (ex; *ae.elements)
240     {
241         if (ex)
242             errors |= checkNewEscape(sc, ex, gag);
243     }
244     return errors;
245 }
246 
247 /******************************************
248  * Associative array literal is going to be allocated on the GC heap.
249  * Check its elements to see if any would escape by going on the heap.
250  * Params:
251  *      sc = used to determine current function and module
252  *      ae = associative array literal expression
253  *      gag = do not print error messages
254  * Returns:
255  *      `true` if any elements escaped
256  */
257 bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag)
258 {
259     bool errors;
260     foreach (ex; *ae.keys)
261     {
262         if (ex)
263             errors |= checkNewEscape(sc, ex, gag);
264     }
265     foreach (ex; *ae.values)
266     {
267         if (ex)
268             errors |= checkNewEscape(sc, ex, gag);
269     }
270     return errors;
271 }
272 
273 /****************************************
274  * Function parameter `par` is being initialized to `arg`,
275  * and `par` may escape.
276  * Detect if scoped values can escape this way.
277  * Print error messages when these are detected.
278  * Params:
279  *      sc = used to determine current function and module
280  *      fdc = function being called, `null` if called indirectly
281  *      par = function parameter (`this` if null)
282  *      arg = initializer for param
283  *      assertmsg = true if the parameter is the msg argument to assert(bool, msg).
284  *      gag = do not print error messages
285  * Returns:
286  *      `true` if pointers to the stack can escape via assignment
287  */
288 bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Expression arg, bool assertmsg, bool gag)
289 {
290     enum log = false;
291     if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n",
292         arg ? arg.toChars() : "null",
293         par ? par.toChars() : "this");
294     //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
295 
296     if (!arg.type.hasPointers())
297         return false;
298 
299     EscapeByResults er;
300 
301     escapeByValue(arg, &er);
302 
303     if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
304         return false;
305 
306     bool result = false;
307 
308     /* 'v' is assigned unsafely to 'par'
309      */
310     void unsafeAssign(VarDeclaration v, const char* desc)
311     {
312         if (global.params.vsafe && sc.func.setUnsafe())
313         {
314             if (!gag)
315             {
316                 if (assertmsg)
317                 {
318                     error(arg.loc, "%s `%s` assigned to non-scope parameter calling `assert()`",
319                         desc, v.toChars());
320                 }
321                 else
322                 {
323                     error(arg.loc, "%s `%s` assigned to non-scope parameter `%s` calling %s",
324                         desc, v.toChars(),
325                         par ? par.toChars() : "this",
326                         fdc ? fdc.toPrettyChars() : "indirectly");
327                 }
328             }
329             result = true;
330         }
331     }
332 
333     foreach (VarDeclaration v; er.byvalue)
334     {
335         if (log) printf("byvalue %s\n", v.toChars());
336         if (v.isDataseg())
337             continue;
338 
339         Dsymbol p = v.toParent2();
340 
341         notMaybeScope(v);
342 
343         if (v.isScope())
344         {
345             unsafeAssign(v, "scope variable");
346         }
347         else if (v.storage_class & STC.variadic && p == sc.func)
348         {
349             Type tb = v.type.toBasetype();
350             if (tb.ty == Tarray || tb.ty == Tsarray)
351             {
352                 unsafeAssign(v, "variadic variable");
353             }
354         }
355         else
356         {
357             /* v is not 'scope', and is assigned to a parameter that may escape.
358              * Therefore, v can never be 'scope'.
359              */
360             if (log) printf("no infer for %s in %s loc %s, fdc %s, %d\n",
361                 v.toChars(), sc.func.ident.toChars(), sc.func.loc.toChars(), fdc.ident.toChars(),  __LINE__);
362             v.doNotInferScope = true;
363         }
364     }
365 
366     foreach (VarDeclaration v; er.byref)
367     {
368         if (log) printf("byref %s\n", v.toChars());
369         if (v.isDataseg())
370             continue;
371 
372         Dsymbol p = v.toParent2();
373 
374         notMaybeScope(v);
375 
376         if ((v.storage_class & (STC.ref_ | STC.out_)) == 0 && p == sc.func)
377         {
378             if (par && (par.storageClass & (STC.scope_ | STC.return_)) == STC.scope_)
379                 continue;
380 
381             unsafeAssign(v, "reference to local variable");
382             continue;
383         }
384     }
385 
386     foreach (FuncDeclaration fd; er.byfunc)
387     {
388         //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
389         VarDeclarations vars;
390         findAllOuterAccessedVariables(fd, &vars);
391 
392         foreach (v; vars)
393         {
394             //printf("v = %s\n", v.toChars());
395             assert(!v.isDataseg());     // these are not put in the closureVars[]
396 
397             Dsymbol p = v.toParent2();
398 
399             notMaybeScope(v);
400 
401             if ((v.storage_class & (STC.ref_ | STC.out_ | STC.scope_)) && p == sc.func)
402             {
403                 unsafeAssign(v, "reference to local");
404                 continue;
405             }
406         }
407     }
408 
409     foreach (Expression ee; er.byexp)
410     {
411         if (sc.func && sc.func.setUnsafe())
412         {
413             if (!gag)
414                 error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`",
415                     ee.toChars(),
416                     par ? par.toChars() : "this");
417             result = true;
418         }
419     }
420 
421     return result;
422 }
423 
424 /*****************************************************
425  * Function argument initializes a `return` parameter,
426  * and that parameter gets assigned to `firstArg`.
427  * Essentially, treat as `firstArg = arg;`
428  * Params:
429  *      sc = used to determine current function and module
430  *      firstArg = `ref` argument through which `arg` may be assigned
431  *      arg = initializer for parameter
432  *      gag = do not print error messages
433  * Returns:
434  *      `true` if assignment to `firstArg` would cause an error
435  */
436 bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, bool gag)
437 {
438     enum log = false;
439     if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n",
440         firstArg.toChars(), arg.toChars());
441     //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
442 
443     if (!arg.type.hasPointers())
444         return false;
445 
446     scope e = new AssignExp(arg.loc, firstArg, arg);
447     return checkAssignEscape(sc, e, gag);
448 }
449 
450 /*****************************************************
451  * Check struct constructor of the form `s.this(args)`, by
452  * checking each `return` parameter to see if it gets
453  * assigned to `s`.
454  * Params:
455  *      sc = used to determine current function and module
456  *      ce = constructor call of the form `s.this(args)`
457  *      gag = do not print error messages
458  * Returns:
459  *      `true` if construction would cause an escaping reference error
460  */
461 bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
462 {
463     enum log = false;
464     if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars());
465     Type tthis = ce.type.toBasetype();
466     assert(tthis.ty == Tstruct);
467     if (!tthis.hasPointers())
468         return false;
469 
470     if (!ce.arguments && ce.arguments.dim)
471         return false;
472 
473     assert(ce.e1.op == TOK.dotVariable);
474     DotVarExp dve = cast(DotVarExp)ce.e1;
475     CtorDeclaration ctor = dve.var.isCtorDeclaration();
476     assert(ctor);
477     assert(ctor.type.ty == Tfunction);
478     TypeFunction tf = cast(TypeFunction)ctor.type;
479 
480     const nparams = tf.parameterList.length;
481     const n = ce.arguments.dim;
482 
483     // j=1 if _arguments[] is first argument
484     const j = tf.isDstyleVariadic();
485 
486     /* Attempt to assign each `return` arg to the `this` reference
487      */
488     foreach (const i; 0 .. n)
489     {
490         Expression arg = (*ce.arguments)[i];
491         if (!arg.type.hasPointers())
492             return false;
493 
494         //printf("\targ[%d]: %s\n", i, arg.toChars());
495 
496         if (i - j < nparams && i >= j)
497         {
498             Parameter p = tf.parameterList[i - j];
499 
500             if (p.storageClass & STC.return_)
501             {
502                 /* Fake `dve.e1 = arg;` and look for scope violations
503                  */
504                 scope e = new AssignExp(arg.loc, dve.e1, arg);
505                 if (checkAssignEscape(sc, e, gag))
506                     return true;
507             }
508         }
509     }
510 
511     return false;
512 }
513 
514 /****************************************
515  * Given an `AssignExp`, determine if the lvalue will cause
516  * the contents of the rvalue to escape.
517  * Print error messages when these are detected.
518  * Infer `scope` attribute for the lvalue where possible, in order
519  * to eliminate the error.
520  * Params:
521  *      sc = used to determine current function and module
522  *      e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack
523  *      gag = do not print error messages
524  * Returns:
525  *      `true` if pointers to the stack can escape via assignment
526  */
527 bool checkAssignEscape(Scope* sc, Expression e, bool gag)
528 {
529     enum log = false;
530     if (log) printf("checkAssignEscape(e: %s)\n", e.toChars());
531     if (e.op != TOK.assign && e.op != TOK.blit && e.op != TOK.construct &&
532         e.op != TOK.concatenateAssign && e.op != TOK.concatenateElemAssign && e.op != TOK.concatenateDcharAssign)
533         return false;
534     auto ae = cast(BinExp)e;
535     Expression e1 = ae.e1;
536     Expression e2 = ae.e2;
537     //printf("type = %s, %d\n", e1.type.toChars(), e1.type.hasPointers());
538 
539     if (!e1.type.hasPointers())
540         return false;
541 
542     if (e1.op == TOK.slice)
543         return false;
544 
545     /* The struct literal case can arise from the S(e2) constructor call:
546      *    return S(e2);
547      * and appears in this function as:
548      *    structLiteral = e2;
549      * Such an assignment does not necessarily remove scope-ness.
550      */
551     if (e1.op == TOK.structLiteral)
552         return false;
553 
554     EscapeByResults er;
555 
556     escapeByValue(e2, &er);
557 
558     if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
559         return false;
560 
561     VarDeclaration va = expToVariable(e1);
562 
563     if (va && e.op == TOK.concatenateElemAssign)
564     {
565         /* https://issues.dlang.org/show_bug.cgi?id=17842
566          * Draw an equivalence between:
567          *   *q = p;
568          * and:
569          *   va ~= e;
570          * since we are not assigning to va, but are assigning indirectly through va.
571          */
572         va = null;
573     }
574 
575     if (va && e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass)
576     {
577         /* https://issues.dlang.org/show_bug.cgi?id=17949
578          * Draw an equivalence between:
579          *   *q = p;
580          * and:
581          *   va.field = e2;
582          * since we are not assigning to va, but are assigning indirectly through class reference va.
583          */
584         va = null;
585     }
586 
587     if (log && va) printf("va: %s\n", va.toChars());
588 
589     // Try to infer 'scope' for va if in a function not marked @system
590     bool inferScope = false;
591     if (va && sc.func && sc.func.type && sc.func.type.ty == Tfunction)
592         inferScope = (cast(TypeFunction)sc.func.type).trust != TRUST.system;
593     //printf("inferScope = %d, %d\n", inferScope, (va.storage_class & STCmaybescope) != 0);
594 
595     // Determine if va is a parameter that is an indirect reference
596     const bool vaIsRef = va && va.storage_class & STC.parameter &&
597         (va.storage_class & (STC.ref_ | STC.out_) || va.type.toBasetype().ty == Tclass);
598     if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars());
599 
600     /* Determine if va is the first parameter, through which other 'return' parameters
601      * can be assigned.
602      */
603     bool isFirstRef()
604     {
605         if (!vaIsRef)
606             return false;
607         Dsymbol p = va.toParent2();
608         FuncDeclaration fd = sc.func;
609         if (p == fd && fd.type && fd.type.ty == Tfunction)
610         {
611             TypeFunction tf = cast(TypeFunction)fd.type;
612             if (!tf.nextOf() || (tf.nextOf().ty != Tvoid && !fd.isCtorDeclaration()))
613                 return false;
614             if (va == fd.vthis)
615                 return true;
616             if (fd.parameters && fd.parameters.dim && (*fd.parameters)[0] == va)
617                 return true;
618         }
619         return false;
620     }
621     const bool vaIsFirstRef = isFirstRef();
622     if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars());
623 
624     bool result = false;
625     foreach (VarDeclaration v; er.byvalue)
626     {
627         if (log) printf("byvalue: %s\n", v.toChars());
628         if (v.isDataseg())
629             continue;
630 
631         if (v == va)
632             continue;
633 
634         Dsymbol p = v.toParent2();
635 
636         if (va && !vaIsRef && !va.isScope() && !v.isScope() &&
637             (va.storage_class & v.storage_class & (STC.maybescope | STC.variadic)) == STC.maybescope &&
638             p == sc.func)
639         {
640             /* Add v to va's list of dependencies
641              */
642             va.addMaybe(v);
643             continue;
644         }
645 
646         if (vaIsFirstRef &&
647             (v.isScope() || (v.storage_class & STC.maybescope)) &&
648             !(v.storage_class & STC.return_) &&
649             v.isParameter() &&
650             sc.func.flags & FUNCFLAG.returnInprocess &&
651             p == sc.func)
652         {
653             if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), sc.func.toChars());
654             inferReturn(sc.func, v);        // infer addition of 'return'
655         }
656 
657         if (!(va && va.isScope()) || vaIsRef)
658             notMaybeScope(v);
659 
660         if (v.isScope())
661         {
662             if (vaIsFirstRef && v.isParameter() && v.storage_class & STC.return_)
663             {
664                 if (va.isScope())
665                     continue;
666 
667                 if (inferScope && !va.doNotInferScope)
668                 {
669                     if (log) printf("inferring scope for lvalue %s\n", va.toChars());
670                     va.storage_class |= STC.scope_ | STC.scopeinferred;
671                     continue;
672                 }
673             }
674 
675             if (va && va.isScope() && va.storage_class & STC.return_ && !(v.storage_class & STC.return_) &&
676                 sc.func.setUnsafe())
677             {
678                 if (!gag)
679                     error(ae.loc, "scope variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars());
680                 result = true;
681                 continue;
682             }
683 
684             // If va's lifetime encloses v's, then error
685             if (va &&
686                 (va.enclosesLifetimeOf(v) && !(v.storage_class & (STC.parameter | STC.temp)) ||
687                  // va is class reference
688                  ae.e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass && (va.enclosesLifetimeOf(v) || !va.isScope()) ||
689                  vaIsRef ||
690                  va.storage_class & (STC.ref_ | STC.out_) && !(v.storage_class & (STC.parameter | STC.temp))) &&
691                 sc.func.setUnsafe())
692             {
693                 if (!gag)
694                     error(ae.loc, "scope variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
695                 result = true;
696                 continue;
697             }
698 
699             if (va && !va.isDataseg() && !va.doNotInferScope)
700             {
701                 if (!va.isScope() && inferScope)
702                 {   //printf("inferring scope for %s\n", va.toChars());
703                     va.storage_class |= STC.scope_ | STC.scopeinferred;
704                     if (v.storage_class & STC.return_ &&
705                         !(va.storage_class & STC.return_))
706                     {
707                         va.storage_class |= STC.return_ | STC.returninferred;
708                     }
709                 }
710                 continue;
711             }
712             if (sc.func.setUnsafe())
713             {
714                 if (!gag)
715                     error(ae.loc, "scope variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
716                 result = true;
717             }
718         }
719         else if (v.storage_class & STC.variadic && p == sc.func)
720         {
721             Type tb = v.type.toBasetype();
722             if (tb.ty == Tarray || tb.ty == Tsarray)
723             {
724                 if (va && !va.isDataseg() && !va.doNotInferScope)
725                 {
726                     if (!va.isScope() && inferScope)
727                     {   //printf("inferring scope for %s\n", va.toChars());
728                         va.storage_class |= STC.scope_ | STC.scopeinferred;
729                     }
730                     continue;
731                 }
732                 if (sc.func.setUnsafe())
733                 {
734                     if (!gag)
735                         error(ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
736                     result = true;
737                 }
738             }
739         }
740         else
741         {
742             /* v is not 'scope', and we didn't check the scope of where we assigned it to.
743              * It may escape via that assignment, therefore, v can never be 'scope'.
744              */
745             //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
746             v.doNotInferScope = true;
747         }
748     }
749 
750 ByRef:
751     foreach (VarDeclaration v; er.byref)
752     {
753         if (log) printf("byref: %s\n", v.toChars());
754         if (v.isDataseg())
755             continue;
756 
757         if (global.params.vsafe)
758         {
759             if (va && va.isScope() && (v.storage_class & (STC.ref_ | STC.out_)) == 0)
760             {
761                 if (!(va.storage_class & STC.return_))
762                 {
763                     va.doNotInferReturn = true;
764                 }
765                 else if (sc.func.setUnsafe())
766                 {
767                     if (!gag)
768                         error(ae.loc, "address of local variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars());
769                     result = true;
770                     continue;
771                 }
772             }
773         }
774 
775         Dsymbol p = v.toParent2();
776 
777         // If va's lifetime encloses v's, then error
778         if (va &&
779             (va.enclosesLifetimeOf(v) && !(v.storage_class & STC.parameter) ||
780              va.storage_class & STC.ref_ ||
781              va.isDataseg()) &&
782             sc.func.setUnsafe())
783         {
784             if (!gag)
785                 error(ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
786             result = true;
787             continue;
788         }
789 
790         if (va && v.storage_class & (STC.ref_ | STC.out_))
791         {
792             Dsymbol pva = va.toParent2();
793             for (Dsymbol pv = p; pv; )
794             {
795                 pv = pv.toParent2();
796                 if (pva == pv)  // if v is nested inside pva
797                 {
798                     if (sc.func.setUnsafe())
799                     {
800                         if (!gag)
801                             error(ae.loc, "reference `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars());
802                         result = true;
803                         continue ByRef;
804                     }
805                     break;
806                 }
807             }
808         }
809 
810         if (!(va && va.isScope()))
811             notMaybeScope(v);
812 
813         if ((v.storage_class & (STC.ref_ | STC.out_)) || p != sc.func)
814             continue;
815 
816         if (va && !va.isDataseg() && !va.doNotInferScope)
817         {
818             if (!va.isScope() && inferScope)
819             {   //printf("inferring scope for %s\n", va.toChars());
820                 va.storage_class |= STC.scope_ | STC.scopeinferred;
821             }
822             continue;
823         }
824         if (e1.op == TOK.structLiteral)
825             continue;
826         if (sc.func.setUnsafe())
827         {
828             if (!gag)
829                 error(ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars());
830             result = true;
831         }
832     }
833 
834     foreach (FuncDeclaration fd; er.byfunc)
835     {
836         if (log) printf("byfunc: %s, %d\n", fd.toChars(), fd.tookAddressOf);
837         VarDeclarations vars;
838         findAllOuterAccessedVariables(fd, &vars);
839 
840         /* https://issues.dlang.org/show_bug.cgi?id=16037
841          * If assigning the address of a delegate to a scope variable,
842          * then uncount that address of. This is so it won't cause a
843          * closure to be allocated.
844          */
845         if (va && va.isScope() && fd.tookAddressOf)
846             --fd.tookAddressOf;
847 
848         foreach (v; vars)
849         {
850             //printf("v = %s\n", v.toChars());
851             assert(!v.isDataseg());     // these are not put in the closureVars[]
852 
853             Dsymbol p = v.toParent2();
854 
855             if (!(va && va.isScope()))
856                 notMaybeScope(v);
857 
858             if (!(v.storage_class & (STC.ref_ | STC.out_ | STC.scope_)) || p != sc.func)
859                 continue;
860 
861             if (va && !va.isDataseg() && !va.doNotInferScope)
862             {
863                 /* Don't infer STC.scope_ for va, because then a closure
864                  * won't be generated for sc.func.
865                  */
866                 //if (!va.isScope() && inferScope)
867                     //va.storage_class |= STC.scope_ | STC.scopeinferred;
868                 continue;
869             }
870             if (sc.func.setUnsafe())
871             {
872                 if (!gag)
873                     error(ae.loc, "reference to local `%s` assigned to non-scope `%s` in @safe code", v.toChars(), e1.toChars());
874                 result = true;
875             }
876         }
877     }
878 
879     foreach (Expression ee; er.byexp)
880     {
881         if (log) printf("byexp: %s\n", ee.toChars());
882 
883         /* Do not allow slicing of a static array returned by a function
884          */
885         if (va && ee.op == TOK.call && ee.type.toBasetype().ty == Tsarray && va.type.toBasetype().ty == Tarray &&
886             !(va.storage_class & STC.temp))
887         {
888             if (!gag)
889                 deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`",
890                     ee.toChars(), va.toChars());
891             //result = true;
892             continue;
893         }
894 
895         if (va && ee.op == TOK.call && ee.type.toBasetype().ty == Tstruct &&
896             !(va.storage_class & STC.temp) && va.ident != Id.withSym &&
897             sc.func.setUnsafe())
898         {
899             if (!gag)
900                 error(ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`",
901                     ee.toChars(), va.toChars());
902             result = true;
903             continue;
904         }
905 
906         if (va && ee.op == TOK.structLiteral &&
907             !(va.storage_class & STC.temp) && va.ident != Id.withSym &&
908             sc.func.setUnsafe())
909         {
910             if (!gag)
911                 error(ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`",
912                     ee.toChars(), va.toChars());
913             result = true;
914             continue;
915         }
916 
917         if (va && !va.isDataseg() && !va.doNotInferScope)
918         {
919             if (!va.isScope() && inferScope)
920             {   //printf("inferring scope for %s\n", va.toChars());
921                 va.storage_class |= STC.scope_ | STC.scopeinferred;
922             }
923             continue;
924         }
925 
926         if (sc.func.setUnsafe())
927         {
928             if (!gag)
929                 error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope `%s`",
930                     ee.toChars(), e1.toChars());
931             result = true;
932         }
933     }
934 
935     return result;
936 }
937 
938 /************************************
939  * Detect cases where pointers to the stack can escape the
940  * lifetime of the stack frame when throwing `e`.
941  * Print error messages when these are detected.
942  * Params:
943  *      sc = used to determine current function and module
944  *      e = expression to check for any pointers to the stack
945  *      gag = do not print error messages
946  * Returns:
947  *      `true` if pointers to the stack can escape
948  */
949 bool checkThrowEscape(Scope* sc, Expression e, bool gag)
950 {
951     //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars());
952     EscapeByResults er;
953 
954     escapeByValue(e, &er);
955 
956     if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
957         return false;
958 
959     bool result = false;
960     foreach (VarDeclaration v; er.byvalue)
961     {
962         //printf("byvalue %s\n", v.toChars());
963         if (v.isDataseg())
964             continue;
965 
966         if (v.isScope() && !v.iscatchvar)       // special case: allow catch var to be rethrown
967                                                 // despite being `scope`
968         {
969             if (sc._module && sc._module.isRoot())
970             {
971                 // Only look for errors if in module listed on command line
972                 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
973                 {
974                     if (!gag)
975                         error(e.loc, "scope variable `%s` may not be thrown", v.toChars());
976                     result = true;
977                 }
978                 continue;
979             }
980         }
981         else
982         {
983             //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
984             v.doNotInferScope = true;
985         }
986     }
987     return result;
988 }
989 
990 /************************************
991  * Detect cases where pointers to the stack can escape the
992  * lifetime of the stack frame by being placed into a GC allocated object.
993  * Print error messages when these are detected.
994  * Params:
995  *      sc = used to determine current function and module
996  *      e = expression to check for any pointers to the stack
997  *      gag = do not print error messages
998  * Returns:
999  *      `true` if pointers to the stack can escape
1000  */
1001 bool checkNewEscape(Scope* sc, Expression e, bool gag)
1002 {
1003     //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars());
1004     enum log = false;
1005     if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars());
1006     EscapeByResults er;
1007 
1008     escapeByValue(e, &er);
1009 
1010     if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
1011         return false;
1012 
1013     bool result = false;
1014     foreach (VarDeclaration v; er.byvalue)
1015     {
1016         if (log) printf("byvalue `%s`\n", v.toChars());
1017         if (v.isDataseg())
1018             continue;
1019 
1020         Dsymbol p = v.toParent2();
1021 
1022         if (v.isScope())
1023         {
1024             if (sc._module && sc._module.isRoot() &&
1025                 /* This case comes up when the ReturnStatement of a __foreachbody is
1026                  * checked for escapes by the caller of __foreachbody. Skip it.
1027                  *
1028                  * struct S { static int opApply(int delegate(S*) dg); }
1029                  * S* foo() {
1030                  *    foreach (S* s; S) // create __foreachbody for body of foreach
1031                  *        return s;     // s is inferred as 'scope' but incorrectly tested in foo()
1032                  *    return null; }
1033                  */
1034                 !(p.parent == sc.func))
1035             {
1036                 // Only look for errors if in module listed on command line
1037                 if (global.params.vsafe         // https://issues.dlang.org/show_bug.cgi?id=17029
1038                     && sc.func.setUnsafe())     // https://issues.dlang.org/show_bug.cgi?id=20868
1039                 {
1040                     if (!gag)
1041                         error(e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars());
1042                     result = true;
1043                 }
1044                 continue;
1045             }
1046         }
1047         else if (v.storage_class & STC.variadic && p == sc.func)
1048         {
1049             Type tb = v.type.toBasetype();
1050             if (tb.ty == Tarray || tb.ty == Tsarray)
1051             {
1052                 if (!gag)
1053                     error(e.loc, "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
1054                 result = false;
1055             }
1056         }
1057         else
1058         {
1059             //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1060             v.doNotInferScope = true;
1061         }
1062     }
1063 
1064     foreach (VarDeclaration v; er.byref)
1065     {
1066         if (log) printf("byref `%s`\n", v.toChars());
1067 
1068         // 'emitError' tells us whether to emit an error or a deprecation,
1069         // depending on the flag passed to the CLI for DIP25
1070         void escapingRef(VarDeclaration v, bool emitError = true)
1071         {
1072             if (!gag)
1073             {
1074                 const(char)* kind = (v.storage_class & STC.parameter) ? "parameter" : "local";
1075                 const(char)* msg = "copying `%s` into allocated memory escapes a reference to %s variable `%s`";
1076                 if (emitError)
1077                     error(e.loc, msg, e.toChars(), kind, v.toChars());
1078                 else if (!sc.isDeprecated())
1079                     deprecation(e.loc, msg, e.toChars(), kind, v.toChars());
1080             }
1081             result |= emitError;
1082         }
1083 
1084         if (v.isDataseg())
1085             continue;
1086 
1087         Dsymbol p = v.toParent2();
1088 
1089         if ((v.storage_class & (STC.ref_ | STC.out_)) == 0)
1090         {
1091             if (p == sc.func)
1092             {
1093                 escapingRef(v);
1094                 continue;
1095             }
1096         }
1097 
1098         /* Check for returning a ref variable by 'ref', but should be 'return ref'
1099          * Infer the addition of 'return', or set result to be the offending expression.
1100          */
1101         if (!(v.storage_class & (STC.ref_ | STC.out_)))
1102             continue;
1103 
1104         if (!sc._module || !sc._module.isRoot())
1105             continue;
1106 
1107         // If -preview=dip25 is used, the user wants an error
1108         // Otherwise, issue a deprecation
1109         const emitError = global.params.useDIP25 == FeatureState.enabled;
1110         // https://dlang.org/spec/function.html#return-ref-parameters
1111         // Only look for errors if in module listed on command line
1112         if (p == sc.func)
1113         {
1114             //printf("escaping reference to local ref variable %s\n", v.toChars());
1115             //printf("storage class = x%llx\n", v.storage_class);
1116             escapingRef(v, emitError);
1117             continue;
1118         }
1119         // Don't need to be concerned if v's parent does not return a ref
1120         FuncDeclaration fd = p.isFuncDeclaration();
1121         if (!fd || !fd.type)
1122             continue;
1123         if (auto tf = fd.type.isTypeFunction())
1124         {
1125             if (!tf.isref)
1126                 continue;
1127 
1128             const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape";
1129             if (!gag && emitError)
1130                 error(e.loc, msg, v.toChars());
1131             else if (!gag)
1132                 deprecation(e.loc, msg, v.toChars());
1133             result |= emitError;
1134         }
1135     }
1136 
1137     foreach (Expression ee; er.byexp)
1138     {
1139         if (log) printf("byexp %s\n", ee.toChars());
1140         if (!gag)
1141             error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape",
1142                   ee.toChars());
1143         result = true;
1144     }
1145 
1146     return result;
1147 }
1148 
1149 
1150 /************************************
1151  * Detect cases where pointers to the stack can escape the
1152  * lifetime of the stack frame by returning `e` by value.
1153  * Print error messages when these are detected.
1154  * Params:
1155  *      sc = used to determine current function and module
1156  *      e = expression to check for any pointers to the stack
1157  *      gag = do not print error messages
1158  * Returns:
1159  *      `true` if pointers to the stack can escape
1160  */
1161 bool checkReturnEscape(Scope* sc, Expression e, bool gag)
1162 {
1163     //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars());
1164     return checkReturnEscapeImpl(sc, e, false, gag);
1165 }
1166 
1167 /************************************
1168  * Detect cases where returning `e` by `ref` can result in a reference to the stack
1169  * being returned.
1170  * Print error messages when these are detected.
1171  * Params:
1172  *      sc = used to determine current function and module
1173  *      e = expression to check
1174  *      gag = do not print error messages
1175  * Returns:
1176  *      `true` if references to the stack can escape
1177  */
1178 bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag)
1179 {
1180     version (none)
1181     {
1182         printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars());
1183         printf("current function %s\n", sc.func.toChars());
1184         printf("parent2 function %s\n", sc.func.toParent2().toChars());
1185     }
1186 
1187     return checkReturnEscapeImpl(sc, e, true, gag);
1188 }
1189 
1190 /***************************************
1191  * Implementation of checking for escapes in return expressions.
1192  * Params:
1193  *      sc = used to determine current function and module
1194  *      e = expression to check
1195  *      refs = `true`: escape by value, `false`: escape by `ref`
1196  *      gag = do not print error messages
1197  * Returns:
1198  *      `true` if references to the stack can escape
1199  */
1200 private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
1201 {
1202     enum log = false;
1203     if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars());
1204     EscapeByResults er;
1205 
1206     if (refs)
1207         escapeByRef(e, &er);
1208     else
1209         escapeByValue(e, &er);
1210 
1211     if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
1212         return false;
1213 
1214     bool result = false;
1215     foreach (VarDeclaration v; er.byvalue)
1216     {
1217         if (log) printf("byvalue `%s`\n", v.toChars());
1218         if (v.isDataseg())
1219             continue;
1220 
1221         Dsymbol p = v.toParent2();
1222 
1223         if ((v.isScope() || (v.storage_class & STC.maybescope)) &&
1224             !(v.storage_class & STC.return_) &&
1225             v.isParameter() &&
1226             !v.doNotInferReturn &&
1227             sc.func.flags & FUNCFLAG.returnInprocess &&
1228             p == sc.func)
1229         {
1230             inferReturn(sc.func, v);        // infer addition of 'return'
1231             continue;
1232         }
1233 
1234         if (v.isScope())
1235         {
1236             if (v.storage_class & STC.return_)
1237                 continue;
1238 
1239             auto pfunc = p.isFuncDeclaration();
1240             if (pfunc && sc._module && sc._module.isRoot() &&
1241                 /* This case comes up when the ReturnStatement of a __foreachbody is
1242                  * checked for escapes by the caller of __foreachbody. Skip it.
1243                  *
1244                  * struct S { static int opApply(int delegate(S*) dg); }
1245                  * S* foo() {
1246                  *    foreach (S* s; S) // create __foreachbody for body of foreach
1247                  *        return s;     // s is inferred as 'scope' but incorrectly tested in foo()
1248                  *    return null; }
1249                  */
1250                 !(!refs && p.parent == sc.func && pfunc.fes) &&
1251                 /*
1252                  *  auto p(scope string s) {
1253                  *      string scfunc() { return s; }
1254                  *  }
1255                  */
1256                 !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0)
1257                )
1258             {
1259                 // Only look for errors if in module listed on command line
1260                 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029
1261                 {
1262                     if (!gag)
1263                         error(e.loc, "scope variable `%s` may not be returned", v.toChars());
1264                     result = true;
1265                 }
1266                 continue;
1267             }
1268         }
1269         else if (v.storage_class & STC.variadic && p == sc.func)
1270         {
1271             Type tb = v.type.toBasetype();
1272             if (tb.ty == Tarray || tb.ty == Tsarray)
1273             {
1274                 if (!gag)
1275                     error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
1276                 result = false;
1277             }
1278         }
1279         else
1280         {
1281             //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1282             v.doNotInferScope = true;
1283         }
1284     }
1285 
1286     foreach (VarDeclaration v; er.byref)
1287     {
1288         if (log) printf("byref `%s`\n", v.toChars());
1289 
1290         // 'emitError' tells us whether to emit an error or a deprecation,
1291         // depending on the flag passed to the CLI for DIP25
1292         void escapingRef(VarDeclaration v, bool emitError = true)
1293         {
1294             if (!gag)
1295             {
1296                 const(char)* msg, supplemental;
1297                 if (v.storage_class & STC.parameter &&
1298                     (v.type.hasPointers() || v.storage_class & STC.ref_))
1299                 {
1300                     msg = "returning `%s` escapes a reference to parameter `%s`";
1301                     supplemental = "perhaps annotate the parameter with `return`";
1302                 }
1303                 else
1304                 {
1305                     msg = "returning `%s` escapes a reference to local variable `%s`";
1306                     if (v.ident is Id.This)
1307                         supplemental = "perhaps annotate the function with `return`";
1308                 }
1309 
1310                 if (emitError)
1311                 {
1312                     e.error(msg, e.toChars(), v.toChars());
1313                     if (supplemental)
1314                         e.errorSupplemental(supplemental);
1315                 }
1316                 else
1317                 {
1318                     e.deprecation(msg, e.toChars(), v.toChars());
1319                     if (supplemental)
1320                         deprecationSupplemental(e.loc, supplemental);
1321                 }
1322             }
1323             result = true;
1324         }
1325 
1326         if (v.isDataseg())
1327             continue;
1328 
1329         Dsymbol p = v.toParent2();
1330 
1331         // https://issues.dlang.org/show_bug.cgi?id=19965
1332         if (!refs && sc.func.vthis == v)
1333             notMaybeScope(v);
1334 
1335         if ((v.storage_class & (STC.ref_ | STC.out_)) == 0)
1336         {
1337             if (p == sc.func)
1338             {
1339                 escapingRef(v);
1340                 continue;
1341             }
1342             FuncDeclaration fd = p.isFuncDeclaration();
1343             if (fd && sc.func.flags & FUNCFLAG.returnInprocess)
1344             {
1345                 /* Code like:
1346                  *   int x;
1347                  *   auto dg = () { return &x; }
1348                  * Making it:
1349                  *   auto dg = () return { return &x; }
1350                  * Because dg.ptr points to x, this is returning dt.ptr+offset
1351                  */
1352                 if (global.params.vsafe)
1353                 {
1354                     sc.func.storage_class |= STC.return_ | STC.returninferred;
1355                 }
1356             }
1357 
1358         }
1359 
1360         /* Check for returning a ref variable by 'ref', but should be 'return ref'
1361          * Infer the addition of 'return', or set result to be the offending expression.
1362          */
1363         if ( (v.storage_class & (STC.ref_ | STC.out_)) &&
1364             !(v.storage_class & (STC.return_ | STC.foreach_)))
1365         {
1366             if (sc.func.flags & FUNCFLAG.returnInprocess && p == sc.func)
1367             {
1368                 inferReturn(sc.func, v);        // infer addition of 'return'
1369             }
1370             else if (sc._module && sc._module.isRoot())
1371             {
1372                 // If -preview=dip25 is used, the user wants an error
1373                 // Otherwise, issue a deprecation
1374                 const emitError = global.params.useDIP25 == FeatureState.enabled;
1375                 // https://dlang.org/spec/function.html#return-ref-parameters
1376                 // Only look for errors if in module listed on command line
1377                 if (p == sc.func)
1378                 {
1379                     //printf("escaping reference to local ref variable %s\n", v.toChars());
1380                     //printf("storage class = x%llx\n", v.storage_class);
1381                     escapingRef(v, emitError);
1382                     continue;
1383                 }
1384                 // Don't need to be concerned if v's parent does not return a ref
1385                 FuncDeclaration fd = p.isFuncDeclaration();
1386                 if (fd && fd.type && fd.type.ty == Tfunction)
1387                 {
1388                     TypeFunction tf = cast(TypeFunction)fd.type;
1389                     if (tf.isref)
1390                     {
1391                         const(char)* msg = "escaping reference to outer local variable `%s`";
1392                         if (!gag && emitError)
1393                             error(e.loc, msg, v.toChars());
1394                         else if (!gag)
1395                             deprecation(e.loc, msg, v.toChars());
1396                         result = true;
1397                         continue;
1398                     }
1399                 }
1400 
1401             }
1402         }
1403     }
1404 
1405     foreach (Expression ee; er.byexp)
1406     {
1407         if (log) printf("byexp %s\n", ee.toChars());
1408         if (!gag)
1409             error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars());
1410         result = true;
1411     }
1412 
1413     return result;
1414 }
1415 
1416 
1417 /*************************************
1418  * Variable v needs to have 'return' inferred for it.
1419  * Params:
1420  *      fd = function that v is a parameter to
1421  *      v = parameter that needs to be STC.return_
1422  */
1423 
1424 private void inferReturn(FuncDeclaration fd, VarDeclaration v)
1425 {
1426     // v is a local in the current function
1427 
1428     //printf("for function '%s' inferring 'return' for variable '%s'\n", fd.toChars(), v.toChars());
1429     v.storage_class |= STC.return_ | STC.returninferred;
1430 
1431     TypeFunction tf = cast(TypeFunction)fd.type;
1432     if (v == fd.vthis)
1433     {
1434         /* v is the 'this' reference, so mark the function
1435          */
1436         fd.storage_class |= STC.return_ | STC.returninferred;
1437         if (tf.ty == Tfunction)
1438         {
1439             //printf("'this' too %p %s\n", tf, sc.func.toChars());
1440             tf.isreturn = true;
1441             tf.isreturninferred = true;
1442         }
1443     }
1444     else
1445     {
1446         // Perform 'return' inference on parameter
1447         if (tf.ty == Tfunction)
1448         {
1449             foreach (i, p; tf.parameterList)
1450             {
1451                 if (p.ident == v.ident)
1452                 {
1453                     p.storageClass |= STC.return_ | STC.returninferred;
1454                     break;              // there can be only one
1455                 }
1456             }
1457         }
1458     }
1459 }
1460 
1461 
1462 /****************************************
1463  * e is an expression to be returned by value, and that value contains pointers.
1464  * Walk e to determine which variables are possibly being
1465  * returned by value, such as:
1466  *      int* function(int* p) { return p; }
1467  * If e is a form of &p, determine which variables have content
1468  * which is being returned as ref, such as:
1469  *      int* function(int i) { return &i; }
1470  * Multiple variables can be inserted, because of expressions like this:
1471  *      int function(bool b, int i, int* p) { return b ? &i : p; }
1472  *
1473  * No side effects.
1474  *
1475  * Params:
1476  *      e = expression to be returned by value
1477  *      er = where to place collected data
1478  *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1479  */
1480 void escapeByValue(Expression e, EscapeByResults* er, bool live = false)
1481 {
1482     //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars());
1483     extern (C++) final class EscapeVisitor : Visitor
1484     {
1485         alias visit = Visitor.visit;
1486     public:
1487         EscapeByResults* er;
1488         bool live;
1489 
1490         extern (D) this(EscapeByResults* er, bool live)
1491         {
1492             this.er = er;
1493             this.live = live;
1494         }
1495 
1496         override void visit(Expression e)
1497         {
1498         }
1499 
1500         override void visit(AddrExp e)
1501         {
1502             /* Taking the address of struct literal is normally not
1503              * allowed, but CTFE can generate one out of a new expression,
1504              * but it'll be placed in static data so no need to check it.
1505              */
1506             if (e.e1.op != TOK.structLiteral)
1507                 escapeByRef(e.e1, er, live);
1508         }
1509 
1510         override void visit(SymOffExp e)
1511         {
1512             VarDeclaration v = e.var.isVarDeclaration();
1513             if (v)
1514                 er.byref.push(v);
1515         }
1516 
1517         override void visit(VarExp e)
1518         {
1519             if (auto v = e.var.isVarDeclaration())
1520             {
1521                 if (v.type.hasPointers() || // not tracking non-pointers
1522                     v.storage_class & STC.lazy_) // lazy variables are actually pointers
1523                     er.byvalue.push(v);
1524             }
1525         }
1526 
1527         override void visit(ThisExp e)
1528         {
1529             if (e.var)
1530                 er.byvalue.push(e.var);
1531         }
1532 
1533         override void visit(PtrExp e)
1534         {
1535             if (live && e.type.hasPointers())
1536                 e.e1.accept(this);
1537         }
1538 
1539         override void visit(DotVarExp e)
1540         {
1541             auto t = e.e1.type.toBasetype();
1542             if (e.type.hasPointers() && (live || t.ty == Tstruct))
1543             {
1544                 e.e1.accept(this);
1545             }
1546         }
1547 
1548         override void visit(DelegateExp e)
1549         {
1550             Type t = e.e1.type.toBasetype();
1551             if (t.ty == Tclass || t.ty == Tpointer)
1552                 escapeByValue(e.e1, er, live);
1553             else
1554                 escapeByRef(e.e1, er, live);
1555             er.byfunc.push(e.func);
1556         }
1557 
1558         override void visit(FuncExp e)
1559         {
1560             if (e.fd.tok == TOK.delegate_)
1561                 er.byfunc.push(e.fd);
1562         }
1563 
1564         override void visit(TupleExp e)
1565         {
1566             assert(0); // should have been lowered by now
1567         }
1568 
1569         override void visit(ArrayLiteralExp e)
1570         {
1571             Type tb = e.type.toBasetype();
1572             if (tb.ty == Tsarray || tb.ty == Tarray)
1573             {
1574                 if (e.basis)
1575                     e.basis.accept(this);
1576                 foreach (el; *e.elements)
1577                 {
1578                     if (el)
1579                         el.accept(this);
1580                 }
1581             }
1582         }
1583 
1584         override void visit(StructLiteralExp e)
1585         {
1586             if (e.elements)
1587             {
1588                 foreach (ex; *e.elements)
1589                 {
1590                     if (ex)
1591                         ex.accept(this);
1592                 }
1593             }
1594         }
1595 
1596         override void visit(NewExp e)
1597         {
1598             Type tb = e.newtype.toBasetype();
1599             if (tb.ty == Tstruct && !e.member && e.arguments)
1600             {
1601                 foreach (ex; *e.arguments)
1602                 {
1603                     if (ex)
1604                         ex.accept(this);
1605                 }
1606             }
1607         }
1608 
1609         override void visit(CastExp e)
1610         {
1611             if (!e.type.hasPointers())
1612                 return;
1613             Type tb = e.type.toBasetype();
1614             if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray)
1615             {
1616                 escapeByRef(e.e1, er, live);
1617             }
1618             else
1619                 e.e1.accept(this);
1620         }
1621 
1622         override void visit(SliceExp e)
1623         {
1624             if (e.e1.op == TOK.variable)
1625             {
1626                 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
1627                 Type tb = e.type.toBasetype();
1628                 if (v)
1629                 {
1630                     if (tb.ty == Tsarray)
1631                         return;
1632                     if (v.storage_class & STC.variadic)
1633                     {
1634                         er.byvalue.push(v);
1635                         return;
1636                     }
1637                 }
1638             }
1639             Type t1b = e.e1.type.toBasetype();
1640             if (t1b.ty == Tsarray)
1641             {
1642                 Type tb = e.type.toBasetype();
1643                 if (tb.ty != Tsarray)
1644                     escapeByRef(e.e1, er, live);
1645             }
1646             else
1647                 e.e1.accept(this);
1648         }
1649 
1650         override void visit(IndexExp e)
1651         {
1652             if (e.e1.type.toBasetype().ty == Tsarray ||
1653                 live && e.type.hasPointers())
1654             {
1655                 e.e1.accept(this);
1656             }
1657         }
1658 
1659         override void visit(BinExp e)
1660         {
1661             Type tb = e.type.toBasetype();
1662             if (tb.ty == Tpointer)
1663             {
1664                 e.e1.accept(this);
1665                 e.e2.accept(this);
1666             }
1667         }
1668 
1669         override void visit(BinAssignExp e)
1670         {
1671             e.e1.accept(this);
1672         }
1673 
1674         override void visit(AssignExp e)
1675         {
1676             e.e1.accept(this);
1677         }
1678 
1679         override void visit(CommaExp e)
1680         {
1681             e.e2.accept(this);
1682         }
1683 
1684         override void visit(CondExp e)
1685         {
1686             e.e1.accept(this);
1687             e.e2.accept(this);
1688         }
1689 
1690         override void visit(CallExp e)
1691         {
1692             //printf("CallExp(): %s\n", e.toChars());
1693             /* Check each argument that is
1694              * passed as 'return scope'.
1695              */
1696             Type t1 = e.e1.type.toBasetype();
1697             TypeFunction tf;
1698             TypeDelegate dg;
1699             if (t1.ty == Tdelegate)
1700             {
1701                 dg = cast(TypeDelegate)t1;
1702                 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next;
1703             }
1704             else if (t1.ty == Tfunction)
1705                 tf = cast(TypeFunction)t1;
1706             else
1707                 return;
1708 
1709             if (!e.type.hasPointers())
1710                 return;
1711 
1712             if (e.arguments && e.arguments.dim)
1713             {
1714                 /* j=1 if _arguments[] is first argument,
1715                  * skip it because it is not passed by ref
1716                  */
1717                 int j = tf.isDstyleVariadic();
1718                 for (size_t i = j; i < e.arguments.dim; ++i)
1719                 {
1720                     Expression arg = (*e.arguments)[i];
1721                     size_t nparams = tf.parameterList.length;
1722                     if (i - j < nparams && i >= j)
1723                     {
1724                         Parameter p = tf.parameterList[i - j];
1725                         const stc = tf.parameterStorageClass(null, p);
1726                         if ((stc & (STC.scope_)) && (stc & STC.return_))
1727                             arg.accept(this);
1728                         else if ((stc & (STC.ref_)) && (stc & STC.return_))
1729                         {
1730                             if (tf.isref)
1731                             {
1732                                 /* Treat:
1733                                  *   ref P foo(return ref P p)
1734                                  * as:
1735                                  *   p;
1736                                  */
1737                                 arg.accept(this);
1738                             }
1739                             else
1740                                 escapeByRef(arg, er, live);
1741                         }
1742                     }
1743                 }
1744             }
1745             // If 'this' is returned, check it too
1746             if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
1747             {
1748                 DotVarExp dve = cast(DotVarExp)e.e1;
1749                 FuncDeclaration fd = dve.var.isFuncDeclaration();
1750                 AggregateDeclaration ad;
1751                 if (global.params.vsafe && tf.isreturn && fd && (ad = fd.isThis()) !is null)
1752                 {
1753                     if (ad.isClassDeclaration() || tf.isScopeQual)       // this is 'return scope'
1754                         dve.e1.accept(this);
1755                     else if (ad.isStructDeclaration()) // this is 'return ref'
1756                     {
1757                         if (tf.isref)
1758                         {
1759                             /* Treat calling:
1760                              *   struct S { ref S foo() return; }
1761                              * as:
1762                              *   this;
1763                              */
1764                             dve.e1.accept(this);
1765                         }
1766                         else
1767                             escapeByRef(dve.e1, er, live);
1768                     }
1769                 }
1770                 else if (dve.var.storage_class & STC.return_ || tf.isreturn)
1771                 {
1772                     if (dve.var.storage_class & STC.scope_)
1773                         dve.e1.accept(this);
1774                     else if (dve.var.storage_class & STC.ref_)
1775                         escapeByRef(dve.e1, er, live);
1776                 }
1777                 // If it's also a nested function that is 'return scope'
1778                 if (fd && fd.isNested())
1779                 {
1780                     if (tf.isreturn && tf.isScopeQual)
1781                         er.byexp.push(e);
1782                 }
1783             }
1784 
1785             /* If returning the result of a delegate call, the .ptr
1786              * field of the delegate must be checked.
1787              */
1788             if (dg)
1789             {
1790                 if (tf.isreturn)
1791                     e.e1.accept(this);
1792             }
1793 
1794             /* If it's a nested function that is 'return scope'
1795              */
1796             if (e.e1.op == TOK.variable)
1797             {
1798                 VarExp ve = cast(VarExp)e.e1;
1799                 FuncDeclaration fd = ve.var.isFuncDeclaration();
1800                 if (fd && fd.isNested())
1801                 {
1802                     if (tf.isreturn && tf.isScopeQual)
1803                         er.byexp.push(e);
1804                 }
1805             }
1806         }
1807     }
1808 
1809     scope EscapeVisitor v = new EscapeVisitor(er, live);
1810     e.accept(v);
1811 }
1812 
1813 
1814 /****************************************
1815  * e is an expression to be returned by 'ref'.
1816  * Walk e to determine which variables are possibly being
1817  * returned by ref, such as:
1818  *      ref int function(int i) { return i; }
1819  * If e is a form of *p, determine which variables have content
1820  * which is being returned as ref, such as:
1821  *      ref int function(int* p) { return *p; }
1822  * Multiple variables can be inserted, because of expressions like this:
1823  *      ref int function(bool b, int i, int* p) { return b ? i : *p; }
1824  *
1825  * No side effects.
1826  *
1827  * Params:
1828  *      e = expression to be returned by 'ref'
1829  *      er = where to place collected data
1830  *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1831  */
1832 void escapeByRef(Expression e, EscapeByResults* er, bool live = false)
1833 {
1834     //printf("[%s] escapeByRef, e: %s\n", e.loc.toChars(), e.toChars());
1835     extern (C++) final class EscapeRefVisitor : Visitor
1836     {
1837         alias visit = Visitor.visit;
1838     public:
1839         EscapeByResults* er;
1840         bool live;
1841 
1842         extern (D) this(EscapeByResults* er, bool live)
1843         {
1844             this.er = er;
1845             this.live = live;
1846         }
1847 
1848         override void visit(Expression e)
1849         {
1850         }
1851 
1852         override void visit(VarExp e)
1853         {
1854             auto v = e.var.isVarDeclaration();
1855             if (v)
1856             {
1857                 if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init)
1858                 {
1859                     /* If compiler generated ref temporary
1860                      *   (ref v = ex; ex)
1861                      * look at the initializer instead
1862                      */
1863                     if (ExpInitializer ez = v._init.isExpInitializer())
1864                     {
1865                         assert(ez.exp && ez.exp.op == TOK.construct);
1866                         Expression ex = (cast(ConstructExp)ez.exp).e2;
1867                         ex.accept(this);
1868                     }
1869                 }
1870                 else
1871                     er.byref.push(v);
1872             }
1873         }
1874 
1875         override void visit(ThisExp e)
1876         {
1877             if (e.var && e.var.toParent2().isFuncDeclaration().isThis2)
1878                 escapeByValue(e, er, live);
1879             else if (e.var)
1880                 er.byref.push(e.var);
1881         }
1882 
1883         override void visit(PtrExp e)
1884         {
1885             escapeByValue(e.e1, er, live);
1886         }
1887 
1888         override void visit(IndexExp e)
1889         {
1890             Type tb = e.e1.type.toBasetype();
1891             if (e.e1.op == TOK.variable)
1892             {
1893                 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration();
1894                 if (tb.ty == Tarray || tb.ty == Tsarray)
1895                 {
1896                     if (v && v.storage_class & STC.variadic)
1897                     {
1898                         er.byref.push(v);
1899                         return;
1900                     }
1901                 }
1902             }
1903             if (tb.ty == Tsarray)
1904             {
1905                 e.e1.accept(this);
1906             }
1907             else if (tb.ty == Tarray)
1908             {
1909                 escapeByValue(e.e1, er, live);
1910             }
1911         }
1912 
1913         override void visit(StructLiteralExp e)
1914         {
1915             if (e.elements)
1916             {
1917                 foreach (ex; *e.elements)
1918                 {
1919                     if (ex)
1920                         ex.accept(this);
1921                 }
1922             }
1923             er.byexp.push(e);
1924         }
1925 
1926         override void visit(DotVarExp e)
1927         {
1928             Type t1b = e.e1.type.toBasetype();
1929             if (t1b.ty == Tclass)
1930                 escapeByValue(e.e1, er, live);
1931             else
1932                 e.e1.accept(this);
1933         }
1934 
1935         override void visit(BinAssignExp e)
1936         {
1937             e.e1.accept(this);
1938         }
1939 
1940         override void visit(AssignExp e)
1941         {
1942             e.e1.accept(this);
1943         }
1944 
1945         override void visit(CommaExp e)
1946         {
1947             e.e2.accept(this);
1948         }
1949 
1950         override void visit(CondExp e)
1951         {
1952             e.e1.accept(this);
1953             e.e2.accept(this);
1954         }
1955 
1956         override void visit(CallExp e)
1957         {
1958             //printf("escapeByRef.CallExp(): %s\n", e.toChars());
1959             /* If the function returns by ref, check each argument that is
1960              * passed as 'return ref'.
1961              */
1962             Type t1 = e.e1.type.toBasetype();
1963             TypeFunction tf;
1964             if (t1.ty == Tdelegate)
1965                 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next;
1966             else if (t1.ty == Tfunction)
1967                 tf = cast(TypeFunction)t1;
1968             else
1969                 return;
1970             if (tf.isref)
1971             {
1972                 if (e.arguments && e.arguments.dim)
1973                 {
1974                     /* j=1 if _arguments[] is first argument,
1975                      * skip it because it is not passed by ref
1976                      */
1977                     int j = tf.isDstyleVariadic();
1978                     for (size_t i = j; i < e.arguments.dim; ++i)
1979                     {
1980                         Expression arg = (*e.arguments)[i];
1981                         size_t nparams = tf.parameterList.length;
1982                         if (i - j < nparams && i >= j)
1983                         {
1984                             Parameter p = tf.parameterList[i - j];
1985                             const stc = tf.parameterStorageClass(null, p);
1986                             if ((stc & (STC.out_ | STC.ref_)) && (stc & STC.return_))
1987                                 arg.accept(this);
1988                             else if ((stc & STC.scope_) && (stc & STC.return_))
1989                             {
1990                                 if (arg.op == TOK.delegate_)
1991                                 {
1992                                     DelegateExp de = cast(DelegateExp)arg;
1993                                     if (de.func.isNested())
1994                                         er.byexp.push(de);
1995                                 }
1996                                 else
1997                                     escapeByValue(arg, er, live);
1998                             }
1999                         }
2000                     }
2001                 }
2002                 // If 'this' is returned by ref, check it too
2003                 if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction)
2004                 {
2005                     DotVarExp dve = cast(DotVarExp)e.e1;
2006 
2007                     // https://issues.dlang.org/show_bug.cgi?id=20149#c10
2008                     if (dve.var.isCtorDeclaration())
2009                     {
2010                         er.byexp.push(e);
2011                         return;
2012                     }
2013 
2014                     if (dve.var.storage_class & STC.return_ || tf.isreturn)
2015                     {
2016                         if (dve.var.storage_class & STC.ref_ || tf.isref)
2017                             dve.e1.accept(this);
2018                         else if (dve.var.storage_class & STC.scope_ || tf.isScopeQual)
2019                             escapeByValue(dve.e1, er, live);
2020                     }
2021                     // If it's also a nested function that is 'return ref'
2022                     FuncDeclaration fd = dve.var.isFuncDeclaration();
2023                     if (fd && fd.isNested())
2024                     {
2025                         if (tf.isreturn)
2026                             er.byexp.push(e);
2027                     }
2028                 }
2029                 // If it's a delegate, check it too
2030                 if (e.e1.op == TOK.variable && t1.ty == Tdelegate)
2031                 {
2032                     escapeByValue(e.e1, er, live);
2033                 }
2034 
2035                 /* If it's a nested function that is 'return ref'
2036                  */
2037                 if (e.e1.op == TOK.variable)
2038                 {
2039                     VarExp ve = cast(VarExp)e.e1;
2040                     FuncDeclaration fd = ve.var.isFuncDeclaration();
2041                     if (fd && fd.isNested())
2042                     {
2043                         if (tf.isreturn)
2044                             er.byexp.push(e);
2045                     }
2046                 }
2047             }
2048             else
2049                 er.byexp.push(e);
2050         }
2051     }
2052 
2053     scope EscapeRefVisitor v = new EscapeRefVisitor(er, live);
2054     e.accept(v);
2055 }
2056 
2057 
2058 /************************************
2059  * Aggregate the data collected by the escapeBy??() functions.
2060  */
2061 struct EscapeByResults
2062 {
2063     VarDeclarations byref;      // array into which variables being returned by ref are inserted
2064     VarDeclarations byvalue;    // array into which variables with values containing pointers are inserted
2065     FuncDeclarations byfunc;    // nested functions that are turned into delegates
2066     Expressions byexp;          // array into which temporaries being returned by ref are inserted
2067 
2068     /** Reset arrays so the storage can be used again
2069      */
2070     void reset()
2071     {
2072         byref.setDim(0);
2073         byvalue.setDim(0);
2074         byfunc.setDim(0);
2075         byexp.setDim(0);
2076     }
2077 }
2078 
2079 /*************************
2080  * Find all variables accessed by this delegate that are
2081  * in functions enclosing it.
2082  * Params:
2083  *      fd = function
2084  *      vars = array to append found variables to
2085  */
2086 public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars)
2087 {
2088     //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
2089     for (auto p = fd.parent; p; p = p.parent)
2090     {
2091         auto fdp = p.isFuncDeclaration();
2092         if (!fdp)
2093             continue;
2094 
2095         foreach (v; fdp.closureVars)
2096         {
2097             foreach (const fdv; v.nestedrefs)
2098             {
2099                 if (fdv == fd)
2100                 {
2101                     //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
2102                     vars.push(v);
2103                 }
2104             }
2105         }
2106     }
2107 }
2108 
2109 /***********************************
2110  * Turn off `STC.maybescope` for variable `v`.
2111  *
2112  * This exists in order to find where `STC.maybescope` is getting turned off.
2113  * Params:
2114  *      v = variable
2115  */
2116 version (none)
2117 {
2118     public void notMaybeScope(string file = __FILE__, int line = __LINE__)(VarDeclaration v)
2119     {
2120         printf("%.*s(%d): notMaybeScope('%s')\n", cast(int)file.length, file.ptr, line, v.toChars());
2121         v.storage_class &= ~STC.maybescope;
2122     }
2123 }
2124 else
2125 {
2126     public void notMaybeScope(VarDeclaration v)
2127     {
2128         v.storage_class &= ~STC.maybescope;
2129     }
2130 }
2131 
2132 
2133 /**********************************************
2134  * Have some variables that are maybescopes that were
2135  * assigned values from other maybescope variables.
2136  * Now that semantic analysis of the function is
2137  * complete, we can finalize this by turning off
2138  * maybescope for array elements that cannot be scope.
2139  *
2140  * $(TABLE2 Scope Table,
2141  * $(THEAD `va`, `v`,    =>,  `va` ,  `v`  )
2142  * $(TROW maybe, maybe,  =>,  scope,  scope)
2143  * $(TROW scope, scope,  =>,  scope,  scope)
2144  * $(TROW scope, maybe,  =>,  scope,  scope)
2145  * $(TROW maybe, scope,  =>,  scope,  scope)
2146  * $(TROW -    , -    ,  =>,  -    ,  -    )
2147  * $(TROW -    , maybe,  =>,  -    ,  -    )
2148  * $(TROW -    , scope,  =>,  error,  error)
2149  * $(TROW maybe, -    ,  =>,  scope,  -    )
2150  * $(TROW scope, -    ,  =>,  scope,  -    )
2151  * )
2152  * Params:
2153  *      array = array of variables that were assigned to from maybescope variables
2154  */
2155 public void eliminateMaybeScopes(VarDeclaration[] array)
2156 {
2157     enum log = false;
2158     if (log) printf("eliminateMaybeScopes()\n");
2159     bool changes;
2160     do
2161     {
2162         changes = false;
2163         foreach (va; array)
2164         {
2165             if (log) printf("  va = %s\n", va.toChars());
2166             if (!(va.storage_class & (STC.maybescope | STC.scope_)))
2167             {
2168                 if (va.maybes)
2169                 {
2170                     foreach (v; *va.maybes)
2171                     {
2172                         if (log) printf("    v = %s\n", v.toChars());
2173                         if (v.storage_class & STC.maybescope)
2174                         {
2175                             // v cannot be scope since it is assigned to a non-scope va
2176                             notMaybeScope(v);
2177                             if (!(v.storage_class & (STC.ref_ | STC.out_)))
2178                                 v.storage_class &= ~(STC.return_ | STC.returninferred);
2179                             changes = true;
2180                         }
2181                     }
2182                 }
2183             }
2184         }
2185     } while (changes);
2186 }
2187 
2188 /************************************************
2189  * Is type a reference to a mutable value?
2190  *
2191  * This is used to determine if an argument that does not have a corresponding
2192  * Parameter, i.e. a variadic argument, is a pointer to mutable data.
2193  * Params:
2194  *      t = type of the argument
2195  * Returns:
2196  *      true if it's a pointer (or reference) to mutable data
2197  */
2198 bool isReferenceToMutable(Type t)
2199 {
2200     t = t.baseElemOf();
2201 
2202     if (!t.isMutable() ||
2203         !t.hasPointers())
2204         return false;
2205 
2206     switch (t.ty)
2207     {
2208         case Tpointer:
2209             if (t.nextOf().isTypeFunction())
2210                 break;
2211             goto case;
2212 
2213         case Tarray:
2214         case Taarray:
2215         case Tdelegate:
2216             if (t.nextOf().isMutable())
2217                 return true;
2218             break;
2219 
2220         case Tclass:
2221             return true;        // even if the class fields are not mutable
2222 
2223         case Tstruct:
2224             // Have to look at each field
2225             foreach (VarDeclaration v; t.isTypeStruct().sym.fields)
2226             {
2227                 if (v.storage_class & STC.ref_)
2228                 {
2229                     if (v.type.isMutable())
2230                         return true;
2231                 }
2232                 else if (v.type.isReferenceToMutable())
2233                     return true;
2234             }
2235             break;
2236 
2237         default:
2238             assert(0);
2239     }
2240     return false;
2241 }
2242 
2243 /****************************************
2244  * Is parameter a reference to a mutable value?
2245  *
2246  * This is used if an argument has a corresponding Parameter.
2247  * The argument type is necessary if the Parameter is inout.
2248  * Params:
2249  *      p = Parameter to check
2250  *      t = type of corresponding argument
2251  * Returns:
2252  *      true if it's a pointer (or reference) to mutable data
2253  */
2254 bool isReferenceToMutable(Parameter p, Type t)
2255 {
2256     if (p.isReference())
2257     {
2258         if (p.type.isConst() || p.type.isImmutable())
2259             return false;
2260         if (p.type.isWild())
2261         {
2262             return t.isMutable();
2263         }
2264         return p.type.isMutable();
2265     }
2266     return isReferenceToMutable(p.type);
2267 }