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 */ 22struct 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 30static bool checkReturnEscapeImpl(Scope *sc, Expression *e, bool refs, bool gag); 31static void inferReturn(FuncDeclaration *fd, VarDeclaration *v); 32static void escapeByValue(Expression *e, EscapeByResults *er); 33static void escapeByRef(Expression *e, EscapeByResults *er); 34static void findAllOuterAccessedVariables(FuncDeclaration *fd, VarDeclarations *vars); 35 36/* 'v' is assigned unsafely to 'par' 37*/ 38static 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 */ 65bool 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 */ 185bool 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 */ 429bool 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 481bool 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 */ 498bool 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 507static 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 521static 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 688static 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 */ 744static 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 */ 1023static 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 */ 1211void 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} 1235