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