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