PR libstdc++/87308 adjust regex used in std::any pretty printer
[official-gcc.git] / gcc / d / dmd / escape.c
blob353e56fd58f9975bada7d79e4bfcb2007947eba1
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
9 */
11 #include "mars.h"
12 #include "init.h"
13 #include "expression.h"
14 #include "scope.h"
15 #include "aggregate.h"
16 #include "declaration.h"
17 #include "module.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())
43 if (!gag)
44 error(arg->loc, "%s %s assigned to non-scope parameter %s calling %s",
45 desc, v->toChars(),
46 par ? par->toChars() : "unnamed",
47 fdc ? fdc->toPrettyChars() : "indirectly");
48 result = true;
52 /****************************************
53 * Function parameter par is being initialized to arg,
54 * and par may escape.
55 * Detect if scoped values can escape this way.
56 * Print error messages when these are detected.
57 * Params:
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
62 * Returns:
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())
71 return false;
73 EscapeByResults er;
75 escapeByValue(arg, &er);
77 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
78 return false;
80 bool result = false;
82 for (size_t i = 0; i < er.byvalue.dim; i++)
84 //printf("byvalue %s\n", v->toChars());
85 VarDeclaration *v = er.byvalue[i];
86 if (v->isDataseg())
87 continue;
89 Dsymbol *p = v->toParent2();
91 v->storage_class &= ~STCmaybescope;
93 if (v->isScope())
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");
105 else
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];
117 if (v->isDataseg())
118 continue;
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");
127 continue;
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");
151 continue;
156 for (size_t i = 0; i < er.byexp.dim; i++)
158 Expression *ee = er.byexp[i];
159 if (sc->func->setUnsafe())
161 if (!gag)
162 error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope parameter %s",
163 ee->toChars(),
164 par ? par->toChars() : "unnamed");
165 result = true;
169 return result;
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.
178 * Params:
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
182 * Returns:
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)
189 return false;
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())
196 return false;
198 if (e1->op == TOKslice)
199 return false;
201 EscapeByResults er;
203 escapeByValue(e2, &er);
205 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
206 return false;
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;
228 bool result = false;
229 for (size_t i = 0; i < er.byvalue.dim; i++)
231 VarDeclaration *v = er.byvalue[i];
232 //printf("byvalue: %s\n", v->toChars());
233 if (v->isDataseg())
234 continue;
236 Dsymbol *p = v->toParent2();
238 if (!(va && va->isScope()))
239 v->storage_class &= ~STCmaybescope;
241 if (v->isScope())
243 if (va && va->isScope() && va->storage_class & STCreturn && !(v->storage_class & STCreturn) &&
244 sc->func->setUnsafe())
246 if (!gag)
247 error(ae->loc, "scope variable %s assigned to return scope %s", v->toChars(), va->toChars());
248 result = true;
249 continue;
252 // If va's lifetime encloses v's, then error
253 if (va &&
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())
260 if (!gag)
261 error(ae->loc, "scope variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
262 result = true;
263 continue;
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;
273 continue;
275 if (sc->func->setUnsafe())
277 if (!gag)
278 error(ae->loc, "scope variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
279 result = true;
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;
293 continue;
295 if (sc->func->setUnsafe())
297 if (!gag)
298 error(ae->loc, "variadic variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
299 result = true;
303 else
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());
316 if (v->isDataseg())
317 continue;
319 Dsymbol *p = v->toParent2();
321 // If va's lifetime encloses v's, then error
322 if (va &&
323 ((va->enclosesLifetimeOf(v) && !(v->storage_class & STCparameter)) || va->storage_class & STCref) &&
324 sc->func->setUnsafe())
326 if (!gag)
327 error(ae->loc, "address of variable %s assigned to %s with longer lifetime", v->toChars(), va->toChars());
328 result = true;
329 continue;
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;
343 continue;
345 if (sc->func->setUnsafe())
347 if (!gag)
348 error(ae->loc, "reference to local variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
349 result = true;
351 continue;
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;
382 continue;
384 if (sc->func->setUnsafe())
386 if (!gag)
387 error(ae->loc, "reference to local %s assigned to non-scope %s in @safe code", v->toChars(), e1->toChars());
388 result = true;
390 continue;
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;
404 continue;
406 if (sc->func->setUnsafe())
408 if (!gag)
409 error(ee->loc, "reference to stack allocated value returned by %s assigned to non-scope %s",
410 ee->toChars(), e1->toChars());
411 result = true;
415 return result;
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.
422 * Params:
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
426 * Returns:
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());
432 EscapeByResults er;
434 escapeByValue(e, &er);
436 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
437 return false;
439 bool result = false;
440 for (size_t i = 0; i < er.byvalue.dim; i++)
442 VarDeclaration *v = er.byvalue[i];
443 //printf("byvalue %s\n", v->toChars());
444 if (v->isDataseg())
445 continue;
447 if (v->isScope())
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
454 if (!gag)
455 error(e->loc, "scope variable %s may not be thrown", v->toChars());
456 result = true;
458 continue;
461 else
463 //printf("no infer for %s\n", v->toChars());
464 v->doNotInferScope = true;
467 return result;
470 /************************************
471 * Detect cases where pointers to the stack can 'escape' the
472 * lifetime of the stack frame by returning 'e' by value.
473 * Params:
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
477 * Returns:
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
489 * being returned.
490 * Print error messages when these are detected.
491 * Params:
492 * sc = used to determine current function and module
493 * e = expression to check
494 * gag = do not print error messages
495 * Returns:
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)
509 if (!gag)
511 const char *msg;
512 if (v->storage_class & STCparameter)
513 msg = "returning `%s` escapes a reference to parameter `%s`, perhaps annotate with `return`";
514 else
515 msg = "returning `%s` escapes a reference to local variable `%s`";
516 error(e->loc, msg, e->toChars(), v->toChars());
518 result = true;
521 static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag)
523 //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars());
524 EscapeByResults er;
526 if (refs)
527 escapeByRef(e, &er);
528 else
529 escapeByValue(e, &er);
531 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
532 return false;
534 bool result = false;
535 for (size_t i = 0; i < er.byvalue.dim; i++)
537 VarDeclaration *v = er.byvalue[i];
538 //printf("byvalue %s\n", v->toChars());
539 if (v->isDataseg())
540 continue;
542 Dsymbol *p = v->toParent2();
544 if ((v->isScope() || (v->storage_class & STCmaybescope)) &&
545 !(v->storage_class & STCreturn) &&
546 v->isParameter() &&
547 sc->func->flags & FUNCFLAGreturnInprocess &&
548 p == sc->func)
550 inferReturn(sc->func, v); // infer addition of 'return'
551 continue;
554 if (v->isScope())
556 if (v->storage_class & STCreturn)
557 continue;
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); }
564 * S* foo() {
565 * foreach (S* s; S) // create __foreachbody for body of foreach
566 * return s; // s is inferred as 'scope' but incorrectly tested in foo()
567 * return null; }
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
574 if (!gag)
575 error(e->loc, "scope variable %s may not be returned", v->toChars());
576 result = true;
578 continue;
581 else if (v->storage_class & STCvariadic && p == sc->func)
583 Type *tb = v->type->toBasetype();
584 if (tb->ty == Tarray || tb->ty == Tsarray)
586 if (!gag)
587 error(e->loc, "returning `%s` escapes a reference to variadic parameter `%s`", e->toChars(), v->toChars());
588 result = false;
591 else
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());
602 if (v->isDataseg())
603 continue;
605 Dsymbol *p = v->toParent2();
607 if ((v->storage_class & (STCref | STCout)) == 0)
609 if (p == sc->func)
611 escapingRef(v, e, result, gag);
612 continue;
614 FuncDeclaration *fd = p->isFuncDeclaration();
615 if (fd && sc->func->flags & FUNCFLAGreturnInprocess)
617 /* Code like:
618 * int x;
619 * auto dg = () { return &x; }
620 * Making it:
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
644 if (p == sc->func)
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);
649 continue;
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;
656 if (tf->isref)
658 if (!gag)
659 error(e->loc, "escaping reference to outer local variable %s", v->toChars());
660 result = true;
661 continue;
668 for (size_t i = 0; i < er.byexp.dim; i++)
670 Expression *ee = er.byexp[i];
671 //printf("byexp %s\n", ee->toChars());
672 if (!gag)
673 error(ee->loc, "escaping reference to stack allocated value returned by %s", ee->toChars());
674 result = true;
677 return result;
681 /*************************************
682 * Variable v needs to have 'return' inferred for it.
683 * Params:
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;
696 if (v == fd->vthis)
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());
704 tf->isreturn = true;
707 else
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; }
738 * No side effects.
740 * Params:
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
750 public:
751 EscapeByResults *er;
753 EscapeVisitor(EscapeByResults *er)
754 : er(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();
770 if (v)
771 er->byref.push(v);
774 void visit(VarExp *e)
776 VarDeclaration *v = e->var->isVarDeclaration();
777 if (v)
778 er->byvalue.push(v);
781 void visit(ThisExp *e)
783 if (e->var)
784 er->byvalue.push(e->var);
787 void visit(DotVarExp *e)
789 Type *t = e->e1->type->toBasetype();
790 if (t->ty == Tstruct)
791 e->e1->accept(this);
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);
799 else
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)
820 if (e->basis)
821 e->basis->accept(this);
822 for (size_t i = 0; i < e->elements->dim; i++)
824 Expression *el = (*e->elements)[i];
825 if (el)
826 el->accept(this);
831 void visit(StructLiteralExp *e)
833 if (e->elements)
835 for (size_t i = 0; i < e->elements->dim; i++)
837 Expression *ex = (*e->elements)[i];
838 if (ex)
839 ex->accept(this);
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];
852 if (ex)
853 ex->accept(this);
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);
866 else
867 e->e1->accept(this);
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();
876 if (v)
878 if (tb->ty == Tsarray)
879 return;
880 if (v->storage_class & STCvariadic)
882 er->byvalue.push(v);
883 return;
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);
894 else
895 e->e1->accept(this);
898 void visit(BinExp *e)
900 Type *tb = e->type->toBasetype();
901 if (tb->ty == Tpointer)
903 e->e1->accept(this);
904 e->e2->accept(this);
908 void visit(BinAssignExp *e)
910 e->e1->accept(this);
913 void visit(AssignExp *e)
915 e->e1->accept(this);
918 void visit(CommaExp *e)
920 e->e2->accept(this);
923 void visit(CondExp *e)
925 e->e1->accept(this);
926 e->e2->accept(this);
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;
945 else
946 return;
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))
963 arg->accept(this);
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.
994 if (dg)
996 if (tf->isreturn)
997 e->e1->accept(this);
1002 EscapeVisitor v(er);
1003 e->accept(&v);
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; }
1017 * No side effects.
1019 * Params:
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
1028 public:
1029 EscapeByResults *er;
1031 EscapeRefVisitor(EscapeByResults *er)
1032 : er(er)
1036 void visit(Expression *)
1040 void visit(VarExp *e)
1042 VarDeclaration *v = e->var->isVarDeclaration();
1043 if (v)
1045 if (v->storage_class & STCref && v->storage_class & (STCforeach | STCtemp) && v->_init)
1047 /* If compiler generated ref temporary
1048 * (ref v = ex; ex)
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;
1055 ex->accept(this);
1058 else
1059 er->byref.push(v);
1063 void visit(ThisExp *e)
1065 if (e->var)
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)
1084 er->byref.push(v);
1085 return;
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);
1104 else
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();
1135 TypeFunction *tf;
1136 if (t1->ty == Tdelegate)
1137 tf = (TypeFunction *)((TypeDelegate *)t1)->next;
1138 else if (t1->ty == Tfunction)
1139 tf = (TypeFunction *)t1;
1140 else
1141 return;
1142 if (tf->isref)
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))
1160 arg->accept(this);
1161 else if ((stc & STCscope) && (stc & STCreturn))
1163 if (arg->op == TOKdelegate)
1165 DelegateExp *de = (DelegateExp *)arg;
1166 if (de->func->isNested())
1167 er->byexp.push(de);
1169 else
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);
1195 else
1196 er->byexp.push(e);
1200 EscapeRefVisitor v(er);
1201 e->accept(&v);
1204 /*************************
1205 * Find all variables accessed by this delegate that are
1206 * in functions enclosing it.
1207 * Params:
1208 * fd = function
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();
1217 if (fdp)
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];
1225 if (fdv == fd)
1227 //printf("accessed: %s, type %s\n", v->toChars(), v->type->toChars());
1228 vars->push(v);