d: Merge dmd, druntime d8e3976a58, phobos 7a6e95688
[official-gcc.git] / gcc / d / dmd / escape.d
blob433907a17be1a401ceecc4cb4a061a90b0aa3443
1 /**
2 * Most of the logic to implement scoped pointers and scoped references is here.
4 * Copyright: Copyright (C) 1999-2024 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
12 module dmd.escape;
14 import core.stdc.stdio : printf;
15 import core.stdc.stdlib;
16 import core.stdc.string;
18 import dmd.root.rmem;
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 : FeatureState;
29 import dmd.id;
30 import dmd.identifier;
31 import dmd.init;
32 import dmd.location;
33 import dmd.mtype;
34 import dmd.printast;
35 import dmd.rootobject;
36 import dmd.tokens;
37 import dmd.visitor;
38 import dmd.arraytypes;
40 private:
42 /// Groups global state for escape checking together
43 package(dmd) struct EscapeState
45 // Maps `sequenceNumber` of a `VarDeclaration` to an object that contains the
46 // reason it failed to infer `scope`
47 // https://issues.dlang.org/show_bug.cgi?id=23295
48 private __gshared RootObject[int] scopeInferFailure;
50 /// Called by `initDMD` / `deinitializeDMD` to reset global state
51 static void reset()
53 scopeInferFailure = null;
57 /******************************************************
58 * Checks memory objects passed to a function.
59 * Checks that if a memory object is passed by ref or by pointer,
60 * all of the refs or pointers are const, or there is only one mutable
61 * ref or pointer to it.
62 * References:
63 * DIP 1021
64 * Params:
65 * sc = used to determine current function and module
66 * fd = function being called
67 * tf = fd's type
68 * ethis = if not null, the `this` pointer
69 * arguments = actual arguments to function
70 * gag = do not print error messages
71 * Returns:
72 * `true` if error
74 public
75 bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf,
76 Expression ethis, Expressions* arguments, bool gag)
78 enum log = false;
79 if (log) printf("[%s] checkMutableArguments, fd: `%s`\n", fd.loc.toChars(), fd.toChars());
80 if (log && ethis) printf("ethis: `%s`\n", ethis.toChars());
81 bool errors = false;
83 /* Outer variable references are treated as if they are extra arguments
84 * passed by ref to the function (which they essentially are via the static link).
86 VarDeclaration[] outerVars = fd ? fd.outerVars[] : null;
88 const len = arguments.length + (ethis !is null) + outerVars.length;
89 if (len <= 1)
90 return errors;
92 struct EscapeBy
94 EscapeByResults er;
95 Parameter param; // null if no Parameter for this argument
96 bool isMutable; // true if reference to mutable
99 auto escapeBy = new EscapeBy[len];
100 const paramLength = tf.parameterList.length;
102 // Fill in escapeBy[] with arguments[], ethis, and outerVars[]
103 foreach (const i, ref eb; escapeBy)
105 bool refs;
106 Expression arg;
107 if (i < arguments.length)
109 arg = (*arguments)[i];
110 if (i < paramLength)
112 eb.param = tf.parameterList[i];
113 refs = eb.param.isReference();
114 eb.isMutable = eb.param.isReferenceToMutable(arg.type);
116 else
118 eb.param = null;
119 refs = false;
120 eb.isMutable = arg.type.isReferenceToMutable();
123 else if (ethis)
125 /* ethis is passed by value if a class reference,
126 * by ref if a struct value
128 eb.param = null;
129 arg = ethis;
130 auto ad = fd.isThis();
131 assert(ad);
132 assert(ethis);
133 if (ad.isClassDeclaration())
135 refs = false;
136 eb.isMutable = arg.type.isReferenceToMutable();
138 else
140 assert(ad.isStructDeclaration());
141 refs = true;
142 eb.isMutable = arg.type.isMutable();
145 else
147 // outer variables are passed by ref
148 eb.param = null;
149 refs = true;
150 auto var = outerVars[i - (len - outerVars.length)];
151 eb.isMutable = var.type.isMutable();
152 eb.er.pushRef(var, false);
153 continue;
156 if (refs)
157 escapeByRef(arg, &eb.er);
158 else
159 escapeByValue(arg, &eb.er);
162 void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2,
163 VarDeclaration v, VarDeclaration v2, bool of)
165 if (log) printf("v2: `%s`\n", v2.toChars());
166 if (v2 != v)
167 return;
168 //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable);
169 if (!(eb.isMutable || eb2.isMutable))
170 return;
172 if (!tf.islive && !(sc.useDIP1000 == FeatureState.enabled && sc.func && sc.func.setUnsafe()))
173 return;
175 if (!gag)
177 // int i; funcThatEscapes(ref int i);
178 // funcThatEscapes(i); // error escaping reference _to_ `i`
179 // int* j; funcThatEscapes2(int* j);
180 // funcThatEscapes2(j); // error escaping reference _of_ `i`
181 const(char)* referenceVerb = of ? "of" : "to";
182 const(char)* msg = eb.isMutable && eb2.isMutable
183 ? "more than one mutable reference %s `%s` in arguments to `%s()`"
184 : "mutable and const references %s `%s` in arguments to `%s()`";
185 sc.eSink.error((*arguments)[i].loc, msg,
186 referenceVerb,
187 v.toChars(),
188 fd ? fd.toPrettyChars() : "indirectly");
190 errors = true;
193 void escape(size_t i, ref EscapeBy eb, bool byval)
195 foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref)
197 if (log)
199 const(char)* by = byval ? "byval" : "byref";
200 printf("%s %s\n", by, v.toChars());
202 if (byval && !v.type.hasPointers())
203 continue;
204 foreach (ref eb2; escapeBy[i + 1 .. $])
206 foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref)
208 checkOnePair(i, eb, eb2, v, v2, byval);
213 foreach (const i, ref eb; escapeBy[0 .. $ - 1])
215 escape(i, eb, true);
216 escape(i, eb, false);
219 return errors;
222 /******************************************
223 * Array literal is going to be allocated on the GC heap.
224 * Check its elements to see if any would escape by going on the heap.
225 * Params:
226 * sc = used to determine current function and module
227 * ae = array literal expression
228 * gag = do not print error messages
229 * Returns:
230 * `true` if any elements escaped
232 public
233 bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag)
235 bool errors;
236 if (ae.basis)
237 errors = checkNewEscape(sc, ae.basis, gag);
238 foreach (ex; *ae.elements)
240 if (ex)
241 errors |= checkNewEscape(sc, ex, gag);
243 return errors;
246 /******************************************
247 * Associative array literal is going to be allocated on the GC heap.
248 * Check its elements to see if any would escape by going on the heap.
249 * Params:
250 * sc = used to determine current function and module
251 * ae = associative array literal expression
252 * gag = do not print error messages
253 * Returns:
254 * `true` if any elements escaped
256 public
257 bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag)
259 bool errors;
260 foreach (ex; *ae.keys)
262 if (ex)
263 errors |= checkNewEscape(sc, ex, gag);
265 foreach (ex; *ae.values)
267 if (ex)
268 errors |= checkNewEscape(sc, ex, gag);
270 return errors;
274 * A `scope` variable was assigned to non-scope parameter `v`.
275 * If applicable, print why the parameter was not inferred `scope`.
277 * Params:
278 * printFunc = error/deprecation print function to use
279 * v = parameter that was not inferred
280 * recursionLimit = recursion limit for printing the reason
282 private
283 void printScopeFailure(E)(E printFunc, VarDeclaration v, int recursionLimit)
285 recursionLimit--;
286 if (recursionLimit < 0 || !v)
287 return;
289 if (RootObject* o = v.sequenceNumber in EscapeState.scopeInferFailure)
291 switch ((*o).dyncast())
293 case DYNCAST.expression:
294 Expression e = cast(Expression) *o;
295 printFunc(e.loc, "which is not `scope` because of `%s`", e.toChars());
296 break;
297 case DYNCAST.dsymbol:
298 VarDeclaration v1 = cast(VarDeclaration) *o;
299 printFunc(v1.loc, "which is assigned to non-scope parameter `%s`", v1.toChars());
300 printScopeFailure(printFunc, v1, recursionLimit);
301 break;
302 default:
303 assert(0);
308 /****************************************
309 * Function parameter `par` is being initialized to `arg`,
310 * and `par` may escape.
311 * Detect if scoped values can escape this way.
312 * Print error messages when these are detected.
313 * Params:
314 * sc = used to determine current function and module
315 * fdc = function being called, `null` if called indirectly
316 * parId = name of function parameter for error messages
317 * vPar = `VarDeclaration` corresponding to `par`
318 * parStc = storage classes of function parameter (may have added `scope` from `pure`)
319 * arg = initializer for param
320 * assertmsg = true if the parameter is the msg argument to assert(bool, msg).
321 * gag = do not print error messages
322 * Returns:
323 * `true` if pointers to the stack can escape via assignment
325 public
326 bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Identifier parId, VarDeclaration vPar, STC parStc, Expression arg, bool assertmsg, bool gag)
328 enum log = false;
329 if (log) printf("checkParamArgumentEscape(arg: %s par: %s parSTC: %llx)\n",
330 arg ? arg.toChars() : "null",
331 parId ? parId.toChars() : "null", parStc);
332 //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
334 if (!arg.type.hasPointers())
335 return false;
337 EscapeByResults er;
339 escapeByValue(arg, &er);
341 if (parStc & STC.scope_)
343 // These errors only apply to non-scope parameters
344 // When the parameter is `scope`, only `checkScopeVarAddr` on `er.byref` is needed
345 er.byfunc.setDim(0);
346 er.byvalue.setDim(0);
347 er.byexp.setDim(0);
350 if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length)
351 return false;
353 bool result = false;
355 /* 'v' is assigned unsafely to 'par'
357 void unsafeAssign(string desc)(VarDeclaration v)
359 if (assertmsg)
361 result |= sc.setUnsafeDIP1000(gag, arg.loc,
362 desc ~ " `%s` assigned to non-scope parameter calling `assert()`", v);
363 return;
366 bool isThis = fdc && fdc.needThis() && fdc.vthis == vPar; // implicit `this` parameter to member function
368 const(char)* msg =
369 (isThis) ? (desc ~ " `%s` calling non-scope member function `%s.%s()`") :
370 (fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s` calling `%s`") :
371 (fdc && !parId) ? (desc ~ " `%s` assigned to non-scope anonymous parameter calling `%s`") :
372 (!fdc && parId) ? (desc ~ " `%s` assigned to non-scope parameter `%s`") :
373 (desc ~ " `%s` assigned to non-scope anonymous parameter");
375 if (isThis ?
376 sc.setUnsafeDIP1000(gag, arg.loc, msg, arg, fdc.toParent2(), fdc) :
377 sc.setUnsafeDIP1000(gag, arg.loc, msg, v, parId ? parId : fdc, fdc))
379 result = true;
380 printScopeFailure(previewSupplementalFunc(sc.isDeprecated(), sc.useDIP1000), vPar, 10);
384 foreach (VarDeclaration v; er.byvalue)
386 if (log) printf("byvalue %s\n", v.toChars());
387 if (v.isDataseg())
388 continue;
390 Dsymbol p = v.toParent2();
392 notMaybeScope(v, vPar);
394 if (v.isScope())
396 unsafeAssign!"scope variable"(v);
398 else if (v.isTypesafeVariadicArray && p == sc.func)
400 unsafeAssign!"variadic variable"(v);
404 foreach (VarDeclaration v; er.byref)
406 if (log) printf("byref %s\n", v.toChars());
407 if (v.isDataseg())
408 continue;
410 Dsymbol p = v.toParent2();
412 notMaybeScope(v, arg);
413 if (checkScopeVarAddr(v, arg, sc, gag))
415 result = true;
416 continue;
419 if (p == sc.func && !(parStc & STC.scope_))
421 unsafeAssign!"reference to local variable"(v);
422 continue;
426 foreach (FuncDeclaration fd; er.byfunc)
428 //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
429 VarDeclarations vars;
430 findAllOuterAccessedVariables(fd, &vars);
432 foreach (v; vars)
434 //printf("v = %s\n", v.toChars());
435 assert(!v.isDataseg()); // these are not put in the closureVars[]
437 Dsymbol p = v.toParent2();
439 notMaybeScope(v, arg);
441 if ((v.isReference() || v.isScope()) && p == sc.func)
443 unsafeAssign!"reference to local"(v);
444 continue;
449 if (!sc.func)
450 return result;
452 foreach (Expression ee; er.byexp)
454 const(char)* msg = parId ?
455 "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`" :
456 "reference to stack allocated value returned by `%s` assigned to non-scope anonymous parameter";
458 result |= sc.setUnsafeDIP1000(gag, ee.loc, msg, ee, parId);
461 return result;
464 /*****************************************************
465 * Function argument initializes a `return` parameter,
466 * and that parameter gets assigned to `firstArg`.
467 * Essentially, treat as `firstArg = arg;`
468 * Params:
469 * sc = used to determine current function and module
470 * firstArg = `ref` argument through which `arg` may be assigned
471 * arg = initializer for parameter
472 * param = parameter declaration corresponding to `arg`
473 * gag = do not print error messages
474 * Returns:
475 * `true` if assignment to `firstArg` would cause an error
477 public
478 bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, Parameter param, bool gag)
480 enum log = false;
481 if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n",
482 firstArg.toChars(), arg.toChars());
483 //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers());
485 if (!(param.storageClass & STC.return_))
486 return false;
488 if (!arg.type.hasPointers() && !param.isReference())
489 return false;
491 // `byRef` needed for `assign(ref int* x, ref int i) {x = &i};`
492 // Note: taking address of scope pointer is not allowed
493 // `assign(ref int** x, return ref scope int* i) {x = &i};`
494 // Thus no return ref/return scope ambiguity here
495 const byRef = param.isReference() && !(param.storageClass & STC.scope_)
496 && !(param.storageClass & STC.returnScope); // fixme: it's possible to infer returnScope without scope with vaIsFirstRef
498 auto e = new AssignExp(arg.loc, firstArg, arg);
499 return checkAssignEscape(sc, e, gag, byRef);
502 /*****************************************************
503 * Check struct constructor of the form `s.this(args)`, by
504 * checking each `return` parameter to see if it gets
505 * assigned to `s`.
506 * Params:
507 * sc = used to determine current function and module
508 * ce = constructor call of the form `s.this(args)`
509 * gag = do not print error messages
510 * Returns:
511 * `true` if construction would cause an escaping reference error
513 public
514 bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag)
516 enum log = false;
517 if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars());
518 Type tthis = ce.type.toBasetype();
519 assert(tthis.ty == Tstruct);
520 if (!tthis.hasPointers())
521 return false;
523 if (!ce.arguments && ce.arguments.length)
524 return false;
526 DotVarExp dve = ce.e1.isDotVarExp();
527 CtorDeclaration ctor = dve.var.isCtorDeclaration();
528 TypeFunction tf = ctor.type.isTypeFunction();
530 const nparams = tf.parameterList.length;
531 const n = ce.arguments.length;
533 // j=1 if _arguments[] is first argument
534 const j = tf.isDstyleVariadic();
536 /* Attempt to assign each `return` arg to the `this` reference
538 foreach (const i; 0 .. n)
540 Expression arg = (*ce.arguments)[i];
541 //printf("\targ[%d]: %s\n", i, arg.toChars());
543 if (i - j < nparams && i >= j)
545 Parameter p = tf.parameterList[i - j];
546 if (checkParamArgumentReturn(sc, dve.e1, arg, p, gag))
547 return true;
551 return false;
554 /// How a `return` parameter escapes its pointer value
555 public
556 enum ReturnParamDest
558 returnVal, /// through return statement: `return x`
559 this_, /// assigned to a struct instance: `this.x = x`
560 firstArg, /// assigned to first argument: `firstArg = x`
563 /****************************************
564 * Find out if instead of returning a `return` parameter via a return statement,
565 * it is returned via assignment to either `this` or the first parameter.
567 * This works the same as returning the value via a return statement.
568 * Although the first argument must be `ref`, it is not regarded as returning by `ref`.
570 * See_Also: https://dlang.org.spec/function.html#return-ref-parameters
572 * Params:
573 * tf = function type
574 * tthis = type of `this` parameter, or `null` if none
575 * Returns: What a `return` parameter should transfer the lifetime of the argument to
577 public
578 ReturnParamDest returnParamDest(TypeFunction tf, Type tthis)
580 assert(tf);
581 if (tf.isctor)
582 return ReturnParamDest.this_;
584 if (!tf.nextOf() || (tf.nextOf().ty != Tvoid))
585 return ReturnParamDest.returnVal;
587 if (tthis && tthis.toBasetype().ty == Tstruct) // class `this` is passed by value
588 return ReturnParamDest.this_;
590 if (tf.parameterList.length > 0 && tf.parameterList[0].isReference)
591 return ReturnParamDest.firstArg;
593 return ReturnParamDest.returnVal;
596 /****************************************
597 * Given an `AssignExp`, determine if the lvalue will cause
598 * the contents of the rvalue to escape.
599 * Print error messages when these are detected.
600 * Infer `scope` attribute for the lvalue where possible, in order
601 * to eliminate the error.
602 * Params:
603 * sc = used to determine current function and module
604 * e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack
605 * gag = do not print error messages
606 * byRef = set to `true` if `e1` of `e` gets assigned a reference to `e2`
607 * Returns:
608 * `true` if pointers to the stack can escape via assignment
610 public
611 bool checkAssignEscape(Scope* sc, Expression e, bool gag, bool byRef)
613 enum log = false;
614 if (log) printf("checkAssignEscape(e: %s, byRef: %d)\n", e.toChars(), byRef);
615 if (e.op != EXP.assign && e.op != EXP.blit && e.op != EXP.construct &&
616 e.op != EXP.concatenateAssign && e.op != EXP.concatenateElemAssign && e.op != EXP.concatenateDcharAssign)
617 return false;
618 auto ae = cast(BinExp)e;
619 Expression e1 = ae.e1;
620 Expression e2 = ae.e2;
621 //printf("type = %s, %d\n", e1.type.toChars(), e1.type.hasPointers());
623 if (!e1.type.hasPointers())
624 return false;
626 if (e1.isSliceExp())
628 if (VarDeclaration va = expToVariable(e1))
630 if (!va.type.toBasetype().isTypeSArray() || // treat static array slice same as a variable
631 !va.type.hasPointers())
632 return false;
634 else
635 return false;
638 /* The struct literal case can arise from the S(e2) constructor call:
639 * return S(e2);
640 * and appears in this function as:
641 * structLiteral = e2;
642 * Such an assignment does not necessarily remove scope-ness.
644 if (e1.isStructLiteralExp())
645 return false;
647 VarDeclaration va = expToVariable(e1);
648 EscapeByResults er;
650 if (byRef)
651 escapeByRef(e2, &er);
652 else
653 escapeByValue(e2, &er);
655 if (!er.byref.length && !er.byvalue.length && !er.byfunc.length && !er.byexp.length)
656 return false;
659 if (va && e.op == EXP.concatenateElemAssign)
661 /* https://issues.dlang.org/show_bug.cgi?id=17842
662 * Draw an equivalence between:
663 * *q = p;
664 * and:
665 * va ~= e;
666 * since we are not assigning to va, but are assigning indirectly through va.
668 va = null;
671 if (va && e1.isDotVarExp() && va.type.toBasetype().isTypeClass())
673 /* https://issues.dlang.org/show_bug.cgi?id=17949
674 * Draw an equivalence between:
675 * *q = p;
676 * and:
677 * va.field = e2;
678 * since we are not assigning to va, but are assigning indirectly through class reference va.
680 va = null;
683 if (log && va) printf("va: %s\n", va.toChars());
685 FuncDeclaration fd = sc.func;
688 // Determine if va is a `ref` parameter, so it has a lifetime exceding the function scope
689 const bool vaIsRef = va && va.isParameter() && va.isReference();
690 if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars());
692 // Determine if va is the first parameter, through which other 'return' parameters
693 // can be assigned.
694 bool vaIsFirstRef = false;
695 if (fd && fd.type)
697 final switch (returnParamDest(fd.type.isTypeFunction(), fd.vthis ? fd.vthis.type : null))
699 case ReturnParamDest.this_:
700 vaIsFirstRef = va == fd.vthis;
701 break;
702 case ReturnParamDest.firstArg:
703 vaIsFirstRef = (*fd.parameters)[0] == va;
704 break;
705 case ReturnParamDest.returnVal:
706 break;
709 if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars());
711 bool result = false;
712 foreach (VarDeclaration v; er.byvalue)
714 if (log) printf("byvalue: %s\n", v.toChars());
715 if (v.isDataseg())
716 continue;
718 if (v == va)
719 continue;
721 Dsymbol p = v.toParent2();
723 if (va && !vaIsRef && !va.isScope() && !v.isScope() &&
724 !v.isTypesafeVariadicArray && !va.isTypesafeVariadicArray &&
725 (va.isParameter() && va.maybeScope && v.isParameter() && v.maybeScope) &&
726 p == fd)
728 /* Add v to va's list of dependencies
730 va.addMaybe(v);
731 continue;
734 if (vaIsFirstRef && p == fd)
736 inferReturn(fd, v, /*returnScope:*/ true);
739 if (!(va && va.isScope()) || vaIsRef)
740 notMaybeScope(v, e);
742 if (v.isScope())
744 if (vaIsFirstRef && v.isParameter() && v.isReturn())
746 // va=v, where v is `return scope`
747 if (inferScope(va))
748 continue;
751 // If va's lifetime encloses v's, then error
752 if (EnclosedBy eb = va.enclosesLifetimeOf(v))
754 const(char)* msg;
755 final switch (eb)
757 case EnclosedBy.none: assert(0);
758 case EnclosedBy.returnScope:
759 msg = "scope variable `%s` assigned to return scope `%s`";
760 break;
761 case EnclosedBy.longerScope:
762 if (v.storage_class & STC.temp)
763 continue;
764 msg = "scope variable `%s` assigned to `%s` with longer lifetime";
765 break;
766 case EnclosedBy.refVar:
767 msg = "scope variable `%s` assigned to `ref` variable `%s` with longer lifetime";
768 break;
769 case EnclosedBy.global:
770 msg = "scope variable `%s` assigned to global variable `%s`";
771 break;
774 if (sc.setUnsafeDIP1000(gag, ae.loc, msg, v, va))
776 result = true;
777 continue;
781 // v = scope, va should be scope as well
782 const vaWasScope = va && va.isScope();
783 if (inferScope(va))
785 // In case of `scope local = returnScopeParam`, do not infer return scope for `x`
786 if (!vaWasScope && v.isReturn() && !va.isReturn())
788 if (log) printf("infer return for %s\n", va.toChars());
789 va.storage_class |= STC.return_ | STC.returninferred;
791 // Added "return scope" so don't confuse it with "return ref"
792 if (isRefReturnScope(va.storage_class))
793 va.storage_class |= STC.returnScope;
795 continue;
797 result |= sc.setUnsafeDIP1000(gag, ae.loc, "scope variable `%s` assigned to non-scope `%s`", v, e1);
799 else if (v.isTypesafeVariadicArray && p == fd)
801 if (inferScope(va))
802 continue;
803 result |= sc.setUnsafeDIP1000(gag, ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v, e1);
805 else
807 /* v is not 'scope', and we didn't check the scope of where we assigned it to.
808 * It may escape via that assignment, therefore, v can never be 'scope'.
810 //printf("no infer for %s in %s, %d\n", v.toChars(), fd.ident.toChars(), __LINE__);
811 doNotInferScope(v, e);
815 foreach (VarDeclaration v; er.byref)
817 if (log) printf("byref: %s\n", v.toChars());
818 if (v.isDataseg())
819 continue;
821 if (checkScopeVarAddr(v, ae, sc, gag))
823 result = true;
824 continue;
827 if (va && va.isScope() && !v.isReference())
829 if (!va.isReturn())
831 va.doNotInferReturn = true;
833 else
835 result |= sc.setUnsafeDIP1000(gag, ae.loc,
836 "address of local variable `%s` assigned to return scope `%s`", v, va);
840 Dsymbol p = v.toParent2();
842 if (vaIsFirstRef && p == fd)
844 //if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), fd.toChars());
845 inferReturn(fd, v, /*returnScope:*/ false);
848 // If va's lifetime encloses v's, then error
849 if (va && !(vaIsFirstRef && v.isReturn()) && va.enclosesLifetimeOf(v))
851 if (sc.setUnsafeDIP1000(gag, ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v, va))
853 result = true;
854 continue;
858 if (!(va && va.isScope()))
859 notMaybeScope(v, e);
861 if (p != sc.func)
862 continue;
864 if (inferScope(va))
866 if (v.isReturn() && !va.isReturn())
867 va.storage_class |= STC.return_ | STC.returninferred;
868 continue;
870 if (e1.op == EXP.structLiteral)
871 continue;
873 result |= sc.setUnsafeDIP1000(gag, ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v, e1);
876 foreach (FuncDeclaration func; er.byfunc)
878 if (log) printf("byfunc: %s, %d\n", func.toChars(), func.tookAddressOf);
879 VarDeclarations vars;
880 findAllOuterAccessedVariables(func, &vars);
882 /* https://issues.dlang.org/show_bug.cgi?id=16037
883 * If assigning the address of a delegate to a scope variable,
884 * then uncount that address of. This is so it won't cause a
885 * closure to be allocated.
887 if (va && va.isScope() && !va.isReturn() && func.tookAddressOf)
888 --func.tookAddressOf;
890 foreach (v; vars)
892 //printf("v = %s\n", v.toChars());
893 assert(!v.isDataseg()); // these are not put in the closureVars[]
895 Dsymbol p = v.toParent2();
897 if (!(va && va.isScope()))
898 notMaybeScope(v, e);
900 if (!(v.isReference() || v.isScope()) || p != fd)
901 continue;
903 if (va && !va.isDataseg() && (va.isScope() || va.maybeScope))
905 /* Don't infer STC.scope_ for va, because then a closure
906 * won't be generated for fd.
908 //if (!va.isScope())
909 //va.storage_class |= STC.scope_ | STC.scopeinferred;
910 continue;
912 result |= sc.setUnsafeDIP1000(gag, ae.loc,
913 "reference to local `%s` assigned to non-scope `%s` in @safe code", v, e1);
917 foreach (Expression ee; er.byexp)
919 if (log) printf("byexp: %s\n", ee.toChars());
921 /* Do not allow slicing of a static array returned by a function
923 if (ee.op == EXP.call && ee.type.toBasetype().isTypeSArray() && e1.type.toBasetype().isTypeDArray() &&
924 !(va && va.storage_class & STC.temp))
926 if (!gag)
927 sc.eSink.deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`",
928 ee.toChars(), e1.toChars());
929 //result = true;
930 continue;
933 if (ee.op == EXP.call && ee.type.toBasetype().isTypeStruct() &&
934 (!va || !(va.storage_class & STC.temp) && !va.isScope()))
936 if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct temporary returned by `%s` assigned to longer lived variable `%s`", ee, e1))
938 result = true;
939 continue;
943 if (ee.op == EXP.structLiteral &&
944 (!va || !(va.storage_class & STC.temp)))
946 if (sc.setUnsafeDIP1000(gag, ee.loc, "address of struct literal `%s` assigned to longer lived variable `%s`", ee, e1))
948 result = true;
949 continue;
953 if (inferScope(va))
954 continue;
956 result |= sc.setUnsafeDIP1000(gag, ee.loc,
957 "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee, e1);
960 return result;
963 /************************************
964 * Detect cases where pointers to the stack can escape the
965 * lifetime of the stack frame when throwing `e`.
966 * Print error messages when these are detected.
967 * Params:
968 * sc = used to determine current function and module
969 * e = expression to check for any pointers to the stack
970 * gag = do not print error messages
971 * Returns:
972 * `true` if pointers to the stack can escape
974 public
975 bool checkThrowEscape(Scope* sc, Expression e, bool gag)
977 //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars());
978 EscapeByResults er;
980 escapeByValue(e, &er);
982 if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
983 return false;
985 bool result = false;
986 foreach (VarDeclaration v; er.byvalue)
988 //printf("byvalue %s\n", v.toChars());
989 if (v.isDataseg())
990 continue;
992 if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown
993 // despite being `scope`
995 // https://issues.dlang.org/show_bug.cgi?id=17029
996 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be thrown", v);
997 continue;
999 else
1001 notMaybeScope(v, new ThrowExp(e.loc, e));
1004 return result;
1007 /************************************
1008 * Detect cases where pointers to the stack can escape the
1009 * lifetime of the stack frame by being placed into a GC allocated object.
1010 * Print error messages when these are detected.
1011 * Params:
1012 * sc = used to determine current function and module
1013 * e = expression to check for any pointers to the stack
1014 * gag = do not print error messages
1015 * Returns:
1016 * `true` if pointers to the stack can escape
1018 public
1019 bool checkNewEscape(Scope* sc, Expression e, bool gag)
1021 import dmd.globals: FeatureState;
1022 import dmd.errors: previewErrorFunc;
1024 //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars());
1025 enum log = false;
1026 if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars());
1027 EscapeByResults er;
1029 escapeByValue(e, &er);
1031 if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
1032 return false;
1034 bool result = false;
1035 foreach (VarDeclaration v; er.byvalue)
1037 if (log) printf("byvalue `%s`\n", v.toChars());
1038 if (v.isDataseg())
1039 continue;
1041 Dsymbol p = v.toParent2();
1043 if (v.isScope())
1045 if (
1046 /* This case comes up when the ReturnStatement of a __foreachbody is
1047 * checked for escapes by the caller of __foreachbody. Skip it.
1049 * struct S { static int opApply(int delegate(S*) dg); }
1050 * S* foo() {
1051 * foreach (S* s; S) // create __foreachbody for body of foreach
1052 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
1053 * return null; }
1055 !(p.parent == sc.func))
1057 // https://issues.dlang.org/show_bug.cgi?id=20868
1058 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be copied into allocated memory", v);
1059 continue;
1062 else if (v.isTypesafeVariadicArray && p == sc.func)
1064 result |= sc.setUnsafeDIP1000(gag, e.loc,
1065 "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e, v);
1067 else
1069 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1070 notMaybeScope(v, e);
1074 foreach (VarDeclaration v; er.byref)
1076 if (log) printf("byref `%s`\n", v.toChars());
1078 // 'featureState' tells us whether to emit an error or a deprecation,
1079 // depending on the flag passed to the CLI for DIP25 / DIP1000
1080 bool escapingRef(VarDeclaration v, FeatureState fs)
1082 const(char)* msg = v.isParameter() ?
1083 "copying `%s` into allocated memory escapes a reference to parameter `%s`" :
1084 "copying `%s` into allocated memory escapes a reference to local variable `%s`";
1085 return sc.setUnsafePreview(fs, gag, e.loc, msg, e, v);
1088 if (v.isDataseg())
1089 continue;
1091 Dsymbol p = v.toParent2();
1093 if (!v.isReference())
1095 if (p == sc.func)
1097 result |= escapingRef(v, sc.useDIP1000);
1098 continue;
1102 /* Check for returning a ref variable by 'ref', but should be 'return ref'
1103 * Infer the addition of 'return', or set result to be the offending expression.
1105 if (!v.isReference())
1106 continue;
1108 // https://dlang.org/spec/function.html#return-ref-parameters
1109 if (p == sc.func)
1111 //printf("escaping reference to local ref variable %s\n", v.toChars());
1112 //printf("storage class = x%llx\n", v.storage_class);
1113 result |= escapingRef(v, sc.useDIP25);
1114 continue;
1116 // Don't need to be concerned if v's parent does not return a ref
1117 FuncDeclaration func = p.isFuncDeclaration();
1118 if (!func || !func.type)
1119 continue;
1120 if (auto tf = func.type.isTypeFunction())
1122 if (!tf.isref)
1123 continue;
1125 const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape";
1126 if (!gag)
1128 previewErrorFunc(sc.isDeprecated(), sc.useDIP25)(e.loc, msg, v.toChars());
1131 // If -preview=dip25 is used, the user wants an error
1132 // Otherwise, issue a deprecation
1133 result |= (sc.useDIP25 == FeatureState.enabled);
1137 foreach (Expression ee; er.byexp)
1139 if (log) printf("byexp %s\n", ee.toChars());
1140 if (!gag)
1141 sc.eSink.error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape",
1142 ee.toChars());
1143 result = true;
1146 return result;
1150 /************************************
1151 * Detect cases where pointers to the stack can escape the
1152 * lifetime of the stack frame by returning `e` by value.
1153 * Print error messages when these are detected.
1154 * Params:
1155 * sc = used to determine current function and module
1156 * e = expression to check for any pointers to the stack
1157 * gag = do not print error messages
1158 * Returns:
1159 * `true` if pointers to the stack can escape
1161 public
1162 bool checkReturnEscape(Scope* sc, Expression e, bool gag)
1164 //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars());
1165 return checkReturnEscapeImpl(sc, e, false, gag);
1168 /************************************
1169 * Detect cases where returning `e` by `ref` can result in a reference to the stack
1170 * being returned.
1171 * Print error messages when these are detected.
1172 * Params:
1173 * sc = used to determine current function and module
1174 * e = expression to check
1175 * gag = do not print error messages
1176 * Returns:
1177 * `true` if references to the stack can escape
1179 public
1180 bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag)
1182 version (none)
1184 printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars());
1185 printf("current function %s\n", sc.func.toChars());
1186 printf("parent2 function %s\n", sc.func.toParent2().toChars());
1189 return checkReturnEscapeImpl(sc, e, true, gag);
1192 /***************************************
1193 * Implementation of checking for escapes in return expressions.
1194 * Params:
1195 * sc = used to determine current function and module
1196 * e = expression to check
1197 * refs = `true`: escape by value, `false`: escape by `ref`
1198 * gag = do not print error messages
1199 * Returns:
1200 * `true` if references to the stack can escape
1202 private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag)
1204 enum log = false;
1205 if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars());
1206 EscapeByResults er;
1208 if (refs)
1209 escapeByRef(e, &er);
1210 else
1211 escapeByValue(e, &er);
1213 if (!er.byref.length && !er.byvalue.length && !er.byexp.length)
1214 return false;
1216 bool result = false;
1217 foreach (VarDeclaration v; er.byvalue)
1219 if (log) printf("byvalue `%s`\n", v.toChars());
1220 if (v.isDataseg())
1221 continue;
1223 const vsr = buildScopeRef(v.storage_class);
1225 Dsymbol p = v.toParent2();
1227 if (p == sc.func && inferReturn(sc.func, v, /*returnScope:*/ true))
1229 continue;
1232 if (v.isScope())
1234 /* If `return scope` applies to v.
1236 if (vsr == ScopeRef.ReturnScope ||
1237 vsr == ScopeRef.Ref_ReturnScope)
1239 continue;
1242 auto pfunc = p.isFuncDeclaration();
1243 if (pfunc &&
1244 /* This case comes up when the ReturnStatement of a __foreachbody is
1245 * checked for escapes by the caller of __foreachbody. Skip it.
1247 * struct S { static int opApply(int delegate(S*) dg); }
1248 * S* foo() {
1249 * foreach (S* s; S) // create __foreachbody for body of foreach
1250 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
1251 * return null; }
1253 !(!refs && p.parent == sc.func && pfunc.fes) &&
1255 * auto p(scope string s) {
1256 * string scfunc() { return s; }
1259 !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0)
1262 if (v.isParameter() && !v.isReturn())
1264 // https://issues.dlang.org/show_bug.cgi?id=23191
1265 if (!gag)
1267 previewErrorFunc(sc.isDeprecated(), sc.useDIP1000)(e.loc,
1268 "scope parameter `%s` may not be returned", v.toChars()
1270 result = true;
1271 continue;
1274 else
1276 // https://issues.dlang.org/show_bug.cgi?id=17029
1277 result |= sc.setUnsafeDIP1000(gag, e.loc, "scope variable `%s` may not be returned", v);
1278 continue;
1282 else if (v.isTypesafeVariadicArray && p == sc.func)
1284 if (!gag)
1285 sc.eSink.error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars());
1286 result = false;
1288 else
1290 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__);
1291 doNotInferScope(v, e);
1295 foreach (i, VarDeclaration v; er.byref[])
1297 if (log)
1299 printf("byref `%s` %s\n", v.toChars(), ScopeRefToChars(buildScopeRef(v.storage_class)));
1302 // 'featureState' tells us whether to emit an error or a deprecation,
1303 // depending on the flag passed to the CLI for DIP25
1304 void escapingRef(VarDeclaration v, FeatureState featureState)
1306 const(char)* msg = v.isParameter() ?
1307 "returning `%s` escapes a reference to parameter `%s`" :
1308 "returning `%s` escapes a reference to local variable `%s`";
1310 if (v.isParameter() && v.isReference())
1312 if (sc.setUnsafePreview(featureState, gag, e.loc, msg, e, v) ||
1313 sc.func.isSafeBypassingInference())
1315 result = true;
1316 if (v.storage_class & STC.returnScope)
1318 previewSupplementalFunc(sc.isDeprecated(), featureState)(v.loc,
1319 "perhaps change the `return scope` into `scope return`");
1321 else
1323 const(char)* annotateKind = (v.ident is Id.This) ? "function" : "parameter";
1324 previewSupplementalFunc(sc.isDeprecated(), featureState)(v.loc,
1325 "perhaps annotate the %s with `return`", annotateKind);
1329 else
1331 if (er.refRetRefTransition[i])
1333 result |= sc.setUnsafeDIP1000(gag, e.loc, msg, e, v);
1335 else
1337 if (!gag)
1338 previewErrorFunc(sc.isDeprecated(), featureState)(e.loc, msg, e.toChars(), v.toChars());
1339 result = true;
1344 if (v.isDataseg())
1345 continue;
1347 const vsr = buildScopeRef(v.storage_class);
1349 Dsymbol p = v.toParent2();
1351 // https://issues.dlang.org/show_bug.cgi?id=19965
1352 if (!refs)
1354 if (sc.func.vthis == v)
1355 notMaybeScope(v, e);
1357 if (checkScopeVarAddr(v, e, sc, gag))
1359 result = true;
1360 continue;
1364 if (!v.isReference())
1366 if (p == sc.func)
1368 escapingRef(v, FeatureState.enabled);
1369 continue;
1371 FuncDeclaration fd = p.isFuncDeclaration();
1372 if (fd && sc.func.returnInprocess)
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
1381 sc.func.storage_class |= STC.return_ | STC.returninferred;
1385 /* Check for returning a ref variable by 'ref', but should be 'return ref'
1386 * Infer the addition of 'return', or set result to be the offending expression.
1388 if ((vsr == ScopeRef.Ref ||
1389 vsr == ScopeRef.RefScope ||
1390 vsr == ScopeRef.Ref_ReturnScope) &&
1391 !(v.storage_class & STC.foreach_))
1393 if (p == sc.func && (vsr == ScopeRef.Ref || vsr == ScopeRef.RefScope) &&
1394 inferReturn(sc.func, v, /*returnScope:*/ false))
1396 continue;
1398 else
1400 // https://dlang.org/spec/function.html#return-ref-parameters
1401 // Only look for errors if in module listed on command line
1402 if (p == sc.func)
1404 //printf("escaping reference to local ref variable %s\n", v.toChars());
1405 //printf("storage class = x%llx\n", v.storage_class);
1406 escapingRef(v, sc.useDIP25);
1407 continue;
1409 // Don't need to be concerned if v's parent does not return a ref
1410 FuncDeclaration fd = p.isFuncDeclaration();
1411 if (fd && fd.type && fd.type.ty == Tfunction)
1413 TypeFunction tf = fd.type.isTypeFunction();
1414 if (tf.isref)
1416 const(char)* msg = "escaping reference to outer local variable `%s`";
1417 if (!gag)
1418 previewErrorFunc(sc.isDeprecated(), sc.useDIP25)(e.loc, msg, v.toChars());
1419 result = true;
1420 continue;
1428 foreach (i, Expression ee; er.byexp[])
1430 if (log) printf("byexp %s\n", ee.toChars());
1431 if (er.expRetRefTransition[i])
1433 result |= sc.setUnsafeDIP1000(gag, ee.loc,
1434 "escaping reference to stack allocated value returned by `%s`", ee);
1436 else
1438 if (!gag)
1439 sc.eSink.error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars());
1440 result = true;
1443 return result;
1446 /***********************************
1447 * Infer `scope` for a variable
1449 * Params:
1450 * va = variable to infer scope for
1451 * Returns: `true` if succesful or already `scope`
1453 private
1454 bool inferScope(VarDeclaration va)
1456 if (!va)
1457 return false;
1458 if (!va.isDataseg() && va.maybeScope && !va.isScope())
1460 //printf("inferring scope for %s\n", va.toChars());
1461 va.maybeScope = false;
1462 va.storage_class |= STC.scope_ | STC.scopeinferred;
1463 return true;
1465 return va.isScope();
1468 /*************************************
1469 * Variable v needs to have 'return' inferred for it.
1470 * Params:
1471 * fd = function that v is a parameter to
1472 * v = parameter that needs to be STC.return_
1473 * returnScope = infer `return scope` instead of `return ref`
1475 * Returns: whether the inference on `v` was successful or `v` already was `return`
1477 private bool inferReturn(FuncDeclaration fd, VarDeclaration v, bool returnScope)
1479 if (v.isReturn())
1480 return !!(v.storage_class & STC.returnScope) == returnScope;
1482 if (!v.isParameter() || v.isTypesafeVariadicArray || (returnScope && v.doNotInferReturn))
1483 return false;
1485 if (!fd.returnInprocess)
1486 return false;
1488 if (returnScope && !(v.isScope() || v.maybeScope))
1489 return false;
1491 //printf("for function '%s' inferring 'return' for variable '%s', returnScope: %d\n", fd.toChars(), v.toChars(), returnScope);
1492 auto newStcs = STC.return_ | STC.returninferred | (returnScope ? STC.returnScope : 0);
1493 v.storage_class |= newStcs;
1495 if (v == fd.vthis)
1497 /* v is the 'this' reference, so mark the function
1499 fd.storage_class |= newStcs;
1500 if (auto tf = fd.type.isTypeFunction())
1502 //printf("'this' too %p %s\n", tf, sc.func.toChars());
1503 tf.isreturnscope = returnScope;
1504 tf.isreturn = true;
1505 tf.isreturninferred = true;
1508 else
1510 // Perform 'return' inference on parameter
1511 if (auto tf = fd.type.isTypeFunction())
1513 foreach (i, p; tf.parameterList)
1515 if (p.ident == v.ident)
1517 p.storageClass |= newStcs;
1518 break; // there can be only one
1523 return true;
1527 /****************************************
1528 * e is an expression to be returned by value, and that value contains pointers.
1529 * Walk e to determine which variables are possibly being
1530 * returned by value, such as:
1531 * int* function(int* p) { return p; }
1532 * If e is a form of &p, determine which variables have content
1533 * which is being returned as ref, such as:
1534 * int* function(int i) { return &i; }
1535 * Multiple variables can be inserted, because of expressions like this:
1536 * int function(bool b, int i, int* p) { return b ? &i : p; }
1538 * No side effects.
1540 * Params:
1541 * e = expression to be returned by value
1542 * er = where to place collected data
1543 * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1544 * retRefTransition = if `e` is returned through a `return ref scope` function call
1546 public
1547 void escapeByValue(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false)
1549 //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars());
1551 void visit(Expression e)
1555 void visitAddr(AddrExp e)
1557 /* Taking the address of struct literal is normally not
1558 * allowed, but CTFE can generate one out of a new expression,
1559 * but it'll be placed in static data so no need to check it.
1561 if (e.e1.op != EXP.structLiteral)
1562 escapeByRef(e.e1, er, live, retRefTransition);
1565 void visitSymOff(SymOffExp e)
1567 VarDeclaration v = e.var.isVarDeclaration();
1568 if (v)
1569 er.pushRef(v, retRefTransition);
1572 void visitVar(VarExp e)
1574 if (auto v = e.var.isVarDeclaration())
1576 if (v.type.hasPointers() || // not tracking non-pointers
1577 v.storage_class & STC.lazy_) // lazy variables are actually pointers
1578 er.byvalue.push(v);
1582 void visitThis(ThisExp e)
1584 if (e.var)
1585 er.byvalue.push(e.var);
1588 void visitPtr(PtrExp e)
1590 if (live && e.type.hasPointers())
1591 escapeByValue(e.e1, er, live, retRefTransition);
1594 void visitDotVar(DotVarExp e)
1596 auto t = e.e1.type.toBasetype();
1597 if (e.type.hasPointers() && (live || t.ty == Tstruct))
1599 escapeByValue(e.e1, er, live, retRefTransition);
1603 void visitDelegate(DelegateExp e)
1605 Type t = e.e1.type.toBasetype();
1606 if (t.ty == Tclass || t.ty == Tpointer)
1607 escapeByValue(e.e1, er, live, retRefTransition);
1608 else
1609 escapeByRef(e.e1, er, live, retRefTransition);
1610 er.byfunc.push(e.func);
1613 void visitFunc(FuncExp e)
1615 if (e.fd.tok == TOK.delegate_)
1616 er.byfunc.push(e.fd);
1619 void visitTuple(TupleExp e)
1621 assert(0); // should have been lowered by now
1624 void visitArrayLiteral(ArrayLiteralExp e)
1626 Type tb = e.type.toBasetype();
1627 if (tb.ty == Tsarray || tb.ty == Tarray)
1629 if (e.basis)
1630 escapeByValue(e.basis, er, live, retRefTransition);
1631 foreach (el; *e.elements)
1633 if (el)
1634 escapeByValue(el, er, live, retRefTransition);
1639 void visitStructLiteral(StructLiteralExp e)
1641 if (e.elements)
1643 foreach (ex; *e.elements)
1645 if (ex)
1646 escapeByValue(ex, er, live, retRefTransition);
1651 void visitNew(NewExp e)
1653 Type tb = e.newtype.toBasetype();
1654 if (tb.ty == Tstruct && !e.member && e.arguments)
1656 foreach (ex; *e.arguments)
1658 if (ex)
1659 escapeByValue(ex, er, live, retRefTransition);
1664 void visitCast(CastExp e)
1666 if (!e.type.hasPointers())
1667 return;
1668 Type tb = e.type.toBasetype();
1669 if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray)
1671 escapeByRef(e.e1, er, live, retRefTransition);
1673 else
1674 escapeByValue(e.e1, er, live, retRefTransition);
1677 void visitSlice(SliceExp e)
1679 if (auto ve = e.e1.isVarExp())
1681 VarDeclaration v = ve.var.isVarDeclaration();
1682 Type tb = e.type.toBasetype();
1683 if (v)
1685 if (tb.ty == Tsarray)
1686 return;
1687 if (v.isTypesafeVariadicArray)
1689 er.byvalue.push(v);
1690 return;
1694 Type t1b = e.e1.type.toBasetype();
1695 if (t1b.ty == Tsarray)
1697 Type tb = e.type.toBasetype();
1698 if (tb.ty != Tsarray)
1699 escapeByRef(e.e1, er, live, retRefTransition);
1701 else
1702 escapeByValue(e.e1, er, live, retRefTransition);
1705 void visitIndex(IndexExp e)
1707 if (e.e1.type.toBasetype().ty == Tsarray ||
1708 live && e.type.hasPointers())
1710 escapeByValue(e.e1, er, live, retRefTransition);
1714 void visitBin(BinExp e)
1716 Type tb = e.type.toBasetype();
1717 if (tb.ty == Tpointer)
1719 escapeByValue(e.e1, er, live, retRefTransition);
1720 escapeByValue(e.e2, er, live, retRefTransition);
1724 void visitBinAssign(BinAssignExp e)
1726 escapeByValue(e.e1, er, live, retRefTransition);
1729 void visitAssign(AssignExp e)
1731 escapeByValue(e.e1, er, live, retRefTransition);
1734 void visitComma(CommaExp e)
1736 escapeByValue(e.e2, er, live, retRefTransition);
1739 void visitCond(CondExp e)
1741 escapeByValue(e.e1, er, live, retRefTransition);
1742 escapeByValue(e.e2, er, live, retRefTransition);
1745 void visitCall(CallExp e)
1747 //printf("CallExp(): %s\n", e.toChars());
1748 /* Check each argument that is
1749 * passed as 'return scope'.
1751 TypeFunction tf = e.calledFunctionType();
1752 if (!tf || !e.type.hasPointers())
1753 return;
1755 if (e.arguments && e.arguments.length)
1757 /* j=1 if _arguments[] is first argument,
1758 * skip it because it is not passed by ref
1760 int j = tf.isDstyleVariadic();
1761 for (size_t i = j; i < e.arguments.length; ++i)
1763 Expression arg = (*e.arguments)[i];
1764 size_t nparams = tf.parameterList.length;
1765 if (i - j < nparams && i >= j)
1767 Parameter p = tf.parameterList[i - j];
1768 const stc = tf.parameterStorageClass(null, p);
1769 ScopeRef psr = buildScopeRef(stc);
1770 if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
1772 if (tf.isref)
1774 /* ignore `ref` on struct constructor return because
1775 * struct S { this(return scope int* q) { this.p = q; } int* p; }
1776 * is different from:
1777 * ref char* front(return scope char** q) { return *q; }
1778 * https://github.com/dlang/dmd/pull/14869
1780 if (auto dve = e.e1.isDotVarExp())
1781 if (auto fd = dve.var.isFuncDeclaration())
1782 if (fd.isCtorDeclaration() && tf.next.toBasetype().isTypeStruct())
1784 escapeByValue(arg, er, live, retRefTransition);
1787 else
1788 escapeByValue(arg, er, live, retRefTransition);
1790 else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
1792 if (tf.isref)
1794 /* Treat:
1795 * ref P foo(return ref P p)
1796 * as:
1797 * p;
1799 escapeByValue(arg, er, live, retRefTransition);
1801 else
1802 escapeByRef(arg, er, live, retRefTransition);
1807 // If 'this' is returned, check it too
1808 Type t1 = e.e1.type.toBasetype();
1809 if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
1811 DotVarExp dve = e.e1.isDotVarExp();
1812 FuncDeclaration fd = dve.var.isFuncDeclaration();
1813 if (fd && fd.isThis())
1815 /* Calling a non-static member function dve.var, which is returning `this`, and with dve.e1 representing `this`
1818 /*****************************
1819 * Concoct storage class for member function's implicit `this` parameter.
1820 * Params:
1821 * fd = member function
1822 * Returns:
1823 * storage class for fd's `this`
1825 StorageClass getThisStorageClass(FuncDeclaration fd)
1827 StorageClass stc;
1828 auto tf = fd.type.toBasetype().isTypeFunction();
1829 if (tf.isreturn)
1830 stc |= STC.return_;
1831 if (tf.isreturnscope)
1832 stc |= STC.returnScope | STC.scope_;
1833 auto ad = fd.isThis();
1834 if (ad.isClassDeclaration() || tf.isScopeQual)
1835 stc |= STC.scope_;
1836 if (ad.isStructDeclaration())
1837 stc |= STC.ref_; // `this` for a struct member function is passed by `ref`
1838 return stc;
1841 const psr = buildScopeRef(getThisStorageClass(fd));
1842 if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
1844 if (!tf.isref || tf.isctor)
1845 escapeByValue(dve.e1, er, live, retRefTransition);
1847 else if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
1849 if (tf.isref)
1851 /* Treat calling:
1852 * struct S { ref S foo() return; }
1853 * as:
1854 * this;
1856 escapeByValue(dve.e1, er, live, retRefTransition);
1858 else
1859 escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope);
1863 // If it's also a nested function that is 'return scope'
1864 if (fd && fd.isNested())
1866 if (tf.isreturn && tf.isScopeQual)
1867 er.pushExp(e, false);
1871 /* If returning the result of a delegate call, the .ptr
1872 * field of the delegate must be checked.
1874 if (t1.isTypeDelegate())
1876 if (tf.isreturn)
1877 escapeByValue(e.e1, er, live, retRefTransition);
1880 /* If it's a nested function that is 'return scope'
1882 if (auto ve = e.e1.isVarExp())
1884 FuncDeclaration fd = ve.var.isFuncDeclaration();
1885 if (fd && fd.isNested())
1887 if (tf.isreturn && tf.isScopeQual)
1888 er.pushExp(e, false);
1893 switch (e.op)
1895 case EXP.address: return visitAddr(e.isAddrExp());
1896 case EXP.symbolOffset: return visitSymOff(e.isSymOffExp());
1897 case EXP.variable: return visitVar(e.isVarExp());
1898 case EXP.this_: return visitThis(e.isThisExp());
1899 case EXP.star: return visitPtr(e.isPtrExp());
1900 case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
1901 case EXP.delegate_: return visitDelegate(e.isDelegateExp());
1902 case EXP.function_: return visitFunc(e.isFuncExp());
1903 case EXP.tuple: return visitTuple(e.isTupleExp());
1904 case EXP.arrayLiteral: return visitArrayLiteral(e.isArrayLiteralExp());
1905 case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
1906 case EXP.new_: return visitNew(e.isNewExp());
1907 case EXP.cast_: return visitCast(e.isCastExp());
1908 case EXP.slice: return visitSlice(e.isSliceExp());
1909 case EXP.index: return visitIndex(e.isIndexExp());
1910 case EXP.blit: return visitAssign(e.isBlitExp());
1911 case EXP.construct: return visitAssign(e.isConstructExp());
1912 case EXP.assign: return visitAssign(e.isAssignExp());
1913 case EXP.comma: return visitComma(e.isCommaExp());
1914 case EXP.question: return visitCond(e.isCondExp());
1915 case EXP.call: return visitCall(e.isCallExp());
1916 default:
1917 if (auto b = e.isBinExp())
1918 return visitBin(b);
1919 if (auto ba = e.isBinAssignExp())
1920 return visitBinAssign(ba);
1921 return visit(e);
1926 /****************************************
1927 * e is an expression to be returned by 'ref'.
1928 * Walk e to determine which variables are possibly being
1929 * returned by ref, such as:
1930 * ref int function(int i) { return i; }
1931 * If e is a form of *p, determine which variables have content
1932 * which is being returned as ref, such as:
1933 * ref int function(int* p) { return *p; }
1934 * Multiple variables can be inserted, because of expressions like this:
1935 * ref int function(bool b, int i, int* p) { return b ? i : *p; }
1937 * No side effects.
1939 * Params:
1940 * e = expression to be returned by 'ref'
1941 * er = where to place collected data
1942 * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`.
1943 * retRefTransition = if `e` is returned through a `return ref scope` function call
1945 private
1946 void escapeByRef(Expression e, EscapeByResults* er, bool live = false, bool retRefTransition = false)
1948 //printf("[%s] escapeByRef, e: %s, retRefTransition: %d\n", e.loc.toChars(), e.toChars(), retRefTransition);
1949 void visit(Expression e)
1953 void visitVar(VarExp e)
1955 auto v = e.var.isVarDeclaration();
1956 if (v)
1958 if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init)
1960 /* If compiler generated ref temporary
1961 * (ref v = ex; ex)
1962 * look at the initializer instead
1964 if (ExpInitializer ez = v._init.isExpInitializer())
1966 if (auto ce = ez.exp.isConstructExp())
1967 escapeByRef(ce.e2, er, live, retRefTransition);
1968 else
1969 escapeByRef(ez.exp, er, live, retRefTransition);
1972 else
1973 er.pushRef(v, retRefTransition);
1977 void visitThis(ThisExp e)
1979 if (e.var && e.var.toParent2().isFuncDeclaration().hasDualContext())
1980 escapeByValue(e, er, live, retRefTransition);
1981 else if (e.var)
1982 er.pushRef(e.var, retRefTransition);
1985 void visitPtr(PtrExp e)
1987 escapeByValue(e.e1, er, live, retRefTransition);
1990 void visitIndex(IndexExp e)
1992 Type tb = e.e1.type.toBasetype();
1993 if (auto ve = e.e1.isVarExp())
1995 VarDeclaration v = ve.var.isVarDeclaration();
1996 if (v && v.isTypesafeVariadicArray)
1998 er.pushRef(v, retRefTransition);
1999 return;
2002 if (tb.ty == Tsarray)
2004 escapeByRef(e.e1, er, live, retRefTransition);
2006 else if (tb.ty == Tarray)
2008 escapeByValue(e.e1, er, live, retRefTransition);
2012 void visitStructLiteral(StructLiteralExp e)
2014 if (e.elements)
2016 foreach (ex; *e.elements)
2018 if (ex)
2019 escapeByRef(ex, er, live, retRefTransition);
2022 er.pushExp(e, retRefTransition);
2025 void visitDotVar(DotVarExp e)
2027 Type t1b = e.e1.type.toBasetype();
2028 if (t1b.ty == Tclass)
2029 escapeByValue(e.e1, er, live, retRefTransition);
2030 else
2031 escapeByRef(e.e1, er, live, retRefTransition);
2034 void visitBinAssign(BinAssignExp e)
2036 escapeByRef(e.e1, er, live, retRefTransition);
2039 void visitAssign(AssignExp e)
2041 escapeByRef(e.e1, er, live, retRefTransition);
2044 void visitComma(CommaExp e)
2046 escapeByRef(e.e2, er, live, retRefTransition);
2049 void visitCond(CondExp e)
2051 escapeByRef(e.e1, er, live, retRefTransition);
2052 escapeByRef(e.e2, er, live, retRefTransition);
2055 void visitCall(CallExp e)
2057 //printf("escapeByRef.CallExp(): %s\n", e.toChars());
2058 /* If the function returns by ref, check each argument that is
2059 * passed as 'return ref'.
2061 TypeFunction tf = e.calledFunctionType();
2062 if (!tf)
2063 return;
2064 if (tf.isref)
2066 if (e.arguments && e.arguments.length)
2068 /* j=1 if _arguments[] is first argument,
2069 * skip it because it is not passed by ref
2071 int j = tf.isDstyleVariadic();
2072 for (size_t i = j; i < e.arguments.length; ++i)
2074 Expression arg = (*e.arguments)[i];
2075 size_t nparams = tf.parameterList.length;
2076 if (i - j < nparams && i >= j)
2078 Parameter p = tf.parameterList[i - j];
2079 const stc = tf.parameterStorageClass(null, p);
2080 ScopeRef psr = buildScopeRef(stc);
2081 if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
2082 escapeByRef(arg, er, live, retRefTransition);
2083 else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
2085 if (auto de = arg.isDelegateExp())
2087 if (de.func.isNested())
2088 er.pushExp(de, false);
2090 else
2091 escapeByValue(arg, er, live, retRefTransition);
2096 // If 'this' is returned by ref, check it too
2097 Type t1 = e.e1.type.toBasetype();
2098 if (e.e1.op == EXP.dotVariable && t1.ty == Tfunction)
2100 DotVarExp dve = e.e1.isDotVarExp();
2102 // https://issues.dlang.org/show_bug.cgi?id=20149#c10
2103 if (dve.var.isCtorDeclaration())
2105 er.pushExp(e, false);
2106 return;
2109 StorageClass stc = dve.var.storage_class & (STC.return_ | STC.scope_ | STC.ref_);
2110 if (tf.isreturn)
2111 stc |= STC.return_;
2112 if (tf.isref)
2113 stc |= STC.ref_;
2114 if (tf.isScopeQual)
2115 stc |= STC.scope_;
2116 if (tf.isreturnscope)
2117 stc |= STC.returnScope;
2119 const psr = buildScopeRef(stc);
2120 if (psr == ScopeRef.ReturnRef || psr == ScopeRef.ReturnRef_Scope)
2121 escapeByRef(dve.e1, er, live, psr == ScopeRef.ReturnRef_Scope);
2122 else if (psr == ScopeRef.ReturnScope || psr == ScopeRef.Ref_ReturnScope)
2123 escapeByValue(dve.e1, er, live, retRefTransition);
2125 // If it's also a nested function that is 'return ref'
2126 if (FuncDeclaration fd = dve.var.isFuncDeclaration())
2128 if (fd.isNested() && tf.isreturn)
2130 er.pushExp(e, false);
2134 // If it's a delegate, check it too
2135 if (e.e1.op == EXP.variable && t1.ty == Tdelegate)
2137 escapeByValue(e.e1, er, live, retRefTransition);
2140 /* If it's a nested function that is 'return ref'
2142 if (auto ve = e.e1.isVarExp())
2144 FuncDeclaration fd = ve.var.isFuncDeclaration();
2145 if (fd && fd.isNested())
2147 if (tf.isreturn)
2148 er.pushExp(e, false);
2152 else
2153 er.pushExp(e, retRefTransition);
2156 switch (e.op)
2158 case EXP.variable: return visitVar(e.isVarExp());
2159 case EXP.this_: return visitThis(e.isThisExp());
2160 case EXP.star: return visitPtr(e.isPtrExp());
2161 case EXP.structLiteral: return visitStructLiteral(e.isStructLiteralExp());
2162 case EXP.dotVariable: return visitDotVar(e.isDotVarExp());
2163 case EXP.index: return visitIndex(e.isIndexExp());
2164 case EXP.blit: return visitAssign(e.isBlitExp());
2165 case EXP.construct: return visitAssign(e.isConstructExp());
2166 case EXP.assign: return visitAssign(e.isAssignExp());
2167 case EXP.comma: return visitComma(e.isCommaExp());
2168 case EXP.question: return visitCond(e.isCondExp());
2169 case EXP.call: return visitCall(e.isCallExp());
2170 default:
2171 if (auto ba = e.isBinAssignExp())
2172 return visitBinAssign(ba);
2173 return visit(e);
2177 /************************************
2178 * Aggregate the data collected by the escapeBy??() functions.
2180 public
2181 struct EscapeByResults
2183 VarDeclarations byref; // array into which variables being returned by ref are inserted
2184 VarDeclarations byvalue; // array into which variables with values containing pointers are inserted
2185 private FuncDeclarations byfunc; // nested functions that are turned into delegates
2186 private Expressions byexp; // array into which temporaries being returned by ref are inserted
2188 import dmd.root.array: Array;
2191 * Whether the variable / expression went through a `return ref scope` function call
2193 * This is needed for the dip1000 by default transition, since the rules for
2194 * disambiguating `return scope ref` have changed. Therefore, functions in legacy code
2195 * can be mistakenly treated as `return ref` making the compiler believe stack variables
2196 * are being escaped, which is an error even in `@system` code. By keeping track of this
2197 * information, variables escaped through `return ref` can be treated as a deprecation instead
2198 * of error, see test/fail_compilation/dip1000_deprecation.d
2200 private Array!bool refRetRefTransition;
2201 private Array!bool expRetRefTransition;
2203 /** Reset arrays so the storage can be used again
2205 void reset()
2207 byref.setDim(0);
2208 byvalue.setDim(0);
2209 byfunc.setDim(0);
2210 byexp.setDim(0);
2212 refRetRefTransition.setDim(0);
2213 expRetRefTransition.setDim(0);
2217 * Escape variable `v` by reference
2218 * Params:
2219 * v = variable to escape
2220 * retRefTransition = `v` is escaped through a `return ref scope` function call
2222 void pushRef(VarDeclaration v, bool retRefTransition)
2224 byref.push(v);
2225 refRetRefTransition.push(retRefTransition);
2229 * Escape a reference to expression `e`
2230 * Params:
2231 * e = expression to escape
2232 * retRefTransition = `e` is escaped through a `return ref scope` function call
2234 void pushExp(Expression e, bool retRefTransition)
2236 byexp.push(e);
2237 expRetRefTransition.push(retRefTransition);
2241 /*************************
2242 * Find all variables accessed by this delegate that are
2243 * in functions enclosing it.
2244 * Params:
2245 * fd = function
2246 * vars = array to append found variables to
2248 public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars)
2250 //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
2251 for (auto p = fd.parent; p; p = p.parent)
2253 auto fdp = p.isFuncDeclaration();
2254 if (!fdp)
2255 continue;
2257 foreach (v; fdp.closureVars)
2259 foreach (const fdv; v.nestedrefs)
2261 if (fdv == fd)
2263 //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
2264 vars.push(v);
2271 /***********************************
2272 * Turn off `maybeScope` for variable `v`.
2274 * This exists in order to find where `maybeScope` is getting turned off.
2275 * Params:
2276 * v = variable
2277 * o = reason for it being turned off:
2278 * - `Expression` such as `throw e` or `&e`
2279 * - `VarDeclaration` of a non-scope parameter it was assigned to
2280 * - `null` for no reason
2282 private void notMaybeScope(VarDeclaration v, RootObject o)
2284 if (v.maybeScope)
2286 v.maybeScope = false;
2287 if (o && v.isParameter())
2288 EscapeState.scopeInferFailure[v.sequenceNumber] = o;
2292 /***********************************
2293 * Turn off `maybeScope` for variable `v` if it's not a parameter.
2295 * This is for compatibility with the old system with both `STC.maybescope` and `VarDeclaration.doNotInferScope`,
2296 * which is now just `VarDeclaration.maybeScope`.
2297 * This function should probably be removed in future refactors.
2299 * Params:
2300 * v = variable
2301 * o = reason for it being turned off
2303 private void doNotInferScope(VarDeclaration v, RootObject o)
2305 if (!v.isParameter)
2306 notMaybeScope(v, o);
2309 /***********************************
2310 * After semantic analysis of the function body,
2311 * try to infer `scope` / `return` on the parameters
2313 * Params:
2314 * funcdecl = function declaration that was analyzed
2315 * f = final function type. `funcdecl.type` started as the 'premature type' before attribute
2316 * inference, then its inferred attributes are copied over to final type `f`
2318 public
2319 void finishScopeParamInference(FuncDeclaration funcdecl, ref TypeFunction f)
2322 if (funcdecl.returnInprocess)
2324 funcdecl.returnInprocess = false;
2325 if (funcdecl.storage_class & STC.return_)
2327 if (funcdecl.type == f)
2328 f = cast(TypeFunction)f.copy();
2329 f.isreturn = true;
2330 f.isreturnscope = cast(bool) (funcdecl.storage_class & STC.returnScope);
2331 if (funcdecl.storage_class & STC.returninferred)
2332 f.isreturninferred = true;
2336 if (!funcdecl.inferScope)
2337 return;
2338 funcdecl.inferScope = false;
2340 // Eliminate maybescope's
2342 // Create and fill array[] with maybe candidates from the `this` and the parameters
2343 VarDeclaration[10] tmp = void;
2344 size_t dim = (funcdecl.vthis !is null) + (funcdecl.parameters ? funcdecl.parameters.length : 0);
2346 import dmd.common.smallbuffer : SmallBuffer;
2347 auto sb = SmallBuffer!VarDeclaration(dim, tmp[]);
2348 VarDeclaration[] array = sb[];
2350 size_t n = 0;
2351 if (funcdecl.vthis)
2352 array[n++] = funcdecl.vthis;
2353 if (funcdecl.parameters)
2355 foreach (v; *funcdecl.parameters)
2357 array[n++] = v;
2360 eliminateMaybeScopes(array[0 .. n]);
2363 // Infer STC.scope_
2364 if (funcdecl.parameters && !funcdecl.errors)
2366 assert(f.parameterList.length == funcdecl.parameters.length);
2367 foreach (u, p; f.parameterList)
2369 auto v = (*funcdecl.parameters)[u];
2370 if (!v.isScope() && v.type.hasPointers() && inferScope(v))
2372 //printf("Inferring scope for %s\n", v.toChars());
2373 p.storageClass |= STC.scope_ | STC.scopeinferred;
2378 if (funcdecl.vthis)
2380 inferScope(funcdecl.vthis);
2381 f.isScopeQual = funcdecl.vthis.isScope();
2382 f.isscopeinferred = !!(funcdecl.vthis.storage_class & STC.scopeinferred);
2386 /**********************************************
2387 * Have some variables that are maybescopes that were
2388 * assigned values from other maybescope variables.
2389 * Now that semantic analysis of the function is
2390 * complete, we can finalize this by turning off
2391 * maybescope for array elements that cannot be scope.
2393 * $(TABLE2 Scope Table,
2394 * $(THEAD `va`, `v`, =>, `va` , `v` )
2395 * $(TROW maybe, maybe, =>, scope, scope)
2396 * $(TROW scope, scope, =>, scope, scope)
2397 * $(TROW scope, maybe, =>, scope, scope)
2398 * $(TROW maybe, scope, =>, scope, scope)
2399 * $(TROW - , - , =>, - , - )
2400 * $(TROW - , maybe, =>, - , - )
2401 * $(TROW - , scope, =>, error, error)
2402 * $(TROW maybe, - , =>, scope, - )
2403 * $(TROW scope, - , =>, scope, - )
2405 * Params:
2406 * array = array of variables that were assigned to from maybescope variables
2408 private void eliminateMaybeScopes(VarDeclaration[] array)
2410 enum log = false;
2411 if (log) printf("eliminateMaybeScopes()\n");
2412 bool changes;
2415 changes = false;
2416 foreach (va; array)
2418 if (log) printf(" va = %s\n", va.toChars());
2419 if (!(va.maybeScope || va.isScope()))
2421 if (va.maybes)
2423 foreach (v; *va.maybes)
2425 if (log) printf(" v = %s\n", v.toChars());
2426 if (v.maybeScope)
2428 // v cannot be scope since it is assigned to a non-scope va
2429 notMaybeScope(v, va);
2430 if (!v.isReference())
2431 v.storage_class &= ~(STC.return_ | STC.returninferred);
2432 changes = true;
2438 } while (changes);
2441 /************************************************
2442 * Is type a reference to a mutable value?
2444 * This is used to determine if an argument that does not have a corresponding
2445 * Parameter, i.e. a variadic argument, is a pointer to mutable data.
2446 * Params:
2447 * t = type of the argument
2448 * Returns:
2449 * true if it's a pointer (or reference) to mutable data
2451 private
2452 bool isReferenceToMutable(Type t)
2454 t = t.baseElemOf();
2456 if (!t.isMutable() ||
2457 !t.hasPointers())
2458 return false;
2460 switch (t.ty)
2462 case Tpointer:
2463 if (t.nextOf().isTypeFunction())
2464 break;
2465 goto case;
2467 case Tarray:
2468 case Taarray:
2469 case Tdelegate:
2470 if (t.nextOf().isMutable())
2471 return true;
2472 break;
2474 case Tclass:
2475 return true; // even if the class fields are not mutable
2477 case Tstruct:
2478 // Have to look at each field
2479 foreach (VarDeclaration v; t.isTypeStruct().sym.fields)
2481 if (v.storage_class & STC.ref_)
2483 if (v.type.isMutable())
2484 return true;
2486 else if (v.type.isReferenceToMutable())
2487 return true;
2489 break;
2491 case Tnull:
2492 return false;
2494 default:
2495 assert(0);
2497 return false;
2500 /****************************************
2501 * Is parameter a reference to a mutable value?
2503 * This is used if an argument has a corresponding Parameter.
2504 * The argument type is necessary if the Parameter is inout.
2505 * Params:
2506 * p = Parameter to check
2507 * t = type of corresponding argument
2508 * Returns:
2509 * true if it's a pointer (or reference) to mutable data
2511 private
2512 bool isReferenceToMutable(Parameter p, Type t)
2514 if (p.isReference())
2516 if (p.type.isConst() || p.type.isImmutable())
2517 return false;
2518 if (p.type.isWild())
2520 return t.isMutable();
2522 return p.type.isMutable();
2524 return isReferenceToMutable(p.type);
2527 /// When checking lifetime for assignment `va=v`, the way `va` encloses `v`
2528 private enum EnclosedBy
2530 none = 0,
2531 refVar, // `va` is a `ref` variable, which may link to a global variable
2532 global, // `va` is a global variable
2533 returnScope, // `va` is a scope variable that may be returned
2534 longerScope, // `va` is another scope variable declared earlier than `v`
2537 /**********************************
2538 * Determine if `va` has a lifetime that lasts past
2539 * the destruction of `v`
2540 * Params:
2541 * va = variable assigned to
2542 * v = variable being assigned
2543 * Returns:
2544 * The way `va` encloses `v` (if any)
2546 private EnclosedBy enclosesLifetimeOf(VarDeclaration va, VarDeclaration v)
2548 if (!va)
2549 return EnclosedBy.none;
2551 if (va.isDataseg())
2552 return EnclosedBy.global;
2554 if (va.isScope() && va.isReturn() && !v.isReturn())
2555 return EnclosedBy.returnScope;
2557 if (va.isReference() && va.isParameter())
2558 return EnclosedBy.refVar;
2560 assert(va.sequenceNumber != va.sequenceNumber.init);
2561 assert(v.sequenceNumber != v.sequenceNumber.init);
2562 if (va.sequenceNumber < v.sequenceNumber)
2563 return EnclosedBy.longerScope;
2565 return EnclosedBy.none;
2568 /***************************************
2569 * Add variable `v` to maybes[]
2571 * When a maybescope variable `v` is assigned to a maybescope variable `va`,
2572 * we cannot determine if `this` is actually scope until the semantic
2573 * analysis for the function is completed. Thus, we save the data
2574 * until then.
2575 * Params:
2576 * v = a variable with `maybeScope == true` that was assigned to `this`
2578 private void addMaybe(VarDeclaration va, VarDeclaration v)
2580 //printf("add %s to %s's list of dependencies\n", v.toChars(), toChars());
2581 if (!va.maybes)
2582 va.maybes = new VarDeclarations();
2583 va.maybes.push(v);
2586 // `setUnsafePreview` partially evaluated for dip1000
2587 public
2588 bool setUnsafeDIP1000(Scope* sc, bool gag, Loc loc, const(char)* msg,
2589 RootObject arg0 = null, RootObject arg1 = null, RootObject arg2 = null)
2591 return setUnsafePreview(sc, sc.useDIP1000, gag, loc, msg, arg0, arg1, arg2);
2594 /***************************************
2595 * Check that taking the address of `v` is `@safe`
2597 * It's not possible to take the address of a scope variable, because `scope` only applies
2598 * to the top level indirection.
2600 * Params:
2601 * v = variable that a reference is created
2602 * e = expression that takes the referene
2603 * sc = used to obtain function / deprecated status
2604 * gag = don't print errors
2605 * Returns:
2606 * true if taking the address of `v` is problematic because of the lack of transitive `scope`
2608 private bool checkScopeVarAddr(VarDeclaration v, Expression e, Scope* sc, bool gag)
2610 if (v.storage_class & STC.temp)
2611 return false;
2613 if (!v.isScope())
2615 notMaybeScope(v, e);
2616 return false;
2619 if (!e.type)
2620 return false;
2622 // When the type after dereferencing has no pointers, it's okay.
2623 // Comes up when escaping `&someStruct.intMember` of a `scope` struct:
2624 // scope does not apply to the `int`
2625 Type t = e.type.baseElemOf();
2626 if ((t.ty == Tarray || t.ty == Tpointer) && !t.nextOf().toBasetype().hasPointers())
2627 return false;
2629 // take address of `scope` variable not allowed, requires transitive scope
2630 return sc.setUnsafeDIP1000(gag, e.loc,
2631 "cannot take address of `scope` variable `%s` since `scope` applies to first indirection only", v);
2634 /****************************
2635 * Determine if `v` is a typesafe variadic array, which is implicitly `scope`
2636 * Params:
2637 * v = variable to check
2638 * Returns:
2639 * true if `v` is a variadic parameter
2641 private bool isTypesafeVariadicArray(VarDeclaration v)
2643 if (v.storage_class & STC.variadic)
2645 Type tb = v.type.toBasetype();
2646 if (tb.ty == Tarray || tb.ty == Tsarray)
2647 return true;
2649 return false;