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