1 /**
2  * Most of the logic to implement scoped pointers and scoped references is here.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://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.astenums;
22 import dmd.declaration;
23 import dmd.dscope;
24 import dmd.dsymbol;
25 import dmd.errors;
26 import dmd.expression;
27 import dmd.func;
28 import dmd.globals;
29 import dmd.id;
30 import dmd.identifier;
31 import dmd.init;
32 import dmd.location;
33 import dmd.mtype;
34 import dmd.printast;
35 import dmd.rootobject;
36 import dmd.tokens;
37 import dmd.visitor;
38 import dmd.arraytypes;
39 
40 private:
41 
42 /// Groups global state for escape checking together
43 package(dmd) struct EscapeState
44 {
45     // Maps `sequenceNumber` of a `VarDeclaration` to an object that contains the
46     // reason it failed to infer `scope`
47     // https://issues.dlang.org/show_bug.cgi?id=23295
48     private __gshared RootObject[int] scopeInferFailure;
49 
50     /// Called by `initDMD` / `deinitializeDMD` to reset global state
51     static void reset()
52     {
53         scopeInferFailure = null;
54     }
55 }
56 
57 /******************************************************
58  * Checks memory objects passed to a function.
59  * Checks that if a memory object is passed by ref or by pointer,
60  * all of the refs or pointers are const, or there is only one mutable
61  * ref or pointer to it.
62  * References:
63  *      DIP 1021
64  * Params:
65  *      sc = used to determine current function and module
66  *      fd = function being called
67  *      tf = fd's type
68  *      ethis = if not null, the `this` pointer
69  *      arguments = actual arguments to function
70  *      gag = do not print error messages
71  * Returns:
72  *      `true` if error
73  */
74 public
75 bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf,
76     Expression ethis, Expressions* arguments, bool gag)
77 {
78     enum log = false;
79     if (log) printf("[%s] checkMutableArguments, fd: `%s`\n", fd.loc.toChars(), fd.toChars());
80     if (log && ethis) printf("ethis: `%s`\n", ethis.toChars());
81     bool errors = false;
82 
83     /* Outer variable references are treated as if they are extra arguments
84      * passed by ref to the function (which they essentially are via the static link).
85      */
86     VarDeclaration[] outerVars = fd ? fd.outerVars[] : null;
87 
88     const len = arguments.length + (ethis !is null) + outerVars.length;
89     if (len <= 1)
90         return errors;
91 
92     struct EscapeBy
93     {
94         EscapeByResults er;
95         Parameter param;        // null if no Parameter for this argument
96         bool isMutable;         // true if reference to mutable
97     }
98 
99     auto escapeBy = new EscapeBy[len];
100     const paramLength = tf.parameterList.length;
101 
102     // Fill in escapeBy[] with arguments[], ethis, and outerVars[]
103     foreach (const i, ref eb; escapeBy)
104     {
105         bool refs;
106         Expression arg;
107         if (i < arguments.length)
108         {
109             arg = (*arguments)[i];
110             if (i < paramLength)
111             {
112                 eb.param = tf.parameterList[i];
113                 refs = eb.param.isReference();
114                 eb.isMutable = eb.param.isReferenceToMutable(arg.type);
115             }
116             else
117             {
118                 eb.param = null;
119                 refs = false;
120                 eb.isMutable = arg.type.isReferenceToMutable();
121             }
122         }
123         else if (ethis)
124         {
125             /* ethis is passed by value if a class reference,
126              * by ref if a struct value
127              */
128             eb.param = null;
129             arg = ethis;
130             auto ad = fd.isThis();
131             assert(ad);
132             assert(ethis);
133             if (ad.isClassDeclaration())
134             {
135                 refs = false;
136                 eb.isMutable = arg.type.isReferenceToMutable();
137             }
138             else
139             {
140                 assert(ad.isStructDeclaration());
141                 refs = true;
142                 eb.isMutable = arg.type.isMutable();
143             }
144         }
145         else
146         {
147             // outer variables are passed by ref
148             eb.param = null;
149             refs = true;
150             auto var = outerVars[i - (len - outerVars.length)];
151             eb.isMutable = var.type.isMutable();
152             eb.er.pushRef(var, false);
153             continue;
154         }
155 
156         if (refs)
157             escapeByRef(arg, &eb.er);
158         else
159             escapeByValue(arg, &eb.er);
160     }
161 
162     void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2,
163                       VarDeclaration v, VarDeclaration v2, bool of)
164     {
165         if (log) printf("v2: `%s`\n", v2.toChars());
166         if (v2 != v)
167             return;
168         //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable);
169         if (!(eb.isMutable || eb2.isMutable))
170             return;
171 
172         if (!tf.islive && !(global.params.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe()))
173             return;
174 
175         if (!gag)
176         {
177             // int i; funcThatEscapes(ref int i);
178             // funcThatEscapes(i); // error escaping reference _to_ `i`
179             // int* j; funcThatEscapes2(int* j);
180             // funcThatEscapes2(j); // error escaping reference _of_ `i`
181             const(char)* referenceVerb = of ? "of" : "to";
182             const(char)* msg = eb.isMutable && eb2.isMutable
183                                 ? "more than one mutable reference %s `%s` in arguments to `%s()`"
184                                 : "mutable and const references %s `%s` in arguments to `%s()`";
185             sc.eSink.error((*arguments)[i].loc, msg,
186                   referenceVerb,
187                   v.toChars(),
188                   fd ? fd.toPrettyChars() : "indirectly");
189         }
190         errors = true;
191     }
192 
193     void escape(size_t i, ref EscapeBy eb, bool byval)
194     {
195         foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref)
196         {
197             if (log)
198             {
199                 const(char)* by = byval ? "byval" : "byref";
200                 printf("%s %s\n", by, v.toChars());
201             }
202             if (byval && !v.type.hasPointers())
203                 continue;
204             foreach (ref eb2; escapeBy[i + 1 .. $])
205             {
206                 foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref)
207                 {
208                     checkOnePair(i, eb, eb2, v, v2, byval);
209                 }
210             }
211         }
212     }
213     foreach (const i, ref eb; escapeBy[0 .. $ - 1])
214     {
215         escape(i, eb, true);
216         escape(i, eb, false);
217     }
218 
219     return errors;
220 }
221 
222 /******************************************
223  * Array literal is going to be allocated on the GC heap.
224  * Check its elements to see if any would escape by going on the heap.
225  * Params:
226  *      sc = used to determine current function and module
227  *      ae = array literal expression
228  *      gag = do not print error messages
229  * Returns:
230  *      `true` if any elements escaped
231  */
232 public
233 bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag)
234 {
235     bool errors;
236     if (ae.basis)
237         errors = checkNewEscape(sc, ae.basis, gag);
238     foreach (ex; *ae.elements)
239     {
240         if (ex)
241             errors |= checkNewEscape(sc, ex, gag);
242     }
243     return errors;
244 }
245 
246 /******************************************
247  * Associative array literal is going to be allocated on the GC heap.
248  * Check its elements to see if any would escape by going on the heap.
249  * Params:
250  *      sc = used to determine current function and module
251  *      ae = associative array literal expression
252  *      gag = do not print error messages
253  * Returns:
254  *      `true` if any elements escaped
255  */
256 public
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  * A `scope` variable was assigned to non-scope parameter `v`.
275  * If applicable, print why the parameter was not inferred `scope`.
276  *
277  * Params:
278  *    printFunc = error/deprecation print function to use
279  *    v = parameter that was not inferred
280  *    recursionLimit = recursion limit for printing the reason
281  */
282 private
283 void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit)
284 {
285     recursionLimit--;
286     if (recursionLimit < 0 || !v)
287         return;
288 
289     if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferFailure)
290     {
291         switch ((*o).dyncast())
292         {
293             case DYNCAST.expression:
294                 Expression e = cast(Expression) *o;
295                 printFunc(e.loc, "which is not `scope` because of `%s`", e.toChars());
296                 break;
297             case DYNCAST.dsymbol:
298                 VarDeclaration v1 = cast(VarDeclaration) *o;
299                 printFunc(v1.loc, "which is assigned to non-scope parameter `%s`", v1.toChars());
300                 printScopeFailure(printFunc, v1, recursionLimit);
301                 break;
302             default:
303                 assert(0);
304         }
305     }
306 }
307 
308 /****************************************
309  * Function parameter `par` is being initialized to `arg`,
310  * and `par` may escape.
311  * Detect if scoped values can escape this way.
312  * Print error messages when these are detected.
313  * Params:
314  *      sc = used to determine current function and module
315  *      fdc = function being called, `null` if called indirectly
316  *      parId = name of function parameter for error messages
317  *      vPar = `VarDeclaration` corresponding to `par`
318  *      parStc = storage classes of function parameter (may have added `scope` from `pure`)
319  *      arg = initializer for param
320  *      assertmsg = true if the parameter is the msg argument to assert(bool, msg).
321  *      gag = do not print error messages
322  * Returns:
323  *      `true` if pointers to the stack can escape via assignment
324  */
325 public
326 bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag)
327 {
328     enum log = false;
329     if (log) printf("checkParamArgumentEscape(arg: %s par: %s parSTC: %llx)\n",
330         arg ? arg.toChars() : "null",
331         parId ? parId.toChars() : "null", parStc);
332     //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
333 
334     if (!arg.type.hasPointers())
335         return false;
336 
337     EscapeByResults er;
338 
339     escapeByValue(arg, &er);
340 
341     if (parStc & STC.scope_)
342     {
343         // These errors only apply to non-scope parameters
344         // When the parameter is `scope`, only `checkScopeVarAddr` on `er.byref` is needed
345         er.byfunc.setDim(0);
346         er.byvalue.setDim(0);
347         er.byexp.setDim(0);
348     }
349 
350     if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length)
351         return false;
352 
353     bool result = false;
354 
355     /* 'v' is assigned unsafely to 'par'
356      */
357     void unsafeAssign(string desc)(VarDeclaration v)
358     {
359         if (assertmsg)
360         {
361             result |= sc.setUnsafeDIP1000(gag, arg.loc,
362                 desc ~ " `%s` assigned to non-scope parameter calling `assert()`", v);
363             return;
364         }
365 
366         bool isThis = fdc && fdc.needThis() && fdc.vthis == vPar; // implicit `this` parameter to member function
367 
368         const(char)* msg =
369             (isThis)        ? (desc ~ " `%s` calling non-scope member function `%s.%s()`") :
370             (fdc &&  parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s` calling `%s`") :
371             (fdc && !parId) ? (desc ~ " `%s` assigned to non-scope anonymous parameter calling `%s`") :
372             (!fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s`") :
373             (desc ~ " `%s` assigned to non-scope anonymous parameter");
374 
375         if (isThis ?
376             sc.setUnsafeDIP1000(gag, arg.loc, msg, arg, fdc.toParent2(), fdc) :
377             sc.setUnsafeDIP1000(gag, arg.loc, msg, v, parId ? parId : fdc, fdc))
378         {
379             result = true;
380             printScopeFailure(previewSupplementalFunc(sc.isDeprecated(), global.params.useDIP1000), vPar, 10);
381         }
382     }
383 
384     foreach (VarDeclaration v; er.byvalue)
385     {
386         if (log) printf("byvalue %s\n", v.toChars());
387         if (v.isDataseg())
388             continue;
389 
390         Dsymbol p = v.toParent2();
391 
392         notMaybeScope(v, vPar);
393 
394         if (v.isScope())
395         {
396             unsafeAssign!"scope variable"(v);
397         }
398         else if (v.isTypesafeVariadicArray && p == sc.func)
399         {
400             unsafeAssign!"variadic variable"(v);
401         }
402     }
403 
404     foreach (VarDeclaration v; er.byref)
405     {
406         if (log) printf("byref %s\n", v.toChars());
407         if (v.isDataseg())
408             continue;
409 
410         Dsymbol p = v.toParent2();
411 
412         notMaybeScope(v, arg);
413         if (checkScopeVarAddr(v, arg, sc, gag))
414         {
415             result = true;
416             continue;
417         }
418 
419         if (p == sc.func && !(parStc & STC.scope_))
420         {
421             unsafeAssign!"reference to local variable"(v);
422             continue;
423         }
424     }
425 
426     foreach (FuncDeclaration fd; er.byfunc)
427     {
428         //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
429         VarDeclarations vars;
430         findAllOuterAccessedVariables(fd, &vars);
431 
432         foreach (v; vars)
433         {
434             //printf("v = %s\n", v.toChars());
435             assert(!v.isDataseg());     // these are not put in the closureVars[]
436 
437             Dsymbol p = v.toParent2();
438 
439             notMaybeScope(v, arg);
440 
441             if ((v.isReference() || v.isScope()) && p == sc.func)
442             {
443                 unsafeAssign!"reference to local"(v);
444                 continue;
445             }
446         }
447     }
448 
449     if (!sc.func)
450         return result;
451 
452     foreach (Expression ee; er.byexp)
453     {
454         const(char)* msg = parId ?
455             "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`" :
456             "reference to stack allocated value returned by `%s` assigned to non-scope anonymous parameter";
457 
458         result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, parId);
459     }
460 
461     return result;
462 }
463 
464 /*****************************************************
465  * Function argument initializes a `return` parameter,
466  * and that parameter gets assigned to `firstArg`.
467  * Essentially, treat as `firstArg = arg;`
468  * Params:
469  *      sc = used to determine current function and module
470  *      firstArg = `ref` argument through which `arg` may be assigned
471  *      arg = initializer for parameter
472  *      param = parameter declaration corresponding to `arg`
473  *      gag = do not print error messages
474  * Returns:
475  *      `true` if assignment to `firstArg` would cause an error
476  */
477 public
478 bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Parameter param, bool gag)
479 {
480     enum log = false;
481     if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n",
482         firstArg.toChars(), arg.toChars());
483     //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
484 
485     if (!(param.storageClass & STC.return_))
486         return false;
487 
488     if (!arg.type.hasPointers() && !param.isReference())
489         return false;
490 
491     // `byRef` needed for `assign(ref int* x, ref int i) {x = &i};`
492     // Note: taking address of scope pointer is not allowed
493     // `assign(ref int** x, return ref scope int* i) {x = &i};`
494     // Thus no return ref/return scope ambiguity here
495     const byRef = param.isReference() && !(param.storageClass & STC.scope_)
496         && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef
497 
498     auto e = new AssignExp(arg.loc, firstArg, arg);
499     return checkAssignEscape(sc, e, gag, byRef);
500 }
501 
502 /*****************************************************
503  * Check struct constructor of the form `s.this(args)`, by
504  * checking each `return` parameter to see if it gets
505  * assigned to `s`.
506  * Params:
507  *      sc = used to determine current function and module
508  *      ce = constructor call of the form `s.this(args)`
509  *      gag = do not print error messages
510  * Returns:
511  *      `true` if construction would cause an escaping reference error
512  */
513 public
514 bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
515 {
516     enum log = false;
517     if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars());
518     Type tthis = ce.type.toBasetype();
519     assert(tthis.ty == Tstruct);
520     if (!tthis.hasPointers())
521         return false;
522 
523     if (!ce.arguments && ce.arguments.length)
524         return false;
525 
526     DotVarExp dve = ce.e1.isDotVarExp();
527     CtorDeclaration ctor = dve.var.isCtorDeclaration();
528     TypeFunction tf = ctor.type.isTypeFunction();
529 
530     const nparams = tf.parameterList.length;
531     const n = ce.arguments.length;
532 
533     // j=1 if _arguments[] is first argument
534     const j = tf.isDstyleVariadic();
535 
536     /* Attempt to assign each `return` arg to the `this` reference
537      */
538     foreach (const i; 0 .. n)
539     {
540         Expression arg = (*ce.arguments)[i];
541         //printf("\targ[%d]: %s\n", i, arg.toChars());
542 
543         if (i - j < nparams && i >= j)
544         {
545             Parameter p = tf.parameterList[i - j];
546             if (checkParamArgumentReturn(sc, dve.e1, arg, p, gag))
547                 return true;
548         }
549     }
550 
551     return false;
552 }
553 
554 /// How a `return` parameter escapes its pointer value
555 public
556 enum ReturnParamDest
557 {
558     returnVal, /// through return statement: `return x`
559     this_,     /// assigned to a struct instance: `this.x = x`
560     firstArg,  /// assigned to first argument: `firstArg = x`
561 }
562 
563 /****************************************
564  * Find out if instead of returning a `return` parameter via a return statement,
565  * it is returned via assignment to either `this` or the first parameter.
566  *
567  * This works the same as returning the value via a return statement.
568  * Although the first argument must be `ref`, it is not regarded as returning by `ref`.
569  *
570  * See_Also: https://dlang.org.spec/function.html#return-ref-parameters
571  *
572  * Params:
573  *   tf = function type
574  *   tthis = type of `this` parameter, or `null` if none
575  * Returns: What a `return` parameter should transfer the lifetime of the argument to
576  */
577 public
578 ReturnParamDest returnParamDest(TypeFunction tf, Type tthis)
579 {
580     assert(tf);
581     if (tf.isctor)
582         return ReturnParamDest.this_;
583 
584     if (!tf.nextOf() || (tf.nextOf().ty != Tvoid))
585         return ReturnParamDest.returnVal;
586 
587     if (tthis && tthis.toBasetype().ty == Tstruct) // class `this` is passed by value
588         return ReturnParamDest.this_;
589 
590     if (tf.parameterList.length > 0 && tf.parameterList[0].isReference)
591         return ReturnParamDest.firstArg;
592 
593     return ReturnParamDest.returnVal;
594 }
595 
596 /****************************************
597  * Given an `AssignExp`, determine if the lvalue will cause
598  * the contents of the rvalue to escape.
599  * Print error messages when these are detected.
600  * Infer `scope` attribute for the lvalue where possible, in order
601  * to eliminate the error.
602  * Params:
603  *      sc = used to determine current function and module
604  *      e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack
605  *      gag = do not print error messages
606  *      byRef = set to `true` if `e1` of `e` gets assigned a reference to `e2`
607  * Returns:
608  *      `true` if pointers to the stack can escape via assignment
609  */
610 public
611 bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef)
612 {
613     enum log = false;
614     if (log) printf("checkAssignEscape(e: %s, byRef: %d)\n", e.toChars(), byRef);
615     if (e.op != EXP.assign && e.op != EXP.blit && e.op != EXP.construct &&
616         e.op != EXP.concatenateAssign && e.op != EXP.concatenateElemAssign && e.op != EXP.concatenateDcharAssign)
617         return false;
618     auto ae = cast(BinExp)e;
619     Expression e1 = ae.e1;
620     Expression e2 = ae.e2;
621     //printf("type = %s, %d\n", e1.type.toChars(), e1.type.hasPointers());
622 
623     if (!e1.type.hasPointers())
624         return false;
625 
626     if (e1.isSliceExp())
627     {
628         if (VarDeclaration va = expToVariable(e1))
629         {
630             if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable
631                 !va.type.hasPointers())
632                 return false;
633         }
634         else
635             return false;
636     }
637 
638     /* The struct literal case can arise from the S(e2) constructor call:
639      *    return S(e2);
640      * and appears in this function as:
641      *    structLiteral = e2;
642      * Such an assignment does not necessarily remove scope-ness.
643      */
644     if (e1.isStructLiteralExp())
645         return false;
646 
647     VarDeclaration va = expToVariable(e1);
648     EscapeByResults er;
649 
650     if (byRef)
651         escapeByRef(e2, &er);
652     else
653         escapeByValue(e2, &er);
654 
655     if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length)
656         return false;
657 
658 
659     if (va && e.op == EXP.concatenateElemAssign)
660     {
661         /* https://issues.dlang.org/show_bug.cgi?id=17842
662          * Draw an equivalence between:
663          *   *q = p;
664          * and:
665          *   va ~= e;
666          * since we are not assigning to va, but are assigning indirectly through va.
667          */
668         va = null;
669     }
670 
671     if (va && e1.isDotVarExp() && va.type.toBasetype().isTypeClass())
672     {
673         /* https://issues.dlang.org/show_bug.cgi?id=17949
674          * Draw an equivalence between:
675          *   *q = p;
676          * and:
677          *   va.field = e2;
678          * since we are not assigning to va, but are assigning indirectly through class reference va.
679          */
680         va = null;
681     }
682 
683     if (log && va) printf("va: %s\n", va.toChars());
684 
685     FuncDeclaration fd = sc.func;
686 
687 
688     // Determine if va is a `ref` parameter, so it has a lifetime exceding the function scope
689     const bool vaIsRef = va && va.isParameter() && va.isReference();
690     if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars());
691 
692     // Determine if va is the first parameter, through which other 'return' parameters
693     // can be assigned.
694     bool vaIsFirstRef = false;
695     if (fd && fd.type)
696     {
697         final switch (returnParamDest(fd.type.isTypeFunction(), fd.vthis ? fd.vthis.type : null))
698         {
699             case ReturnParamDest.this_:
700                 vaIsFirstRef = va == fd.vthis;
701                 break;
702             case ReturnParamDest.firstArg:
703                 vaIsFirstRef = (*fd.parameters)[0] == va;
704                 break;
705             case ReturnParamDest.returnVal:
706                 break;
707         }
708     }
709     if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars());
710 
711     bool result = false;
712     foreach (VarDeclaration v; er.byvalue)
713     {
714         if (log) printf("byvalue: %s\n", v.toChars());
715         if (v.isDataseg())
716             continue;
717 
718         if (v == va)
719             continue;
720 
721         Dsymbol p = v.toParent2();
722 
723         if (va && !vaIsRef && !va.isScope() && !v.isScope() &&
724             !v.isTypesafeVariadicArray && !va.isTypesafeVariadicArray &&
725             (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) &&
726             p == fd)
727         {
728             /* Add v to va's list of dependencies
729              */
730             va.addMaybe(v);
731             continue;
732         }
733 
734         if (vaIsFirstRef && p == fd)
735         {
736             inferReturn(fd, v, /*returnScope:*/ true);
737         }
738 
739         if (!(va && va.isScope()) || vaIsRef)
740             notMaybeScope(v, e);
741 
742         if (v.isScope())
743         {
744             if (vaIsFirstRef && v.isParameter() && v.isReturn())
745             {
746                 // va=v, where v is `return scope`
747                 if (inferScope(va))
748                     continue;
749             }
750 
751             // If va's lifetime encloses v's, then error
752             if (EnclosedBy eb = va.enclosesLifetimeOf(v))
753             {
754                 const(char)* msg;
755                 final switch (eb)
756                 {
757                     case EnclosedBy.none: assert(0);
758                     case EnclosedBy.returnScope:
759                         msg = "scope variable `%s` assigned to return scope `%s`";
760                         break;
761                     case EnclosedBy.longerScope:
762                         if (v.storage_class & STC.temp)
763                             continue;
764                         msg = "scope variable `%s` assigned to `%s` with longer lifetime";
765                         break;
766                     case EnclosedBy.refVar:
767                         msg = "scope variable `%s` assigned to `ref` variable `%s` with longer lifetime";
768                         break;
769                     case EnclosedBy.global:
770                         msg = "scope variable `%s` assigned to global variable `%s`";
771                         break;
772                 }
773 
774                 if (sc.setUnsafeDIP1000(gag, ae.loc, msg, v, va))
775                 {
776                     result = true;
777                     continue;
778                 }
779             }
780 
781             // v = scope, va should be scope as well
782             const vaWasScope = va && va.isScope();
783             if (inferScope(va))
784             {
785                 // In case of `scope local = returnScopeParam`, do not infer return scope for `x`
786                 if (!vaWasScope && v.isReturn() && !va.isReturn())
787                 {
788                     if (log) printf("infer return for %s\n", va.toChars());
789                     va.storage_class |= STC.return_ | STC.returninferred;
790 
791                     // Added "return scope" so don't confuse it with "return ref"
792                     if (isRefReturnScope(va.storage_class))
793                         va.storage_class |= STC.returnScope;
794                 }
795                 continue;
796             }
797             result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1);
798         }
799         else if (v.isTypesafeVariadicArray && p == fd)
800         {
801             if (inferScope(va))
802                 continue;
803             result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1);
804         }
805         else
806         {
807             /* v is not 'scope', and we didn't check the scope of where we assigned it to.
808              * It may escape via that assignment, therefore, v can never be 'scope'.
809              */
810             //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__);
811             doNotInferScope(v, e);
812         }
813     }
814 
815     foreach (VarDeclaration v; er.byref)
816     {
817         if (log) printf("byref: %s\n", v.toChars());
818         if (v.isDataseg())
819             continue;
820 
821         if (checkScopeVarAddr(v, ae, sc, gag))
822         {
823             result = true;
824             continue;
825         }
826 
827         if (va && va.isScope() && !v.isReference())
828         {
829             if (!va.isReturn())
830             {
831                 va.doNotInferReturn = true;
832             }
833             else
834             {
835                 result |= sc.setUnsafeDIP1000(gag, ae.loc,
836                     "address of local variable `%s` assigned to return scope `%s`", v, va);
837             }
838         }
839 
840         Dsymbol p = v.toParent2();
841 
842         if (vaIsFirstRef && p == fd)
843         {
844             //if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), fd.toChars());
845             inferReturn(fd, v, /*returnScope:*/ false);
846         }
847 
848         // If va's lifetime encloses v's, then error
849         if (va && !(vaIsFirstRef && v.isReturn()) && va.enclosesLifetimeOf(v))
850         {
851             if (sc.setUnsafeDIP1000(gag, ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v, va))
852             {
853                 result = true;
854                 continue;
855             }
856         }
857 
858         if (!(va && va.isScope()))
859             notMaybeScope(v, e);
860 
861         if (p != sc.func)
862             continue;
863 
864         if (inferScope(va))
865         {
866             if (v.isReturn() && !va.isReturn())
867                 va.storage_class |= STC.return_ | STC.returninferred;
868             continue;
869         }
870         if (e1.op == EXP.structLiteral)
871             continue;
872 
873         result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1);
874     }
875 
876     foreach (FuncDeclaration func; er.byfunc)
877     {
878         if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf);
879         VarDeclarations vars;
880         findAllOuterAccessedVariables(func, &vars);
881 
882         /* https://issues.dlang.org/show_bug.cgi?id=16037
883          * If assigning the address of a delegate to a scope variable,
884          * then uncount that address of. This is so it won't cause a
885          * closure to be allocated.
886          */
887         if (va && va.isScope() && !va.isReturn() && func.tookAddressOf)
888             --func.tookAddressOf;
889 
890         foreach (v; vars)
891         {
892             //printf("v = %s\n", v.toChars());
893             assert(!v.isDataseg());     // these are not put in the closureVars[]
894 
895             Dsymbol p = v.toParent2();
896 
897             if (!(va && va.isScope()))
898                 notMaybeScope(v, e);
899 
900             if (!(v.isReference() || v.isScope()) || p != fd)
901                 continue;
902 
903             if (va && !va.isDataseg() && (va.isScope() || va.maybeScope))
904             {
905                 /* Don't infer STC.scope_ for va, because then a closure
906                  * won't be generated for fd.
907                  */
908                 //if (!va.isScope())
909                     //va.storage_class |= STC.scope_ | STC.scopeinferred;
910                 continue;
911             }
912             result |= sc.setUnsafeDIP1000(gag, ae.loc,
913                 "reference to local `%s` assigned to non-scope `%s` in @safe code", v, e1);
914         }
915     }
916 
917     foreach (Expression ee; er.byexp)
918     {
919         if (log) printf("byexp: %s\n", ee.toChars());
920 
921         /* Do not allow slicing of a static array returned by a function
922          */
923         if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() &&
924             !(va && va.storage_class & STC.temp))
925         {
926             if (!gag)
927                 sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`",
928                     ee.toChars(), e1.toChars());
929             //result = true;
930             continue;
931         }
932 
933         if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() &&
934             (!va || !(va.storage_class & STC.temp) && !va.isScope()))
935         {
936             if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1))
937             {
938                 result = true;
939                 continue;
940             }
941         }
942 
943         if (ee.op == EXP.structLiteral &&
944             (!va || !(va.storage_class & STC.temp)))
945         {
946             if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1))
947             {
948                 result = true;
949                 continue;
950             }
951         }
952 
953         if (inferScope(va))
954             continue;
955 
956         result |= sc.setUnsafeDIP1000(gag, ee.loc,
957             "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1);
958     }
959 
960     return result;
961 }
962 
963 /************************************
964  * Detect cases where pointers to the stack can escape the
965  * lifetime of the stack frame when throwing `e`.
966  * Print error messages when these are detected.
967  * Params:
968  *      sc = used to determine current function and module
969  *      e = expression to check for any pointers to the stack
970  *      gag = do not print error messages
971  * Returns:
972  *      `true` if pointers to the stack can escape
973  */
974 public
975 bool checkThrowEscape(Scope* sc, Expression e, bool gag)
976 {
977     //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars());
978     EscapeByResults er;
979 
980     escapeByValue(e, &er);
981 
982     if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
983         return false;
984 
985     bool result = false;
986     foreach (VarDeclaration v; er.byvalue)
987     {
988         //printf("byvalue %s\n", v.toChars());
989         if (v.isDataseg())
990             continue;
991 
992         if (v.isScope() && !v.iscatchvar)       // special case: allow catch var to be rethrown
993                                                 // despite being `scope`
994         {
995             // https://issues.dlang.org/show_bug.cgi?id=17029
996             result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be thrown", v);
997             continue;
998         }
999         else
1000         {
1001             notMaybeScope(v, new ThrowExp(e.loc, e));
1002         }
1003     }
1004     return result;
1005 }
1006 
1007 /************************************
1008  * Detect cases where pointers to the stack can escape the
1009  * lifetime of the stack frame by being placed into a GC allocated object.
1010  * Print error messages when these are detected.
1011  * Params:
1012  *      sc = used to determine current function and module
1013  *      e = expression to check for any pointers to the stack
1014  *      gag = do not print error messages
1015  * Returns:
1016  *      `true` if pointers to the stack can escape
1017  */
1018 public
1019 bool checkNewEscape(Scope* sc, Expression e, bool gag)
1020 {
1021     import dmd.globals: FeatureState;
1022     import dmd.errors: previewErrorFunc;
1023 
1024     //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars());
1025     enum log = false;
1026     if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars());
1027     EscapeByResults er;
1028 
1029     escapeByValue(e, &er);
1030 
1031     if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
1032         return false;
1033 
1034     bool result = false;
1035     foreach (VarDeclaration v; er.byvalue)
1036     {
1037         if (log) printf("byvalue `%s`\n", v.toChars());
1038         if (v.isDataseg())
1039             continue;
1040 
1041         Dsymbol p = v.toParent2();
1042 
1043         if (v.isScope())
1044         {
1045             if (
1046                 /* This case comes up when the ReturnStatement of a __foreachbody is
1047                  * checked for escapes by the caller of __foreachbody. Skip it.
1048                  *
1049                  * struct S { static int opApply(int delegate(S*) dg); }
1050                  * S* foo() {
1051                  *    foreach (S* s; S) // create __foreachbody for body of foreach
1052                  *        return s;     // s is inferred as 'scope' but incorrectly tested in foo()
1053                  *    return null; }
1054                  */
1055                 !(p.parent == sc.func))
1056             {
1057                 // https://issues.dlang.org/show_bug.cgi?id=20868
1058                 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be copied into allocated memory", v);
1059                 continue;
1060             }
1061         }
1062         else if (v.isTypesafeVariadicArray && p == sc.func)
1063         {
1064             result |= sc.setUnsafeDIP1000(gag, e.loc,
1065                 "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e, v);
1066         }
1067         else
1068         {
1069             //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1070             notMaybeScope(v, e);
1071         }
1072     }
1073 
1074     foreach (VarDeclaration v; er.byref)
1075     {
1076         if (log) printf("byref `%s`\n", v.toChars());
1077 
1078         // 'featureState' tells us whether to emit an error or a deprecation,
1079         // depending on the flag passed to the CLI for DIP25 / DIP1000
1080         bool escapingRef(VarDeclaration v, FeatureState fs)
1081         {
1082             const(char)* msg = v.isParameter() ?
1083                 "copying `%s` into allocated memory escapes a reference to parameter `%s`" :
1084                 "copying `%s` into allocated memory escapes a reference to local variable `%s`";
1085             return sc.setUnsafePreview(fs, gag, e.loc, msg, e, v);
1086         }
1087 
1088         if (v.isDataseg())
1089             continue;
1090 
1091         Dsymbol p = v.toParent2();
1092 
1093         if (!v.isReference())
1094         {
1095             if (p == sc.func)
1096             {
1097                 result |= escapingRef(v, global.params.useDIP1000);
1098                 continue;
1099             }
1100         }
1101 
1102         /* Check for returning a ref variable by 'ref', but should be 'return ref'
1103          * Infer the addition of 'return', or set result to be the offending expression.
1104          */
1105         if (!v.isReference())
1106             continue;
1107 
1108         // https://dlang.org/spec/function.html#return-ref-parameters
1109         if (p == sc.func)
1110         {
1111             //printf("escaping reference to local ref variable %s\n", v.toChars());
1112             //printf("storage class = x%llx\n", v.storage_class);
1113             result |= escapingRef(v, global.params.useDIP25);
1114             continue;
1115         }
1116         // Don't need to be concerned if v's parent does not return a ref
1117         FuncDeclaration func = p.isFuncDeclaration();
1118         if (!func || !func.type)
1119             continue;
1120         if (auto tf = func.type.isTypeFunction())
1121         {
1122             if (!tf.isref)
1123                 continue;
1124 
1125             const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape";
1126             if (!gag)
1127             {
1128                 previewErrorFunc(sc.isDeprecated(), global.params.useDIP25)(e.loc, msg, v.toChars());
1129             }
1130 
1131             // If -preview=dip25 is used, the user wants an error
1132             // Otherwise, issue a deprecation
1133             result |= (global.params.useDIP25 == FeatureState.enabled);
1134         }
1135     }
1136 
1137     foreach (Expression ee; er.byexp)
1138     {
1139         if (log) printf("byexp %s\n", ee.toChars());
1140         if (!gag)
1141             sc.eSink.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 public
1162 bool checkReturnEscape(Scope* sc, Expression e, bool gag)
1163 {
1164     //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars());
1165     return checkReturnEscapeImpl(sc, e, false, gag);
1166 }
1167 
1168 /************************************
1169  * Detect cases where returning `e` by `ref` can result in a reference to the stack
1170  * being returned.
1171  * Print error messages when these are detected.
1172  * Params:
1173  *      sc = used to determine current function and module
1174  *      e = expression to check
1175  *      gag = do not print error messages
1176  * Returns:
1177  *      `true` if references to the stack can escape
1178  */
1179 public
1180 bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag)
1181 {
1182     version (none)
1183     {
1184         printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars());
1185         printf("current function %s\n", sc.func.toChars());
1186         printf("parent2 function %s\n", sc.func.toParent2().toChars());
1187     }
1188 
1189     return checkReturnEscapeImpl(sc, e, true, gag);
1190 }
1191 
1192 /***************************************
1193  * Implementation of checking for escapes in return expressions.
1194  * Params:
1195  *      sc = used to determine current function and module
1196  *      e = expression to check
1197  *      refs = `true`: escape by value, `false`: escape by `ref`
1198  *      gag = do not print error messages
1199  * Returns:
1200  *      `true` if references to the stack can escape
1201  */
1202 private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
1203 {
1204     enum log = false;
1205     if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars());
1206     EscapeByResults er;
1207 
1208     if (refs)
1209         escapeByRef(e, &er);
1210     else
1211         escapeByValue(e, &er);
1212 
1213     if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
1214         return false;
1215 
1216     bool result = false;
1217     foreach (VarDeclaration v; er.byvalue)
1218     {
1219         if (log) printf("byvalue `%s`\n", v.toChars());
1220         if (v.isDataseg())
1221             continue;
1222 
1223         const vsr = buildScopeRef(v.storage_class);
1224 
1225         Dsymbol p = v.toParent2();
1226 
1227         if (p == sc.func && inferReturn(sc.func, v, /*returnScope:*/ true))
1228         {
1229             continue;
1230         }
1231 
1232         if (v.isScope())
1233         {
1234             /* If `return scope` applies to v.
1235              */
1236             if (vsr == ScopeRef.ReturnScope ||
1237                 vsr == ScopeRef.Ref_ReturnScope)
1238             {
1239                 continue;
1240             }
1241 
1242             auto pfunc = p.isFuncDeclaration();
1243             if (pfunc &&
1244                 /* This case comes up when the ReturnStatement of a __foreachbody is
1245                  * checked for escapes by the caller of __foreachbody. Skip it.
1246                  *
1247                  * struct S { static int opApply(int delegate(S*) dg); }
1248                  * S* foo() {
1249                  *    foreach (S* s; S) // create __foreachbody for body of foreach
1250                  *        return s;     // s is inferred as 'scope' but incorrectly tested in foo()
1251                  *    return null; }
1252                  */
1253                 !(!refs && p.parent == sc.func && pfunc.fes) &&
1254                 /*
1255                  *  auto p(scope string s) {
1256                  *      string scfunc() { return s; }
1257                  *  }
1258                  */
1259                 !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0)
1260                )
1261             {
1262                 if (v.isParameter() && !v.isReturn())
1263                 {
1264                     // https://issues.dlang.org/show_bug.cgi?id=23191
1265                     if (!gag)
1266                     {
1267                         previewErrorFunc(sc.isDeprecated(), global.params.useDIP1000)(e.loc,
1268                             "scope parameter `%s` may not be returned", v.toChars()
1269                         );
1270                         result = true;
1271                         continue;
1272                     }
1273                 }
1274                 else
1275                 {
1276                     // https://issues.dlang.org/show_bug.cgi?id=17029
1277                     result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be returned", v);
1278                     continue;
1279                 }
1280             }
1281         }
1282         else if (v.isTypesafeVariadicArray && p == sc.func)
1283         {
1284             if (!gag)
1285                 sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
1286             result = false;
1287         }
1288         else
1289         {
1290             //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1291             doNotInferScope(v, e);
1292         }
1293     }
1294 
1295     foreach (i, VarDeclaration v; er.byref[])
1296     {
1297         if (log)
1298         {
1299             printf("byref `%s` %s\n", v.toChars(), toChars(buildScopeRef(v.storage_class)));
1300         }
1301 
1302         // 'featureState' tells us whether to emit an error or a deprecation,
1303         // depending on the flag passed to the CLI for DIP25
1304         void escapingRef(VarDeclaration v, FeatureState featureState)
1305         {
1306             const(char)* msg = v.isParameter() ?
1307                 "returning `%s` escapes a reference to parameter `%s`" :
1308                 "returning `%s` escapes a reference to local variable `%s`";
1309 
1310             if (v.isParameter() && v.isReference())
1311             {
1312                 if (sc.setUnsafePreview(featureState, gag, e.loc, msg, e, v) ||
1313                     sc.func.isSafeBypassingInference())
1314                 {
1315                     result = true;
1316                     if (v.storage_class & STC.returnScope)
1317                     {
1318                         previewSupplementalFunc(sc.isDeprecated(), featureState)(v.loc,
1319                             "perhaps change the `return scope` into `scope return`");
1320                     }
1321                     else
1322                     {
1323                         const(char)* annotateKind = (v.ident is Id.This) ? "function" : "parameter";
1324                         previewSupplementalFunc(sc.isDeprecated(), featureState)(v.loc,
1325                             "perhaps annotate the %s with `return`", annotateKind);
1326                     }
1327                 }
1328             }
1329             else
1330             {
1331                 if (er.refRetRefTransition[i])
1332                 {
1333                     result |= sc.setUnsafeDIP1000(gag, e.loc, msg, e, v);
1334                 }
1335                 else
1336                 {
1337                     if (!gag)
1338                         previewErrorFunc(sc.isDeprecated(), featureState)(e.loc, msg, e.toChars(), v.toChars());
1339                     result = true;
1340                 }
1341             }
1342         }
1343 
1344         if (v.isDataseg())
1345             continue;
1346 
1347         const vsr = buildScopeRef(v.storage_class);
1348 
1349         Dsymbol p = v.toParent2();
1350 
1351         // https://issues.dlang.org/show_bug.cgi?id=19965
1352         if (!refs)
1353         {
1354             if (sc.func.vthis == v)
1355                 notMaybeScope(v, e);
1356 
1357             if (checkScopeVarAddr(v, e, sc, gag))
1358             {
1359                 result = true;
1360                 continue;
1361             }
1362         }
1363 
1364         if (!v.isReference())
1365         {
1366             if (p == sc.func)
1367             {
1368                 escapingRef(v, FeatureState.enabled);
1369                 continue;
1370             }
1371             FuncDeclaration fd = p.isFuncDeclaration();
1372             if (fd && sc.func.returnInprocess)
1373             {
1374                 /* Code like:
1375                  *   int x;
1376                  *   auto dg = () { return &x; }
1377                  * Making it:
1378                  *   auto dg = () return { return &x; }
1379                  * Because dg.ptr points to x, this is returning dt.ptr+offset
1380                  */
1381                 sc.func.storage_class |= STC.return_ | STC.returninferred;
1382             }
1383         }
1384 
1385         /* Check for returning a ref variable by 'ref', but should be 'return ref'
1386          * Infer the addition of 'return', or set result to be the offending expression.
1387          */
1388         if ((vsr == ScopeRef.Ref ||
1389              vsr == ScopeRef.RefScope ||
1390              vsr == ScopeRef.Ref_ReturnScope) &&
1391             !(v.storage_class & STC.foreach_))
1392         {
1393             if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) &&
1394                 inferReturn(sc.func, v, /*returnScope:*/ false))
1395             {
1396                 continue;
1397             }
1398             else
1399             {
1400                 // https://dlang.org/spec/function.html#return-ref-parameters
1401                 // Only look for errors if in module listed on command line
1402                 if (p == sc.func)
1403                 {
1404                     //printf("escaping reference to local ref variable %s\n", v.toChars());
1405                     //printf("storage class = x%llx\n", v.storage_class);
1406                     escapingRef(v, global.params.useDIP25);
1407                     continue;
1408                 }
1409                 // Don't need to be concerned if v's parent does not return a ref
1410                 FuncDeclaration fd = p.isFuncDeclaration();
1411                 if (fd && fd.type && fd.type.ty == Tfunction)
1412                 {
1413                     TypeFunction tf = fd.type.isTypeFunction();
1414                     if (tf.isref)
1415                     {
1416                         const(char)* msg = "escaping reference to outer local variable `%s`";
1417                         if (!gag)
1418                             previewErrorFunc(sc.isDeprecated(), global.params.useDIP25)(e.loc, msg, v.toChars());
1419                         result = true;
1420                         continue;
1421                     }
1422                 }
1423 
1424             }
1425         }
1426     }
1427 
1428     foreach (i, Expression ee; er.byexp[])
1429     {
1430         if (log) printf("byexp %s\n", ee.toChars());
1431         if (er.expRetRefTransition[i])
1432         {
1433             result |= sc.setUnsafeDIP1000(gag, ee.loc,
1434                 "escaping reference to stack allocated value returned by `%s`", ee);
1435         }
1436         else
1437         {
1438             if (!gag)
1439                 sc.eSink.error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars());
1440             result = true;
1441         }
1442     }
1443     return result;
1444 }
1445 
1446 /***********************************
1447  * Infer `scope` for a variable
1448  *
1449  * Params:
1450  *      va = variable to infer scope for
1451  * Returns: `true` if succesful or already `scope`
1452  */
1453 private
1454 bool inferScope(VarDeclaration va)
1455 {
1456     if (!va)
1457         return false;
1458     if (!va.isDataseg() && va.maybeScope && !va.isScope())
1459     {
1460         //printf("inferring scope for %s\n", va.toChars());
1461         va.maybeScope = false;
1462         va.storage_class |= STC.scope_ | STC.scopeinferred;
1463         return true;
1464     }
1465     return va.isScope();
1466 }
1467 
1468 /*************************************
1469  * Variable v needs to have 'return' inferred for it.
1470  * Params:
1471  *      fd = function that v is a parameter to
1472  *      v = parameter that needs to be STC.return_
1473  *      returnScope = infer `return scope` instead of `return ref`
1474  *
1475  * Returns: whether the inference on `v` was successful or `v` already was `return`
1476  */
1477 private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope)
1478 {
1479     if (v.isReturn())
1480         return !!(v.storage_class & STC.returnScope) == returnScope;
1481 
1482     if (!v.isParameter() || v.isTypesafeVariadicArray || (returnScope && v.doNotInferReturn))
1483         return false;
1484 
1485     if (!fd.returnInprocess)
1486         return false;
1487 
1488     if (returnScope && !(v.isScope() || v.maybeScope))
1489         return false;
1490 
1491     //printf("for function '%s' inferring 'return' for variable '%s', returnScope: %d\n", fd.toChars(), v.toChars(), returnScope);
1492     auto newStcs = STC.return_ | STC.returninferred | (returnScope ? STC.returnScope : 0);
1493     v.storage_class |= newStcs;
1494 
1495     if (v == fd.vthis)
1496     {
1497         /* v is the 'this' reference, so mark the function
1498          */
1499         fd.storage_class |= newStcs;
1500         if (auto tf = fd.type.isTypeFunction())
1501         {
1502             //printf("'this' too %p %s\n", tf, sc.func.toChars());
1503             tf.isreturnscope = returnScope;
1504             tf.isreturn = true;
1505             tf.isreturninferred = true;
1506         }
1507     }
1508     else
1509     {
1510         // Perform 'return' inference on parameter
1511         if (auto tf = fd.type.isTypeFunction())
1512         {
1513             foreach (i, p; tf.parameterList)
1514             {
1515                 if (p.ident == v.ident)
1516                 {
1517                     p.storageClass |= newStcs;
1518                     break;              // there can be only one
1519                 }
1520             }
1521         }
1522     }
1523     return true;
1524 }
1525 
1526 
1527 /****************************************
1528  * e is an expression to be returned by value, and that value contains pointers.
1529  * Walk e to determine which variables are possibly being
1530  * returned by value, such as:
1531  *      int* function(int* p) { return p; }
1532  * If e is a form of &p, determine which variables have content
1533  * which is being returned as ref, such as:
1534  *      int* function(int i) { return &i; }
1535  * Multiple variables can be inserted, because of expressions like this:
1536  *      int function(bool b, int i, int* p) { return b ? &i : p; }
1537  *
1538  * No side effects.
1539  *
1540  * Params:
1541  *      e = expression to be returned by value
1542  *      er = where to place collected data
1543  *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1544   *     retRefTransition = if `e` is returned through a `return ref scope` function call
1545  */
1546 public
1547 void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false)
1548 {
1549     //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars());
1550 
1551     void visit(Expression e)
1552     {
1553     }
1554 
1555     void visitAddr(AddrExp e)
1556     {
1557         /* Taking the address of struct literal is normally not
1558          * allowed, but CTFE can generate one out of a new expression,
1559          * but it'll be placed in static data so no need to check it.
1560          */
1561         if (e.e1.op != EXP.structLiteral)
1562             escapeByRef(e.e1, er, live, retRefTransition);
1563     }
1564 
1565     void visitSymOff(SymOffExp e)
1566     {
1567         VarDeclaration v = e.var.isVarDeclaration();
1568         if (v)
1569             er.pushRef(v, retRefTransition);
1570     }
1571 
1572     void visitVar(VarExp e)
1573     {
1574         if (auto v = e.var.isVarDeclaration())
1575         {
1576             if (v.type.hasPointers() || // not tracking non-pointers
1577                 v.storage_class & STC.lazy_) // lazy variables are actually pointers
1578                 er.byvalue.push(v);
1579         }
1580     }
1581 
1582     void visitThis(ThisExp e)
1583     {
1584         if (e.var)
1585             er.byvalue.push(e.var);
1586     }
1587 
1588     void visitPtr(PtrExp e)
1589     {
1590         if (live && e.type.hasPointers())
1591             escapeByValue(e.e1, er, live, retRefTransition);
1592     }
1593 
1594     void visitDotVar(DotVarExp e)
1595     {
1596         auto t = e.e1.type.toBasetype();
1597         if (e.type.hasPointers() && (live || t.ty == Tstruct))
1598         {
1599             escapeByValue(e.e1, er, live, retRefTransition);
1600         }
1601     }
1602 
1603     void visitDelegate(DelegateExp e)
1604     {
1605         Type t = e.e1.type.toBasetype();
1606         if (t.ty == Tclass || t.ty == Tpointer)
1607             escapeByValue(e.e1, er, live, retRefTransition);
1608         else
1609             escapeByRef(e.e1, er, live, retRefTransition);
1610         er.byfunc.push(e.func);
1611     }
1612 
1613     void visitFunc(FuncExp e)
1614     {
1615         if (e.fd.tok == TOK.delegate_)
1616             er.byfunc.push(e.fd);
1617     }
1618 
1619     void visitTuple(TupleExp e)
1620     {
1621         assert(0); // should have been lowered by now
1622     }
1623 
1624     void visitArrayLiteral(ArrayLiteralExp e)
1625     {
1626         Type tb = e.type.toBasetype();
1627         if (tb.ty == Tsarray || tb.ty == Tarray)
1628         {
1629             if (e.basis)
1630                 escapeByValue(e.basis, er, live, retRefTransition);
1631             foreach (el; *e.elements)
1632             {
1633                 if (el)
1634                     escapeByValue(el, er, live, retRefTransition);
1635             }
1636         }
1637     }
1638 
1639     void visitStructLiteral(StructLiteralExp e)
1640     {
1641         if (e.elements)
1642         {
1643             foreach (ex; *e.elements)
1644             {
1645                 if (ex)
1646                     escapeByValue(ex, er, live, retRefTransition);
1647             }
1648         }
1649     }
1650 
1651     void visitNew(NewExp e)
1652     {
1653         Type tb = e.newtype.toBasetype();
1654         if (tb.ty == Tstruct && !e.member && e.arguments)
1655         {
1656             foreach (ex; *e.arguments)
1657             {
1658                 if (ex)
1659                     escapeByValue(ex, er, live, retRefTransition);
1660             }
1661         }
1662     }
1663 
1664     void visitCast(CastExp e)
1665     {
1666         if (!e.type.hasPointers())
1667             return;
1668         Type tb = e.type.toBasetype();
1669         if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray)
1670         {
1671             escapeByRef(e.e1, er, live, retRefTransition);
1672         }
1673         else
1674             escapeByValue(e.e1, er, live, retRefTransition);
1675     }
1676 
1677     void visitSlice(SliceExp e)
1678     {
1679         if (auto ve = e.e1.isVarExp())
1680         {
1681             VarDeclaration v = ve.var.isVarDeclaration();
1682             Type tb = e.type.toBasetype();
1683             if (v)
1684             {
1685                 if (tb.ty == Tsarray)
1686                     return;
1687                 if (v.isTypesafeVariadicArray)
1688                 {
1689                     er.byvalue.push(v);
1690                     return;
1691                 }
1692             }
1693         }
1694         Type t1b = e.e1.type.toBasetype();
1695         if (t1b.ty == Tsarray)
1696         {
1697             Type tb = e.type.toBasetype();
1698             if (tb.ty != Tsarray)
1699                 escapeByRef(e.e1, er, live, retRefTransition);
1700         }
1701         else
1702             escapeByValue(e.e1, er, live, retRefTransition);
1703     }
1704 
1705     void visitIndex(IndexExp e)
1706     {
1707         if (e.e1.type.toBasetype().ty == Tsarray ||
1708             live && e.type.hasPointers())
1709         {
1710             escapeByValue(e.e1, er, live, retRefTransition);
1711         }
1712     }
1713 
1714     void visitBin(BinExp e)
1715     {
1716         Type tb = e.type.toBasetype();
1717         if (tb.ty == Tpointer)
1718         {
1719             escapeByValue(e.e1, er, live, retRefTransition);
1720             escapeByValue(e.e2, er, live, retRefTransition);
1721         }
1722     }
1723 
1724     void visitBinAssign(BinAssignExp e)
1725     {
1726         escapeByValue(e.e1, er, live, retRefTransition);
1727     }
1728 
1729     void visitAssign(AssignExp e)
1730     {
1731         escapeByValue(e.e1, er, live, retRefTransition);
1732     }
1733 
1734     void visitComma(CommaExp e)
1735     {
1736         escapeByValue(e.e2, er, live, retRefTransition);
1737     }
1738 
1739     void visitCond(CondExp e)
1740     {
1741         escapeByValue(e.e1, er, live, retRefTransition);
1742         escapeByValue(e.e2, er, live, retRefTransition);
1743     }
1744 
1745     void visitCall(CallExp e)
1746     {
1747         //printf("CallExp(): %s\n", e.toChars());
1748         /* Check each argument that is
1749          * passed as 'return scope'.
1750          */
1751         TypeFunction tf = e.calledFunctionType();
1752         if (!tf || !e.type.hasPointers())
1753             return;
1754 
1755         if (e.arguments && e.arguments.length)
1756         {
1757             /* j=1 if _arguments[] is first argument,
1758              * skip it because it is not passed by ref
1759              */
1760             int j = tf.isDstyleVariadic();
1761             for (size_t i = j; i < e.arguments.length; ++i)
1762             {
1763                 Expression arg = (*e.arguments)[i];
1764                 size_t nparams = tf.parameterList.length;
1765                 if (i - j < nparams && i >= j)
1766                 {
1767                     Parameter p = tf.parameterList[i - j];
1768                     const stc = tf.parameterStorageClass(null, p);
1769                     ScopeRef psr = buildScopeRef(stc);
1770                     if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
1771                     {
1772                         if (tf.isref)
1773                         {
1774                             /* ignore `ref` on struct constructor return because
1775                              *   struct S { this(return scope int* q) { this.p = q; } int* p; }
1776                              * is different from:
1777                              *   ref char* front(return scope char** q) { return *q; }
1778                              * https://github.com/dlang/dmd/pull/14869
1779                              */
1780                             if (auto dve = e.e1.isDotVarExp())
1781                                 if (auto fd = dve.var.isFuncDeclaration())
1782                                     if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct())
1783                                     {
1784                                         escapeByValue(arg, er, live, retRefTransition);
1785                                     }
1786                         }
1787                         else
1788                             escapeByValue(arg, er, live, retRefTransition);
1789                     }
1790                     else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
1791                     {
1792                         if (tf.isref)
1793                         {
1794                             /* Treat:
1795                              *   ref P foo(return ref P p)
1796                              * as:
1797                              *   p;
1798                              */
1799                             escapeByValue(arg, er, live, retRefTransition);
1800                         }
1801                         else
1802                             escapeByRef(arg, er, live, retRefTransition);
1803                     }
1804                 }
1805             }
1806         }
1807         // If 'this' is returned, check it too
1808         Type t1 = e.e1.type.toBasetype();
1809         if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
1810         {
1811             DotVarExp dve = e.e1.isDotVarExp();
1812             FuncDeclaration fd = dve.var.isFuncDeclaration();
1813             if (fd && fd.isThis())
1814             {
1815                 /* Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this`
1816                  */
1817 
1818                 /*****************************
1819                  * Concoct storage class for member function's implicit `this` parameter.
1820                  * Params:
1821                  *      fd = member function
1822                  * Returns:
1823                  *      storage class for fd's `this`
1824                  */
1825                 StorageClass getThisStorageClass(FuncDeclaration fd)
1826                 {
1827                     StorageClass stc;
1828                     auto tf = fd.type.toBasetype().isTypeFunction();
1829                     if (tf.isreturn)
1830                         stc |= STC.return_;
1831                     if (tf.isreturnscope)
1832                         stc |= STC.returnScope | STC.scope_;
1833                     auto ad = fd.isThis();
1834                     if (ad.isClassDeclaration() || tf.isScopeQual)
1835                         stc |= STC.scope_;
1836                     if (ad.isStructDeclaration())
1837                         stc |= STC.ref_;        // `this` for a struct member function is passed by `ref`
1838                     return stc;
1839                 }
1840 
1841                 const psr = buildScopeRef(getThisStorageClass(fd));
1842                 if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
1843                 {
1844                     if (!tf.isref || tf.isctor)
1845                         escapeByValue(dve.e1, er, live, retRefTransition);
1846                 }
1847                 else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
1848                 {
1849                     if (tf.isref)
1850                     {
1851                         /* Treat calling:
1852                          *   struct S { ref S foo() return; }
1853                          * as:
1854                          *   this;
1855                          */
1856                         escapeByValue(dve.e1, er, live, retRefTransition);
1857                     }
1858                     else
1859                         escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope);
1860                 }
1861             }
1862 
1863             // If it's also a nested function that is 'return scope'
1864             if (fd && fd.isNested())
1865             {
1866                 if (tf.isreturn && tf.isScopeQual)
1867                     er.pushExp(e, false);
1868             }
1869         }
1870 
1871         /* If returning the result of a delegate call, the .ptr
1872          * field of the delegate must be checked.
1873          */
1874         if (t1.isTypeDelegate())
1875         {
1876             if (tf.isreturn)
1877                 escapeByValue(e.e1, er, live, retRefTransition);
1878         }
1879 
1880         /* If it's a nested function that is 'return scope'
1881          */
1882         if (auto ve = e.e1.isVarExp())
1883         {
1884             FuncDeclaration fd = ve.var.isFuncDeclaration();
1885             if (fd && fd.isNested())
1886             {
1887                 if (tf.isreturn && tf.isScopeQual)
1888                     er.pushExp(e, false);
1889             }
1890         }
1891     }
1892 
1893     switch (e.op)
1894     {
1895         case EXP.address: return visitAddr(e.isAddrExp());
1896         case EXP.symbolOffset: return visitSymOff(e.isSymOffExp());
1897         case EXP.variable: return visitVar(e.isVarExp());
1898         case EXP.this_: return visitThis(e.isThisExp());
1899         case EXP.star: return visitPtr(e.isPtrExp());
1900         case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
1901         case EXP.delegate_: return visitDelegate(e.isDelegateExp());
1902         case EXP.function_: return visitFunc(e.isFuncExp());
1903         case EXP.tuple: return visitTuple(e.isTupleExp());
1904         case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp());
1905         case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
1906         case EXP.new_: return visitNew(e.isNewExp());
1907         case EXP.cast_: return visitCast(e.isCastExp());
1908         case EXP.slice: return visitSlice(e.isSliceExp());
1909         case EXP.index: return visitIndex(e.isIndexExp());
1910         case EXP.blit: return visitAssign(e.isBlitExp());
1911         case EXP.construct: return visitAssign(e.isConstructExp());
1912         case EXP.assign: return visitAssign(e.isAssignExp());
1913         case EXP.comma: return visitComma(e.isCommaExp());
1914         case EXP.question: return visitCond(e.isCondExp());
1915         case EXP.call: return visitCall(e.isCallExp());
1916         default:
1917             if (auto b = e.isBinExp())
1918                 return visitBin(b);
1919             if (auto ba = e.isBinAssignExp())
1920                 return visitBinAssign(ba);
1921             return visit(e);
1922     }
1923 }
1924 
1925 
1926 /****************************************
1927  * e is an expression to be returned by 'ref'.
1928  * Walk e to determine which variables are possibly being
1929  * returned by ref, such as:
1930  *      ref int function(int i) { return i; }
1931  * If e is a form of *p, determine which variables have content
1932  * which is being returned as ref, such as:
1933  *      ref int function(int* p) { return *p; }
1934  * Multiple variables can be inserted, because of expressions like this:
1935  *      ref int function(bool b, int i, int* p) { return b ? i : *p; }
1936  *
1937  * No side effects.
1938  *
1939  * Params:
1940  *      e = expression to be returned by 'ref'
1941  *      er = where to place collected data
1942  *      live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1943  *      retRefTransition = if `e` is returned through a `return ref scope` function call
1944  */
1945 private
1946 void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false)
1947 {
1948     //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition);
1949     void visit(Expression e)
1950     {
1951     }
1952 
1953     void visitVar(VarExp e)
1954     {
1955         auto v = e.var.isVarDeclaration();
1956         if (v)
1957         {
1958             if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init)
1959             {
1960                 /* If compiler generated ref temporary
1961                     *   (ref v = ex; ex)
1962                     * look at the initializer instead
1963                     */
1964                 if (ExpInitializer ez = v._init.isExpInitializer())
1965                 {
1966                     if (auto ce = ez.exp.isConstructExp())
1967                         escapeByRef(ce.e2, er, live, retRefTransition);
1968                     else
1969                         escapeByRef(ez.exp, er, live, retRefTransition);
1970                 }
1971             }
1972             else
1973                 er.pushRef(v, retRefTransition);
1974         }
1975     }
1976 
1977     void visitThis(ThisExp e)
1978     {
1979         if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
1980             escapeByValue(e, er, live, retRefTransition);
1981         else if (e.var)
1982             er.pushRef(e.var, retRefTransition);
1983     }
1984 
1985     void visitPtr(PtrExp e)
1986     {
1987         escapeByValue(e.e1, er, live, retRefTransition);
1988     }
1989 
1990     void visitIndex(IndexExp e)
1991     {
1992         Type tb = e.e1.type.toBasetype();
1993         if (auto ve = e.e1.isVarExp())
1994         {
1995             VarDeclaration v = ve.var.isVarDeclaration();
1996             if (v && v.isTypesafeVariadicArray)
1997             {
1998                 er.pushRef(v, retRefTransition);
1999                 return;
2000             }
2001         }
2002         if (tb.ty == Tsarray)
2003         {
2004             escapeByRef(e.e1, er, live, retRefTransition);
2005         }
2006         else if (tb.ty == Tarray)
2007         {
2008             escapeByValue(e.e1, er, live, retRefTransition);
2009         }
2010     }
2011 
2012     void visitStructLiteral(StructLiteralExp e)
2013     {
2014         if (e.elements)
2015         {
2016             foreach (ex; *e.elements)
2017             {
2018                 if (ex)
2019                     escapeByRef(ex, er, live, retRefTransition);
2020             }
2021         }
2022         er.pushExp(e, retRefTransition);
2023     }
2024 
2025     void visitDotVar(DotVarExp e)
2026     {
2027         Type t1b = e.e1.type.toBasetype();
2028         if (t1b.ty == Tclass)
2029             escapeByValue(e.e1, er, live, retRefTransition);
2030         else
2031             escapeByRef(e.e1, er, live, retRefTransition);
2032     }
2033 
2034     void visitBinAssign(BinAssignExp e)
2035     {
2036         escapeByRef(e.e1, er, live, retRefTransition);
2037     }
2038 
2039     void visitAssign(AssignExp e)
2040     {
2041         escapeByRef(e.e1, er, live, retRefTransition);
2042     }
2043 
2044     void visitComma(CommaExp e)
2045     {
2046         escapeByRef(e.e2, er, live, retRefTransition);
2047     }
2048 
2049     void visitCond(CondExp e)
2050     {
2051         escapeByRef(e.e1, er, live, retRefTransition);
2052         escapeByRef(e.e2, er, live, retRefTransition);
2053     }
2054 
2055     void visitCall(CallExp e)
2056     {
2057         //printf("escapeByRef.CallExp(): %s\n", e.toChars());
2058         /* If the function returns by ref, check each argument that is
2059          * passed as 'return ref'.
2060          */
2061         TypeFunction tf = e.calledFunctionType();
2062         if (!tf)
2063             return;
2064         if (tf.isref)
2065         {
2066             if (e.arguments && e.arguments.length)
2067             {
2068                 /* j=1 if _arguments[] is first argument,
2069                  * skip it because it is not passed by ref
2070                  */
2071                 int j = tf.isDstyleVariadic();
2072                 for (size_t i = j; i < e.arguments.length; ++i)
2073                 {
2074                     Expression arg = (*e.arguments)[i];
2075                     size_t nparams = tf.parameterList.length;
2076                     if (i - j < nparams && i >= j)
2077                     {
2078                         Parameter p = tf.parameterList[i - j];
2079                         const stc = tf.parameterStorageClass(null, p);
2080                         ScopeRef psr = buildScopeRef(stc);
2081                         if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
2082                             escapeByRef(arg, er, live, retRefTransition);
2083                         else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
2084                         {
2085                             if (auto de = arg.isDelegateExp())
2086                             {
2087                                 if (de.func.isNested())
2088                                     er.pushExp(de, false);
2089                             }
2090                             else
2091                                 escapeByValue(arg, er, live, retRefTransition);
2092                         }
2093                     }
2094                 }
2095             }
2096             // If 'this' is returned by ref, check it too
2097             Type t1 = e.e1.type.toBasetype();
2098             if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
2099             {
2100                 DotVarExp dve = e.e1.isDotVarExp();
2101 
2102                 // https://issues.dlang.org/show_bug.cgi?id=20149#c10
2103                 if (dve.var.isCtorDeclaration())
2104                 {
2105                     er.pushExp(e, false);
2106                     return;
2107                 }
2108 
2109                 StorageClass stc = dve.var.storage_class & (STC.return_ | STC.scope_ | STC.ref_);
2110                 if (tf.isreturn)
2111                     stc |= STC.return_;
2112                 if (tf.isref)
2113                     stc |= STC.ref_;
2114                 if (tf.isScopeQual)
2115                     stc |= STC.scope_;
2116                 if (tf.isreturnscope)
2117                     stc |= STC.returnScope;
2118 
2119                 const psr = buildScopeRef(stc);
2120                 if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
2121                     escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope);
2122                 else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
2123                     escapeByValue(dve.e1, er, live, retRefTransition);
2124 
2125                 // If it's also a nested function that is 'return ref'
2126                 if (FuncDeclaration fd = dve.var.isFuncDeclaration())
2127                 {
2128                     if (fd.isNested() && tf.isreturn)
2129                     {
2130                         er.pushExp(e, false);
2131                     }
2132                 }
2133             }
2134             // If it's a delegate, check it too
2135             if (e.e1.op == EXP.variable && t1.ty == Tdelegate)
2136             {
2137                 escapeByValue(e.e1, er, live, retRefTransition);
2138             }
2139 
2140             /* If it's a nested function that is 'return ref'
2141              */
2142             if (auto ve = e.e1.isVarExp())
2143             {
2144                 FuncDeclaration fd = ve.var.isFuncDeclaration();
2145                 if (fd && fd.isNested())
2146                 {
2147                     if (tf.isreturn)
2148                         er.pushExp(e, false);
2149                 }
2150             }
2151         }
2152         else
2153             er.pushExp(e, retRefTransition);
2154     }
2155 
2156     switch (e.op)
2157     {
2158         case EXP.variable: return visitVar(e.isVarExp());
2159         case EXP.this_: return visitThis(e.isThisExp());
2160         case EXP.star: return visitPtr(e.isPtrExp());
2161         case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
2162         case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
2163         case EXP.index: return visitIndex(e.isIndexExp());
2164         case EXP.blit: return visitAssign(e.isBlitExp());
2165         case EXP.construct: return visitAssign(e.isConstructExp());
2166         case EXP.assign: return visitAssign(e.isAssignExp());
2167         case EXP.comma: return visitComma(e.isCommaExp());
2168         case EXP.question: return visitCond(e.isCondExp());
2169         case EXP.call: return visitCall(e.isCallExp());
2170         default:
2171             if (auto ba = e.isBinAssignExp())
2172                 return visitBinAssign(ba);
2173             return visit(e);
2174     }
2175 }
2176 
2177 /************************************
2178  * Aggregate the data collected by the escapeBy??() functions.
2179  */
2180 public
2181 struct EscapeByResults
2182 {
2183     VarDeclarations byref;      // array into which variables being returned by ref are inserted
2184     VarDeclarations byvalue;    // array into which variables with values containing pointers are inserted
2185     private FuncDeclarations byfunc; // nested functions that are turned into delegates
2186     private Expressions byexp;       // array into which temporaries being returned by ref are inserted
2187 
2188     import dmd.root.array: Array;
2189 
2190     /**
2191      * Whether the variable / expression went through a `return ref scope` function call
2192      *
2193      * This is needed for the dip1000 by default transition, since the rules for
2194      * disambiguating `return scope ref` have changed. Therefore, functions in legacy code
2195      * can be mistakenly treated as `return ref` making the compiler believe stack variables
2196      * are being escaped, which is an error even in `@system` code. By keeping track of this
2197      * information, variables escaped through `return ref` can be treated as a deprecation instead
2198      * of error, see test/fail_compilation/dip1000_deprecation.d
2199      */
2200     private Array!bool refRetRefTransition;
2201     private Array!bool expRetRefTransition;
2202 
2203     /** Reset arrays so the storage can be used again
2204      */
2205     void reset()
2206     {
2207         byref.setDim(0);
2208         byvalue.setDim(0);
2209         byfunc.setDim(0);
2210         byexp.setDim(0);
2211 
2212         refRetRefTransition.setDim(0);
2213         expRetRefTransition.setDim(0);
2214     }
2215 
2216     /**
2217      * Escape variable `v` by reference
2218      * Params:
2219      *   v = variable to escape
2220      *   retRefTransition = `v` is escaped through a `return ref scope` function call
2221      */
2222     void pushRef(VarDeclaration v, bool retRefTransition)
2223     {
2224         byref.push(v);
2225         refRetRefTransition.push(retRefTransition);
2226     }
2227 
2228     /**
2229      * Escape a reference to expression `e`
2230      * Params:
2231      *   e = expression to escape
2232      *   retRefTransition = `e` is escaped through a `return ref scope` function call
2233      */
2234     void pushExp(Expression e, bool retRefTransition)
2235     {
2236         byexp.push(e);
2237         expRetRefTransition.push(retRefTransition);
2238     }
2239 }
2240 
2241 /*************************
2242  * Find all variables accessed by this delegate that are
2243  * in functions enclosing it.
2244  * Params:
2245  *      fd = function
2246  *      vars = array to append found variables to
2247  */
2248 public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars)
2249 {
2250     //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
2251     for (auto p = fd.parent; p; p = p.parent)
2252     {
2253         auto fdp = p.isFuncDeclaration();
2254         if (!fdp)
2255             continue;
2256 
2257         foreach (v; fdp.closureVars)
2258         {
2259             foreach (const fdv; v.nestedrefs)
2260             {
2261                 if (fdv == fd)
2262                 {
2263                     //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
2264                     vars.push(v);
2265                 }
2266             }
2267         }
2268     }
2269 }
2270 
2271 /***********************************
2272  * Turn off `maybeScope` for variable `v`.
2273  *
2274  * This exists in order to find where `maybeScope` is getting turned off.
2275  * Params:
2276  *      v = variable
2277  *      o = reason for it being turned off:
2278  *          - `Expression` such as `throw e` or `&e`
2279  *          - `VarDeclaration` of a non-scope parameter it was assigned to
2280  *          - `null` for no reason
2281  */
2282 private void notMaybeScope(VarDeclaration v, RootObject o)
2283 {
2284     if (v.maybeScope)
2285     {
2286         v.maybeScope = false;
2287         if (o && v.isParameter())
2288             EscapeState.scopeInferFailure[v.sequenceNumber] = o;
2289     }
2290 }
2291 
2292 /***********************************
2293  * Turn off `maybeScope` for variable `v` if it's not a parameter.
2294  *
2295  * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`,
2296  * which is now just `VarDeclaration.maybeScope`.
2297  * This function should probably be removed in future refactors.
2298  *
2299  * Params:
2300  *      v = variable
2301  *      o = reason for it being turned off
2302  */
2303 private void doNotInferScope(VarDeclaration v, RootObject o)
2304 {
2305     if (!v.isParameter)
2306         notMaybeScope(v, o);
2307 }
2308 
2309 /***********************************
2310  * After semantic analysis of the function body,
2311  * try to infer `scope` / `return` on the parameters
2312  *
2313  * Params:
2314  *   funcdecl = function declaration that was analyzed
2315  *   f = final function type. `funcdecl.type` started as the 'premature type' before attribute
2316  *       inference, then its inferred attributes are copied over to final type `f`
2317  */
2318 public
2319 void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
2320 {
2321 
2322     if (funcdecl.returnInprocess)
2323     {
2324         funcdecl.returnInprocess = false;
2325         if (funcdecl.storage_class & STC.return_)
2326         {
2327             if (funcdecl.type == f)
2328                 f = cast(TypeFunction)f.copy();
2329             f.isreturn = true;
2330             f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope);
2331             if (funcdecl.storage_class & STC.returninferred)
2332                 f.isreturninferred = true;
2333         }
2334     }
2335 
2336     if (!funcdecl.inferScope)
2337         return;
2338     funcdecl.inferScope = false;
2339 
2340     // Eliminate maybescope's
2341     {
2342         // Create and fill array[] with maybe candidates from the `this` and the parameters
2343         VarDeclaration[10] tmp = void;
2344         size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0);
2345 
2346         import dmd.common.string : SmallBuffer;
2347         auto sb = SmallBuffer!VarDeclaration(dim, tmp[]);
2348         VarDeclaration[] array = sb[];
2349 
2350         size_t n = 0;
2351         if (funcdecl.vthis)
2352             array[n++] = funcdecl.vthis;
2353         if (funcdecl.parameters)
2354         {
2355             foreach (v; *funcdecl.parameters)
2356             {
2357                 array[n++] = v;
2358             }
2359         }
2360         eliminateMaybeScopes(array[0 .. n]);
2361     }
2362 
2363     // Infer STC.scope_
2364     if (funcdecl.parameters && !funcdecl.errors)
2365     {
2366         assert(f.parameterList.length == funcdecl.parameters.length);
2367         foreach (u, p; f.parameterList)
2368         {
2369             auto v = (*funcdecl.parameters)[u];
2370             if (!v.isScope() && v.type.hasPointers() && inferScope(v))
2371             {
2372                 //printf("Inferring scope for %s\n", v.toChars());
2373                 p.storageClass |= STC.scope_ | STC.scopeinferred;
2374             }
2375         }
2376     }
2377 
2378     if (funcdecl.vthis)
2379     {
2380         inferScope(funcdecl.vthis);
2381         f.isScopeQual = funcdecl.vthis.isScope();
2382         f.isscopeinferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred);
2383     }
2384 }
2385 
2386 /**********************************************
2387  * Have some variables that are maybescopes that were
2388  * assigned values from other maybescope variables.
2389  * Now that semantic analysis of the function is
2390  * complete, we can finalize this by turning off
2391  * maybescope for array elements that cannot be scope.
2392  *
2393  * $(TABLE2 Scope Table,
2394  * $(THEAD `va`, `v`,    =>,  `va` ,  `v`  )
2395  * $(TROW maybe, maybe,  =>,  scope,  scope)
2396  * $(TROW scope, scope,  =>,  scope,  scope)
2397  * $(TROW scope, maybe,  =>,  scope,  scope)
2398  * $(TROW maybe, scope,  =>,  scope,  scope)
2399  * $(TROW -    , -    ,  =>,  -    ,  -    )
2400  * $(TROW -    , maybe,  =>,  -    ,  -    )
2401  * $(TROW -    , scope,  =>,  error,  error)
2402  * $(TROW maybe, -    ,  =>,  scope,  -    )
2403  * $(TROW scope, -    ,  =>,  scope,  -    )
2404  * )
2405  * Params:
2406  *      array = array of variables that were assigned to from maybescope variables
2407  */
2408 private void eliminateMaybeScopes(VarDeclaration[] array)
2409 {
2410     enum log = false;
2411     if (log) printf("eliminateMaybeScopes()\n");
2412     bool changes;
2413     do
2414     {
2415         changes = false;
2416         foreach (va; array)
2417         {
2418             if (log) printf("  va = %s\n", va.toChars());
2419             if (!(va.maybeScope || va.isScope()))
2420             {
2421                 if (va.maybes)
2422                 {
2423                     foreach (v; *va.maybes)
2424                     {
2425                         if (log) printf("    v = %s\n", v.toChars());
2426                         if (v.maybeScope)
2427                         {
2428                             // v cannot be scope since it is assigned to a non-scope va
2429                             notMaybeScope(v, va);
2430                             if (!v.isReference())
2431                                 v.storage_class &= ~(STC.return_ | STC.returninferred);
2432                             changes = true;
2433                         }
2434                     }
2435                 }
2436             }
2437         }
2438     } while (changes);
2439 }
2440 
2441 /************************************************
2442  * Is type a reference to a mutable value?
2443  *
2444  * This is used to determine if an argument that does not have a corresponding
2445  * Parameter, i.e. a variadic argument, is a pointer to mutable data.
2446  * Params:
2447  *      t = type of the argument
2448  * Returns:
2449  *      true if it's a pointer (or reference) to mutable data
2450  */
2451 private
2452 bool isReferenceToMutable(Type t)
2453 {
2454     t = t.baseElemOf();
2455 
2456     if (!t.isMutable() ||
2457         !t.hasPointers())
2458         return false;
2459 
2460     switch (t.ty)
2461     {
2462         case Tpointer:
2463             if (t.nextOf().isTypeFunction())
2464                 break;
2465             goto case;
2466 
2467         case Tarray:
2468         case Taarray:
2469         case Tdelegate:
2470             if (t.nextOf().isMutable())
2471                 return true;
2472             break;
2473 
2474         case Tclass:
2475             return true;        // even if the class fields are not mutable
2476 
2477         case Tstruct:
2478             // Have to look at each field
2479             foreach (VarDeclaration v; t.isTypeStruct().sym.fields)
2480             {
2481                 if (v.storage_class & STC.ref_)
2482                 {
2483                     if (v.type.isMutable())
2484                         return true;
2485                 }
2486                 else if (v.type.isReferenceToMutable())
2487                     return true;
2488             }
2489             break;
2490 
2491         case Tnull:
2492             return false;
2493 
2494         default:
2495             assert(0);
2496     }
2497     return false;
2498 }
2499 
2500 /****************************************
2501  * Is parameter a reference to a mutable value?
2502  *
2503  * This is used if an argument has a corresponding Parameter.
2504  * The argument type is necessary if the Parameter is inout.
2505  * Params:
2506  *      p = Parameter to check
2507  *      t = type of corresponding argument
2508  * Returns:
2509  *      true if it's a pointer (or reference) to mutable data
2510  */
2511 private
2512 bool isReferenceToMutable(Parameter p, Type t)
2513 {
2514     if (p.isReference())
2515     {
2516         if (p.type.isConst() || p.type.isImmutable())
2517             return false;
2518         if (p.type.isWild())
2519         {
2520             return t.isMutable();
2521         }
2522         return p.type.isMutable();
2523     }
2524     return isReferenceToMutable(p.type);
2525 }
2526 
2527 /// When checking lifetime for assignment `va=v`, the way `va` encloses `v`
2528 private enum EnclosedBy
2529 {
2530     none = 0,
2531     refVar, // `va` is a `ref` variable, which may link to a global variable
2532     global, // `va` is a global variable
2533     returnScope, // `va` is a scope variable that may be returned
2534     longerScope, // `va` is another scope variable declared earlier than `v`
2535 }
2536 
2537 /**********************************
2538  * Determine if `va` has a lifetime that lasts past
2539  * the destruction of `v`
2540  * Params:
2541  *     va = variable assigned to
2542  *     v = variable being assigned
2543  * Returns:
2544  *     The way `va` encloses `v` (if any)
2545  */
2546 private EnclosedBy enclosesLifetimeOf(VarDeclaration va, VarDeclaration v)
2547 {
2548     if (!va)
2549         return EnclosedBy.none;
2550 
2551     if (va.isDataseg())
2552         return EnclosedBy.global;
2553 
2554     if (va.isScope() && va.isReturn() && !v.isReturn())
2555         return EnclosedBy.returnScope;
2556 
2557     if (va.isReference() && va.isParameter())
2558         return EnclosedBy.refVar;
2559 
2560     assert(va.sequenceNumber != va.sequenceNumber.init);
2561     assert(v.sequenceNumber != v.sequenceNumber.init);
2562     if (va.sequenceNumber < v.sequenceNumber)
2563         return EnclosedBy.longerScope;
2564 
2565     return EnclosedBy.none;
2566 }
2567 
2568 /***************************************
2569  * Add variable `v` to maybes[]
2570  *
2571  * When a maybescope variable `v` is assigned to a maybescope variable `va`,
2572  * we cannot determine if `this` is actually scope until the semantic
2573  * analysis for the function is completed. Thus, we save the data
2574  * until then.
2575  * Params:
2576  *     v = a variable with `maybeScope == true` that was assigned to `this`
2577  */
2578 private void addMaybe(VarDeclaration va, VarDeclaration v)
2579 {
2580     //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars());
2581     if (!va.maybes)
2582         va.maybes = new VarDeclarations();
2583     va.maybes.push(v);
2584 }
2585 
2586 // `setUnsafePreview` partially evaluated for dip1000
2587 public
2588 bool setUnsafeDIP1000(Scope* sc, bool gag, Loc loc, const(char)* msg,
2589     RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
2590 {
2591     return setUnsafePreview(sc, global.params.useDIP1000, gag, loc, msg, arg0, arg1, arg2);
2592 }
2593 
2594 /***************************************
2595  * Check that taking the address of `v` is `@safe`
2596  *
2597  * It's not possible to take the address of a scope variable, because `scope` only applies
2598  * to the top level indirection.
2599  *
2600  * Params:
2601  *     v = variable that a reference is created
2602  *     e = expression that takes the referene
2603  *     sc = used to obtain function / deprecated status
2604  *     gag = don't print errors
2605  * Returns:
2606  *     true if taking the address of `v` is problematic because of the lack of transitive `scope`
2607  */
2608 private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool gag)
2609 {
2610     if (v.storage_class & STC.temp)
2611         return false;
2612 
2613     if (!v.isScope())
2614     {
2615         notMaybeScope(v, e);
2616         return false;
2617     }
2618 
2619     if (!e.type)
2620         return false;
2621 
2622     // When the type after dereferencing has no pointers, it's okay.
2623     // Comes up when escaping `&someStruct.intMember` of a `scope` struct:
2624     // scope does not apply to the `int`
2625     Type t = e.type.baseElemOf();
2626     if ((t.ty == Tarray || t.ty == Tpointer) && !t.nextOf().toBasetype().hasPointers())
2627         return false;
2628 
2629     // take address of `scope` variable not allowed, requires transitive scope
2630     return sc.setUnsafeDIP1000(gag, e.loc,
2631         "cannot take address of `scope` variable `%s` since `scope` applies to first indirection only", v);
2632 }
2633 
2634 /****************************
2635  * Determine if `v` is a typesafe variadic array, which is implicitly `scope`
2636  * Params:
2637  *      v = variable to check
2638  * Returns:
2639  *      true if `v` is a variadic parameter
2640  */
2641 private bool isTypesafeVariadicArray(VarDeclaration v)
2642 {
2643     if (v.storage_class & STC.variadic)
2644     {
2645         Type tb = v.type.toBasetype();
2646         if (tb.ty == Tarray || tb.ty == Tsarray)
2647             return true;
2648     }
2649     return false;
2650 }