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