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