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