2 /* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2018 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/escape.c
13 #include "expression.h"
15 #include "aggregate.h"
16 #include "declaration.h"
19 /************************************
20 * Aggregate the data collected by the escapeBy??() functions.
22 struct EscapeByResults
24 VarDeclarations byref
; // array into which variables being returned by ref are inserted
25 VarDeclarations byvalue
; // array into which variables with values containing pointers are inserted
26 FuncDeclarations byfunc
; // nested functions that are turned into delegates
27 Expressions byexp
; // array into which temporaries being returned by ref are inserted
30 static bool checkReturnEscapeImpl(Scope
*sc
, Expression
*e
, bool refs
, bool gag
);
31 static void inferReturn(FuncDeclaration
*fd
, VarDeclaration
*v
);
32 static void escapeByValue(Expression
*e
, EscapeByResults
*er
);
33 static void escapeByRef(Expression
*e
, EscapeByResults
*er
);
34 static void findAllOuterAccessedVariables(FuncDeclaration
*fd
, VarDeclarations
*vars
);
36 /* 'v' is assigned unsafely to 'par'
38 static void unsafeAssign(Scope
*sc
, FuncDeclaration
*fdc
, Identifier
*par
, Expression
*arg
, bool gag
,
39 bool &result
, VarDeclaration
*v
, const char *desc
)
41 if (global
.params
.vsafe
&& sc
->func
->setUnsafe())
44 error(arg
->loc
, "%s %s assigned to non-scope parameter %s calling %s",
46 par
? par
->toChars() : "unnamed",
47 fdc
? fdc
->toPrettyChars() : "indirectly");
52 /****************************************
53 * Function parameter par is being initialized to arg,
55 * Detect if scoped values can escape this way.
56 * Print error messages when these are detected.
58 * sc = used to determine current function and module
59 * par = identifier of function parameter
60 * arg = initializer for param
61 * gag = do not print error messages
63 * true if pointers to the stack can escape via assignment
65 bool checkParamArgumentEscape(Scope
*sc
, FuncDeclaration
*fdc
, Identifier
*par
, Expression
*arg
, bool gag
)
67 //printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg->toChars(), par->toChars());
68 //printf("type = %s, %d\n", arg->type->toChars(), arg->type->hasPointers());
70 if (!arg
->type
->hasPointers())
75 escapeByValue(arg
, &er
);
77 if (!er
.byref
.dim
&& !er
.byvalue
.dim
&& !er
.byfunc
.dim
&& !er
.byexp
.dim
)
82 for (size_t i
= 0; i
< er
.byvalue
.dim
; i
++)
84 //printf("byvalue %s\n", v->toChars());
85 VarDeclaration
*v
= er
.byvalue
[i
];
89 Dsymbol
*p
= v
->toParent2();
91 v
->storage_class
&= ~STCmaybescope
;
95 unsafeAssign(sc
, fdc
, par
, arg
, gag
, result
, v
, "scope variable");
97 else if (v
->storage_class
& STCvariadic
&& p
== sc
->func
)
99 Type
*tb
= v
->type
->toBasetype();
100 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
102 unsafeAssign(sc
, fdc
, par
, arg
, gag
, result
, v
, "variadic variable");
107 /* v is not 'scope', and is assigned to a parameter that may escape.
108 * Therefore, v can never be 'scope'.
110 v
->doNotInferScope
= true;
114 for (size_t i
= 0; i
< er
.byref
.dim
; i
++)
116 VarDeclaration
*v
= er
.byref
[i
];
120 Dsymbol
*p
= v
->toParent2();
122 v
->storage_class
&= ~STCmaybescope
;
124 if ((v
->storage_class
& (STCref
| STCout
)) == 0 && p
== sc
->func
)
126 unsafeAssign(sc
, fdc
, par
, arg
, gag
, result
, v
, "reference to local variable");
131 for (size_t i
= 0; i
< er
.byfunc
.dim
; i
++)
133 FuncDeclaration
*fd
= er
.byfunc
[i
];
134 //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
135 VarDeclarations vars
;
136 findAllOuterAccessedVariables(fd
, &vars
);
138 for (size_t j
= 0; j
< vars
.dim
; j
++)
140 VarDeclaration
*v
= vars
[j
];
141 //printf("v = %s\n", v->toChars());
142 assert(!v
->isDataseg()); // these are not put in the closureVars[]
144 Dsymbol
*p
= v
->toParent2();
146 v
->storage_class
&= ~STCmaybescope
;
148 if ((v
->storage_class
& (STCref
| STCout
| STCscope
)) && p
== sc
->func
)
150 unsafeAssign(sc
, fdc
, par
, arg
, gag
, result
, v
, "reference to local");
156 for (size_t i
= 0; i
< er
.byexp
.dim
; i
++)
158 Expression
*ee
= er
.byexp
[i
];
159 if (sc
->func
->setUnsafe())
162 error(ee
->loc
, "reference to stack allocated value returned by %s assigned to non-scope parameter %s",
164 par
? par
->toChars() : "unnamed");
172 /****************************************
173 * Given an AssignExp, determine if the lvalue will cause
174 * the contents of the rvalue to escape.
175 * Print error messages when these are detected.
176 * Infer 'scope' for the lvalue where possible, in order
177 * to eliminate the error.
179 * sc = used to determine current function and module
180 * ae = AssignExp to check for any pointers to the stack
181 * gag = do not print error messages
183 * true if pointers to the stack can escape via assignment
185 bool checkAssignEscape(Scope
*sc
, Expression
*e
, bool gag
)
187 //printf("checkAssignEscape(e: %s)\n", e->toChars());
188 if (e
->op
!= TOKassign
&& e
->op
!= TOKblit
&& e
->op
!= TOKconstruct
)
190 AssignExp
*ae
= (AssignExp
*)e
;
191 Expression
*e1
= ae
->e1
;
192 Expression
*e2
= ae
->e2
;
193 //printf("type = %s, %d\n", e1->type->toChars(), e1->type->hasPointers());
195 if (!e1
->type
->hasPointers())
198 if (e1
->op
== TOKslice
)
203 escapeByValue(e2
, &er
);
205 if (!er
.byref
.dim
&& !er
.byvalue
.dim
&& !er
.byfunc
.dim
&& !er
.byexp
.dim
)
208 VarDeclaration
*va
= NULL
;
209 while (e1
->op
== TOKdotvar
)
210 e1
= ((DotVarExp
*)e1
)->e1
;
212 if (e1
->op
== TOKvar
)
213 va
= ((VarExp
*)e1
)->var
->isVarDeclaration();
214 else if (e1
->op
== TOKthis
)
215 va
= ((ThisExp
*)e1
)->var
->isVarDeclaration();
216 else if (e1
->op
== TOKindex
)
218 IndexExp
*ie
= (IndexExp
*)e1
;
219 if (ie
->e1
->op
== TOKvar
&& ie
->e1
->type
->toBasetype()->ty
== Tsarray
)
220 va
= ((VarExp
*)ie
->e1
)->var
->isVarDeclaration();
223 // Try to infer 'scope' for va if in a function not marked @system
224 bool inferScope
= false;
225 if (va
&& sc
->func
&& sc
->func
->type
&& sc
->func
->type
->ty
== Tfunction
)
226 inferScope
= ((TypeFunction
*)sc
->func
->type
)->trust
!= TRUSTsystem
;
229 for (size_t i
= 0; i
< er
.byvalue
.dim
; i
++)
231 VarDeclaration
*v
= er
.byvalue
[i
];
232 //printf("byvalue: %s\n", v->toChars());
236 Dsymbol
*p
= v
->toParent2();
238 if (!(va
&& va
->isScope()))
239 v
->storage_class
&= ~STCmaybescope
;
243 if (va
&& va
->isScope() && va
->storage_class
& STCreturn
&& !(v
->storage_class
& STCreturn
) &&
244 sc
->func
->setUnsafe())
247 error(ae
->loc
, "scope variable %s assigned to return scope %s", v
->toChars(), va
->toChars());
252 // If va's lifetime encloses v's, then error
254 ((va
->enclosesLifetimeOf(v
) && !(v
->storage_class
& STCparameter
)) ||
255 // va is class reference
256 (ae
->e1
->op
== TOKdotvar
&& va
->type
->toBasetype()->ty
== Tclass
&& (va
->enclosesLifetimeOf(v
) || !va
->isScope())) ||
257 va
->storage_class
& STCref
) &&
258 sc
->func
->setUnsafe())
261 error(ae
->loc
, "scope variable %s assigned to %s with longer lifetime", v
->toChars(), va
->toChars());
266 if (va
&& !va
->isDataseg() && !va
->doNotInferScope
)
268 if (!va
->isScope() && inferScope
)
269 { //printf("inferring scope for %s\n", va->toChars());
270 va
->storage_class
|= STCscope
| STCscopeinferred
;
271 va
->storage_class
|= v
->storage_class
& STCreturn
;
275 if (sc
->func
->setUnsafe())
278 error(ae
->loc
, "scope variable %s assigned to non-scope %s", v
->toChars(), e1
->toChars());
282 else if (v
->storage_class
& STCvariadic
&& p
== sc
->func
)
284 Type
*tb
= v
->type
->toBasetype();
285 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
287 if (va
&& !va
->isDataseg() && !va
->doNotInferScope
)
289 if (!va
->isScope() && inferScope
)
290 { //printf("inferring scope for %s\n", va->toChars());
291 va
->storage_class
|= STCscope
| STCscopeinferred
;
295 if (sc
->func
->setUnsafe())
298 error(ae
->loc
, "variadic variable %s assigned to non-scope %s", v
->toChars(), e1
->toChars());
305 /* v is not 'scope', and we didn't check the scope of where we assigned it to.
306 * It may escape via that assignment, therefore, v can never be 'scope'.
308 v
->doNotInferScope
= true;
312 for (size_t i
= 0; i
< er
.byref
.dim
; i
++)
314 VarDeclaration
*v
= er
.byref
[i
];
315 //printf("byref: %s\n", v->toChars());
319 Dsymbol
*p
= v
->toParent2();
321 // If va's lifetime encloses v's, then error
323 ((va
->enclosesLifetimeOf(v
) && !(v
->storage_class
& STCparameter
)) || va
->storage_class
& STCref
) &&
324 sc
->func
->setUnsafe())
327 error(ae
->loc
, "address of variable %s assigned to %s with longer lifetime", v
->toChars(), va
->toChars());
332 if (!(va
&& va
->isScope()))
333 v
->storage_class
&= ~STCmaybescope
;
335 if ((v
->storage_class
& (STCref
| STCout
)) == 0 && p
== sc
->func
)
337 if (va
&& !va
->isDataseg() && !va
->doNotInferScope
)
339 if (!va
->isScope() && inferScope
)
340 { //printf("inferring scope for %s\n", va->toChars());
341 va
->storage_class
|= STCscope
| STCscopeinferred
;
345 if (sc
->func
->setUnsafe())
348 error(ae
->loc
, "reference to local variable %s assigned to non-scope %s", v
->toChars(), e1
->toChars());
355 for (size_t i
= 0; i
< er
.byfunc
.dim
; i
++)
357 FuncDeclaration
*fd
= er
.byfunc
[i
];
358 //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
359 VarDeclarations vars
;
360 findAllOuterAccessedVariables(fd
, &vars
);
362 for (size_t j
= 0; j
< vars
.dim
; j
++)
364 VarDeclaration
*v
= vars
[j
];
365 //printf("v = %s\n", v->toChars());
366 assert(!v
->isDataseg()); // these are not put in the closureVars[]
368 Dsymbol
*p
= v
->toParent2();
370 if (!(va
&& va
->isScope()))
371 v
->storage_class
&= ~STCmaybescope
;
373 if ((v
->storage_class
& (STCref
| STCout
| STCscope
)) && p
== sc
->func
)
375 if (va
&& !va
->isDataseg() && !va
->doNotInferScope
)
377 /* Don't infer STCscope for va, because then a closure
378 * won't be generated for sc->func.
380 //if (!va->isScope() && inferScope)
381 //va->storage_class |= STCscope | STCscopeinferred;
384 if (sc
->func
->setUnsafe())
387 error(ae
->loc
, "reference to local %s assigned to non-scope %s in @safe code", v
->toChars(), e1
->toChars());
395 for (size_t i
= 0; i
< er
.byexp
.dim
; i
++)
397 Expression
*ee
= er
.byexp
[i
];
398 if (va
&& !va
->isDataseg() && !va
->doNotInferScope
)
400 if (!va
->isScope() && inferScope
)
401 { //printf("inferring scope for %s\n", va->toChars());
402 va
->storage_class
|= STCscope
| STCscopeinferred
;
406 if (sc
->func
->setUnsafe())
409 error(ee
->loc
, "reference to stack allocated value returned by %s assigned to non-scope %s",
410 ee
->toChars(), e1
->toChars());
418 /************************************
419 * Detect cases where pointers to the stack can 'escape' the
420 * lifetime of the stack frame when throwing `e`.
421 * Print error messages when these are detected.
423 * sc = used to determine current function and module
424 * e = expression to check for any pointers to the stack
425 * gag = do not print error messages
427 * true if pointers to the stack can escape
429 bool checkThrowEscape(Scope
*sc
, Expression
*e
, bool gag
)
431 //printf("[%s] checkThrowEscape, e = %s\n", e->loc->toChars(), e->toChars());
434 escapeByValue(e
, &er
);
436 if (!er
.byref
.dim
&& !er
.byvalue
.dim
&& !er
.byexp
.dim
)
440 for (size_t i
= 0; i
< er
.byvalue
.dim
; i
++)
442 VarDeclaration
*v
= er
.byvalue
[i
];
443 //printf("byvalue %s\n", v->toChars());
449 if (sc
->_module
&& sc
->_module
->isRoot())
451 // Only look for errors if in module listed on command line
452 if (global
.params
.vsafe
) // https://issues.dlang.org/show_bug.cgi?id=17029
455 error(e
->loc
, "scope variable %s may not be thrown", v
->toChars());
463 //printf("no infer for %s\n", v->toChars());
464 v
->doNotInferScope
= true;
470 /************************************
471 * Detect cases where pointers to the stack can 'escape' the
472 * lifetime of the stack frame by returning 'e' by value.
474 * sc = used to determine current function and module
475 * e = expression to check for any pointers to the stack
476 * gag = do not print error messages
478 * true if pointers to the stack can escape
481 bool checkReturnEscape(Scope
*sc
, Expression
*e
, bool gag
)
483 //printf("[%s] checkReturnEscape, e = %s\n", e->loc->toChars(), e->toChars());
484 return checkReturnEscapeImpl(sc
, e
, false, gag
);
487 /************************************
488 * Detect cases where returning 'e' by ref can result in a reference to the stack
490 * Print error messages when these are detected.
492 * sc = used to determine current function and module
493 * e = expression to check
494 * gag = do not print error messages
496 * true if references to the stack can escape
498 bool checkReturnEscapeRef(Scope
*sc
, Expression
*e
, bool gag
)
500 //printf("[%s] checkReturnEscapeRef, e = %s\n", e->loc.toChars(), e->toChars());
501 //printf("current function %s\n", sc->func->toChars());
502 //printf("parent2 function %s\n", sc->func->toParent2()->toChars());
504 return checkReturnEscapeImpl(sc
, e
, true, gag
);
507 static void escapingRef(VarDeclaration
*v
, Expression
*e
, bool &result
, bool gag
)
512 if (v
->storage_class
& STCparameter
)
513 msg
= "returning `%s` escapes a reference to parameter `%s`, perhaps annotate with `return`";
515 msg
= "returning `%s` escapes a reference to local variable `%s`";
516 error(e
->loc
, msg
, e
->toChars(), v
->toChars());
521 static bool checkReturnEscapeImpl(Scope
*sc
, Expression
*e
, bool refs
, bool gag
)
523 //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars());
529 escapeByValue(e
, &er
);
531 if (!er
.byref
.dim
&& !er
.byvalue
.dim
&& !er
.byexp
.dim
)
535 for (size_t i
= 0; i
< er
.byvalue
.dim
; i
++)
537 VarDeclaration
*v
= er
.byvalue
[i
];
538 //printf("byvalue %s\n", v->toChars());
542 Dsymbol
*p
= v
->toParent2();
544 if ((v
->isScope() || (v
->storage_class
& STCmaybescope
)) &&
545 !(v
->storage_class
& STCreturn
) &&
547 sc
->func
->flags
& FUNCFLAGreturnInprocess
&&
550 inferReturn(sc
->func
, v
); // infer addition of 'return'
556 if (v
->storage_class
& STCreturn
)
559 if (sc
->_module
&& sc
->_module
->isRoot() &&
560 /* This case comes up when the ReturnStatement of a __foreachbody is
561 * checked for escapes by the caller of __foreachbody. Skip it.
563 * struct S { static int opApply(int delegate(S*) dg); }
565 * foreach (S* s; S) // create __foreachbody for body of foreach
566 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
569 !(!refs
&& p
->parent
== sc
->func
))
571 // Only look for errors if in module listed on command line
572 if (global
.params
.vsafe
) // https://issues.dlang.org/show_bug.cgi?id=17029
575 error(e
->loc
, "scope variable %s may not be returned", v
->toChars());
581 else if (v
->storage_class
& STCvariadic
&& p
== sc
->func
)
583 Type
*tb
= v
->type
->toBasetype();
584 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
587 error(e
->loc
, "returning `%s` escapes a reference to variadic parameter `%s`", e
->toChars(), v
->toChars());
593 //printf("no infer for %s\n", v->toChars());
594 v
->doNotInferScope
= true;
598 for (size_t i
= 0; i
< er
.byref
.dim
; i
++)
600 VarDeclaration
*v
= er
.byref
[i
];
601 //printf("byref %s\n", v->toChars());
605 Dsymbol
*p
= v
->toParent2();
607 if ((v
->storage_class
& (STCref
| STCout
)) == 0)
611 escapingRef(v
, e
, result
, gag
);
614 FuncDeclaration
*fd
= p
->isFuncDeclaration();
615 if (fd
&& sc
->func
->flags
& FUNCFLAGreturnInprocess
)
619 * auto dg = () { return &x; }
621 * auto dg = () return { return &x; }
622 * Because dg.ptr points to x, this is returning dt.ptr+offset
624 if (global
.params
.vsafe
)
625 sc
->func
->storage_class
|= STCreturn
;
629 /* Check for returning a ref variable by 'ref', but should be 'return ref'
630 * Infer the addition of 'return', or set result to be the offending expression.
632 if ( (v
->storage_class
& (STCref
| STCout
)) &&
633 !(v
->storage_class
& (STCreturn
| STCforeach
)))
635 if ((sc
->func
->flags
& FUNCFLAGreturnInprocess
) && p
== sc
->func
)
637 inferReturn(sc
->func
, v
); // infer addition of 'return'
639 else if (global
.params
.useDIP25
&&
640 sc
->_module
&& sc
->_module
->isRoot())
642 // Only look for errors if in module listed on command line
646 //printf("escaping reference to local ref variable %s\n", v->toChars());
647 //printf("storage class = x%llx\n", v->storage_class);
648 escapingRef(v
, e
, result
, gag
);
651 // Don't need to be concerned if v's parent does not return a ref
652 FuncDeclaration
*fd
= p
->isFuncDeclaration();
653 if (fd
&& fd
->type
&& fd
->type
->ty
== Tfunction
)
655 TypeFunction
*tf
= (TypeFunction
*)fd
->type
;
659 error(e
->loc
, "escaping reference to outer local variable %s", v
->toChars());
668 for (size_t i
= 0; i
< er
.byexp
.dim
; i
++)
670 Expression
*ee
= er
.byexp
[i
];
671 //printf("byexp %s\n", ee->toChars());
673 error(ee
->loc
, "escaping reference to stack allocated value returned by %s", ee
->toChars());
681 /*************************************
682 * Variable v needs to have 'return' inferred for it.
684 * fd = function that v is a parameter to
685 * v = parameter that needs to be STCreturn
688 static void inferReturn(FuncDeclaration
*fd
, VarDeclaration
*v
)
690 // v is a local in the current function
692 //printf("for function '%s' inferring 'return' for variable '%s'\n", fd->toChars(), v->toChars());
693 v
->storage_class
|= STCreturn
;
695 TypeFunction
*tf
= (TypeFunction
*)fd
->type
;
698 /* v is the 'this' reference, so mark the function
700 fd
->storage_class
|= STCreturn
;
701 if (tf
->ty
== Tfunction
)
703 //printf("'this' too %p %s\n", tf, sc->func->toChars());
709 // Perform 'return' inference on parameter
710 if (tf
->ty
== Tfunction
&& tf
->parameters
)
712 const size_t dim
= Parameter::dim(tf
->parameters
);
713 for (size_t i
= 0; i
< dim
; i
++)
715 Parameter
*p
= Parameter::getNth(tf
->parameters
, i
);
716 if (p
->ident
== v
->ident
)
718 p
->storageClass
|= STCreturn
;
719 break; // there can be only one
727 /****************************************
728 * e is an expression to be returned by value, and that value contains pointers.
729 * Walk e to determine which variables are possibly being
730 * returned by value, such as:
731 * int* function(int* p) { return p; }
732 * If e is a form of &p, determine which variables have content
733 * which is being returned as ref, such as:
734 * int* function(int i) { return &i; }
735 * Multiple variables can be inserted, because of expressions like this:
736 * int function(bool b, int i, int* p) { return b ? &i : p; }
741 * e = expression to be returned by value
742 * er = where to place collected data
744 static void escapeByValue(Expression
*e
, EscapeByResults
*er
)
746 //printf("[%s] escapeByValue, e: %s\n", e->loc.toChars(), e->toChars());
748 class EscapeVisitor
: public Visitor
753 EscapeVisitor(EscapeByResults
*er
)
758 void visit(Expression
*)
762 void visit(AddrExp
*e
)
764 escapeByRef(e
->e1
, er
);
767 void visit(SymOffExp
*e
)
769 VarDeclaration
*v
= e
->var
->isVarDeclaration();
774 void visit(VarExp
*e
)
776 VarDeclaration
*v
= e
->var
->isVarDeclaration();
781 void visit(ThisExp
*e
)
784 er
->byvalue
.push(e
->var
);
787 void visit(DotVarExp
*e
)
789 Type
*t
= e
->e1
->type
->toBasetype();
790 if (t
->ty
== Tstruct
)
794 void visit(DelegateExp
*e
)
796 Type
*t
= e
->e1
->type
->toBasetype();
797 if (t
->ty
== Tclass
|| t
->ty
== Tpointer
)
798 escapeByValue(e
->e1
, er
);
800 escapeByRef(e
->e1
, er
);
801 er
->byfunc
.push(e
->func
);
804 void visit(FuncExp
*e
)
806 if (e
->fd
->tok
== TOKdelegate
)
807 er
->byfunc
.push(e
->fd
);
810 void visit(TupleExp
*)
812 assert(0); // should have been lowered by now
815 void visit(ArrayLiteralExp
*e
)
817 Type
*tb
= e
->type
->toBasetype();
818 if (tb
->ty
== Tsarray
|| tb
->ty
== Tarray
)
821 e
->basis
->accept(this);
822 for (size_t i
= 0; i
< e
->elements
->dim
; i
++)
824 Expression
*el
= (*e
->elements
)[i
];
831 void visit(StructLiteralExp
*e
)
835 for (size_t i
= 0; i
< e
->elements
->dim
; i
++)
837 Expression
*ex
= (*e
->elements
)[i
];
844 void visit(NewExp
*e
)
846 Type
*tb
= e
->newtype
->toBasetype();
847 if (tb
->ty
== Tstruct
&& !e
->member
&& e
->arguments
)
849 for (size_t i
= 0; i
< e
->arguments
->dim
; i
++)
851 Expression
*ex
= (*e
->arguments
)[i
];
858 void visit(CastExp
*e
)
860 Type
*tb
= e
->type
->toBasetype();
861 if (tb
->ty
== Tarray
&&
862 e
->e1
->type
->toBasetype()->ty
== Tsarray
)
864 escapeByRef(e
->e1
, er
);
870 void visit(SliceExp
*e
)
872 if (e
->e1
->op
== TOKvar
)
874 VarDeclaration
*v
= ((VarExp
*)e
->e1
)->var
->isVarDeclaration();
875 Type
*tb
= e
->type
->toBasetype();
878 if (tb
->ty
== Tsarray
)
880 if (v
->storage_class
& STCvariadic
)
887 Type
*t1b
= e
->e1
->type
->toBasetype();
888 if (t1b
->ty
== Tsarray
)
890 Type
*tb
= e
->type
->toBasetype();
891 if (tb
->ty
!= Tsarray
)
892 escapeByRef(e
->e1
, er
);
898 void visit(BinExp
*e
)
900 Type
*tb
= e
->type
->toBasetype();
901 if (tb
->ty
== Tpointer
)
908 void visit(BinAssignExp
*e
)
913 void visit(AssignExp
*e
)
918 void visit(CommaExp
*e
)
923 void visit(CondExp
*e
)
929 void visit(CallExp
*e
)
931 //printf("CallExp(): %s\n", e->toChars());
932 /* Check each argument that is
933 * passed as 'return scope'.
935 Type
*t1
= e
->e1
->type
->toBasetype();
936 TypeFunction
*tf
= NULL
;
937 TypeDelegate
*dg
= NULL
;
938 if (t1
->ty
== Tdelegate
)
940 dg
= (TypeDelegate
*)t1
;
941 tf
= (TypeFunction
*)dg
->next
;
943 else if (t1
->ty
== Tfunction
)
944 tf
= (TypeFunction
*)t1
;
948 if (e
->arguments
&& e
->arguments
->dim
)
950 /* j=1 if _arguments[] is first argument,
951 * skip it because it is not passed by ref
953 size_t j
= (tf
->linkage
== LINKd
&& tf
->varargs
== 1);
954 for (size_t i
= j
; i
< e
->arguments
->dim
; ++i
)
956 Expression
*arg
= (*e
->arguments
)[i
];
957 size_t nparams
= Parameter::dim(tf
->parameters
);
958 if (i
- j
< nparams
&& i
>= j
)
960 Parameter
*p
= Parameter::getNth(tf
->parameters
, i
- j
);
961 const StorageClass stc
= tf
->parameterStorageClass(p
);
962 if ((stc
& (STCscope
)) && (stc
& STCreturn
))
964 else if ((stc
& (STCref
)) && (stc
& STCreturn
))
965 escapeByRef(arg
, er
);
969 // If 'this' is returned, check it too
970 if (e
->e1
->op
== TOKdotvar
&& t1
->ty
== Tfunction
)
972 DotVarExp
*dve
= (DotVarExp
*)e
->e1
;
973 FuncDeclaration
*fd
= dve
->var
->isFuncDeclaration();
974 AggregateDeclaration
*ad
= NULL
;
975 if (global
.params
.vsafe
&& tf
->isreturn
&& fd
&& (ad
= fd
->isThis()) != NULL
)
977 if (ad
->isClassDeclaration() || tf
->isscope
) // this is 'return scope'
978 dve
->e1
->accept(this);
979 else if (ad
->isStructDeclaration()) // this is 'return ref'
980 escapeByRef(dve
->e1
, er
);
982 else if (dve
->var
->storage_class
& STCreturn
|| tf
->isreturn
)
984 if (dve
->var
->storage_class
& STCscope
)
985 dve
->e1
->accept(this);
986 else if (dve
->var
->storage_class
& STCref
)
987 escapeByRef(dve
->e1
, er
);
991 /* If returning the result of a delegate call, the .ptr
992 * field of the delegate must be checked.
1002 EscapeVisitor
v(er
);
1006 /****************************************
1007 * e is an expression to be returned by 'ref'.
1008 * Walk e to determine which variables are possibly being
1009 * returned by ref, such as:
1010 * ref int function(int i) { return i; }
1011 * If e is a form of *p, determine which variables have content
1012 * which is being returned as ref, such as:
1013 * ref int function(int* p) { return *p; }
1014 * Multiple variables can be inserted, because of expressions like this:
1015 * ref int function(bool b, int i, int* p) { return b ? i : *p; }
1020 * e = expression to be returned by 'ref'
1021 * er = where to place collected data
1023 static void escapeByRef(Expression
*e
, EscapeByResults
*er
)
1025 //printf("[%s] escapeByRef, e: %s\n", e->loc->toChars(), e->toChars());
1026 class EscapeRefVisitor
: public Visitor
1029 EscapeByResults
*er
;
1031 EscapeRefVisitor(EscapeByResults
*er
)
1036 void visit(Expression
*)
1040 void visit(VarExp
*e
)
1042 VarDeclaration
*v
= e
->var
->isVarDeclaration();
1045 if (v
->storage_class
& STCref
&& v
->storage_class
& (STCforeach
| STCtemp
) && v
->_init
)
1047 /* If compiler generated ref temporary
1049 * look at the initializer instead
1051 if (ExpInitializer
*ez
= v
->_init
->isExpInitializer())
1053 assert(ez
->exp
&& ez
->exp
->op
== TOKconstruct
);
1054 Expression
*ex
= ((ConstructExp
*)ez
->exp
)->e2
;
1063 void visit(ThisExp
*e
)
1066 er
->byref
.push(e
->var
);
1069 void visit(PtrExp
*e
)
1071 escapeByValue(e
->e1
, er
);
1074 void visit(IndexExp
*e
)
1076 Type
*tb
= e
->e1
->type
->toBasetype();
1077 if (e
->e1
->op
== TOKvar
)
1079 VarDeclaration
*v
= ((VarExp
*)e
->e1
)->var
->isVarDeclaration();
1080 if (tb
->ty
== Tarray
|| tb
->ty
== Tsarray
)
1082 if (v
->storage_class
& STCvariadic
)
1089 if (tb
->ty
== Tsarray
)
1091 e
->e1
->accept(this);
1093 else if (tb
->ty
== Tarray
)
1095 escapeByValue(e
->e1
, er
);
1099 void visit(DotVarExp
*e
)
1101 Type
*t1b
= e
->e1
->type
->toBasetype();
1102 if (t1b
->ty
== Tclass
)
1103 escapeByValue(e
->e1
, er
);
1105 e
->e1
->accept(this);
1108 void visit(BinAssignExp
*e
)
1110 e
->e1
->accept(this);
1113 void visit(AssignExp
*e
)
1115 e
->e1
->accept(this);
1118 void visit(CommaExp
*e
)
1120 e
->e2
->accept(this);
1123 void visit(CondExp
*e
)
1125 e
->e1
->accept(this);
1126 e
->e2
->accept(this);
1129 void visit(CallExp
*e
)
1131 /* If the function returns by ref, check each argument that is
1132 * passed as 'return ref'.
1134 Type
*t1
= e
->e1
->type
->toBasetype();
1136 if (t1
->ty
== Tdelegate
)
1137 tf
= (TypeFunction
*)((TypeDelegate
*)t1
)->next
;
1138 else if (t1
->ty
== Tfunction
)
1139 tf
= (TypeFunction
*)t1
;
1144 if (e
->arguments
&& e
->arguments
->dim
)
1146 /* j=1 if _arguments[] is first argument,
1147 * skip it because it is not passed by ref
1149 size_t j
= (tf
->linkage
== LINKd
&& tf
->varargs
== 1);
1151 for (size_t i
= j
; i
< e
->arguments
->dim
; ++i
)
1153 Expression
*arg
= (*e
->arguments
)[i
];
1154 size_t nparams
= Parameter::dim(tf
->parameters
);
1155 if (i
- j
< nparams
&& i
>= j
)
1157 Parameter
*p
= Parameter::getNth(tf
->parameters
, i
- j
);
1158 const StorageClass stc
= tf
->parameterStorageClass(p
);
1159 if ((stc
& (STCout
| STCref
)) && (stc
& STCreturn
))
1161 else if ((stc
& STCscope
) && (stc
& STCreturn
))
1163 if (arg
->op
== TOKdelegate
)
1165 DelegateExp
*de
= (DelegateExp
*)arg
;
1166 if (de
->func
->isNested())
1170 escapeByValue(arg
, er
);
1176 // If 'this' is returned by ref, check it too
1177 if (e
->e1
->op
== TOKdotvar
&& t1
->ty
== Tfunction
)
1179 DotVarExp
*dve
= (DotVarExp
*)e
->e1
;
1180 if (dve
->var
->storage_class
& STCreturn
|| tf
->isreturn
)
1182 if ((dve
->var
->storage_class
& STCscope
) || tf
->isscope
)
1183 escapeByValue(dve
->e1
, er
);
1184 else if ((dve
->var
->storage_class
& STCref
) || tf
->isref
)
1185 dve
->e1
->accept(this);
1189 // If it's a delegate, check it too
1190 if (e
->e1
->op
== TOKvar
&& t1
->ty
== Tdelegate
)
1192 escapeByValue(e
->e1
, er
);
1200 EscapeRefVisitor
v(er
);
1204 /*************************
1205 * Find all variables accessed by this delegate that are
1206 * in functions enclosing it.
1209 * vars = array to append found variables to
1211 void findAllOuterAccessedVariables(FuncDeclaration
*fd
, VarDeclarations
*vars
)
1213 //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
1214 for (Dsymbol
*p
= fd
->parent
; p
; p
= p
->parent
)
1216 FuncDeclaration
*fdp
= p
->isFuncDeclaration();
1219 for (size_t i
= 0; i
< fdp
->closureVars
.dim
; i
++)
1221 VarDeclaration
*v
= fdp
->closureVars
[i
];
1222 for (size_t j
= 0; j
< v
->nestedrefs
.dim
; j
++)
1224 FuncDeclaration
*fdv
= v
->nestedrefs
[j
];
1227 //printf("accessed: %s, type %s\n", v->toChars(), v->type->toChars());