1 /** 2 * Most of the logic to implement scoped pointers and scoped references is here. 3 * 4 * Copyright: Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved 5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright) 6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/escape.d, _escape.d) 8 * Documentation: https://dlang.org/phobos/dmd_escape.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/escape.d 10 */ 11 12 module dmd.escape; 13 14 import core.stdc.stdio : printf; 15 import core.stdc.stdlib; 16 import core.stdc..string; 17 18 import dmd.root.rmem; 19 20 import dmd.aggregate; 21 import dmd.declaration; 22 import dmd.dscope; 23 import dmd.dsymbol; 24 import dmd.errors; 25 import dmd.expression; 26 import dmd.func; 27 import dmd.globals; 28 import dmd.identifier; 29 import dmd.init; 30 import dmd.mtype; 31 import dmd.printast; 32 import dmd.root.rootobject; 33 import dmd.tokens; 34 import dmd.visitor; 35 import dmd.arraytypes; 36 37 /****************************************************** 38 * Checks memory objects passed to a function. 39 * Checks that if a memory object is passed by ref or by pointer, 40 * all of the refs or pointers are const, or there is only one mutable 41 * ref or pointer to it. 42 * References: 43 * DIP 1021 44 * Params: 45 * sc = used to determine current function and module 46 * fd = function being called 47 * tf = fd's type 48 * ethis = if not null, the `this` pointer 49 * arguments = actual arguments to function 50 * gag = do not print error messages 51 * Returns: 52 * `true` if error 53 */ 54 bool checkMutableArguments(Scope* sc, FuncDeclaration fd, TypeFunction tf, 55 Expression ethis, Expressions* arguments, bool gag) 56 { 57 enum log = false; 58 if (log) printf("[%s] checkMutableArguments, fd: `%s`\n", fd.loc.toChars(), fd.toChars()); 59 if (log && ethis) printf("ethis: `%s`\n", ethis.toChars()); 60 bool errors = false; 61 62 /* Outer variable references are treated as if they are extra arguments 63 * passed by ref to the function (which they essentially are via the static link). 64 */ 65 VarDeclaration[] outerVars = fd ? fd.outerVars[] : null; 66 67 const len = arguments.length + (ethis !is null) + outerVars.length; 68 if (len <= 1) 69 return errors; 70 71 struct EscapeBy 72 { 73 EscapeByResults er; 74 Parameter param; // null if no Parameter for this argument 75 bool isMutable; // true if reference to mutable 76 } 77 78 /* Store escapeBy as static data escapeByStorage so we can keep reusing the same 79 * arrays rather than reallocating them. 80 */ 81 __gshared EscapeBy[] escapeByStorage; 82 auto escapeBy = escapeByStorage; 83 if (escapeBy.length < len) 84 { 85 auto newPtr = cast(EscapeBy*)mem.xrealloc(escapeBy.ptr, len * EscapeBy.sizeof); 86 // Clear the new section 87 memset(newPtr + escapeBy.length, 0, (len - escapeBy.length) * EscapeBy.sizeof); 88 escapeBy = newPtr[0 .. len]; 89 escapeByStorage = escapeBy; 90 } 91 92 const paramLength = tf.parameterList.length; 93 94 // Fill in escapeBy[] with arguments[], ethis, and outerVars[] 95 foreach (const i, ref eb; escapeBy) 96 { 97 bool refs; 98 Expression arg; 99 if (i < arguments.length) 100 { 101 arg = (*arguments)[i]; 102 if (i < paramLength) 103 { 104 eb.param = tf.parameterList[i]; 105 refs = (eb.param.storageClass & (STC.out_ | STC.ref_)) != 0; 106 eb.isMutable = eb.param.isReferenceToMutable(arg.type); 107 } 108 else 109 { 110 eb.param = null; 111 refs = false; 112 eb.isMutable = arg.type.isReferenceToMutable(); 113 } 114 } 115 else if (ethis) 116 { 117 /* ethis is passed by value if a class reference, 118 * by ref if a struct value 119 */ 120 eb.param = null; 121 arg = ethis; 122 auto ad = fd.isThis(); 123 assert(ad); 124 assert(ethis); 125 if (ad.isClassDeclaration()) 126 { 127 refs = false; 128 eb.isMutable = arg.type.isReferenceToMutable(); 129 } 130 else 131 { 132 assert(ad.isStructDeclaration()); 133 refs = true; 134 eb.isMutable = arg.type.isMutable(); 135 } 136 } 137 else 138 { 139 // outer variables are passed by ref 140 eb.param = null; 141 refs = true; 142 auto var = outerVars[i - (len - outerVars.length)]; 143 eb.isMutable = var.type.isMutable(); 144 eb.er.byref.push(var); 145 continue; 146 } 147 148 if (refs) 149 escapeByRef(arg, &eb.er); 150 else 151 escapeByValue(arg, &eb.er); 152 } 153 154 void checkOnePair(size_t i, ref EscapeBy eb, ref EscapeBy eb2, 155 VarDeclaration v, VarDeclaration v2, bool of) 156 { 157 if (log) printf("v2: `%s`\n", v2.toChars()); 158 if (v2 != v) 159 return; 160 //printf("v %d v2 %d\n", eb.isMutable, eb2.isMutable); 161 if (!(eb.isMutable || eb2.isMutable)) 162 return; 163 164 if (!(global.params.vsafe && sc.func.setUnsafe())) 165 return; 166 167 if (!gag) 168 { 169 // int i; funcThatEscapes(ref int i); 170 // funcThatEscapes(i); // error escaping reference _to_ `i` 171 // int* j; funcThatEscapes2(int* j); 172 // funcThatEscapes2(j); // error escaping reference _of_ `i` 173 const(char)* referenceVerb = of ? "of" : "to"; 174 const(char)* msg = eb.isMutable && eb2.isMutable 175 ? "more than one mutable reference %s `%s` in arguments to `%s()`" 176 : "mutable and const references %s `%s` in arguments to `%s()`"; 177 error((*arguments)[i].loc, msg, 178 referenceVerb, 179 v.toChars(), 180 fd ? fd.toPrettyChars() : "indirectly"); 181 } 182 errors = true; 183 } 184 185 void escape(size_t i, ref EscapeBy eb, bool byval) 186 { 187 foreach (VarDeclaration v; byval ? eb.er.byvalue : eb.er.byref) 188 { 189 if (log) 190 { 191 const(char)* by = byval ? "byval" : "byref"; 192 printf("%s %s\n", by, v.toChars()); 193 } 194 if (byval && !v.type.hasPointers()) 195 continue; 196 foreach (ref eb2; escapeBy[i + 1 .. $]) 197 { 198 foreach (VarDeclaration v2; byval ? eb2.er.byvalue : eb2.er.byref) 199 { 200 checkOnePair(i, eb, eb2, v, v2, byval); 201 } 202 } 203 } 204 } 205 foreach (const i, ref eb; escapeBy[0 .. $ - 1]) 206 { 207 escape(i, eb, true); 208 escape(i, eb, false); 209 } 210 211 /* Reset the arrays in escapeBy[] so we can reuse them next time through 212 */ 213 foreach (ref eb; escapeBy) 214 { 215 eb.er.reset(); 216 } 217 218 return errors; 219 } 220 221 /****************************************** 222 * Array literal is going to be allocated on the GC heap. 223 * Check its elements to see if any would escape by going on the heap. 224 * Params: 225 * sc = used to determine current function and module 226 * ae = array literal expression 227 * gag = do not print error messages 228 * Returns: 229 * `true` if any elements escaped 230 */ 231 bool checkArrayLiteralEscape(Scope *sc, ArrayLiteralExp ae, bool gag) 232 { 233 bool errors; 234 if (ae.basis) 235 errors = checkNewEscape(sc, ae.basis, gag); 236 foreach (ex; *ae.elements) 237 { 238 if (ex) 239 errors |= checkNewEscape(sc, ex, gag); 240 } 241 return errors; 242 } 243 244 /****************************************** 245 * Associative array literal is going to be allocated on the GC heap. 246 * Check its elements to see if any would escape by going on the heap. 247 * Params: 248 * sc = used to determine current function and module 249 * ae = associative array literal expression 250 * gag = do not print error messages 251 * Returns: 252 * `true` if any elements escaped 253 */ 254 bool checkAssocArrayLiteralEscape(Scope *sc, AssocArrayLiteralExp ae, bool gag) 255 { 256 bool errors; 257 foreach (ex; *ae.keys) 258 { 259 if (ex) 260 errors |= checkNewEscape(sc, ex, gag); 261 } 262 foreach (ex; *ae.values) 263 { 264 if (ex) 265 errors |= checkNewEscape(sc, ex, gag); 266 } 267 return errors; 268 } 269 270 /**************************************** 271 * Function parameter `par` is being initialized to `arg`, 272 * and `par` may escape. 273 * Detect if scoped values can escape this way. 274 * Print error messages when these are detected. 275 * Params: 276 * sc = used to determine current function and module 277 * fdc = function being called, `null` if called indirectly 278 * par = function parameter (`this` if null) 279 * arg = initializer for param 280 * assertmsg = true if the parameter is the msg argument to assert(bool, msg). 281 * gag = do not print error messages 282 * Returns: 283 * `true` if pointers to the stack can escape via assignment 284 */ 285 bool checkParamArgumentEscape(Scope* sc, FuncDeclaration fdc, Parameter par, Expression arg, bool assertmsg, bool gag) 286 { 287 enum log = false; 288 if (log) printf("checkParamArgumentEscape(arg: %s par: %s)\n", 289 arg ? arg.toChars() : "null", 290 par ? par.toChars() : "this"); 291 //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers()); 292 293 if (!arg.type.hasPointers()) 294 return false; 295 296 EscapeByResults er; 297 298 escapeByValue(arg, &er); 299 300 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim) 301 return false; 302 303 bool result = false; 304 305 /* 'v' is assigned unsafely to 'par' 306 */ 307 void unsafeAssign(VarDeclaration v, const char* desc) 308 { 309 if (global.params.vsafe && sc.func.setUnsafe()) 310 { 311 if (!gag) 312 { 313 if (assertmsg) 314 { 315 error(arg.loc, "%s `%s` assigned to non-scope parameter calling `assert()`", 316 desc, v.toChars()); 317 } 318 else 319 { 320 error(arg.loc, "%s `%s` assigned to non-scope parameter `%s` calling %s", 321 desc, v.toChars(), 322 par ? par.toChars() : "this", 323 fdc ? fdc.toPrettyChars() : "indirectly"); 324 } 325 } 326 result = true; 327 } 328 } 329 330 foreach (VarDeclaration v; er.byvalue) 331 { 332 if (log) printf("byvalue %s\n", v.toChars()); 333 if (v.isDataseg()) 334 continue; 335 336 Dsymbol p = v.toParent2(); 337 338 notMaybeScope(v); 339 340 if (v.isScope()) 341 { 342 unsafeAssign(v, "scope variable"); 343 } 344 else if (v.storage_class & STC.variadic && p == sc.func) 345 { 346 Type tb = v.type.toBasetype(); 347 if (tb.ty == Tarray || tb.ty == Tsarray) 348 { 349 unsafeAssign(v, "variadic variable"); 350 } 351 } 352 else 353 { 354 /* v is not 'scope', and is assigned to a parameter that may escape. 355 * Therefore, v can never be 'scope'. 356 */ 357 if (log) printf("no infer for %s in %s loc %s, fdc %s, %d\n", 358 v.toChars(), sc.func.ident.toChars(), sc.func.loc.toChars(), fdc.ident.toChars(), __LINE__); 359 v.doNotInferScope = true; 360 } 361 } 362 363 foreach (VarDeclaration v; er.byref) 364 { 365 if (log) printf("byref %s\n", v.toChars()); 366 if (v.isDataseg()) 367 continue; 368 369 Dsymbol p = v.toParent2(); 370 371 notMaybeScope(v); 372 373 if ((v.storage_class & (STC.ref_ | STC.out_)) == 0 && p == sc.func) 374 { 375 if (par && (par.storageClass & (STC.scope_ | STC.return_)) == STC.scope_) 376 continue; 377 378 unsafeAssign(v, "reference to local variable"); 379 continue; 380 } 381 } 382 383 foreach (FuncDeclaration fd; er.byfunc) 384 { 385 //printf("fd = %s, %d\n", fd.toChars(), fd.tookAddressOf); 386 VarDeclarations vars; 387 findAllOuterAccessedVariables(fd, &vars); 388 389 foreach (v; vars) 390 { 391 //printf("v = %s\n", v.toChars()); 392 assert(!v.isDataseg()); // these are not put in the closureVars[] 393 394 Dsymbol p = v.toParent2(); 395 396 notMaybeScope(v); 397 398 if ((v.storage_class & (STC.ref_ | STC.out_ | STC.scope_)) && p == sc.func) 399 { 400 unsafeAssign(v, "reference to local"); 401 continue; 402 } 403 } 404 } 405 406 foreach (Expression ee; er.byexp) 407 { 408 if (sc.func.setUnsafe()) 409 { 410 if (!gag) 411 error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope parameter `%s`", 412 ee.toChars(), 413 par ? par.toChars() : "this"); 414 result = true; 415 } 416 } 417 418 return result; 419 } 420 421 /***************************************************** 422 * Function argument initializes a `return` parameter, 423 * and that parameter gets assigned to `firstArg`. 424 * Essentially, treat as `firstArg = arg;` 425 * Params: 426 * sc = used to determine current function and module 427 * firstArg = `ref` argument through which `arg` may be assigned 428 * arg = initializer for parameter 429 * gag = do not print error messages 430 * Returns: 431 * `true` if assignment to `firstArg` would cause an error 432 */ 433 bool checkParamArgumentReturn(Scope* sc, Expression firstArg, Expression arg, bool gag) 434 { 435 enum log = false; 436 if (log) printf("checkParamArgumentReturn(firstArg: %s arg: %s)\n", 437 firstArg.toChars(), arg.toChars()); 438 //printf("type = %s, %d\n", arg.type.toChars(), arg.type.hasPointers()); 439 440 if (!arg.type.hasPointers()) 441 return false; 442 443 scope e = new AssignExp(arg.loc, firstArg, arg); 444 return checkAssignEscape(sc, e, gag); 445 } 446 447 /***************************************************** 448 * Check struct constructor of the form `s.this(args)`, by 449 * checking each `return` parameter to see if it gets 450 * assigned to `s`. 451 * Params: 452 * sc = used to determine current function and module 453 * ce = constructor call of the form `s.this(args)` 454 * gag = do not print error messages 455 * Returns: 456 * `true` if construction would cause an escaping reference error 457 */ 458 bool checkConstructorEscape(Scope* sc, CallExp ce, bool gag) 459 { 460 enum log = false; 461 if (log) printf("checkConstructorEscape(%s, %s)\n", ce.toChars(), ce.type.toChars()); 462 Type tthis = ce.type.toBasetype(); 463 assert(tthis.ty == Tstruct); 464 if (!tthis.hasPointers()) 465 return false; 466 467 if (!ce.arguments && ce.arguments.dim) 468 return false; 469 470 assert(ce.e1.op == TOK.dotVariable); 471 DotVarExp dve = cast(DotVarExp)ce.e1; 472 CtorDeclaration ctor = dve.var.isCtorDeclaration(); 473 assert(ctor); 474 assert(ctor.type.ty == Tfunction); 475 TypeFunction tf = cast(TypeFunction)ctor.type; 476 477 const nparams = tf.parameterList.length; 478 const n = ce.arguments.dim; 479 480 // j=1 if _arguments[] is first argument 481 const j = tf.isDstyleVariadic(); 482 483 /* Attempt to assign each `return` arg to the `this` reference 484 */ 485 foreach (const i; 0 .. n) 486 { 487 Expression arg = (*ce.arguments)[i]; 488 if (!arg.type.hasPointers()) 489 return false; 490 491 //printf("\targ[%d]: %s\n", i, arg.toChars()); 492 493 if (i - j < nparams && i >= j) 494 { 495 Parameter p = tf.parameterList[i - j]; 496 497 if (p.storageClass & STC.return_) 498 { 499 /* Fake `dve.e1 = arg;` and look for scope violations 500 */ 501 scope e = new AssignExp(arg.loc, dve.e1, arg); 502 if (checkAssignEscape(sc, e, gag)) 503 return true; 504 } 505 } 506 } 507 508 return false; 509 } 510 511 /**************************************** 512 * Given an `AssignExp`, determine if the lvalue will cause 513 * the contents of the rvalue to escape. 514 * Print error messages when these are detected. 515 * Infer `scope` attribute for the lvalue where possible, in order 516 * to eliminate the error. 517 * Params: 518 * sc = used to determine current function and module 519 * e = `AssignExp` or `CatAssignExp` to check for any pointers to the stack 520 * gag = do not print error messages 521 * Returns: 522 * `true` if pointers to the stack can escape via assignment 523 */ 524 bool checkAssignEscape(Scope* sc, Expression e, bool gag) 525 { 526 enum log = false; 527 if (log) printf("checkAssignEscape(e: %s)\n", e.toChars()); 528 if (e.op != TOK.assign && e.op != TOK.blit && e.op != TOK.construct && 529 e.op != TOK.concatenateAssign && e.op != TOK.concatenateElemAssign && e.op != TOK.concatenateDcharAssign) 530 return false; 531 auto ae = cast(BinExp)e; 532 Expression e1 = ae.e1; 533 Expression e2 = ae.e2; 534 //printf("type = %s, %d\n", e1.type.toChars(), e1.type.hasPointers()); 535 536 if (!e1.type.hasPointers()) 537 return false; 538 539 if (e1.op == TOK.slice) 540 return false; 541 542 /* The struct literal case can arise from the S(e2) constructor call: 543 * return S(e2); 544 * and appears in this function as: 545 * structLiteral = e2; 546 * Such an assignment does not necessarily remove scope-ness. 547 */ 548 if (e1.op == TOK.structLiteral) 549 return false; 550 551 EscapeByResults er; 552 553 escapeByValue(e2, &er); 554 555 if (!er.byref.dim && !er.byvalue.dim && !er.byfunc.dim && !er.byexp.dim) 556 return false; 557 558 VarDeclaration va = expToVariable(e1); 559 560 if (va && e.op == TOK.concatenateElemAssign) 561 { 562 /* https://issues.dlang.org/show_bug.cgi?id=17842 563 * Draw an equivalence between: 564 * *q = p; 565 * and: 566 * va ~= e; 567 * since we are not assigning to va, but are assigning indirectly through va. 568 */ 569 va = null; 570 } 571 572 if (va && e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass) 573 { 574 /* https://issues.dlang.org/show_bug.cgi?id=17949 575 * Draw an equivalence between: 576 * *q = p; 577 * and: 578 * va.field = e2; 579 * since we are not assigning to va, but are assigning indirectly through class reference va. 580 */ 581 va = null; 582 } 583 584 if (log && va) printf("va: %s\n", va.toChars()); 585 586 // Try to infer 'scope' for va if in a function not marked @system 587 bool inferScope = false; 588 if (va && sc.func && sc.func.type && sc.func.type.ty == Tfunction) 589 inferScope = (cast(TypeFunction)sc.func.type).trust != TRUST.system; 590 //printf("inferScope = %d, %d\n", inferScope, (va.storage_class & STCmaybescope) != 0); 591 592 // Determine if va is a parameter that is an indirect reference 593 const bool vaIsRef = va && va.storage_class & STC.parameter && 594 (va.storage_class & (STC.ref_ | STC.out_) || va.type.toBasetype().ty == Tclass); 595 if (log && vaIsRef) printf("va is ref `%s`\n", va.toChars()); 596 597 /* Determine if va is the first parameter, through which other 'return' parameters 598 * can be assigned. 599 */ 600 bool isFirstRef() 601 { 602 if (!vaIsRef) 603 return false; 604 Dsymbol p = va.toParent2(); 605 FuncDeclaration fd = sc.func; 606 if (p == fd && fd.type && fd.type.ty == Tfunction) 607 { 608 TypeFunction tf = cast(TypeFunction)fd.type; 609 if (!tf.nextOf() || (tf.nextOf().ty != Tvoid && !fd.isCtorDeclaration())) 610 return false; 611 if (va == fd.vthis) 612 return true; 613 if (fd.parameters && fd.parameters.dim && (*fd.parameters)[0] == va) 614 return true; 615 } 616 return false; 617 } 618 const bool vaIsFirstRef = isFirstRef(); 619 if (log && vaIsFirstRef) printf("va is first ref `%s`\n", va.toChars()); 620 621 bool result = false; 622 foreach (VarDeclaration v; er.byvalue) 623 { 624 if (log) printf("byvalue: %s\n", v.toChars()); 625 if (v.isDataseg()) 626 continue; 627 628 if (v == va) 629 continue; 630 631 Dsymbol p = v.toParent2(); 632 633 if (va && !vaIsRef && !va.isScope() && !v.isScope() && 634 (va.storage_class & v.storage_class & (STC.maybescope | STC.variadic)) == STC.maybescope && 635 p == sc.func) 636 { 637 /* Add v to va's list of dependencies 638 */ 639 va.addMaybe(v); 640 continue; 641 } 642 643 if (vaIsFirstRef && 644 (v.isScope() || (v.storage_class & STC.maybescope)) && 645 !(v.storage_class & STC.return_) && 646 v.isParameter() && 647 sc.func.flags & FUNCFLAG.returnInprocess && 648 p == sc.func) 649 { 650 if (log) printf("inferring 'return' for parameter %s in function %s\n", v.toChars(), sc.func.toChars()); 651 inferReturn(sc.func, v); // infer addition of 'return' 652 } 653 654 if (!(va && va.isScope()) || vaIsRef) 655 notMaybeScope(v); 656 657 if (v.isScope()) 658 { 659 if (vaIsFirstRef && v.isParameter() && v.storage_class & STC.return_) 660 { 661 if (va.isScope()) 662 continue; 663 664 if (inferScope && !va.doNotInferScope) 665 { 666 if (log) printf("inferring scope for lvalue %s\n", va.toChars()); 667 va.storage_class |= STC.scope_ | STC.scopeinferred; 668 continue; 669 } 670 } 671 672 if (va && va.isScope() && va.storage_class & STC.return_ && !(v.storage_class & STC.return_) && 673 sc.func.setUnsafe()) 674 { 675 if (!gag) 676 error(ae.loc, "scope variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars()); 677 result = true; 678 continue; 679 } 680 681 // If va's lifetime encloses v's, then error 682 if (va && 683 (va.enclosesLifetimeOf(v) && !(v.storage_class & (STC.parameter | STC.temp)) || 684 // va is class reference 685 ae.e1.op == TOK.dotVariable && va.type.toBasetype().ty == Tclass && (va.enclosesLifetimeOf(v) || !va.isScope()) || 686 vaIsRef || 687 va.storage_class & (STC.ref_ | STC.out_) && !(v.storage_class & (STC.parameter | STC.temp))) && 688 sc.func.setUnsafe()) 689 { 690 if (!gag) 691 error(ae.loc, "scope variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars()); 692 result = true; 693 continue; 694 } 695 696 if (va && !va.isDataseg() && !va.doNotInferScope) 697 { 698 if (!va.isScope() && inferScope) 699 { //printf("inferring scope for %s\n", va.toChars()); 700 va.storage_class |= STC.scope_ | STC.scopeinferred; 701 if (v.storage_class & STC.return_ && 702 !(va.storage_class & STC.return_)) 703 { 704 va.storage_class |= STC.return_ | STC.returninferred; 705 } 706 } 707 continue; 708 } 709 if (sc.func.setUnsafe()) 710 { 711 if (!gag) 712 error(ae.loc, "scope variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars()); 713 result = true; 714 } 715 } 716 else if (v.storage_class & STC.variadic && p == sc.func) 717 { 718 Type tb = v.type.toBasetype(); 719 if (tb.ty == Tarray || tb.ty == Tsarray) 720 { 721 if (va && !va.isDataseg() && !va.doNotInferScope) 722 { 723 if (!va.isScope() && inferScope) 724 { //printf("inferring scope for %s\n", va.toChars()); 725 va.storage_class |= STC.scope_ | STC.scopeinferred; 726 } 727 continue; 728 } 729 if (sc.func.setUnsafe()) 730 { 731 if (!gag) 732 error(ae.loc, "variadic variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars()); 733 result = true; 734 } 735 } 736 } 737 else 738 { 739 /* v is not 'scope', and we didn't check the scope of where we assigned it to. 740 * It may escape via that assignment, therefore, v can never be 'scope'. 741 */ 742 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); 743 v.doNotInferScope = true; 744 } 745 } 746 747 ByRef: 748 foreach (VarDeclaration v; er.byref) 749 { 750 if (log) printf("byref: %s\n", v.toChars()); 751 if (v.isDataseg()) 752 continue; 753 754 if (global.params.vsafe) 755 { 756 if (va && va.isScope() && (v.storage_class & (STC.ref_ | STC.out_)) == 0) 757 { 758 if (!(va.storage_class & STC.return_)) 759 { 760 va.doNotInferReturn = true; 761 } 762 else if (sc.func.setUnsafe()) 763 { 764 if (!gag) 765 error(ae.loc, "address of local variable `%s` assigned to return scope `%s`", v.toChars(), va.toChars()); 766 result = true; 767 continue; 768 } 769 } 770 } 771 772 Dsymbol p = v.toParent2(); 773 774 // If va's lifetime encloses v's, then error 775 if (va && 776 (va.enclosesLifetimeOf(v) && !(v.storage_class & STC.parameter) || 777 va.storage_class & STC.ref_ || 778 va.isDataseg()) && 779 sc.func.setUnsafe()) 780 { 781 if (!gag) 782 error(ae.loc, "address of variable `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars()); 783 result = true; 784 continue; 785 } 786 787 if (va && v.storage_class & (STC.ref_ | STC.out_)) 788 { 789 Dsymbol pva = va.toParent2(); 790 for (Dsymbol pv = p; pv; ) 791 { 792 pv = pv.toParent2(); 793 if (pva == pv) // if v is nested inside pva 794 { 795 if (sc.func.setUnsafe()) 796 { 797 if (!gag) 798 error(ae.loc, "reference `%s` assigned to `%s` with longer lifetime", v.toChars(), va.toChars()); 799 result = true; 800 continue ByRef; 801 } 802 break; 803 } 804 } 805 } 806 807 if (!(va && va.isScope())) 808 notMaybeScope(v); 809 810 if ((v.storage_class & (STC.ref_ | STC.out_)) || p != sc.func) 811 continue; 812 813 if (va && !va.isDataseg() && !va.doNotInferScope) 814 { 815 if (!va.isScope() && inferScope) 816 { //printf("inferring scope for %s\n", va.toChars()); 817 va.storage_class |= STC.scope_ | STC.scopeinferred; 818 } 819 continue; 820 } 821 if (e1.op == TOK.structLiteral) 822 continue; 823 if (sc.func.setUnsafe()) 824 { 825 if (!gag) 826 error(ae.loc, "reference to local variable `%s` assigned to non-scope `%s`", v.toChars(), e1.toChars()); 827 result = true; 828 } 829 } 830 831 foreach (FuncDeclaration fd; er.byfunc) 832 { 833 if (log) printf("byfunc: %s, %d\n", fd.toChars(), fd.tookAddressOf); 834 VarDeclarations vars; 835 findAllOuterAccessedVariables(fd, &vars); 836 837 /* https://issues.dlang.org/show_bug.cgi?id=16037 838 * If assigning the address of a delegate to a scope variable, 839 * then uncount that address of. This is so it won't cause a 840 * closure to be allocated. 841 */ 842 if (va && va.isScope() && fd.tookAddressOf) 843 --fd.tookAddressOf; 844 845 foreach (v; vars) 846 { 847 //printf("v = %s\n", v.toChars()); 848 assert(!v.isDataseg()); // these are not put in the closureVars[] 849 850 Dsymbol p = v.toParent2(); 851 852 if (!(va && va.isScope())) 853 notMaybeScope(v); 854 855 if (!(v.storage_class & (STC.ref_ | STC.out_ | STC.scope_)) || p != sc.func) 856 continue; 857 858 if (va && !va.isDataseg() && !va.doNotInferScope) 859 { 860 /* Don't infer STC.scope_ for va, because then a closure 861 * won't be generated for sc.func. 862 */ 863 //if (!va.isScope() && inferScope) 864 //va.storage_class |= STC.scope_ | STC.scopeinferred; 865 continue; 866 } 867 if (sc.func.setUnsafe()) 868 { 869 if (!gag) 870 error(ae.loc, "reference to local `%s` assigned to non-scope `%s` in @safe code", v.toChars(), e1.toChars()); 871 result = true; 872 } 873 } 874 } 875 876 foreach (Expression ee; er.byexp) 877 { 878 if (log) printf("byexp: %s\n", ee.toChars()); 879 880 /* Do not allow slicing of a static array returned by a function 881 */ 882 if (va && ee.op == TOK.call && ee.type.toBasetype().ty == Tsarray && va.type.toBasetype().ty == Tarray && 883 !(va.storage_class & STC.temp)) 884 { 885 if (!gag) 886 deprecation(ee.loc, "slice of static array temporary returned by `%s` assigned to longer lived variable `%s`", 887 ee.toChars(), va.toChars()); 888 //result = true; 889 continue; 890 } 891 892 if (va && !va.isDataseg() && !va.doNotInferScope) 893 { 894 if (!va.isScope() && inferScope) 895 { //printf("inferring scope for %s\n", va.toChars()); 896 va.storage_class |= STC.scope_ | STC.scopeinferred; 897 } 898 continue; 899 } 900 901 if (sc.func.setUnsafe()) 902 { 903 if (!gag) 904 error(ee.loc, "reference to stack allocated value returned by `%s` assigned to non-scope `%s`", 905 ee.toChars(), e1.toChars()); 906 result = true; 907 } 908 } 909 910 return result; 911 } 912 913 /************************************ 914 * Detect cases where pointers to the stack can escape the 915 * lifetime of the stack frame when throwing `e`. 916 * Print error messages when these are detected. 917 * Params: 918 * sc = used to determine current function and module 919 * e = expression to check for any pointers to the stack 920 * gag = do not print error messages 921 * Returns: 922 * `true` if pointers to the stack can escape 923 */ 924 bool checkThrowEscape(Scope* sc, Expression e, bool gag) 925 { 926 //printf("[%s] checkThrowEscape, e = %s\n", e.loc.toChars(), e.toChars()); 927 EscapeByResults er; 928 929 escapeByValue(e, &er); 930 931 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim) 932 return false; 933 934 bool result = false; 935 foreach (VarDeclaration v; er.byvalue) 936 { 937 //printf("byvalue %s\n", v.toChars()); 938 if (v.isDataseg()) 939 continue; 940 941 if (v.isScope() && !v.iscatchvar) // special case: allow catch var to be rethrown 942 // despite being `scope` 943 { 944 if (sc._module && sc._module.isRoot()) 945 { 946 // Only look for errors if in module listed on command line 947 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029 948 { 949 if (!gag) 950 error(e.loc, "scope variable `%s` may not be thrown", v.toChars()); 951 result = true; 952 } 953 continue; 954 } 955 } 956 else 957 { 958 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); 959 v.doNotInferScope = true; 960 } 961 } 962 return result; 963 } 964 965 /************************************ 966 * Detect cases where pointers to the stack can escape the 967 * lifetime of the stack frame by being placed into a GC allocated object. 968 * Print error messages when these are detected. 969 * Params: 970 * sc = used to determine current function and module 971 * e = expression to check for any pointers to the stack 972 * gag = do not print error messages 973 * Returns: 974 * `true` if pointers to the stack can escape 975 */ 976 bool checkNewEscape(Scope* sc, Expression e, bool gag) 977 { 978 //printf("[%s] checkNewEscape, e = %s\n", e.loc.toChars(), e.toChars()); 979 enum log = false; 980 if (log) printf("[%s] checkNewEscape, e: `%s`\n", e.loc.toChars(), e.toChars()); 981 EscapeByResults er; 982 983 escapeByValue(e, &er); 984 985 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim) 986 return false; 987 988 bool result = false; 989 foreach (VarDeclaration v; er.byvalue) 990 { 991 if (log) printf("byvalue `%s`\n", v.toChars()); 992 if (v.isDataseg()) 993 continue; 994 995 Dsymbol p = v.toParent2(); 996 997 if (v.isScope()) 998 { 999 if (sc._module && sc._module.isRoot() && 1000 /* This case comes up when the ReturnStatement of a __foreachbody is 1001 * checked for escapes by the caller of __foreachbody. Skip it. 1002 * 1003 * struct S { static int opApply(int delegate(S*) dg); } 1004 * S* foo() { 1005 * foreach (S* s; S) // create __foreachbody for body of foreach 1006 * return s; // s is inferred as 'scope' but incorrectly tested in foo() 1007 * return null; } 1008 */ 1009 !(p.parent == sc.func)) 1010 { 1011 // Only look for errors if in module listed on command line 1012 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029 1013 { 1014 if (!gag) 1015 error(e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars()); 1016 result = true; 1017 } 1018 continue; 1019 } 1020 } 1021 else if (v.storage_class & STC.variadic && p == sc.func) 1022 { 1023 Type tb = v.type.toBasetype(); 1024 if (tb.ty == Tarray || tb.ty == Tsarray) 1025 { 1026 if (!gag) 1027 error(e.loc, "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); 1028 result = false; 1029 } 1030 } 1031 else 1032 { 1033 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); 1034 v.doNotInferScope = true; 1035 } 1036 } 1037 1038 foreach (VarDeclaration v; er.byref) 1039 { 1040 if (log) printf("byref `%s`\n", v.toChars()); 1041 1042 // 'emitError' tells us whether to emit an error or a deprecation, 1043 // depending on the flag passed to the CLI for DIP25 1044 void escapingRef(VarDeclaration v, bool emitError = true) 1045 { 1046 if (!gag) 1047 { 1048 const(char)* kind = (v.storage_class & STC.parameter) ? "parameter" : "local"; 1049 const(char)* msg = "copying `%s` into allocated memory escapes a reference to %s variable `%s`"; 1050 if (emitError) 1051 error(e.loc, msg, e.toChars(), kind, v.toChars()); 1052 else 1053 deprecation(e.loc, msg, e.toChars(), kind, v.toChars()); 1054 } 1055 result |= emitError; 1056 } 1057 1058 if (v.isDataseg()) 1059 continue; 1060 1061 Dsymbol p = v.toParent2(); 1062 1063 if ((v.storage_class & (STC.ref_ | STC.out_)) == 0) 1064 { 1065 if (p == sc.func) 1066 { 1067 escapingRef(v); 1068 continue; 1069 } 1070 } 1071 1072 /* Check for returning a ref variable by 'ref', but should be 'return ref' 1073 * Infer the addition of 'return', or set result to be the offending expression. 1074 */ 1075 if (!(v.storage_class & (STC.ref_ | STC.out_))) 1076 continue; 1077 1078 if (!sc._module || !sc._module.isRoot()) 1079 continue; 1080 1081 // If -preview=dip25 is used, the user wants an error 1082 // Otherwise, issue a deprecation 1083 const emitError = global.params.useDIP25; 1084 // https://dlang.org/spec/function.html#return-ref-parameters 1085 // Only look for errors if in module listed on command line 1086 if (p == sc.func) 1087 { 1088 //printf("escaping reference to local ref variable %s\n", v.toChars()); 1089 //printf("storage class = x%llx\n", v.storage_class); 1090 escapingRef(v, emitError); 1091 continue; 1092 } 1093 // Don't need to be concerned if v's parent does not return a ref 1094 FuncDeclaration fd = p.isFuncDeclaration(); 1095 if (!fd || !fd.type) 1096 continue; 1097 if (auto tf = fd.type.isTypeFunction()) 1098 { 1099 if (!tf.isref) 1100 continue; 1101 1102 const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape"; 1103 if (!gag && emitError) 1104 error(e.loc, msg, v.toChars()); 1105 else if (!gag) 1106 deprecation(e.loc, msg, v.toChars()); 1107 result |= emitError; 1108 } 1109 } 1110 1111 foreach (Expression ee; er.byexp) 1112 { 1113 if (log) printf("byexp %s\n", ee.toChars()); 1114 if (!gag) 1115 error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape", 1116 ee.toChars()); 1117 result = true; 1118 } 1119 1120 return result; 1121 } 1122 1123 1124 /************************************ 1125 * Detect cases where pointers to the stack can escape the 1126 * lifetime of the stack frame by returning `e` by value. 1127 * Print error messages when these are detected. 1128 * Params: 1129 * sc = used to determine current function and module 1130 * e = expression to check for any pointers to the stack 1131 * gag = do not print error messages 1132 * Returns: 1133 * `true` if pointers to the stack can escape 1134 */ 1135 bool checkReturnEscape(Scope* sc, Expression e, bool gag) 1136 { 1137 //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars()); 1138 return checkReturnEscapeImpl(sc, e, false, gag); 1139 } 1140 1141 /************************************ 1142 * Detect cases where returning `e` by `ref` can result in a reference to the stack 1143 * being returned. 1144 * Print error messages when these are detected. 1145 * Params: 1146 * sc = used to determine current function and module 1147 * e = expression to check 1148 * gag = do not print error messages 1149 * Returns: 1150 * `true` if references to the stack can escape 1151 */ 1152 bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag) 1153 { 1154 version (none) 1155 { 1156 printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars()); 1157 printf("current function %s\n", sc.func.toChars()); 1158 printf("parent2 function %s\n", sc.func.toParent2().toChars()); 1159 } 1160 1161 return checkReturnEscapeImpl(sc, e, true, gag); 1162 } 1163 1164 /*************************************** 1165 * Implementation of checking for escapes in return expressions. 1166 * Params: 1167 * sc = used to determine current function and module 1168 * e = expression to check 1169 * refs = `true`: escape by value, `false`: escape by `ref` 1170 * gag = do not print error messages 1171 * Returns: 1172 * `true` if references to the stack can escape 1173 */ 1174 private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) 1175 { 1176 enum log = false; 1177 if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars()); 1178 EscapeByResults er; 1179 1180 if (refs) 1181 escapeByRef(e, &er); 1182 else 1183 escapeByValue(e, &er); 1184 1185 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim) 1186 return false; 1187 1188 bool result = false; 1189 foreach (VarDeclaration v; er.byvalue) 1190 { 1191 if (log) printf("byvalue `%s`\n", v.toChars()); 1192 if (v.isDataseg()) 1193 continue; 1194 1195 Dsymbol p = v.toParent2(); 1196 1197 if ((v.isScope() || (v.storage_class & STC.maybescope)) && 1198 !(v.storage_class & STC.return_) && 1199 v.isParameter() && 1200 !v.doNotInferReturn && 1201 sc.func.flags & FUNCFLAG.returnInprocess && 1202 p == sc.func) 1203 { 1204 inferReturn(sc.func, v); // infer addition of 'return' 1205 continue; 1206 } 1207 1208 if (v.isScope()) 1209 { 1210 if (v.storage_class & STC.return_) 1211 continue; 1212 1213 if (sc._module && sc._module.isRoot() && 1214 /* This case comes up when the ReturnStatement of a __foreachbody is 1215 * checked for escapes by the caller of __foreachbody. Skip it. 1216 * 1217 * struct S { static int opApply(int delegate(S*) dg); } 1218 * S* foo() { 1219 * foreach (S* s; S) // create __foreachbody for body of foreach 1220 * return s; // s is inferred as 'scope' but incorrectly tested in foo() 1221 * return null; } 1222 */ 1223 !(!refs && p.parent == sc.func && p.isFuncDeclaration() && p.isFuncDeclaration().fes) && 1224 /* 1225 * auto p(scope string s) { 1226 * string scfunc() { return s; } 1227 * } 1228 */ 1229 !(!refs && p.isFuncDeclaration() && sc.func.isFuncDeclaration().getLevel(p.isFuncDeclaration(), sc.intypeof) > 0) 1230 ) 1231 { 1232 // Only look for errors if in module listed on command line 1233 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029 1234 { 1235 if (!gag) 1236 error(e.loc, "scope variable `%s` may not be returned", v.toChars()); 1237 result = true; 1238 } 1239 continue; 1240 } 1241 } 1242 else if (v.storage_class & STC.variadic && p == sc.func) 1243 { 1244 Type tb = v.type.toBasetype(); 1245 if (tb.ty == Tarray || tb.ty == Tsarray) 1246 { 1247 if (!gag) 1248 error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); 1249 result = false; 1250 } 1251 } 1252 else 1253 { 1254 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); 1255 v.doNotInferScope = true; 1256 } 1257 } 1258 1259 foreach (VarDeclaration v; er.byref) 1260 { 1261 if (log) printf("byref `%s`\n", v.toChars()); 1262 1263 // 'emitError' tells us whether to emit an error or a deprecation, 1264 // depending on the flag passed to the CLI for DIP25 1265 void escapingRef(VarDeclaration v, bool emitError = true) 1266 { 1267 if (!gag) 1268 { 1269 const(char)* msg; 1270 if (v.storage_class & STC.parameter) 1271 msg = "returning `%s` escapes a reference to parameter `%s`, perhaps annotate with `return`"; 1272 else 1273 msg = "returning `%s` escapes a reference to local variable `%s`"; 1274 if (emitError) 1275 error(e.loc, msg, e.toChars(), v.toChars()); 1276 else 1277 deprecation(e.loc, msg, e.toChars(), v.toChars()); 1278 } 1279 result = true; 1280 } 1281 1282 if (v.isDataseg()) 1283 continue; 1284 1285 Dsymbol p = v.toParent2(); 1286 1287 // https://issues.dlang.org/show_bug.cgi?id=19965 1288 if (!refs && sc.func.vthis == v) 1289 notMaybeScope(v); 1290 1291 if ((v.storage_class & (STC.ref_ | STC.out_)) == 0) 1292 { 1293 if (p == sc.func) 1294 { 1295 escapingRef(v); 1296 continue; 1297 } 1298 FuncDeclaration fd = p.isFuncDeclaration(); 1299 if (fd && sc.func.flags & FUNCFLAG.returnInprocess) 1300 { 1301 /* Code like: 1302 * int x; 1303 * auto dg = () { return &x; } 1304 * Making it: 1305 * auto dg = () return { return &x; } 1306 * Because dg.ptr points to x, this is returning dt.ptr+offset 1307 */ 1308 if (global.params.vsafe) 1309 { 1310 sc.func.storage_class |= STC.return_ | STC.returninferred; 1311 } 1312 } 1313 1314 } 1315 1316 /* Check for returning a ref variable by 'ref', but should be 'return ref' 1317 * Infer the addition of 'return', or set result to be the offending expression. 1318 */ 1319 if ( (v.storage_class & (STC.ref_ | STC.out_)) && 1320 !(v.storage_class & (STC.return_ | STC.foreach_))) 1321 { 1322 if (sc.func.flags & FUNCFLAG.returnInprocess && p == sc.func) 1323 { 1324 inferReturn(sc.func, v); // infer addition of 'return' 1325 } 1326 else if (sc._module && sc._module.isRoot()) 1327 { 1328 // If -preview=dip25 is used, the user wants an error 1329 // Otherwise, issue a deprecation 1330 const emitError = global.params.useDIP25; 1331 // https://dlang.org/spec/function.html#return-ref-parameters 1332 // Only look for errors if in module listed on command line 1333 if (p == sc.func) 1334 { 1335 //printf("escaping reference to local ref variable %s\n", v.toChars()); 1336 //printf("storage class = x%llx\n", v.storage_class); 1337 escapingRef(v, emitError); 1338 continue; 1339 } 1340 // Don't need to be concerned if v's parent does not return a ref 1341 FuncDeclaration fd = p.isFuncDeclaration(); 1342 if (fd && fd.type && fd.type.ty == Tfunction) 1343 { 1344 TypeFunction tf = cast(TypeFunction)fd.type; 1345 if (tf.isref) 1346 { 1347 const(char)* msg = "escaping reference to outer local variable `%s`"; 1348 if (!gag && emitError) 1349 error(e.loc, msg, v.toChars()); 1350 else if (!gag) 1351 deprecation(e.loc, msg, v.toChars()); 1352 result = true; 1353 continue; 1354 } 1355 } 1356 1357 } 1358 } 1359 } 1360 1361 foreach (Expression ee; er.byexp) 1362 { 1363 if (log) printf("byexp %s\n", ee.toChars()); 1364 if (!gag) 1365 error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars()); 1366 result = true; 1367 } 1368 1369 return result; 1370 } 1371 1372 1373 /************************************* 1374 * Variable v needs to have 'return' inferred for it. 1375 * Params: 1376 * fd = function that v is a parameter to 1377 * v = parameter that needs to be STC.return_ 1378 */ 1379 1380 private void inferReturn(FuncDeclaration fd, VarDeclaration v) 1381 { 1382 // v is a local in the current function 1383 1384 //printf("for function '%s' inferring 'return' for variable '%s'\n", fd.toChars(), v.toChars()); 1385 v.storage_class |= STC.return_ | STC.returninferred; 1386 1387 TypeFunction tf = cast(TypeFunction)fd.type; 1388 if (v == fd.vthis) 1389 { 1390 /* v is the 'this' reference, so mark the function 1391 */ 1392 fd.storage_class |= STC.return_ | STC.returninferred; 1393 if (tf.ty == Tfunction) 1394 { 1395 //printf("'this' too %p %s\n", tf, sc.func.toChars()); 1396 tf.isreturn = true; 1397 tf.isreturninferred = true; 1398 } 1399 } 1400 else 1401 { 1402 // Perform 'return' inference on parameter 1403 if (tf.ty == Tfunction) 1404 { 1405 const dim = tf.parameterList.length; 1406 foreach (const i; 0 .. dim) 1407 { 1408 Parameter p = tf.parameterList[i]; 1409 if (p.ident == v.ident) 1410 { 1411 p.storageClass |= STC.return_ | STC.returninferred; 1412 break; // there can be only one 1413 } 1414 } 1415 } 1416 } 1417 } 1418 1419 1420 /**************************************** 1421 * e is an expression to be returned by value, and that value contains pointers. 1422 * Walk e to determine which variables are possibly being 1423 * returned by value, such as: 1424 * int* function(int* p) { return p; } 1425 * If e is a form of &p, determine which variables have content 1426 * which is being returned as ref, such as: 1427 * int* function(int i) { return &i; } 1428 * Multiple variables can be inserted, because of expressions like this: 1429 * int function(bool b, int i, int* p) { return b ? &i : p; } 1430 * 1431 * No side effects. 1432 * 1433 * Params: 1434 * e = expression to be returned by value 1435 * er = where to place collected data 1436 * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. 1437 */ 1438 void escapeByValue(Expression e, EscapeByResults* er, bool live = false) 1439 { 1440 //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars()); 1441 extern (C++) final class EscapeVisitor : Visitor 1442 { 1443 alias visit = Visitor.visit; 1444 public: 1445 EscapeByResults* er; 1446 bool live; 1447 1448 extern (D) this(EscapeByResults* er, bool live) 1449 { 1450 this.er = er; 1451 this.live = live; 1452 } 1453 1454 override void visit(Expression e) 1455 { 1456 } 1457 1458 override void visit(AddrExp e) 1459 { 1460 /* Taking the address of struct literal is normally not 1461 * allowed, but CTFE can generate one out of a new expression, 1462 * but it'll be placed in static data so no need to check it. 1463 */ 1464 if (e.e1.op != TOK.structLiteral) 1465 escapeByRef(e.e1, er, live); 1466 } 1467 1468 override void visit(SymOffExp e) 1469 { 1470 VarDeclaration v = e.var.isVarDeclaration(); 1471 if (v) 1472 er.byref.push(v); 1473 } 1474 1475 override void visit(VarExp e) 1476 { 1477 if (auto v = e.var.isVarDeclaration()) 1478 { 1479 if (v.type.hasPointers() || // not tracking non-pointers 1480 v.storage_class & STC.lazy_) // lazy variables are actually pointers 1481 er.byvalue.push(v); 1482 } 1483 } 1484 1485 override void visit(ThisExp e) 1486 { 1487 if (e.var) 1488 er.byvalue.push(e.var); 1489 } 1490 1491 override void visit(PtrExp e) 1492 { 1493 if (live && e.type.hasPointers()) 1494 e.e1.accept(this); 1495 } 1496 1497 override void visit(DotVarExp e) 1498 { 1499 auto t = e.e1.type.toBasetype(); 1500 if (e.type.hasPointers() && (live || t.ty == Tstruct)) 1501 { 1502 e.e1.accept(this); 1503 } 1504 } 1505 1506 override void visit(DelegateExp e) 1507 { 1508 Type t = e.e1.type.toBasetype(); 1509 if (t.ty == Tclass || t.ty == Tpointer) 1510 escapeByValue(e.e1, er, live); 1511 else 1512 escapeByRef(e.e1, er, live); 1513 er.byfunc.push(e.func); 1514 } 1515 1516 override void visit(FuncExp e) 1517 { 1518 if (e.fd.tok == TOK.delegate_) 1519 er.byfunc.push(e.fd); 1520 } 1521 1522 override void visit(TupleExp e) 1523 { 1524 assert(0); // should have been lowered by now 1525 } 1526 1527 override void visit(ArrayLiteralExp e) 1528 { 1529 Type tb = e.type.toBasetype(); 1530 if (tb.ty == Tsarray || tb.ty == Tarray) 1531 { 1532 if (e.basis) 1533 e.basis.accept(this); 1534 foreach (el; *e.elements) 1535 { 1536 if (el) 1537 el.accept(this); 1538 } 1539 } 1540 } 1541 1542 override void visit(StructLiteralExp e) 1543 { 1544 if (e.elements) 1545 { 1546 foreach (ex; *e.elements) 1547 { 1548 if (ex) 1549 ex.accept(this); 1550 } 1551 } 1552 } 1553 1554 override void visit(NewExp e) 1555 { 1556 Type tb = e.newtype.toBasetype(); 1557 if (tb.ty == Tstruct && !e.member && e.arguments) 1558 { 1559 foreach (ex; *e.arguments) 1560 { 1561 if (ex) 1562 ex.accept(this); 1563 } 1564 } 1565 } 1566 1567 override void visit(CastExp e) 1568 { 1569 if (!e.type.hasPointers()) 1570 return; 1571 Type tb = e.type.toBasetype(); 1572 if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) 1573 { 1574 escapeByRef(e.e1, er, live); 1575 } 1576 else 1577 e.e1.accept(this); 1578 } 1579 1580 override void visit(SliceExp e) 1581 { 1582 if (e.e1.op == TOK.variable) 1583 { 1584 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 1585 Type tb = e.type.toBasetype(); 1586 if (v) 1587 { 1588 if (tb.ty == Tsarray) 1589 return; 1590 if (v.storage_class & STC.variadic) 1591 { 1592 er.byvalue.push(v); 1593 return; 1594 } 1595 } 1596 } 1597 Type t1b = e.e1.type.toBasetype(); 1598 if (t1b.ty == Tsarray) 1599 { 1600 Type tb = e.type.toBasetype(); 1601 if (tb.ty != Tsarray) 1602 escapeByRef(e.e1, er, live); 1603 } 1604 else 1605 e.e1.accept(this); 1606 } 1607 1608 override void visit(IndexExp e) 1609 { 1610 if (e.e1.type.toBasetype().ty == Tsarray || 1611 live && e.type.hasPointers()) 1612 { 1613 e.e1.accept(this); 1614 } 1615 } 1616 1617 override void visit(BinExp e) 1618 { 1619 Type tb = e.type.toBasetype(); 1620 if (tb.ty == Tpointer) 1621 { 1622 e.e1.accept(this); 1623 e.e2.accept(this); 1624 } 1625 } 1626 1627 override void visit(BinAssignExp e) 1628 { 1629 e.e1.accept(this); 1630 } 1631 1632 override void visit(AssignExp e) 1633 { 1634 e.e1.accept(this); 1635 } 1636 1637 override void visit(CommaExp e) 1638 { 1639 e.e2.accept(this); 1640 } 1641 1642 override void visit(CondExp e) 1643 { 1644 e.e1.accept(this); 1645 e.e2.accept(this); 1646 } 1647 1648 override void visit(CallExp e) 1649 { 1650 //printf("CallExp(): %s\n", e.toChars()); 1651 /* Check each argument that is 1652 * passed as 'return scope'. 1653 */ 1654 Type t1 = e.e1.type.toBasetype(); 1655 TypeFunction tf; 1656 TypeDelegate dg; 1657 if (t1.ty == Tdelegate) 1658 { 1659 dg = cast(TypeDelegate)t1; 1660 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next; 1661 } 1662 else if (t1.ty == Tfunction) 1663 tf = cast(TypeFunction)t1; 1664 else 1665 return; 1666 1667 if (!e.type.hasPointers()) 1668 return; 1669 1670 if (e.arguments && e.arguments.dim) 1671 { 1672 /* j=1 if _arguments[] is first argument, 1673 * skip it because it is not passed by ref 1674 */ 1675 int j = tf.isDstyleVariadic(); 1676 for (size_t i = j; i < e.arguments.dim; ++i) 1677 { 1678 Expression arg = (*e.arguments)[i]; 1679 size_t nparams = tf.parameterList.length; 1680 if (i - j < nparams && i >= j) 1681 { 1682 Parameter p = tf.parameterList[i - j]; 1683 const stc = tf.parameterStorageClass(null, p); 1684 if ((stc & (STC.scope_)) && (stc & STC.return_)) 1685 arg.accept(this); 1686 else if ((stc & (STC.ref_)) && (stc & STC.return_)) 1687 { 1688 if (tf.isref) 1689 { 1690 /* Treat: 1691 * ref P foo(return ref P p) 1692 * as: 1693 * p; 1694 */ 1695 arg.accept(this); 1696 } 1697 else 1698 escapeByRef(arg, er, live); 1699 } 1700 } 1701 } 1702 } 1703 // If 'this' is returned, check it too 1704 if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction) 1705 { 1706 DotVarExp dve = cast(DotVarExp)e.e1; 1707 FuncDeclaration fd = dve.var.isFuncDeclaration(); 1708 AggregateDeclaration ad; 1709 if (global.params.vsafe && tf.isreturn && fd && (ad = fd.isThis()) !is null) 1710 { 1711 if (ad.isClassDeclaration() || tf.isscope) // this is 'return scope' 1712 dve.e1.accept(this); 1713 else if (ad.isStructDeclaration()) // this is 'return ref' 1714 { 1715 if (tf.isref) 1716 { 1717 /* Treat calling: 1718 * struct S { ref S foo() return; } 1719 * as: 1720 * this; 1721 */ 1722 dve.e1.accept(this); 1723 } 1724 else 1725 escapeByRef(dve.e1, er, live); 1726 } 1727 } 1728 else if (dve.var.storage_class & STC.return_ || tf.isreturn) 1729 { 1730 if (dve.var.storage_class & STC.scope_) 1731 dve.e1.accept(this); 1732 else if (dve.var.storage_class & STC.ref_) 1733 escapeByRef(dve.e1, er, live); 1734 } 1735 // If it's also a nested function that is 'return scope' 1736 if (fd && fd.isNested()) 1737 { 1738 if (tf.isreturn && tf.isscope) 1739 er.byexp.push(e); 1740 } 1741 } 1742 1743 /* If returning the result of a delegate call, the .ptr 1744 * field of the delegate must be checked. 1745 */ 1746 if (dg) 1747 { 1748 if (tf.isreturn) 1749 e.e1.accept(this); 1750 } 1751 1752 /* If it's a nested function that is 'return scope' 1753 */ 1754 if (e.e1.op == TOK.variable) 1755 { 1756 VarExp ve = cast(VarExp)e.e1; 1757 FuncDeclaration fd = ve.var.isFuncDeclaration(); 1758 if (fd && fd.isNested()) 1759 { 1760 if (tf.isreturn && tf.isscope) 1761 er.byexp.push(e); 1762 } 1763 } 1764 } 1765 } 1766 1767 scope EscapeVisitor v = new EscapeVisitor(er, live); 1768 e.accept(v); 1769 } 1770 1771 1772 /**************************************** 1773 * e is an expression to be returned by 'ref'. 1774 * Walk e to determine which variables are possibly being 1775 * returned by ref, such as: 1776 * ref int function(int i) { return i; } 1777 * If e is a form of *p, determine which variables have content 1778 * which is being returned as ref, such as: 1779 * ref int function(int* p) { return *p; } 1780 * Multiple variables can be inserted, because of expressions like this: 1781 * ref int function(bool b, int i, int* p) { return b ? i : *p; } 1782 * 1783 * No side effects. 1784 * 1785 * Params: 1786 * e = expression to be returned by 'ref' 1787 * er = where to place collected data 1788 * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. 1789 */ 1790 void escapeByRef(Expression e, EscapeByResults* er, bool live = false) 1791 { 1792 //printf("[%s] escapeByRef, e: %s\n", e.loc.toChars(), e.toChars()); 1793 extern (C++) final class EscapeRefVisitor : Visitor 1794 { 1795 alias visit = Visitor.visit; 1796 public: 1797 EscapeByResults* er; 1798 bool live; 1799 1800 extern (D) this(EscapeByResults* er, bool live) 1801 { 1802 this.er = er; 1803 this.live = live; 1804 } 1805 1806 override void visit(Expression e) 1807 { 1808 } 1809 1810 override void visit(VarExp e) 1811 { 1812 auto v = e.var.isVarDeclaration(); 1813 if (v) 1814 { 1815 if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init) 1816 { 1817 /* If compiler generated ref temporary 1818 * (ref v = ex; ex) 1819 * look at the initializer instead 1820 */ 1821 if (ExpInitializer ez = v._init.isExpInitializer()) 1822 { 1823 assert(ez.exp && ez.exp.op == TOK.construct); 1824 Expression ex = (cast(ConstructExp)ez.exp).e2; 1825 ex.accept(this); 1826 } 1827 } 1828 else 1829 er.byref.push(v); 1830 } 1831 } 1832 1833 override void visit(ThisExp e) 1834 { 1835 if (e.var && e.var.toParent2().isFuncDeclaration().isThis2) 1836 escapeByValue(e, er, live); 1837 else if (e.var) 1838 er.byref.push(e.var); 1839 } 1840 1841 override void visit(PtrExp e) 1842 { 1843 escapeByValue(e.e1, er, live); 1844 } 1845 1846 override void visit(IndexExp e) 1847 { 1848 Type tb = e.e1.type.toBasetype(); 1849 if (e.e1.op == TOK.variable) 1850 { 1851 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 1852 if (tb.ty == Tarray || tb.ty == Tsarray) 1853 { 1854 if (v && v.storage_class & STC.variadic) 1855 { 1856 er.byref.push(v); 1857 return; 1858 } 1859 } 1860 } 1861 if (tb.ty == Tsarray) 1862 { 1863 e.e1.accept(this); 1864 } 1865 else if (tb.ty == Tarray) 1866 { 1867 escapeByValue(e.e1, er, live); 1868 } 1869 } 1870 1871 override void visit(StructLiteralExp e) 1872 { 1873 if (e.elements) 1874 { 1875 foreach (ex; *e.elements) 1876 { 1877 if (ex) 1878 ex.accept(this); 1879 } 1880 } 1881 er.byexp.push(e); 1882 } 1883 1884 override void visit(DotVarExp e) 1885 { 1886 Type t1b = e.e1.type.toBasetype(); 1887 if (t1b.ty == Tclass) 1888 escapeByValue(e.e1, er, live); 1889 else 1890 e.e1.accept(this); 1891 } 1892 1893 override void visit(BinAssignExp e) 1894 { 1895 e.e1.accept(this); 1896 } 1897 1898 override void visit(AssignExp e) 1899 { 1900 e.e1.accept(this); 1901 } 1902 1903 override void visit(CommaExp e) 1904 { 1905 e.e2.accept(this); 1906 } 1907 1908 override void visit(CondExp e) 1909 { 1910 e.e1.accept(this); 1911 e.e2.accept(this); 1912 } 1913 1914 override void visit(CallExp e) 1915 { 1916 //printf("escapeByRef.CallExp(): %s\n", e.toChars()); 1917 /* If the function returns by ref, check each argument that is 1918 * passed as 'return ref'. 1919 */ 1920 Type t1 = e.e1.type.toBasetype(); 1921 TypeFunction tf; 1922 if (t1.ty == Tdelegate) 1923 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next; 1924 else if (t1.ty == Tfunction) 1925 tf = cast(TypeFunction)t1; 1926 else 1927 return; 1928 if (tf.isref) 1929 { 1930 if (e.arguments && e.arguments.dim) 1931 { 1932 /* j=1 if _arguments[] is first argument, 1933 * skip it because it is not passed by ref 1934 */ 1935 int j = tf.isDstyleVariadic(); 1936 for (size_t i = j; i < e.arguments.dim; ++i) 1937 { 1938 Expression arg = (*e.arguments)[i]; 1939 size_t nparams = tf.parameterList.length; 1940 if (i - j < nparams && i >= j) 1941 { 1942 Parameter p = tf.parameterList[i - j]; 1943 const stc = tf.parameterStorageClass(null, p); 1944 if ((stc & (STC.out_ | STC.ref_)) && (stc & STC.return_)) 1945 arg.accept(this); 1946 else if ((stc & STC.scope_) && (stc & STC.return_)) 1947 { 1948 if (arg.op == TOK.delegate_) 1949 { 1950 DelegateExp de = cast(DelegateExp)arg; 1951 if (de.func.isNested()) 1952 er.byexp.push(de); 1953 } 1954 else 1955 escapeByValue(arg, er, live); 1956 } 1957 } 1958 } 1959 } 1960 // If 'this' is returned by ref, check it too 1961 if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction) 1962 { 1963 DotVarExp dve = cast(DotVarExp)e.e1; 1964 1965 // https://issues.dlang.org/show_bug.cgi?id=20149#c10 1966 if (dve.var.isCtorDeclaration()) 1967 { 1968 er.byexp.push(e); 1969 return; 1970 } 1971 1972 if (dve.var.storage_class & STC.return_ || tf.isreturn) 1973 { 1974 if (dve.var.storage_class & STC.ref_ || tf.isref) 1975 dve.e1.accept(this); 1976 else if (dve.var.storage_class & STC.scope_ || tf.isscope) 1977 escapeByValue(dve.e1, er, live); 1978 } 1979 // If it's also a nested function that is 'return ref' 1980 FuncDeclaration fd = dve.var.isFuncDeclaration(); 1981 if (fd && fd.isNested()) 1982 { 1983 if (tf.isreturn) 1984 er.byexp.push(e); 1985 } 1986 } 1987 // If it's a delegate, check it too 1988 if (e.e1.op == TOK.variable && t1.ty == Tdelegate) 1989 { 1990 escapeByValue(e.e1, er, live); 1991 } 1992 1993 /* If it's a nested function that is 'return ref' 1994 */ 1995 if (e.e1.op == TOK.variable) 1996 { 1997 VarExp ve = cast(VarExp)e.e1; 1998 FuncDeclaration fd = ve.var.isFuncDeclaration(); 1999 if (fd && fd.isNested()) 2000 { 2001 if (tf.isreturn) 2002 er.byexp.push(e); 2003 } 2004 } 2005 } 2006 else 2007 er.byexp.push(e); 2008 } 2009 } 2010 2011 scope EscapeRefVisitor v = new EscapeRefVisitor(er, live); 2012 e.accept(v); 2013 } 2014 2015 2016 /************************************ 2017 * Aggregate the data collected by the escapeBy??() functions. 2018 */ 2019 struct EscapeByResults 2020 { 2021 VarDeclarations byref; // array into which variables being returned by ref are inserted 2022 VarDeclarations byvalue; // array into which variables with values containing pointers are inserted 2023 FuncDeclarations byfunc; // nested functions that are turned into delegates 2024 Expressions byexp; // array into which temporaries being returned by ref are inserted 2025 2026 /** Reset arrays so the storage can be used again 2027 */ 2028 void reset() 2029 { 2030 byref.setDim(0); 2031 byvalue.setDim(0); 2032 byfunc.setDim(0); 2033 byexp.setDim(0); 2034 } 2035 } 2036 2037 /************************* 2038 * Find all variables accessed by this delegate that are 2039 * in functions enclosing it. 2040 * Params: 2041 * fd = function 2042 * vars = array to append found variables to 2043 */ 2044 public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars) 2045 { 2046 //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars()); 2047 for (auto p = fd.parent; p; p = p.parent) 2048 { 2049 auto fdp = p.isFuncDeclaration(); 2050 if (!fdp) 2051 continue; 2052 2053 foreach (v; fdp.closureVars) 2054 { 2055 foreach (const fdv; v.nestedrefs) 2056 { 2057 if (fdv == fd) 2058 { 2059 //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars()); 2060 vars.push(v); 2061 } 2062 } 2063 } 2064 } 2065 } 2066 2067 /*********************************** 2068 * Turn off `STC.maybescope` for variable `v`. 2069 * 2070 * This exists in order to find where `STC.maybescope` is getting turned off. 2071 * Params: 2072 * v = variable 2073 */ 2074 version (none) 2075 { 2076 public void notMaybeScope(string file = __FILE__, int line = __LINE__)(VarDeclaration v) 2077 { 2078 printf("%.*s(%d): notMaybeScope('%s')\n", cast(int)file.length, file.ptr, line, v.toChars()); 2079 v.storage_class &= ~STC.maybescope; 2080 } 2081 } 2082 else 2083 { 2084 public void notMaybeScope(VarDeclaration v) 2085 { 2086 v.storage_class &= ~STC.maybescope; 2087 } 2088 } 2089 2090 2091 /********************************************** 2092 * Have some variables that are maybescopes that were 2093 * assigned values from other maybescope variables. 2094 * Now that semantic analysis of the function is 2095 * complete, we can finalize this by turning off 2096 * maybescope for array elements that cannot be scope. 2097 * 2098 * $(TABLE2 Scope Table, 2099 * $(THEAD `va`, `v`, =>, `va` , `v` ) 2100 * $(TROW maybe, maybe, =>, scope, scope) 2101 * $(TROW scope, scope, =>, scope, scope) 2102 * $(TROW scope, maybe, =>, scope, scope) 2103 * $(TROW maybe, scope, =>, scope, scope) 2104 * $(TROW - , - , =>, - , - ) 2105 * $(TROW - , maybe, =>, - , - ) 2106 * $(TROW - , scope, =>, error, error) 2107 * $(TROW maybe, - , =>, scope, - ) 2108 * $(TROW scope, - , =>, scope, - ) 2109 * ) 2110 * Params: 2111 * array = array of variables that were assigned to from maybescope variables 2112 */ 2113 public void eliminateMaybeScopes(VarDeclaration[] array) 2114 { 2115 enum log = false; 2116 if (log) printf("eliminateMaybeScopes()\n"); 2117 bool changes; 2118 do 2119 { 2120 changes = false; 2121 foreach (va; array) 2122 { 2123 if (log) printf(" va = %s\n", va.toChars()); 2124 if (!(va.storage_class & (STC.maybescope | STC.scope_))) 2125 { 2126 if (va.maybes) 2127 { 2128 foreach (v; *va.maybes) 2129 { 2130 if (log) printf(" v = %s\n", v.toChars()); 2131 if (v.storage_class & STC.maybescope) 2132 { 2133 // v cannot be scope since it is assigned to a non-scope va 2134 notMaybeScope(v); 2135 if (!(v.storage_class & (STC.ref_ | STC.out_))) 2136 v.storage_class &= ~(STC.return_ | STC.returninferred); 2137 changes = true; 2138 } 2139 } 2140 } 2141 } 2142 } 2143 } while (changes); 2144 } 2145 2146 /************************************************ 2147 * Is type a reference to a mutable value? 2148 * 2149 * This is used to determine if an argument that does not have a corresponding 2150 * Parameter, i.e. a variadic argument, is a pointer to mutable data. 2151 * Params: 2152 * t = type of the argument 2153 * Returns: 2154 * true if it's a pointer (or reference) to mutable data 2155 */ 2156 bool isReferenceToMutable(Type t) 2157 { 2158 t = t.baseElemOf(); 2159 2160 if (!t.isMutable() || 2161 !t.hasPointers()) 2162 return false; 2163 2164 switch (t.ty) 2165 { 2166 case Tpointer: 2167 if (t.nextOf().isTypeFunction()) 2168 break; 2169 goto case; 2170 2171 case Tarray: 2172 case Taarray: 2173 case Tdelegate: 2174 if (t.nextOf().isMutable()) 2175 return true; 2176 break; 2177 2178 case Tclass: 2179 return true; // even if the class fields are not mutable 2180 2181 case Tstruct: 2182 // Have to look at each field 2183 foreach (VarDeclaration v; t.isTypeStruct().sym.fields) 2184 { 2185 if (v.storage_class & STC.ref_) 2186 { 2187 if (v.type.isMutable()) 2188 return true; 2189 } 2190 else if (v.type.isReferenceToMutable()) 2191 return true; 2192 } 2193 break; 2194 2195 default: 2196 assert(0); 2197 } 2198 return false; 2199 } 2200 2201 /**************************************** 2202 * Is parameter a reference to a mutable value? 2203 * 2204 * This is used if an argument has a corresponding Parameter. 2205 * The argument type is necessary if the Parameter is inout. 2206 * Params: 2207 * p = Parameter to check 2208 * t = type of corresponding argument 2209 * Returns: 2210 * true if it's a pointer (or reference) to mutable data 2211 */ 2212 bool isReferenceToMutable(Parameter p, Type t) 2213 { 2214 if (p.storageClass & (STC.ref_ | STC.out_)) 2215 { 2216 if (p.type.isConst() || p.type.isImmutable()) 2217 return false; 2218 if (p.type.isWild()) 2219 { 2220 return t.isMutable(); 2221 } 2222 return p.type.isMutable(); 2223 } 2224 return isReferenceToMutable(p.type); 2225 }