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