145
|
1
|
|
2 /* Compiler implementation of the D programming language
|
|
3 * Copyright (C) 1999-2019 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 */
|
|
10
|
|
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"
|
|
18
|
|
19 /************************************
|
|
20 * Aggregate the data collected by the escapeBy??() functions.
|
|
21 */
|
|
22 struct EscapeByResults
|
|
23 {
|
|
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
|
|
28 };
|
|
29
|
|
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);
|
|
35
|
|
36 /* 'v' is assigned unsafely to 'par'
|
|
37 */
|
|
38 static void unsafeAssign(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag,
|
|
39 bool &result, VarDeclaration *v, const char *desc)
|
|
40 {
|
|
41 if (global.params.vsafe && sc->func->setUnsafe())
|
|
42 {
|
|
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;
|
|
49 }
|
|
50 }
|
|
51
|
|
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
|
|
64 */
|
|
65 bool checkParamArgumentEscape(Scope *sc, FuncDeclaration *fdc, Identifier *par, Expression *arg, bool gag)
|
|
66 {
|
|
67 //printf("checkParamArgumentEscape(arg: %s par: %s)\n", arg->toChars(), par->toChars());
|
|
68 //printf("type = %s, %d\n", arg->type->toChars(), arg->type->hasPointers());
|
|
69
|
|
70 if (!arg->type->hasPointers())
|
|
71 return false;
|
|
72
|
|
73 EscapeByResults er;
|
|
74
|
|
75 escapeByValue(arg, &er);
|
|
76
|
|
77 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
|
|
78 return false;
|
|
79
|
|
80 bool result = false;
|
|
81
|
|
82 for (size_t i = 0; i < er.byvalue.dim; i++)
|
|
83 {
|
|
84 //printf("byvalue %s\n", v->toChars());
|
|
85 VarDeclaration *v = er.byvalue[i];
|
|
86 if (v->isDataseg())
|
|
87 continue;
|
|
88
|
|
89 Dsymbol *p = v->toParent2();
|
|
90
|
|
91 v->storage_class &= ~STCmaybescope;
|
|
92
|
|
93 if (v->isScope())
|
|
94 {
|
|
95 unsafeAssign(sc, fdc, par, arg, gag, result, v, "scope variable");
|
|
96 }
|
|
97 else if (v->storage_class & STCvariadic && p == sc->func)
|
|
98 {
|
|
99 Type *tb = v->type->toBasetype();
|
|
100 if (tb->ty == Tarray || tb->ty == Tsarray)
|
|
101 {
|
|
102 unsafeAssign(sc, fdc, par, arg, gag, result, v, "variadic variable");
|
|
103 }
|
|
104 }
|
|
105 else
|
|
106 {
|
|
107 /* v is not 'scope', and is assigned to a parameter that may escape.
|
|
108 * Therefore, v can never be 'scope'.
|
|
109 */
|
|
110 v->doNotInferScope = true;
|
|
111 }
|
|
112 }
|
|
113
|
|
114 for (size_t i = 0; i < er.byref.dim; i++)
|
|
115 {
|
|
116 VarDeclaration *v = er.byref[i];
|
|
117 if (v->isDataseg())
|
|
118 continue;
|
|
119
|
|
120 Dsymbol *p = v->toParent2();
|
|
121
|
|
122 v->storage_class &= ~STCmaybescope;
|
|
123
|
|
124 if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
|
|
125 {
|
|
126 unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local variable");
|
|
127 continue;
|
|
128 }
|
|
129 }
|
|
130
|
|
131 for (size_t i = 0; i < er.byfunc.dim; i++)
|
|
132 {
|
|
133 FuncDeclaration *fd = er.byfunc[i];
|
|
134 //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
|
|
135 VarDeclarations vars;
|
|
136 findAllOuterAccessedVariables(fd, &vars);
|
|
137
|
|
138 for (size_t j = 0; j < vars.dim; j++)
|
|
139 {
|
|
140 VarDeclaration *v = vars[j];
|
|
141 //printf("v = %s\n", v->toChars());
|
|
142 assert(!v->isDataseg()); // these are not put in the closureVars[]
|
|
143
|
|
144 Dsymbol *p = v->toParent2();
|
|
145
|
|
146 v->storage_class &= ~STCmaybescope;
|
|
147
|
|
148 if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
|
|
149 {
|
|
150 unsafeAssign(sc, fdc, par, arg, gag, result, v, "reference to local");
|
|
151 continue;
|
|
152 }
|
|
153 }
|
|
154 }
|
|
155
|
|
156 for (size_t i = 0; i < er.byexp.dim; i++)
|
|
157 {
|
|
158 Expression *ee = er.byexp[i];
|
|
159 if (sc->func->setUnsafe())
|
|
160 {
|
|
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;
|
|
166 }
|
|
167 }
|
|
168
|
|
169 return result;
|
|
170 }
|
|
171
|
|
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
|
|
184 */
|
|
185 bool checkAssignEscape(Scope *sc, Expression *e, bool gag)
|
|
186 {
|
|
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());
|
|
194
|
|
195 if (!e1->type->hasPointers())
|
|
196 return false;
|
|
197
|
|
198 if (e1->op == TOKslice)
|
|
199 return false;
|
|
200
|
|
201 EscapeByResults er;
|
|
202
|
|
203 escapeByValue(e2, &er);
|
|
204
|
|
205 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim)
|
|
206 return false;
|
|
207
|
|
208 VarDeclaration *va = NULL;
|
|
209 while (e1->op == TOKdotvar)
|
|
210 e1 = ((DotVarExp *)e1)->e1;
|
|
211
|
|
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)
|
|
217 {
|
|
218 IndexExp *ie = (IndexExp *)e1;
|
|
219 if (ie->e1->op == TOKvar && ie->e1->type->toBasetype()->ty == Tsarray)
|
|
220 va = ((VarExp *)ie->e1)->var->isVarDeclaration();
|
|
221 }
|
|
222
|
|
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;
|
|
227
|
|
228 bool result = false;
|
|
229 for (size_t i = 0; i < er.byvalue.dim; i++)
|
|
230 {
|
|
231 VarDeclaration *v = er.byvalue[i];
|
|
232 //printf("byvalue: %s\n", v->toChars());
|
|
233 if (v->isDataseg())
|
|
234 continue;
|
|
235
|
|
236 Dsymbol *p = v->toParent2();
|
|
237
|
|
238 if (!(va && va->isScope()))
|
|
239 v->storage_class &= ~STCmaybescope;
|
|
240
|
|
241 if (v->isScope())
|
|
242 {
|
|
243 if (va && va->isScope() && va->storage_class & STCreturn && !(v->storage_class & STCreturn) &&
|
|
244 sc->func->setUnsafe())
|
|
245 {
|
|
246 if (!gag)
|
|
247 error(ae->loc, "scope variable %s assigned to return scope %s", v->toChars(), va->toChars());
|
|
248 result = true;
|
|
249 continue;
|
|
250 }
|
|
251
|
|
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())
|
|
259 {
|
|
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;
|
|
264 }
|
|
265
|
|
266 if (va && !va->isDataseg() && !va->doNotInferScope)
|
|
267 {
|
|
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;
|
|
272 }
|
|
273 continue;
|
|
274 }
|
|
275 if (sc->func->setUnsafe())
|
|
276 {
|
|
277 if (!gag)
|
|
278 error(ae->loc, "scope variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
|
|
279 result = true;
|
|
280 }
|
|
281 }
|
|
282 else if (v->storage_class & STCvariadic && p == sc->func)
|
|
283 {
|
|
284 Type *tb = v->type->toBasetype();
|
|
285 if (tb->ty == Tarray || tb->ty == Tsarray)
|
|
286 {
|
|
287 if (va && !va->isDataseg() && !va->doNotInferScope)
|
|
288 {
|
|
289 if (!va->isScope() && inferScope)
|
|
290 { //printf("inferring scope for %s\n", va->toChars());
|
|
291 va->storage_class |= STCscope | STCscopeinferred;
|
|
292 }
|
|
293 continue;
|
|
294 }
|
|
295 if (sc->func->setUnsafe())
|
|
296 {
|
|
297 if (!gag)
|
|
298 error(ae->loc, "variadic variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
|
|
299 result = true;
|
|
300 }
|
|
301 }
|
|
302 }
|
|
303 else
|
|
304 {
|
|
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'.
|
|
307 */
|
|
308 v->doNotInferScope = true;
|
|
309 }
|
|
310 }
|
|
311
|
|
312 for (size_t i = 0; i < er.byref.dim; i++)
|
|
313 {
|
|
314 VarDeclaration *v = er.byref[i];
|
|
315 //printf("byref: %s\n", v->toChars());
|
|
316 if (v->isDataseg())
|
|
317 continue;
|
|
318
|
|
319 Dsymbol *p = v->toParent2();
|
|
320
|
|
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())
|
|
325 {
|
|
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;
|
|
330 }
|
|
331
|
|
332 if (!(va && va->isScope()))
|
|
333 v->storage_class &= ~STCmaybescope;
|
|
334
|
|
335 if ((v->storage_class & (STCref | STCout)) == 0 && p == sc->func)
|
|
336 {
|
|
337 if (va && !va->isDataseg() && !va->doNotInferScope)
|
|
338 {
|
|
339 if (!va->isScope() && inferScope)
|
|
340 { //printf("inferring scope for %s\n", va->toChars());
|
|
341 va->storage_class |= STCscope | STCscopeinferred;
|
|
342 }
|
|
343 continue;
|
|
344 }
|
|
345 if (sc->func->setUnsafe())
|
|
346 {
|
|
347 if (!gag)
|
|
348 error(ae->loc, "reference to local variable %s assigned to non-scope %s", v->toChars(), e1->toChars());
|
|
349 result = true;
|
|
350 }
|
|
351 continue;
|
|
352 }
|
|
353 }
|
|
354
|
|
355 for (size_t i = 0; i < er.byfunc.dim; i++)
|
|
356 {
|
|
357 FuncDeclaration *fd = er.byfunc[i];
|
|
358 //printf("fd = %s, %d\n", fd->toChars(), fd->tookAddressOf);
|
|
359 VarDeclarations vars;
|
|
360 findAllOuterAccessedVariables(fd, &vars);
|
|
361
|
|
362 for (size_t j = 0; j < vars.dim; j++)
|
|
363 {
|
|
364 VarDeclaration *v = vars[j];
|
|
365 //printf("v = %s\n", v->toChars());
|
|
366 assert(!v->isDataseg()); // these are not put in the closureVars[]
|
|
367
|
|
368 Dsymbol *p = v->toParent2();
|
|
369
|
|
370 if (!(va && va->isScope()))
|
|
371 v->storage_class &= ~STCmaybescope;
|
|
372
|
|
373 if ((v->storage_class & (STCref | STCout | STCscope)) && p == sc->func)
|
|
374 {
|
|
375 if (va && !va->isDataseg() && !va->doNotInferScope)
|
|
376 {
|
|
377 /* Don't infer STCscope for va, because then a closure
|
|
378 * won't be generated for sc->func.
|
|
379 */
|
|
380 //if (!va->isScope() && inferScope)
|
|
381 //va->storage_class |= STCscope | STCscopeinferred;
|
|
382 continue;
|
|
383 }
|
|
384 if (sc->func->setUnsafe())
|
|
385 {
|
|
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;
|
|
389 }
|
|
390 continue;
|
|
391 }
|
|
392 }
|
|
393 }
|
|
394
|
|
395 for (size_t i = 0; i < er.byexp.dim; i++)
|
|
396 {
|
|
397 Expression *ee = er.byexp[i];
|
|
398 if (va && !va->isDataseg() && !va->doNotInferScope)
|
|
399 {
|
|
400 if (!va->isScope() && inferScope)
|
|
401 { //printf("inferring scope for %s\n", va->toChars());
|
|
402 va->storage_class |= STCscope | STCscopeinferred;
|
|
403 }
|
|
404 continue;
|
|
405 }
|
|
406 if (sc->func->setUnsafe())
|
|
407 {
|
|
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;
|
|
412 }
|
|
413 }
|
|
414
|
|
415 return result;
|
|
416 }
|
|
417
|
|
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
|
|
428 */
|
|
429 bool checkThrowEscape(Scope *sc, Expression *e, bool gag)
|
|
430 {
|
|
431 //printf("[%s] checkThrowEscape, e = %s\n", e->loc->toChars(), e->toChars());
|
|
432 EscapeByResults er;
|
|
433
|
|
434 escapeByValue(e, &er);
|
|
435
|
|
436 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
|
|
437 return false;
|
|
438
|
|
439 bool result = false;
|
|
440 for (size_t i = 0; i < er.byvalue.dim; i++)
|
|
441 {
|
|
442 VarDeclaration *v = er.byvalue[i];
|
|
443 //printf("byvalue %s\n", v->toChars());
|
|
444 if (v->isDataseg())
|
|
445 continue;
|
|
446
|
|
447 if (v->isScope())
|
|
448 {
|
|
449 if (sc->_module && sc->_module->isRoot())
|
|
450 {
|
|
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
|
|
453 {
|
|
454 if (!gag)
|
|
455 error(e->loc, "scope variable %s may not be thrown", v->toChars());
|
|
456 result = true;
|
|
457 }
|
|
458 continue;
|
|
459 }
|
|
460 }
|
|
461 else
|
|
462 {
|
|
463 //printf("no infer for %s\n", v->toChars());
|
|
464 v->doNotInferScope = true;
|
|
465 }
|
|
466 }
|
|
467 return result;
|
|
468 }
|
|
469
|
|
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
|
|
479 */
|
|
480
|
|
481 bool checkReturnEscape(Scope *sc, Expression *e, bool gag)
|
|
482 {
|
|
483 //printf("[%s] checkReturnEscape, e = %s\n", e->loc->toChars(), e->toChars());
|
|
484 return checkReturnEscapeImpl(sc, e, false, gag);
|
|
485 }
|
|
486
|
|
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
|
|
497 */
|
|
498 bool checkReturnEscapeRef(Scope *sc, Expression *e, bool gag)
|
|
499 {
|
|
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());
|
|
503
|
|
504 return checkReturnEscapeImpl(sc, e, true, gag);
|
|
505 }
|
|
506
|
|
507 static void escapingRef(VarDeclaration *v, Expression *e, bool &result, bool gag)
|
|
508 {
|
|
509 if (!gag)
|
|
510 {
|
|
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());
|
|
517 }
|
|
518 result = true;
|
|
519 }
|
|
520
|
|
521 static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag)
|
|
522 {
|
|
523 //printf("[%s] checkReturnEscapeImpl, e = %s\n", e->loc->toChars(), e->toChars());
|
|
524 EscapeByResults er;
|
|
525
|
|
526 if (refs)
|
|
527 escapeByRef(e, &er);
|
|
528 else
|
|
529 escapeByValue(e, &er);
|
|
530
|
|
531 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim)
|
|
532 return false;
|
|
533
|
|
534 bool result = false;
|
|
535 for (size_t i = 0; i < er.byvalue.dim; i++)
|
|
536 {
|
|
537 VarDeclaration *v = er.byvalue[i];
|
|
538 //printf("byvalue %s\n", v->toChars());
|
|
539 if (v->isDataseg())
|
|
540 continue;
|
|
541
|
|
542 Dsymbol *p = v->toParent2();
|
|
543
|
|
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)
|
|
549 {
|
|
550 inferReturn(sc->func, v); // infer addition of 'return'
|
|
551 continue;
|
|
552 }
|
|
553
|
|
554 if (v->isScope())
|
|
555 {
|
|
556 if (v->storage_class & STCreturn)
|
|
557 continue;
|
|
558
|
|
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.
|
|
562 *
|
|
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; }
|
|
568 */
|
|
569 !(!refs && p->parent == sc->func))
|
|
570 {
|
|
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
|
|
573 {
|
|
574 if (!gag)
|
|
575 error(e->loc, "scope variable %s may not be returned", v->toChars());
|
|
576 result = true;
|
|
577 }
|
|
578 continue;
|
|
579 }
|
|
580 }
|
|
581 else if (v->storage_class & STCvariadic && p == sc->func)
|
|
582 {
|
|
583 Type *tb = v->type->toBasetype();
|
|
584 if (tb->ty == Tarray || tb->ty == Tsarray)
|
|
585 {
|
|
586 if (!gag)
|
|
587 error(e->loc, "returning `%s` escapes a reference to variadic parameter `%s`", e->toChars(), v->toChars());
|
|
588 result = false;
|
|
589 }
|
|
590 }
|
|
591 else
|
|
592 {
|
|
593 //printf("no infer for %s\n", v->toChars());
|
|
594 v->doNotInferScope = true;
|
|
595 }
|
|
596 }
|
|
597
|
|
598 for (size_t i = 0; i < er.byref.dim; i++)
|
|
599 {
|
|
600 VarDeclaration *v = er.byref[i];
|
|
601 //printf("byref %s\n", v->toChars());
|
|
602 if (v->isDataseg())
|
|
603 continue;
|
|
604
|
|
605 Dsymbol *p = v->toParent2();
|
|
606
|
|
607 if ((v->storage_class & (STCref | STCout)) == 0)
|
|
608 {
|
|
609 if (p == sc->func)
|
|
610 {
|
|
611 escapingRef(v, e, result, gag);
|
|
612 continue;
|
|
613 }
|
|
614 FuncDeclaration *fd = p->isFuncDeclaration();
|
|
615 if (fd && sc->func->flags & FUNCFLAGreturnInprocess)
|
|
616 {
|
|
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
|
|
623 */
|
|
624 if (global.params.vsafe)
|
|
625 sc->func->storage_class |= STCreturn;
|
|
626 }
|
|
627 }
|
|
628
|
|
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.
|
|
631 */
|
|
632 if ( (v->storage_class & (STCref | STCout)) &&
|
|
633 !(v->storage_class & (STCreturn | STCforeach)))
|
|
634 {
|
|
635 if ((sc->func->flags & FUNCFLAGreturnInprocess) && p == sc->func)
|
|
636 {
|
|
637 inferReturn(sc->func, v); // infer addition of 'return'
|
|
638 }
|
|
639 else if (global.params.useDIP25 &&
|
|
640 sc->_module && sc->_module->isRoot())
|
|
641 {
|
|
642 // Only look for errors if in module listed on command line
|
|
643
|
|
644 if (p == sc->func)
|
|
645 {
|
|
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;
|
|
650 }
|
|
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)
|
|
654 {
|
|
655 TypeFunction *tf = (TypeFunction *)fd->type;
|
|
656 if (tf->isref)
|
|
657 {
|
|
658 if (!gag)
|
|
659 error(e->loc, "escaping reference to outer local variable %s", v->toChars());
|
|
660 result = true;
|
|
661 continue;
|
|
662 }
|
|
663 }
|
|
664 }
|
|
665 }
|
|
666 }
|
|
667
|
|
668 for (size_t i = 0; i < er.byexp.dim; i++)
|
|
669 {
|
|
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;
|
|
675 }
|
|
676
|
|
677 return result;
|
|
678 }
|
|
679
|
|
680
|
|
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
|
|
686 */
|
|
687
|
|
688 static void inferReturn(FuncDeclaration *fd, VarDeclaration *v)
|
|
689 {
|
|
690 // v is a local in the current function
|
|
691
|
|
692 //printf("for function '%s' inferring 'return' for variable '%s'\n", fd->toChars(), v->toChars());
|
|
693 v->storage_class |= STCreturn;
|
|
694
|
|
695 TypeFunction *tf = (TypeFunction *)fd->type;
|
|
696 if (v == fd->vthis)
|
|
697 {
|
|
698 /* v is the 'this' reference, so mark the function
|
|
699 */
|
|
700 fd->storage_class |= STCreturn;
|
|
701 if (tf->ty == Tfunction)
|
|
702 {
|
|
703 //printf("'this' too %p %s\n", tf, sc->func->toChars());
|
|
704 tf->isreturn = true;
|
|
705 }
|
|
706 }
|
|
707 else
|
|
708 {
|
|
709 // Perform 'return' inference on parameter
|
|
710 if (tf->ty == Tfunction && tf->parameters)
|
|
711 {
|
|
712 const size_t dim = Parameter::dim(tf->parameters);
|
|
713 for (size_t i = 0; i < dim; i++)
|
|
714 {
|
|
715 Parameter *p = Parameter::getNth(tf->parameters, i);
|
|
716 if (p->ident == v->ident)
|
|
717 {
|
|
718 p->storageClass |= STCreturn;
|
|
719 break; // there can be only one
|
|
720 }
|
|
721 }
|
|
722 }
|
|
723 }
|
|
724 }
|
|
725
|
|
726
|
|
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; }
|
|
737 *
|
|
738 * No side effects.
|
|
739 *
|
|
740 * Params:
|
|
741 * e = expression to be returned by value
|
|
742 * er = where to place collected data
|
|
743 */
|
|
744 static void escapeByValue(Expression *e, EscapeByResults *er)
|
|
745 {
|
|
746 //printf("[%s] escapeByValue, e: %s\n", e->loc.toChars(), e->toChars());
|
|
747
|
|
748 class EscapeVisitor : public Visitor
|
|
749 {
|
|
750 public:
|
|
751 EscapeByResults *er;
|
|
752
|
|
753 EscapeVisitor(EscapeByResults *er)
|
|
754 : er(er)
|
|
755 {
|
|
756 }
|
|
757
|
|
758 void visit(Expression *)
|
|
759 {
|
|
760 }
|
|
761
|
|
762 void visit(AddrExp *e)
|
|
763 {
|
|
764 escapeByRef(e->e1, er);
|
|
765 }
|
|
766
|
|
767 void visit(SymOffExp *e)
|
|
768 {
|
|
769 VarDeclaration *v = e->var->isVarDeclaration();
|
|
770 if (v)
|
|
771 er->byref.push(v);
|
|
772 }
|
|
773
|
|
774 void visit(VarExp *e)
|
|
775 {
|
|
776 VarDeclaration *v = e->var->isVarDeclaration();
|
|
777 if (v)
|
|
778 er->byvalue.push(v);
|
|
779 }
|
|
780
|
|
781 void visit(ThisExp *e)
|
|
782 {
|
|
783 if (e->var)
|
|
784 er->byvalue.push(e->var);
|
|
785 }
|
|
786
|
|
787 void visit(DotVarExp *e)
|
|
788 {
|
|
789 Type *t = e->e1->type->toBasetype();
|
|
790 if (t->ty == Tstruct)
|
|
791 e->e1->accept(this);
|
|
792 }
|
|
793
|
|
794 void visit(DelegateExp *e)
|
|
795 {
|
|
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);
|
|
802 }
|
|
803
|
|
804 void visit(FuncExp *e)
|
|
805 {
|
|
806 if (e->fd->tok == TOKdelegate)
|
|
807 er->byfunc.push(e->fd);
|
|
808 }
|
|
809
|
|
810 void visit(TupleExp *)
|
|
811 {
|
|
812 assert(0); // should have been lowered by now
|
|
813 }
|
|
814
|
|
815 void visit(ArrayLiteralExp *e)
|
|
816 {
|
|
817 Type *tb = e->type->toBasetype();
|
|
818 if (tb->ty == Tsarray || tb->ty == Tarray)
|
|
819 {
|
|
820 if (e->basis)
|
|
821 e->basis->accept(this);
|
|
822 for (size_t i = 0; i < e->elements->dim; i++)
|
|
823 {
|
|
824 Expression *el = (*e->elements)[i];
|
|
825 if (el)
|
|
826 el->accept(this);
|
|
827 }
|
|
828 }
|
|
829 }
|
|
830
|
|
831 void visit(StructLiteralExp *e)
|
|
832 {
|
|
833 if (e->elements)
|
|
834 {
|
|
835 for (size_t i = 0; i < e->elements->dim; i++)
|
|
836 {
|
|
837 Expression *ex = (*e->elements)[i];
|
|
838 if (ex)
|
|
839 ex->accept(this);
|
|
840 }
|
|
841 }
|
|
842 }
|
|
843
|
|
844 void visit(NewExp *e)
|
|
845 {
|
|
846 Type *tb = e->newtype->toBasetype();
|
|
847 if (tb->ty == Tstruct && !e->member && e->arguments)
|
|
848 {
|
|
849 for (size_t i = 0; i < e->arguments->dim; i++)
|
|
850 {
|
|
851 Expression *ex = (*e->arguments)[i];
|
|
852 if (ex)
|
|
853 ex->accept(this);
|
|
854 }
|
|
855 }
|
|
856 }
|
|
857
|
|
858 void visit(CastExp *e)
|
|
859 {
|
|
860 Type *tb = e->type->toBasetype();
|
|
861 if (tb->ty == Tarray &&
|
|
862 e->e1->type->toBasetype()->ty == Tsarray)
|
|
863 {
|
|
864 escapeByRef(e->e1, er);
|
|
865 }
|
|
866 else
|
|
867 e->e1->accept(this);
|
|
868 }
|
|
869
|
|
870 void visit(SliceExp *e)
|
|
871 {
|
|
872 if (e->e1->op == TOKvar)
|
|
873 {
|
|
874 VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
|
|
875 Type *tb = e->type->toBasetype();
|
|
876 if (v)
|
|
877 {
|
|
878 if (tb->ty == Tsarray)
|
|
879 return;
|
|
880 if (v->storage_class & STCvariadic)
|
|
881 {
|
|
882 er->byvalue.push(v);
|
|
883 return;
|
|
884 }
|
|
885 }
|
|
886 }
|
|
887 Type *t1b = e->e1->type->toBasetype();
|
|
888 if (t1b->ty == Tsarray)
|
|
889 {
|
|
890 Type *tb = e->type->toBasetype();
|
|
891 if (tb->ty != Tsarray)
|
|
892 escapeByRef(e->e1, er);
|
|
893 }
|
|
894 else
|
|
895 e->e1->accept(this);
|
|
896 }
|
|
897
|
|
898 void visit(BinExp *e)
|
|
899 {
|
|
900 Type *tb = e->type->toBasetype();
|
|
901 if (tb->ty == Tpointer)
|
|
902 {
|
|
903 e->e1->accept(this);
|
|
904 e->e2->accept(this);
|
|
905 }
|
|
906 }
|
|
907
|
|
908 void visit(BinAssignExp *e)
|
|
909 {
|
|
910 e->e1->accept(this);
|
|
911 }
|
|
912
|
|
913 void visit(AssignExp *e)
|
|
914 {
|
|
915 e->e1->accept(this);
|
|
916 }
|
|
917
|
|
918 void visit(CommaExp *e)
|
|
919 {
|
|
920 e->e2->accept(this);
|
|
921 }
|
|
922
|
|
923 void visit(CondExp *e)
|
|
924 {
|
|
925 e->e1->accept(this);
|
|
926 e->e2->accept(this);
|
|
927 }
|
|
928
|
|
929 void visit(CallExp *e)
|
|
930 {
|
|
931 //printf("CallExp(): %s\n", e->toChars());
|
|
932 /* Check each argument that is
|
|
933 * passed as 'return scope'.
|
|
934 */
|
|
935 Type *t1 = e->e1->type->toBasetype();
|
|
936 TypeFunction *tf = NULL;
|
|
937 TypeDelegate *dg = NULL;
|
|
938 if (t1->ty == Tdelegate)
|
|
939 {
|
|
940 dg = (TypeDelegate *)t1;
|
|
941 tf = (TypeFunction *)dg->next;
|
|
942 }
|
|
943 else if (t1->ty == Tfunction)
|
|
944 tf = (TypeFunction *)t1;
|
|
945 else
|
|
946 return;
|
|
947
|
|
948 if (e->arguments && e->arguments->dim)
|
|
949 {
|
|
950 /* j=1 if _arguments[] is first argument,
|
|
951 * skip it because it is not passed by ref
|
|
952 */
|
|
953 size_t j = (tf->linkage == LINKd && tf->varargs == 1);
|
|
954 for (size_t i = j; i < e->arguments->dim; ++i)
|
|
955 {
|
|
956 Expression *arg = (*e->arguments)[i];
|
|
957 size_t nparams = Parameter::dim(tf->parameters);
|
|
958 if (i - j < nparams && i >= j)
|
|
959 {
|
|
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);
|
|
966 }
|
|
967 }
|
|
968 }
|
|
969 // If 'this' is returned, check it too
|
|
970 if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
|
|
971 {
|
|
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)
|
|
976 {
|
|
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);
|
|
981 }
|
|
982 else if (dve->var->storage_class & STCreturn || tf->isreturn)
|
|
983 {
|
|
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);
|
|
988 }
|
|
989 }
|
|
990
|
|
991 /* If returning the result of a delegate call, the .ptr
|
|
992 * field of the delegate must be checked.
|
|
993 */
|
|
994 if (dg)
|
|
995 {
|
|
996 if (tf->isreturn)
|
|
997 e->e1->accept(this);
|
|
998 }
|
|
999 }
|
|
1000 };
|
|
1001
|
|
1002 EscapeVisitor v(er);
|
|
1003 e->accept(&v);
|
|
1004 }
|
|
1005
|
|
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; }
|
|
1016 *
|
|
1017 * No side effects.
|
|
1018 *
|
|
1019 * Params:
|
|
1020 * e = expression to be returned by 'ref'
|
|
1021 * er = where to place collected data
|
|
1022 */
|
|
1023 static void escapeByRef(Expression *e, EscapeByResults *er)
|
|
1024 {
|
|
1025 //printf("[%s] escapeByRef, e: %s\n", e->loc->toChars(), e->toChars());
|
|
1026 class EscapeRefVisitor : public Visitor
|
|
1027 {
|
|
1028 public:
|
|
1029 EscapeByResults *er;
|
|
1030
|
|
1031 EscapeRefVisitor(EscapeByResults *er)
|
|
1032 : er(er)
|
|
1033 {
|
|
1034 }
|
|
1035
|
|
1036 void visit(Expression *)
|
|
1037 {
|
|
1038 }
|
|
1039
|
|
1040 void visit(VarExp *e)
|
|
1041 {
|
|
1042 VarDeclaration *v = e->var->isVarDeclaration();
|
|
1043 if (v)
|
|
1044 {
|
|
1045 if (v->storage_class & STCref && v->storage_class & (STCforeach | STCtemp) && v->_init)
|
|
1046 {
|
|
1047 /* If compiler generated ref temporary
|
|
1048 * (ref v = ex; ex)
|
|
1049 * look at the initializer instead
|
|
1050 */
|
|
1051 if (ExpInitializer *ez = v->_init->isExpInitializer())
|
|
1052 {
|
|
1053 assert(ez->exp && ez->exp->op == TOKconstruct);
|
|
1054 Expression *ex = ((ConstructExp *)ez->exp)->e2;
|
|
1055 ex->accept(this);
|
|
1056 }
|
|
1057 }
|
|
1058 else
|
|
1059 er->byref.push(v);
|
|
1060 }
|
|
1061 }
|
|
1062
|
|
1063 void visit(ThisExp *e)
|
|
1064 {
|
|
1065 if (e->var)
|
|
1066 er->byref.push(e->var);
|
|
1067 }
|
|
1068
|
|
1069 void visit(PtrExp *e)
|
|
1070 {
|
|
1071 escapeByValue(e->e1, er);
|
|
1072 }
|
|
1073
|
|
1074 void visit(IndexExp *e)
|
|
1075 {
|
|
1076 Type *tb = e->e1->type->toBasetype();
|
|
1077 if (e->e1->op == TOKvar)
|
|
1078 {
|
|
1079 VarDeclaration *v = ((VarExp *)e->e1)->var->isVarDeclaration();
|
|
1080 if (tb->ty == Tarray || tb->ty == Tsarray)
|
|
1081 {
|
|
1082 if (v->storage_class & STCvariadic)
|
|
1083 {
|
|
1084 er->byref.push(v);
|
|
1085 return;
|
|
1086 }
|
|
1087 }
|
|
1088 }
|
|
1089 if (tb->ty == Tsarray)
|
|
1090 {
|
|
1091 e->e1->accept(this);
|
|
1092 }
|
|
1093 else if (tb->ty == Tarray)
|
|
1094 {
|
|
1095 escapeByValue(e->e1, er);
|
|
1096 }
|
|
1097 }
|
|
1098
|
|
1099 void visit(DotVarExp *e)
|
|
1100 {
|
|
1101 Type *t1b = e->e1->type->toBasetype();
|
|
1102 if (t1b->ty == Tclass)
|
|
1103 escapeByValue(e->e1, er);
|
|
1104 else
|
|
1105 e->e1->accept(this);
|
|
1106 }
|
|
1107
|
|
1108 void visit(BinAssignExp *e)
|
|
1109 {
|
|
1110 e->e1->accept(this);
|
|
1111 }
|
|
1112
|
|
1113 void visit(AssignExp *e)
|
|
1114 {
|
|
1115 e->e1->accept(this);
|
|
1116 }
|
|
1117
|
|
1118 void visit(CommaExp *e)
|
|
1119 {
|
|
1120 e->e2->accept(this);
|
|
1121 }
|
|
1122
|
|
1123 void visit(CondExp *e)
|
|
1124 {
|
|
1125 e->e1->accept(this);
|
|
1126 e->e2->accept(this);
|
|
1127 }
|
|
1128
|
|
1129 void visit(CallExp *e)
|
|
1130 {
|
|
1131 /* If the function returns by ref, check each argument that is
|
|
1132 * passed as 'return ref'.
|
|
1133 */
|
|
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)
|
|
1143 {
|
|
1144 if (e->arguments && e->arguments->dim)
|
|
1145 {
|
|
1146 /* j=1 if _arguments[] is first argument,
|
|
1147 * skip it because it is not passed by ref
|
|
1148 */
|
|
1149 size_t j = (tf->linkage == LINKd && tf->varargs == 1);
|
|
1150
|
|
1151 for (size_t i = j; i < e->arguments->dim; ++i)
|
|
1152 {
|
|
1153 Expression *arg = (*e->arguments)[i];
|
|
1154 size_t nparams = Parameter::dim(tf->parameters);
|
|
1155 if (i - j < nparams && i >= j)
|
|
1156 {
|
|
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))
|
|
1162 {
|
|
1163 if (arg->op == TOKdelegate)
|
|
1164 {
|
|
1165 DelegateExp *de = (DelegateExp *)arg;
|
|
1166 if (de->func->isNested())
|
|
1167 er->byexp.push(de);
|
|
1168 }
|
|
1169 else
|
|
1170 escapeByValue(arg, er);
|
|
1171 }
|
|
1172 }
|
|
1173 }
|
|
1174 }
|
|
1175
|
|
1176 // If 'this' is returned by ref, check it too
|
|
1177 if (e->e1->op == TOKdotvar && t1->ty == Tfunction)
|
|
1178 {
|
|
1179 DotVarExp *dve = (DotVarExp *)e->e1;
|
|
1180 if (dve->var->storage_class & STCreturn || tf->isreturn)
|
|
1181 {
|
|
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);
|
|
1186 }
|
|
1187
|
|
1188 }
|
|
1189 // If it's a delegate, check it too
|
|
1190 if (e->e1->op == TOKvar && t1->ty == Tdelegate)
|
|
1191 {
|
|
1192 escapeByValue(e->e1, er);
|
|
1193 }
|
|
1194 }
|
|
1195 else
|
|
1196 er->byexp.push(e);
|
|
1197 }
|
|
1198 };
|
|
1199
|
|
1200 EscapeRefVisitor v(er);
|
|
1201 e->accept(&v);
|
|
1202 }
|
|
1203
|
|
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
|
|
1210 */
|
|
1211 void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars)
|
|
1212 {
|
|
1213 //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars());
|
|
1214 for (Dsymbol *p = fd->parent; p; p = p->parent)
|
|
1215 {
|
|
1216 FuncDeclaration *fdp = p->isFuncDeclaration();
|
|
1217 if (fdp)
|
|
1218 {
|
|
1219 for (size_t i = 0; i < fdp->closureVars.dim; i++)
|
|
1220 {
|
|
1221 VarDeclaration *v = fdp->closureVars[i];
|
|
1222 for (size_t j = 0; j < v->nestedrefs.dim; j++)
|
|
1223 {
|
|
1224 FuncDeclaration *fdv = v->nestedrefs[j];
|
|
1225 if (fdv == fd)
|
|
1226 {
|
|
1227 //printf("accessed: %s, type %s\n", v->toChars(), v->type->toChars());
|
|
1228 vars->push(v);
|
|
1229 }
|
|
1230 }
|
|
1231 }
|
|
1232 }
|
|
1233 }
|
|
1234 }
|