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