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
14 import core
.stdc
.stdio
: printf
;
15 import core
.stdc
.stdlib
;
16 import core
.stdc
.string
;
22 import dmd
.declaration
;
26 import dmd
.expression
;
28 import dmd
.globals
: FeatureState
;
30 import dmd
.identifier
;
35 import dmd
.rootobject
;
38 import dmd
.arraytypes
;
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
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.
65 * sc = used to determine current function and module
66 * fd = function being called
68 * ethis = if not null, the `this` pointer
69 * arguments = actual arguments to function
70 * gag = do not print error messages
75 bool checkMutableArguments(Scope
* sc
, FuncDeclaration fd
, TypeFunction tf
,
76 Expression ethis
, Expressions
* arguments
, bool gag
)
79 if (log
) printf("[%s] checkMutableArguments, fd: `%s`\n", fd
.loc
.toChars(), fd
.toChars());
80 if (log
&& ethis
) printf("ethis: `%s`\n", ethis
.toChars());
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
;
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
)
107 if (i
< arguments
.length
)
109 arg
= (*arguments
)[i
];
112 eb
.param
= tf
.parameterList
[i
];
113 refs
= eb
.param
.isReference();
114 eb
.isMutable
= eb
.param
.isReferenceToMutable(arg
.type
);
120 eb
.isMutable
= arg
.type
.isReferenceToMutable();
125 /* ethis is passed by value if a class reference,
126 * by ref if a struct value
130 auto ad
= fd
.isThis();
133 if (ad
.isClassDeclaration())
136 eb
.isMutable
= arg
.type
.isReferenceToMutable();
140 assert(ad
.isStructDeclaration());
142 eb
.isMutable
= arg
.type
.isMutable();
147 // outer variables are passed by ref
150 auto var
= outerVars
[i
- (len
- outerVars
.length
)];
151 eb
.isMutable
= var
.type
.isMutable();
152 eb
.er
.pushRef(var
, false);
157 escapeByRef(arg
, &eb
.er
);
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());
168 //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable);
169 if (!(eb
.isMutable || eb2
.isMutable
))
172 if (!tf
.islive
&& !(sc
.useDIP1000
== FeatureState
.enabled
&& sc
.func
&& sc
.func
.setUnsafe()))
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
,
188 fd ? fd
.toPrettyChars() : "indirectly");
193 void escape(size_t i
, ref EscapeBy eb
, bool byval
)
195 foreach (VarDeclaration v
; byval ? eb
.er
.byvalue
: eb
.er
.byref
)
199 const(char)* by
= byval ?
"byval" : "byref";
200 printf("%s %s\n", by
, v
.toChars());
202 if (byval
&& !v
.type
.hasPointers())
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])
216 escape(i
, eb
, false);
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.
226 * sc = used to determine current function and module
227 * ae = array literal expression
228 * gag = do not print error messages
230 * `true` if any elements escaped
233 bool checkArrayLiteralEscape(Scope
*sc
, ArrayLiteralExp ae
, bool gag
)
237 errors
= checkNewEscape(sc
, ae
.basis
, gag
);
238 foreach (ex
; *ae
.elements
)
241 errors |
= checkNewEscape(sc
, ex
, gag
);
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.
250 * sc = used to determine current function and module
251 * ae = associative array literal expression
252 * gag = do not print error messages
254 * `true` if any elements escaped
257 bool checkAssocArrayLiteralEscape(Scope
*sc
, AssocArrayLiteralExp ae
, bool gag
)
260 foreach (ex
; *ae
.keys
)
263 errors |
= checkNewEscape(sc
, ex
, gag
);
265 foreach (ex
; *ae
.values
)
268 errors |
= checkNewEscape(sc
, ex
, gag
);
274 * A `scope` variable was assigned to non-scope parameter `v`.
275 * If applicable, print why the parameter was not inferred `scope`.
278 * printFunc = error/deprecation print function to use
279 * v = parameter that was not inferred
280 * recursionLimit = recursion limit for printing the reason
283 void printScopeFailure(E
)(E printFunc
, VarDeclaration v
, int recursionLimit
)
286 if (recursionLimit
< 0 ||
!v
)
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());
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
);
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.
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
323 * `true` if pointers to the stack can escape via assignment
326 bool checkParamArgumentEscape(Scope
* sc
, FuncDeclaration fdc
, Identifier parId
, VarDeclaration vPar
, STC parStc
, Expression arg
, bool assertmsg
, bool gag
)
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())
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
346 er
.byvalue
.setDim(0);
350 if (!er
.byref
.length
&& !er
.byvalue
.length
&& !er
.byfunc
.length
&& !er
.byexp
.length
)
355 /* 'v' is assigned unsafely to 'par'
357 void unsafeAssign(string desc
)(VarDeclaration v
)
361 result |
= sc
.setUnsafeDIP1000(gag
, arg
.loc
,
362 desc
~ " `%s` assigned to non-scope parameter calling `assert()`", v
);
366 bool isThis
= fdc
&& fdc
.needThis() && fdc
.vthis
== vPar
; // implicit `this` parameter to member function
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");
376 sc
.setUnsafeDIP1000(gag
, arg
.loc
, msg
, arg
, fdc
.toParent2(), fdc
) :
377 sc
.setUnsafeDIP1000(gag
, arg
.loc
, msg
, v
, parId ? parId
: fdc
, fdc
))
380 printScopeFailure(previewSupplementalFunc(sc
.isDeprecated(), sc
.useDIP1000
), vPar
, 10);
384 foreach (VarDeclaration v
; er
.byvalue
)
386 if (log
) printf("byvalue %s\n", v
.toChars());
390 Dsymbol p
= v
.toParent2();
392 notMaybeScope(v
, vPar
);
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());
410 Dsymbol p
= v
.toParent2();
412 notMaybeScope(v
, arg
);
413 if (checkScopeVarAddr(v
, arg
, sc
, gag
))
419 if (p
== sc
.func
&& !(parStc
& STC
.scope_
))
421 unsafeAssign
!"reference to local variable"(v
);
426 foreach (FuncDeclaration fd
; er
.byfunc
)
428 //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf);
429 VarDeclarations vars
;
430 findAllOuterAccessedVariables(fd
, &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
);
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
);
464 /*****************************************************
465 * Function argument initializes a `return` parameter,
466 * and that parameter gets assigned to `firstArg`.
467 * Essentially, treat as `firstArg = arg;`
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
475 * `true` if assignment to `firstArg` would cause an error
478 bool checkParamArgumentReturn(Scope
* sc
, Expression firstArg
, Expression arg
, Parameter param
, bool gag
)
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_
))
488 if (!arg
.type
.hasPointers() && !param
.isReference())
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
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
511 * `true` if construction would cause an escaping reference error
514 bool checkConstructorEscape(Scope
* sc
, CallExp ce
, bool gag
)
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())
523 if (!ce
.arguments
&& ce
.arguments
.length
)
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
))
554 /// How a `return` parameter escapes its pointer value
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
574 * tthis = type of `this` parameter, or `null` if none
575 * Returns: What a `return` parameter should transfer the lifetime of the argument to
578 ReturnParamDest
returnParamDest(TypeFunction tf
, Type tthis
)
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.
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`
608 * `true` if pointers to the stack can escape via assignment
611 bool checkAssignEscape(Scope
* sc
, Expression e
, bool gag
, bool byRef
)
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
)
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())
628 if (VarDeclaration va
= expToVariable(e1
))
630 if (!va
.type
.toBasetype().isTypeSArray() ||
// treat static array slice same as a variable
631 !va
.type
.hasPointers())
638 /* The struct literal case can arise from the S(e2) constructor call:
640 * and appears in this function as:
641 * structLiteral = e2;
642 * Such an assignment does not necessarily remove scope-ness.
644 if (e1
.isStructLiteralExp())
647 VarDeclaration va
= expToVariable(e1
);
651 escapeByRef(e2
, &er
);
653 escapeByValue(e2
, &er
);
655 if (!er
.byref
.length
&& !er
.byvalue
.length
&& !er
.byfunc
.length
&& !er
.byexp
.length
)
659 if (va
&& e
.op
== EXP
.concatenateElemAssign
)
661 /* https://issues.dlang.org/show_bug.cgi?id=17842
662 * Draw an equivalence between:
666 * since we are not assigning to va, but are assigning indirectly through va.
671 if (va
&& e1
.isDotVarExp() && va
.type
.toBasetype().isTypeClass())
673 /* https://issues.dlang.org/show_bug.cgi?id=17949
674 * Draw an equivalence between:
678 * since we are not assigning to va, but are assigning indirectly through class reference va.
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
694 bool vaIsFirstRef
= false;
697 final switch (returnParamDest(fd
.type
.isTypeFunction(), fd
.vthis ? fd
.vthis
.type
: null))
699 case ReturnParamDest
.this_
:
700 vaIsFirstRef
= va
== fd
.vthis
;
702 case ReturnParamDest
.firstArg
:
703 vaIsFirstRef
= (*fd
.parameters
)[0] == va
;
705 case ReturnParamDest
.returnVal
:
709 if (log
&& vaIsFirstRef
) printf("va is first ref `%s`\n", va
.toChars());
712 foreach (VarDeclaration v
; er
.byvalue
)
714 if (log
) printf("byvalue: %s\n", v
.toChars());
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
) &&
728 /* Add v to va's list of dependencies
734 if (vaIsFirstRef
&& p
== fd
)
736 inferReturn(fd
, v
, /*returnScope:*/ true);
739 if (!(va
&& va
.isScope()) || vaIsRef
)
744 if (vaIsFirstRef
&& v
.isParameter() && v
.isReturn())
746 // va=v, where v is `return scope`
751 // If va's lifetime encloses v's, then error
752 if (EnclosedBy eb
= va
.enclosesLifetimeOf(v
))
757 case EnclosedBy
.none
: assert(0);
758 case EnclosedBy
.returnScope
:
759 msg
= "scope variable `%s` assigned to return scope `%s`";
761 case EnclosedBy
.longerScope
:
762 if (v
.storage_class
& STC
.temp
)
764 msg
= "scope variable `%s` assigned to `%s` with longer lifetime";
766 case EnclosedBy
.refVar
:
767 msg
= "scope variable `%s` assigned to `ref` variable `%s` with longer lifetime";
769 case EnclosedBy
.global
:
770 msg
= "scope variable `%s` assigned to global variable `%s`";
774 if (sc
.setUnsafeDIP1000(gag
, ae
.loc
, msg
, v
, va
))
781 // v = scope, va should be scope as well
782 const vaWasScope
= va
&& va
.isScope();
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
;
797 result |
= sc
.setUnsafeDIP1000(gag
, ae
.loc
, "scope variable `%s` assigned to non-scope `%s`", v
, e1
);
799 else if (v
.isTypesafeVariadicArray
&& p
== fd
)
803 result |
= sc
.setUnsafeDIP1000(gag
, ae
.loc
, "variadic variable `%s` assigned to non-scope `%s`", v
, e1
);
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());
821 if (checkScopeVarAddr(v
, ae
, sc
, gag
))
827 if (va
&& va
.isScope() && !v
.isReference())
831 va
.doNotInferReturn
= true;
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
))
858 if (!(va
&& va
.isScope()))
866 if (v
.isReturn() && !va
.isReturn())
867 va
.storage_class |
= STC
.return_ | STC
.returninferred
;
870 if (e1
.op
== EXP
.structLiteral
)
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
;
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()))
900 if (!(v
.isReference() || v
.isScope()) || p
!= fd
)
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.
909 //va.storage_class |= STC.scope_ | STC.scopeinferred;
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
))
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());
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
))
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
))
956 result |
= sc
.setUnsafeDIP1000(gag
, ee
.loc
,
957 "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", ee
, e1
);
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.
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
972 * `true` if pointers to the stack can escape
975 bool checkThrowEscape(Scope
* sc
, Expression e
, bool gag
)
977 //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars());
980 escapeByValue(e
, &er
);
982 if (!er
.byref
.length
&& !er
.byvalue
.length
&& !er
.byexp
.length
)
986 foreach (VarDeclaration v
; er
.byvalue
)
988 //printf("byvalue %s\n", v.toChars());
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
);
1001 notMaybeScope(v
, new ThrowExp(e
.loc
, e
));
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.
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
1016 * `true` if pointers to the stack can escape
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());
1026 if (log
) printf("[%s] checkNewEscape, e: `%s`\n", e
.loc
.toChars(), e
.toChars());
1029 escapeByValue(e
, &er
);
1031 if (!er
.byref
.length
&& !er
.byvalue
.length
&& !er
.byexp
.length
)
1034 bool result
= false;
1035 foreach (VarDeclaration v
; er
.byvalue
)
1037 if (log
) printf("byvalue `%s`\n", v
.toChars());
1041 Dsymbol p
= v
.toParent2();
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); }
1051 * foreach (S* s; S) // create __foreachbody for body of foreach
1052 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
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
);
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
);
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
);
1091 Dsymbol p
= v
.toParent2();
1093 if (!v
.isReference())
1097 result |
= escapingRef(v
, sc
.useDIP1000
);
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())
1108 // https://dlang.org/spec/function.html#return-ref-parameters
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
);
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
)
1120 if (auto tf
= func
.type
.isTypeFunction())
1125 const(char)* msg
= "storing reference to outer local variable `%s` into allocated memory causes it to escape";
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());
1141 sc
.eSink
.error(ee
.loc
, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape",
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.
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
1159 * `true` if pointers to the stack can escape
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
1171 * Print error messages when these are detected.
1173 * sc = used to determine current function and module
1174 * e = expression to check
1175 * gag = do not print error messages
1177 * `true` if references to the stack can escape
1180 bool checkReturnEscapeRef(Scope
* sc
, Expression e
, bool gag
)
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.
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
1200 * `true` if references to the stack can escape
1202 private bool checkReturnEscapeImpl(Scope
* sc
, Expression e
, bool refs
, bool gag
)
1205 if (log
) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e
.loc
.toChars(), refs
, e
.toChars());
1209 escapeByRef(e
, &er
);
1211 escapeByValue(e
, &er
);
1213 if (!er
.byref
.length
&& !er
.byvalue
.length
&& !er
.byexp
.length
)
1216 bool result
= false;
1217 foreach (VarDeclaration v
; er
.byvalue
)
1219 if (log
) printf("byvalue `%s`\n", v
.toChars());
1223 const vsr
= buildScopeRef(v
.storage_class
);
1225 Dsymbol p
= v
.toParent2();
1227 if (p
== sc
.func
&& inferReturn(sc
.func
, v
, /*returnScope:*/ true))
1234 /* If `return scope` applies to v.
1236 if (vsr
== ScopeRef
.ReturnScope ||
1237 vsr
== ScopeRef
.Ref_ReturnScope
)
1242 auto pfunc
= p
.isFuncDeclaration();
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); }
1249 * foreach (S* s; S) // create __foreachbody for body of foreach
1250 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
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
1267 previewErrorFunc(sc
.isDeprecated(), sc
.useDIP1000
)(e
.loc
,
1268 "scope parameter `%s` may not be returned", v
.toChars()
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
);
1282 else if (v
.isTypesafeVariadicArray
&& p
== sc
.func
)
1285 sc
.eSink
.error(e
.loc
, "returning `%s` escapes a reference to variadic parameter `%s`", e
.toChars(), v
.toChars());
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
[])
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())
1316 if (v
.storage_class
& STC
.returnScope
)
1318 previewSupplementalFunc(sc
.isDeprecated(), featureState
)(v
.loc
,
1319 "perhaps change the `return scope` into `scope return`");
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
);
1331 if (er
.refRetRefTransition
[i
])
1333 result |
= sc
.setUnsafeDIP1000(gag
, e
.loc
, msg
, e
, v
);
1338 previewErrorFunc(sc
.isDeprecated(), featureState
)(e
.loc
, msg
, e
.toChars(), v
.toChars());
1347 const vsr
= buildScopeRef(v
.storage_class
);
1349 Dsymbol p
= v
.toParent2();
1351 // https://issues.dlang.org/show_bug.cgi?id=19965
1354 if (sc
.func
.vthis
== v
)
1355 notMaybeScope(v
, e
);
1357 if (checkScopeVarAddr(v
, e
, sc
, gag
))
1364 if (!v
.isReference())
1368 escapingRef(v
, FeatureState
.enabled
);
1371 FuncDeclaration fd
= p
.isFuncDeclaration();
1372 if (fd
&& sc
.func
.returnInprocess
)
1376 * auto dg = () { return &x; }
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))
1400 // https://dlang.org/spec/function.html#return-ref-parameters
1401 // Only look for errors if in module listed on command line
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
);
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();
1416 const(char)* msg
= "escaping reference to outer local variable `%s`";
1418 previewErrorFunc(sc
.isDeprecated(), sc
.useDIP25
)(e
.loc
, msg
, v
.toChars());
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
);
1439 sc
.eSink
.error(ee
.loc
, "escaping reference to stack allocated value returned by `%s`", ee
.toChars());
1446 /***********************************
1447 * Infer `scope` for a variable
1450 * va = variable to infer scope for
1451 * Returns: `true` if succesful or already `scope`
1454 bool inferScope(VarDeclaration va
)
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
;
1465 return va
.isScope();
1468 /*************************************
1469 * Variable v needs to have 'return' inferred for it.
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
)
1480 return !!(v
.storage_class
& STC
.returnScope
) == returnScope
;
1482 if (!v
.isParameter() || v
.isTypesafeVariadicArray ||
(returnScope
&& v
.doNotInferReturn
))
1485 if (!fd
.returnInprocess
)
1488 if (returnScope
&& !(v
.isScope() || v
.maybeScope
))
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
;
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
;
1505 tf
.isreturninferred
= true;
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
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; }
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
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();
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
1582 void visitThis(ThisExp e
)
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
);
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
)
1630 escapeByValue(e
.basis
, er
, live
, retRefTransition
);
1631 foreach (el
; *e
.elements
)
1634 escapeByValue(el
, er
, live
, retRefTransition
);
1639 void visitStructLiteral(StructLiteralExp e
)
1643 foreach (ex
; *e
.elements
)
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
)
1659 escapeByValue(ex
, er
, live
, retRefTransition
);
1664 void visitCast(CastExp e
)
1666 if (!e
.type
.hasPointers())
1668 Type tb
= e
.type
.toBasetype();
1669 if (tb
.ty
== Tarray
&& e
.e1
.type
.toBasetype().ty
== Tsarray
)
1671 escapeByRef(e
.e1
, er
, live
, retRefTransition
);
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();
1685 if (tb
.ty
== Tsarray
)
1687 if (v
.isTypesafeVariadicArray
)
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
);
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())
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
)
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
);
1788 escapeByValue(arg
, er
, live
, retRefTransition
);
1790 else if (psr
== ScopeRef
.ReturnRef || psr
== ScopeRef
.ReturnRef_Scope
)
1795 * ref P foo(return ref P p)
1799 escapeByValue(arg
, er
, live
, retRefTransition
);
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.
1821 * fd = member function
1823 * storage class for fd's `this`
1825 StorageClass
getThisStorageClass(FuncDeclaration fd
)
1828 auto tf
= fd
.type
.toBasetype().isTypeFunction();
1831 if (tf
.isreturnscope
)
1832 stc |
= STC
.returnScope | STC
.scope_
;
1833 auto ad
= fd
.isThis();
1834 if (ad
.isClassDeclaration() || tf
.isScopeQual
)
1836 if (ad
.isStructDeclaration())
1837 stc |
= STC
.ref_
; // `this` for a struct member function is passed by `ref`
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
)
1852 * struct S { ref S foo() return; }
1856 escapeByValue(dve
.e1
, er
, live
, retRefTransition
);
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())
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);
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());
1917 if (auto b
= e
.isBinExp())
1919 if (auto ba
= e
.isBinAssignExp())
1920 return visitBinAssign(ba
);
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; }
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
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();
1958 if (v
.storage_class
& STC
.ref_
&& v
.storage_class
& (STC
.foreach_ | STC
.temp
) && v
._init
)
1960 /* If compiler generated ref temporary
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
);
1969 escapeByRef(ez
.exp
, er
, live
, retRefTransition
);
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
);
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
);
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
)
2016 foreach (ex
; *e
.elements
)
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
);
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();
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);
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);
2109 StorageClass
stc = dve
.var
.storage_class
& (STC
.return_ | STC
.scope_ | STC
.ref_
);
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())
2148 er
.pushExp(e
, false);
2153 er
.pushExp(e
, retRefTransition
);
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());
2171 if (auto ba
= e
.isBinAssignExp())
2172 return visitBinAssign(ba
);
2177 /************************************
2178 * Aggregate the data collected by the escapeBy??() functions.
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
2212 refRetRefTransition
.setDim(0);
2213 expRetRefTransition
.setDim(0);
2217 * Escape variable `v` by reference
2219 * v = variable to escape
2220 * retRefTransition = `v` is escaped through a `return ref scope` function call
2222 void pushRef(VarDeclaration v
, bool retRefTransition
)
2225 refRetRefTransition
.push(retRefTransition
);
2229 * Escape a reference to expression `e`
2231 * e = expression to escape
2232 * retRefTransition = `e` is escaped through a `return ref scope` function call
2234 void pushExp(Expression e
, bool retRefTransition
)
2237 expRetRefTransition
.push(retRefTransition
);
2241 /*************************
2242 * Find all variables accessed by this delegate that are
2243 * in functions enclosing it.
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();
2257 foreach (v
; fdp
.closureVars
)
2259 foreach (const fdv
; v
.nestedrefs
)
2263 //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars());
2271 /***********************************
2272 * Turn off `maybeScope` for variable `v`.
2274 * This exists in order to find where `maybeScope` is getting turned off.
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
)
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.
2301 * o = reason for it being turned off
2303 private void doNotInferScope(VarDeclaration v
, RootObject o
)
2306 notMaybeScope(v
, o
);
2309 /***********************************
2310 * After semantic analysis of the function body,
2311 * try to infer `scope` / `return` on the parameters
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`
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();
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
)
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
[];
2352 array
[n
++] = funcdecl
.vthis
;
2353 if (funcdecl
.parameters
)
2355 foreach (v
; *funcdecl
.parameters
)
2360 eliminateMaybeScopes(array
[0 .. n
]);
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
;
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, - )
2406 * array = array of variables that were assigned to from maybescope variables
2408 private void eliminateMaybeScopes(VarDeclaration
[] array
)
2411 if (log
) printf("eliminateMaybeScopes()\n");
2418 if (log
) printf(" va = %s\n", va
.toChars());
2419 if (!(va
.maybeScope || va
.isScope()))
2423 foreach (v
; *va
.maybes
)
2425 if (log
) printf(" v = %s\n", v
.toChars());
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
);
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.
2447 * t = type of the argument
2449 * true if it's a pointer (or reference) to mutable data
2452 bool isReferenceToMutable(Type t
)
2456 if (!t
.isMutable() ||
2463 if (t
.nextOf().isTypeFunction())
2470 if (t
.nextOf().isMutable())
2475 return true; // even if the class fields are not mutable
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())
2486 else if (v
.type
.isReferenceToMutable())
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.
2506 * p = Parameter to check
2507 * t = type of corresponding argument
2509 * true if it's a pointer (or reference) to mutable data
2512 bool isReferenceToMutable(Parameter p
, Type t
)
2514 if (p
.isReference())
2516 if (p
.type
.isConst() || p
.type
.isImmutable())
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
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`
2541 * va = variable assigned to
2542 * v = variable being assigned
2544 * The way `va` encloses `v` (if any)
2546 private EnclosedBy
enclosesLifetimeOf(VarDeclaration va
, VarDeclaration v
)
2549 return EnclosedBy
.none
;
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
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());
2582 va
.maybes
= new VarDeclarations();
2586 // `setUnsafePreview` partially evaluated for dip1000
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.
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
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
)
2615 notMaybeScope(v
, e
);
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())
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`
2637 * v = variable to check
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
)