1 /** 2 * Most of the logic to implement scoped pointers and scoped references is here. 3 * 4 * Copyright: Copyright (C) 1999-2021 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 && sc.func.setUnsafe()) // https://issues.dlang.org/show_bug.cgi?id=20868 1039 { 1040 if (!gag) 1041 error(e.loc, "scope variable `%s` may not be copied into allocated memory", v.toChars()); 1042 result = true; 1043 } 1044 continue; 1045 } 1046 } 1047 else if (v.storage_class & STC.variadic && p == sc.func) 1048 { 1049 Type tb = v.type.toBasetype(); 1050 if (tb.ty == Tarray || tb.ty == Tsarray) 1051 { 1052 if (!gag) 1053 error(e.loc, "copying `%s` into allocated memory escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); 1054 result = false; 1055 } 1056 } 1057 else 1058 { 1059 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); 1060 v.doNotInferScope = true; 1061 } 1062 } 1063 1064 foreach (VarDeclaration v; er.byref) 1065 { 1066 if (log) printf("byref `%s`\n", v.toChars()); 1067 1068 // 'emitError' tells us whether to emit an error or a deprecation, 1069 // depending on the flag passed to the CLI for DIP25 1070 void escapingRef(VarDeclaration v, bool emitError = true) 1071 { 1072 if (!gag) 1073 { 1074 const(char)* kind = (v.storage_class & STC.parameter) ? "parameter" : "local"; 1075 const(char)* msg = "copying `%s` into allocated memory escapes a reference to %s variable `%s`"; 1076 if (emitError) 1077 error(e.loc, msg, e.toChars(), kind, v.toChars()); 1078 else if (!sc.isDeprecated()) 1079 deprecation(e.loc, msg, e.toChars(), kind, v.toChars()); 1080 } 1081 result |= emitError; 1082 } 1083 1084 if (v.isDataseg()) 1085 continue; 1086 1087 Dsymbol p = v.toParent2(); 1088 1089 if ((v.storage_class & (STC.ref_ | STC.out_)) == 0) 1090 { 1091 if (p == sc.func) 1092 { 1093 escapingRef(v); 1094 continue; 1095 } 1096 } 1097 1098 /* Check for returning a ref variable by 'ref', but should be 'return ref' 1099 * Infer the addition of 'return', or set result to be the offending expression. 1100 */ 1101 if (!(v.storage_class & (STC.ref_ | STC.out_))) 1102 continue; 1103 1104 if (!sc._module || !sc._module.isRoot()) 1105 continue; 1106 1107 // If -preview=dip25 is used, the user wants an error 1108 // Otherwise, issue a deprecation 1109 const emitError = global.params.useDIP25 == FeatureState.enabled; 1110 // https://dlang.org/spec/function.html#return-ref-parameters 1111 // Only look for errors if in module listed on command line 1112 if (p == sc.func) 1113 { 1114 //printf("escaping reference to local ref variable %s\n", v.toChars()); 1115 //printf("storage class = x%llx\n", v.storage_class); 1116 escapingRef(v, emitError); 1117 continue; 1118 } 1119 // Don't need to be concerned if v's parent does not return a ref 1120 FuncDeclaration fd = p.isFuncDeclaration(); 1121 if (!fd || !fd.type) 1122 continue; 1123 if (auto tf = fd.type.isTypeFunction()) 1124 { 1125 if (!tf.isref) 1126 continue; 1127 1128 const(char)* msg = "storing reference to outer local variable `%s` into allocated memory causes it to escape"; 1129 if (!gag && emitError) 1130 error(e.loc, msg, v.toChars()); 1131 else if (!gag) 1132 deprecation(e.loc, msg, v.toChars()); 1133 result |= emitError; 1134 } 1135 } 1136 1137 foreach (Expression ee; er.byexp) 1138 { 1139 if (log) printf("byexp %s\n", ee.toChars()); 1140 if (!gag) 1141 error(ee.loc, "storing reference to stack allocated value returned by `%s` into allocated memory causes it to escape", 1142 ee.toChars()); 1143 result = true; 1144 } 1145 1146 return result; 1147 } 1148 1149 1150 /************************************ 1151 * Detect cases where pointers to the stack can escape the 1152 * lifetime of the stack frame by returning `e` by value. 1153 * Print error messages when these are detected. 1154 * Params: 1155 * sc = used to determine current function and module 1156 * e = expression to check for any pointers to the stack 1157 * gag = do not print error messages 1158 * Returns: 1159 * `true` if pointers to the stack can escape 1160 */ 1161 bool checkReturnEscape(Scope* sc, Expression e, bool gag) 1162 { 1163 //printf("[%s] checkReturnEscape, e: %s\n", e.loc.toChars(), e.toChars()); 1164 return checkReturnEscapeImpl(sc, e, false, gag); 1165 } 1166 1167 /************************************ 1168 * Detect cases where returning `e` by `ref` can result in a reference to the stack 1169 * being returned. 1170 * Print error messages when these are detected. 1171 * Params: 1172 * sc = used to determine current function and module 1173 * e = expression to check 1174 * gag = do not print error messages 1175 * Returns: 1176 * `true` if references to the stack can escape 1177 */ 1178 bool checkReturnEscapeRef(Scope* sc, Expression e, bool gag) 1179 { 1180 version (none) 1181 { 1182 printf("[%s] checkReturnEscapeRef, e = %s\n", e.loc.toChars(), e.toChars()); 1183 printf("current function %s\n", sc.func.toChars()); 1184 printf("parent2 function %s\n", sc.func.toParent2().toChars()); 1185 } 1186 1187 return checkReturnEscapeImpl(sc, e, true, gag); 1188 } 1189 1190 /*************************************** 1191 * Implementation of checking for escapes in return expressions. 1192 * Params: 1193 * sc = used to determine current function and module 1194 * e = expression to check 1195 * refs = `true`: escape by value, `false`: escape by `ref` 1196 * gag = do not print error messages 1197 * Returns: 1198 * `true` if references to the stack can escape 1199 */ 1200 private bool checkReturnEscapeImpl(Scope* sc, Expression e, bool refs, bool gag) 1201 { 1202 enum log = false; 1203 if (log) printf("[%s] checkReturnEscapeImpl, refs: %d e: `%s`\n", e.loc.toChars(), refs, e.toChars()); 1204 EscapeByResults er; 1205 1206 if (refs) 1207 escapeByRef(e, &er); 1208 else 1209 escapeByValue(e, &er); 1210 1211 if (!er.byref.dim && !er.byvalue.dim && !er.byexp.dim) 1212 return false; 1213 1214 bool result = false; 1215 foreach (VarDeclaration v; er.byvalue) 1216 { 1217 if (log) printf("byvalue `%s`\n", v.toChars()); 1218 if (v.isDataseg()) 1219 continue; 1220 1221 Dsymbol p = v.toParent2(); 1222 1223 if ((v.isScope() || (v.storage_class & STC.maybescope)) && 1224 !(v.storage_class & STC.return_) && 1225 v.isParameter() && 1226 !v.doNotInferReturn && 1227 sc.func.flags & FUNCFLAG.returnInprocess && 1228 p == sc.func) 1229 { 1230 inferReturn(sc.func, v); // infer addition of 'return' 1231 continue; 1232 } 1233 1234 if (v.isScope()) 1235 { 1236 if (v.storage_class & STC.return_) 1237 continue; 1238 1239 auto pfunc = p.isFuncDeclaration(); 1240 if (pfunc && sc._module && sc._module.isRoot() && 1241 /* This case comes up when the ReturnStatement of a __foreachbody is 1242 * checked for escapes by the caller of __foreachbody. Skip it. 1243 * 1244 * struct S { static int opApply(int delegate(S*) dg); } 1245 * S* foo() { 1246 * foreach (S* s; S) // create __foreachbody for body of foreach 1247 * return s; // s is inferred as 'scope' but incorrectly tested in foo() 1248 * return null; } 1249 */ 1250 !(!refs && p.parent == sc.func && pfunc.fes) && 1251 /* 1252 * auto p(scope string s) { 1253 * string scfunc() { return s; } 1254 * } 1255 */ 1256 !(!refs && sc.func.isFuncDeclaration().getLevel(pfunc, sc.intypeof) > 0) 1257 ) 1258 { 1259 // Only look for errors if in module listed on command line 1260 if (global.params.vsafe) // https://issues.dlang.org/show_bug.cgi?id=17029 1261 { 1262 if (!gag) 1263 error(e.loc, "scope variable `%s` may not be returned", v.toChars()); 1264 result = true; 1265 } 1266 continue; 1267 } 1268 } 1269 else if (v.storage_class & STC.variadic && p == sc.func) 1270 { 1271 Type tb = v.type.toBasetype(); 1272 if (tb.ty == Tarray || tb.ty == Tsarray) 1273 { 1274 if (!gag) 1275 error(e.loc, "returning `%s` escapes a reference to variadic parameter `%s`", e.toChars(), v.toChars()); 1276 result = false; 1277 } 1278 } 1279 else 1280 { 1281 //printf("no infer for %s in %s, %d\n", v.toChars(), sc.func.ident.toChars(), __LINE__); 1282 v.doNotInferScope = true; 1283 } 1284 } 1285 1286 foreach (VarDeclaration v; er.byref) 1287 { 1288 if (log) printf("byref `%s`\n", v.toChars()); 1289 1290 // 'emitError' tells us whether to emit an error or a deprecation, 1291 // depending on the flag passed to the CLI for DIP25 1292 void escapingRef(VarDeclaration v, bool emitError = true) 1293 { 1294 if (!gag) 1295 { 1296 const(char)* msg, supplemental; 1297 if (v.storage_class & STC.parameter && 1298 (v.type.hasPointers() || v.storage_class & STC.ref_)) 1299 { 1300 msg = "returning `%s` escapes a reference to parameter `%s`"; 1301 supplemental = "perhaps annotate the parameter with `return`"; 1302 } 1303 else 1304 { 1305 msg = "returning `%s` escapes a reference to local variable `%s`"; 1306 if (v.ident is Id.This) 1307 supplemental = "perhaps annotate the function with `return`"; 1308 } 1309 1310 if (emitError) 1311 { 1312 e.error(msg, e.toChars(), v.toChars()); 1313 if (supplemental) 1314 e.errorSupplemental(supplemental); 1315 } 1316 else 1317 { 1318 e.deprecation(msg, e.toChars(), v.toChars()); 1319 if (supplemental) 1320 deprecationSupplemental(e.loc, supplemental); 1321 } 1322 } 1323 result = true; 1324 } 1325 1326 if (v.isDataseg()) 1327 continue; 1328 1329 Dsymbol p = v.toParent2(); 1330 1331 // https://issues.dlang.org/show_bug.cgi?id=19965 1332 if (!refs && sc.func.vthis == v) 1333 notMaybeScope(v); 1334 1335 if ((v.storage_class & (STC.ref_ | STC.out_)) == 0) 1336 { 1337 if (p == sc.func) 1338 { 1339 escapingRef(v); 1340 continue; 1341 } 1342 FuncDeclaration fd = p.isFuncDeclaration(); 1343 if (fd && sc.func.flags & FUNCFLAG.returnInprocess) 1344 { 1345 /* Code like: 1346 * int x; 1347 * auto dg = () { return &x; } 1348 * Making it: 1349 * auto dg = () return { return &x; } 1350 * Because dg.ptr points to x, this is returning dt.ptr+offset 1351 */ 1352 if (global.params.vsafe) 1353 { 1354 sc.func.storage_class |= STC.return_ | STC.returninferred; 1355 } 1356 } 1357 1358 } 1359 1360 /* Check for returning a ref variable by 'ref', but should be 'return ref' 1361 * Infer the addition of 'return', or set result to be the offending expression. 1362 */ 1363 if ( (v.storage_class & (STC.ref_ | STC.out_)) && 1364 !(v.storage_class & (STC.return_ | STC.foreach_))) 1365 { 1366 if (sc.func.flags & FUNCFLAG.returnInprocess && p == sc.func) 1367 { 1368 inferReturn(sc.func, v); // infer addition of 'return' 1369 } 1370 else if (sc._module && sc._module.isRoot()) 1371 { 1372 // If -preview=dip25 is used, the user wants an error 1373 // Otherwise, issue a deprecation 1374 const emitError = global.params.useDIP25 == FeatureState.enabled; 1375 // https://dlang.org/spec/function.html#return-ref-parameters 1376 // Only look for errors if in module listed on command line 1377 if (p == sc.func) 1378 { 1379 //printf("escaping reference to local ref variable %s\n", v.toChars()); 1380 //printf("storage class = x%llx\n", v.storage_class); 1381 escapingRef(v, emitError); 1382 continue; 1383 } 1384 // Don't need to be concerned if v's parent does not return a ref 1385 FuncDeclaration fd = p.isFuncDeclaration(); 1386 if (fd && fd.type && fd.type.ty == Tfunction) 1387 { 1388 TypeFunction tf = cast(TypeFunction)fd.type; 1389 if (tf.isref) 1390 { 1391 const(char)* msg = "escaping reference to outer local variable `%s`"; 1392 if (!gag && emitError) 1393 error(e.loc, msg, v.toChars()); 1394 else if (!gag) 1395 deprecation(e.loc, msg, v.toChars()); 1396 result = true; 1397 continue; 1398 } 1399 } 1400 1401 } 1402 } 1403 } 1404 1405 foreach (Expression ee; er.byexp) 1406 { 1407 if (log) printf("byexp %s\n", ee.toChars()); 1408 if (!gag) 1409 error(ee.loc, "escaping reference to stack allocated value returned by `%s`", ee.toChars()); 1410 result = true; 1411 } 1412 1413 return result; 1414 } 1415 1416 1417 /************************************* 1418 * Variable v needs to have 'return' inferred for it. 1419 * Params: 1420 * fd = function that v is a parameter to 1421 * v = parameter that needs to be STC.return_ 1422 */ 1423 1424 private void inferReturn(FuncDeclaration fd, VarDeclaration v) 1425 { 1426 // v is a local in the current function 1427 1428 //printf("for function '%s' inferring 'return' for variable '%s'\n", fd.toChars(), v.toChars()); 1429 v.storage_class |= STC.return_ | STC.returninferred; 1430 1431 TypeFunction tf = cast(TypeFunction)fd.type; 1432 if (v == fd.vthis) 1433 { 1434 /* v is the 'this' reference, so mark the function 1435 */ 1436 fd.storage_class |= STC.return_ | STC.returninferred; 1437 if (tf.ty == Tfunction) 1438 { 1439 //printf("'this' too %p %s\n", tf, sc.func.toChars()); 1440 tf.isreturn = true; 1441 tf.isreturninferred = true; 1442 } 1443 } 1444 else 1445 { 1446 // Perform 'return' inference on parameter 1447 if (tf.ty == Tfunction) 1448 { 1449 foreach (i, p; tf.parameterList) 1450 { 1451 if (p.ident == v.ident) 1452 { 1453 p.storageClass |= STC.return_ | STC.returninferred; 1454 break; // there can be only one 1455 } 1456 } 1457 } 1458 } 1459 } 1460 1461 1462 /**************************************** 1463 * e is an expression to be returned by value, and that value contains pointers. 1464 * Walk e to determine which variables are possibly being 1465 * returned by value, such as: 1466 * int* function(int* p) { return p; } 1467 * If e is a form of &p, determine which variables have content 1468 * which is being returned as ref, such as: 1469 * int* function(int i) { return &i; } 1470 * Multiple variables can be inserted, because of expressions like this: 1471 * int function(bool b, int i, int* p) { return b ? &i : p; } 1472 * 1473 * No side effects. 1474 * 1475 * Params: 1476 * e = expression to be returned by value 1477 * er = where to place collected data 1478 * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. 1479 */ 1480 void escapeByValue(Expression e, EscapeByResults* er, bool live = false) 1481 { 1482 //printf("[%s] escapeByValue, e: %s\n", e.loc.toChars(), e.toChars()); 1483 extern (C++) final class EscapeVisitor : Visitor 1484 { 1485 alias visit = Visitor.visit; 1486 public: 1487 EscapeByResults* er; 1488 bool live; 1489 1490 extern (D) this(EscapeByResults* er, bool live) 1491 { 1492 this.er = er; 1493 this.live = live; 1494 } 1495 1496 override void visit(Expression e) 1497 { 1498 } 1499 1500 override void visit(AddrExp e) 1501 { 1502 /* Taking the address of struct literal is normally not 1503 * allowed, but CTFE can generate one out of a new expression, 1504 * but it'll be placed in static data so no need to check it. 1505 */ 1506 if (e.e1.op != TOK.structLiteral) 1507 escapeByRef(e.e1, er, live); 1508 } 1509 1510 override void visit(SymOffExp e) 1511 { 1512 VarDeclaration v = e.var.isVarDeclaration(); 1513 if (v) 1514 er.byref.push(v); 1515 } 1516 1517 override void visit(VarExp e) 1518 { 1519 if (auto v = e.var.isVarDeclaration()) 1520 { 1521 if (v.type.hasPointers() || // not tracking non-pointers 1522 v.storage_class & STC.lazy_) // lazy variables are actually pointers 1523 er.byvalue.push(v); 1524 } 1525 } 1526 1527 override void visit(ThisExp e) 1528 { 1529 if (e.var) 1530 er.byvalue.push(e.var); 1531 } 1532 1533 override void visit(PtrExp e) 1534 { 1535 if (live && e.type.hasPointers()) 1536 e.e1.accept(this); 1537 } 1538 1539 override void visit(DotVarExp e) 1540 { 1541 auto t = e.e1.type.toBasetype(); 1542 if (e.type.hasPointers() && (live || t.ty == Tstruct)) 1543 { 1544 e.e1.accept(this); 1545 } 1546 } 1547 1548 override void visit(DelegateExp e) 1549 { 1550 Type t = e.e1.type.toBasetype(); 1551 if (t.ty == Tclass || t.ty == Tpointer) 1552 escapeByValue(e.e1, er, live); 1553 else 1554 escapeByRef(e.e1, er, live); 1555 er.byfunc.push(e.func); 1556 } 1557 1558 override void visit(FuncExp e) 1559 { 1560 if (e.fd.tok == TOK.delegate_) 1561 er.byfunc.push(e.fd); 1562 } 1563 1564 override void visit(TupleExp e) 1565 { 1566 assert(0); // should have been lowered by now 1567 } 1568 1569 override void visit(ArrayLiteralExp e) 1570 { 1571 Type tb = e.type.toBasetype(); 1572 if (tb.ty == Tsarray || tb.ty == Tarray) 1573 { 1574 if (e.basis) 1575 e.basis.accept(this); 1576 foreach (el; *e.elements) 1577 { 1578 if (el) 1579 el.accept(this); 1580 } 1581 } 1582 } 1583 1584 override void visit(StructLiteralExp e) 1585 { 1586 if (e.elements) 1587 { 1588 foreach (ex; *e.elements) 1589 { 1590 if (ex) 1591 ex.accept(this); 1592 } 1593 } 1594 } 1595 1596 override void visit(NewExp e) 1597 { 1598 Type tb = e.newtype.toBasetype(); 1599 if (tb.ty == Tstruct && !e.member && e.arguments) 1600 { 1601 foreach (ex; *e.arguments) 1602 { 1603 if (ex) 1604 ex.accept(this); 1605 } 1606 } 1607 } 1608 1609 override void visit(CastExp e) 1610 { 1611 if (!e.type.hasPointers()) 1612 return; 1613 Type tb = e.type.toBasetype(); 1614 if (tb.ty == Tarray && e.e1.type.toBasetype().ty == Tsarray) 1615 { 1616 escapeByRef(e.e1, er, live); 1617 } 1618 else 1619 e.e1.accept(this); 1620 } 1621 1622 override void visit(SliceExp e) 1623 { 1624 if (e.e1.op == TOK.variable) 1625 { 1626 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 1627 Type tb = e.type.toBasetype(); 1628 if (v) 1629 { 1630 if (tb.ty == Tsarray) 1631 return; 1632 if (v.storage_class & STC.variadic) 1633 { 1634 er.byvalue.push(v); 1635 return; 1636 } 1637 } 1638 } 1639 Type t1b = e.e1.type.toBasetype(); 1640 if (t1b.ty == Tsarray) 1641 { 1642 Type tb = e.type.toBasetype(); 1643 if (tb.ty != Tsarray) 1644 escapeByRef(e.e1, er, live); 1645 } 1646 else 1647 e.e1.accept(this); 1648 } 1649 1650 override void visit(IndexExp e) 1651 { 1652 if (e.e1.type.toBasetype().ty == Tsarray || 1653 live && e.type.hasPointers()) 1654 { 1655 e.e1.accept(this); 1656 } 1657 } 1658 1659 override void visit(BinExp e) 1660 { 1661 Type tb = e.type.toBasetype(); 1662 if (tb.ty == Tpointer) 1663 { 1664 e.e1.accept(this); 1665 e.e2.accept(this); 1666 } 1667 } 1668 1669 override void visit(BinAssignExp e) 1670 { 1671 e.e1.accept(this); 1672 } 1673 1674 override void visit(AssignExp e) 1675 { 1676 e.e1.accept(this); 1677 } 1678 1679 override void visit(CommaExp e) 1680 { 1681 e.e2.accept(this); 1682 } 1683 1684 override void visit(CondExp e) 1685 { 1686 e.e1.accept(this); 1687 e.e2.accept(this); 1688 } 1689 1690 override void visit(CallExp e) 1691 { 1692 //printf("CallExp(): %s\n", e.toChars()); 1693 /* Check each argument that is 1694 * passed as 'return scope'. 1695 */ 1696 Type t1 = e.e1.type.toBasetype(); 1697 TypeFunction tf; 1698 TypeDelegate dg; 1699 if (t1.ty == Tdelegate) 1700 { 1701 dg = cast(TypeDelegate)t1; 1702 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next; 1703 } 1704 else if (t1.ty == Tfunction) 1705 tf = cast(TypeFunction)t1; 1706 else 1707 return; 1708 1709 if (!e.type.hasPointers()) 1710 return; 1711 1712 if (e.arguments && e.arguments.dim) 1713 { 1714 /* j=1 if _arguments[] is first argument, 1715 * skip it because it is not passed by ref 1716 */ 1717 int j = tf.isDstyleVariadic(); 1718 for (size_t i = j; i < e.arguments.dim; ++i) 1719 { 1720 Expression arg = (*e.arguments)[i]; 1721 size_t nparams = tf.parameterList.length; 1722 if (i - j < nparams && i >= j) 1723 { 1724 Parameter p = tf.parameterList[i - j]; 1725 const stc = tf.parameterStorageClass(null, p); 1726 if ((stc & (STC.scope_)) && (stc & STC.return_)) 1727 arg.accept(this); 1728 else if ((stc & (STC.ref_)) && (stc & STC.return_)) 1729 { 1730 if (tf.isref) 1731 { 1732 /* Treat: 1733 * ref P foo(return ref P p) 1734 * as: 1735 * p; 1736 */ 1737 arg.accept(this); 1738 } 1739 else 1740 escapeByRef(arg, er, live); 1741 } 1742 } 1743 } 1744 } 1745 // If 'this' is returned, check it too 1746 if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction) 1747 { 1748 DotVarExp dve = cast(DotVarExp)e.e1; 1749 FuncDeclaration fd = dve.var.isFuncDeclaration(); 1750 AggregateDeclaration ad; 1751 if (global.params.vsafe && tf.isreturn && fd && (ad = fd.isThis()) !is null) 1752 { 1753 if (ad.isClassDeclaration() || tf.isScopeQual) // this is 'return scope' 1754 dve.e1.accept(this); 1755 else if (ad.isStructDeclaration()) // this is 'return ref' 1756 { 1757 if (tf.isref) 1758 { 1759 /* Treat calling: 1760 * struct S { ref S foo() return; } 1761 * as: 1762 * this; 1763 */ 1764 dve.e1.accept(this); 1765 } 1766 else 1767 escapeByRef(dve.e1, er, live); 1768 } 1769 } 1770 else if (dve.var.storage_class & STC.return_ || tf.isreturn) 1771 { 1772 if (dve.var.storage_class & STC.scope_) 1773 dve.e1.accept(this); 1774 else if (dve.var.storage_class & STC.ref_) 1775 escapeByRef(dve.e1, er, live); 1776 } 1777 // If it's also a nested function that is 'return scope' 1778 if (fd && fd.isNested()) 1779 { 1780 if (tf.isreturn && tf.isScopeQual) 1781 er.byexp.push(e); 1782 } 1783 } 1784 1785 /* If returning the result of a delegate call, the .ptr 1786 * field of the delegate must be checked. 1787 */ 1788 if (dg) 1789 { 1790 if (tf.isreturn) 1791 e.e1.accept(this); 1792 } 1793 1794 /* If it's a nested function that is 'return scope' 1795 */ 1796 if (e.e1.op == TOK.variable) 1797 { 1798 VarExp ve = cast(VarExp)e.e1; 1799 FuncDeclaration fd = ve.var.isFuncDeclaration(); 1800 if (fd && fd.isNested()) 1801 { 1802 if (tf.isreturn && tf.isScopeQual) 1803 er.byexp.push(e); 1804 } 1805 } 1806 } 1807 } 1808 1809 scope EscapeVisitor v = new EscapeVisitor(er, live); 1810 e.accept(v); 1811 } 1812 1813 1814 /**************************************** 1815 * e is an expression to be returned by 'ref'. 1816 * Walk e to determine which variables are possibly being 1817 * returned by ref, such as: 1818 * ref int function(int i) { return i; } 1819 * If e is a form of *p, determine which variables have content 1820 * which is being returned as ref, such as: 1821 * ref int function(int* p) { return *p; } 1822 * Multiple variables can be inserted, because of expressions like this: 1823 * ref int function(bool b, int i, int* p) { return b ? i : *p; } 1824 * 1825 * No side effects. 1826 * 1827 * Params: 1828 * e = expression to be returned by 'ref' 1829 * er = where to place collected data 1830 * live = if @live semantics apply, i.e. expressions `p`, `*p`, `**p`, etc., all return `p`. 1831 */ 1832 void escapeByRef(Expression e, EscapeByResults* er, bool live = false) 1833 { 1834 //printf("[%s] escapeByRef, e: %s\n", e.loc.toChars(), e.toChars()); 1835 extern (C++) final class EscapeRefVisitor : Visitor 1836 { 1837 alias visit = Visitor.visit; 1838 public: 1839 EscapeByResults* er; 1840 bool live; 1841 1842 extern (D) this(EscapeByResults* er, bool live) 1843 { 1844 this.er = er; 1845 this.live = live; 1846 } 1847 1848 override void visit(Expression e) 1849 { 1850 } 1851 1852 override void visit(VarExp e) 1853 { 1854 auto v = e.var.isVarDeclaration(); 1855 if (v) 1856 { 1857 if (v.storage_class & STC.ref_ && v.storage_class & (STC.foreach_ | STC.temp) && v._init) 1858 { 1859 /* If compiler generated ref temporary 1860 * (ref v = ex; ex) 1861 * look at the initializer instead 1862 */ 1863 if (ExpInitializer ez = v._init.isExpInitializer()) 1864 { 1865 assert(ez.exp && ez.exp.op == TOK.construct); 1866 Expression ex = (cast(ConstructExp)ez.exp).e2; 1867 ex.accept(this); 1868 } 1869 } 1870 else 1871 er.byref.push(v); 1872 } 1873 } 1874 1875 override void visit(ThisExp e) 1876 { 1877 if (e.var && e.var.toParent2().isFuncDeclaration().isThis2) 1878 escapeByValue(e, er, live); 1879 else if (e.var) 1880 er.byref.push(e.var); 1881 } 1882 1883 override void visit(PtrExp e) 1884 { 1885 escapeByValue(e.e1, er, live); 1886 } 1887 1888 override void visit(IndexExp e) 1889 { 1890 Type tb = e.e1.type.toBasetype(); 1891 if (e.e1.op == TOK.variable) 1892 { 1893 VarDeclaration v = (cast(VarExp)e.e1).var.isVarDeclaration(); 1894 if (tb.ty == Tarray || tb.ty == Tsarray) 1895 { 1896 if (v && v.storage_class & STC.variadic) 1897 { 1898 er.byref.push(v); 1899 return; 1900 } 1901 } 1902 } 1903 if (tb.ty == Tsarray) 1904 { 1905 e.e1.accept(this); 1906 } 1907 else if (tb.ty == Tarray) 1908 { 1909 escapeByValue(e.e1, er, live); 1910 } 1911 } 1912 1913 override void visit(StructLiteralExp e) 1914 { 1915 if (e.elements) 1916 { 1917 foreach (ex; *e.elements) 1918 { 1919 if (ex) 1920 ex.accept(this); 1921 } 1922 } 1923 er.byexp.push(e); 1924 } 1925 1926 override void visit(DotVarExp e) 1927 { 1928 Type t1b = e.e1.type.toBasetype(); 1929 if (t1b.ty == Tclass) 1930 escapeByValue(e.e1, er, live); 1931 else 1932 e.e1.accept(this); 1933 } 1934 1935 override void visit(BinAssignExp e) 1936 { 1937 e.e1.accept(this); 1938 } 1939 1940 override void visit(AssignExp e) 1941 { 1942 e.e1.accept(this); 1943 } 1944 1945 override void visit(CommaExp e) 1946 { 1947 e.e2.accept(this); 1948 } 1949 1950 override void visit(CondExp e) 1951 { 1952 e.e1.accept(this); 1953 e.e2.accept(this); 1954 } 1955 1956 override void visit(CallExp e) 1957 { 1958 //printf("escapeByRef.CallExp(): %s\n", e.toChars()); 1959 /* If the function returns by ref, check each argument that is 1960 * passed as 'return ref'. 1961 */ 1962 Type t1 = e.e1.type.toBasetype(); 1963 TypeFunction tf; 1964 if (t1.ty == Tdelegate) 1965 tf = cast(TypeFunction)(cast(TypeDelegate)t1).next; 1966 else if (t1.ty == Tfunction) 1967 tf = cast(TypeFunction)t1; 1968 else 1969 return; 1970 if (tf.isref) 1971 { 1972 if (e.arguments && e.arguments.dim) 1973 { 1974 /* j=1 if _arguments[] is first argument, 1975 * skip it because it is not passed by ref 1976 */ 1977 int j = tf.isDstyleVariadic(); 1978 for (size_t i = j; i < e.arguments.dim; ++i) 1979 { 1980 Expression arg = (*e.arguments)[i]; 1981 size_t nparams = tf.parameterList.length; 1982 if (i - j < nparams && i >= j) 1983 { 1984 Parameter p = tf.parameterList[i - j]; 1985 const stc = tf.parameterStorageClass(null, p); 1986 if ((stc & (STC.out_ | STC.ref_)) && (stc & STC.return_)) 1987 arg.accept(this); 1988 else if ((stc & STC.scope_) && (stc & STC.return_)) 1989 { 1990 if (arg.op == TOK.delegate_) 1991 { 1992 DelegateExp de = cast(DelegateExp)arg; 1993 if (de.func.isNested()) 1994 er.byexp.push(de); 1995 } 1996 else 1997 escapeByValue(arg, er, live); 1998 } 1999 } 2000 } 2001 } 2002 // If 'this' is returned by ref, check it too 2003 if (e.e1.op == TOK.dotVariable && t1.ty == Tfunction) 2004 { 2005 DotVarExp dve = cast(DotVarExp)e.e1; 2006 2007 // https://issues.dlang.org/show_bug.cgi?id=20149#c10 2008 if (dve.var.isCtorDeclaration()) 2009 { 2010 er.byexp.push(e); 2011 return; 2012 } 2013 2014 if (dve.var.storage_class & STC.return_ || tf.isreturn) 2015 { 2016 if (dve.var.storage_class & STC.ref_ || tf.isref) 2017 dve.e1.accept(this); 2018 else if (dve.var.storage_class & STC.scope_ || tf.isScopeQual) 2019 escapeByValue(dve.e1, er, live); 2020 } 2021 // If it's also a nested function that is 'return ref' 2022 FuncDeclaration fd = dve.var.isFuncDeclaration(); 2023 if (fd && fd.isNested()) 2024 { 2025 if (tf.isreturn) 2026 er.byexp.push(e); 2027 } 2028 } 2029 // If it's a delegate, check it too 2030 if (e.e1.op == TOK.variable && t1.ty == Tdelegate) 2031 { 2032 escapeByValue(e.e1, er, live); 2033 } 2034 2035 /* If it's a nested function that is 'return ref' 2036 */ 2037 if (e.e1.op == TOK.variable) 2038 { 2039 VarExp ve = cast(VarExp)e.e1; 2040 FuncDeclaration fd = ve.var.isFuncDeclaration(); 2041 if (fd && fd.isNested()) 2042 { 2043 if (tf.isreturn) 2044 er.byexp.push(e); 2045 } 2046 } 2047 } 2048 else 2049 er.byexp.push(e); 2050 } 2051 } 2052 2053 scope EscapeRefVisitor v = new EscapeRefVisitor(er, live); 2054 e.accept(v); 2055 } 2056 2057 2058 /************************************ 2059 * Aggregate the data collected by the escapeBy??() functions. 2060 */ 2061 struct EscapeByResults 2062 { 2063 VarDeclarations byref; // array into which variables being returned by ref are inserted 2064 VarDeclarations byvalue; // array into which variables with values containing pointers are inserted 2065 FuncDeclarations byfunc; // nested functions that are turned into delegates 2066 Expressions byexp; // array into which temporaries being returned by ref are inserted 2067 2068 /** Reset arrays so the storage can be used again 2069 */ 2070 void reset() 2071 { 2072 byref.setDim(0); 2073 byvalue.setDim(0); 2074 byfunc.setDim(0); 2075 byexp.setDim(0); 2076 } 2077 } 2078 2079 /************************* 2080 * Find all variables accessed by this delegate that are 2081 * in functions enclosing it. 2082 * Params: 2083 * fd = function 2084 * vars = array to append found variables to 2085 */ 2086 public void findAllOuterAccessedVariables(FuncDeclaration fd, VarDeclarations* vars) 2087 { 2088 //printf("findAllOuterAccessedVariables(fd: %s)\n", fd.toChars()); 2089 for (auto p = fd.parent; p; p = p.parent) 2090 { 2091 auto fdp = p.isFuncDeclaration(); 2092 if (!fdp) 2093 continue; 2094 2095 foreach (v; fdp.closureVars) 2096 { 2097 foreach (const fdv; v.nestedrefs) 2098 { 2099 if (fdv == fd) 2100 { 2101 //printf("accessed: %s, type %s\n", v.toChars(), v.type.toChars()); 2102 vars.push(v); 2103 } 2104 } 2105 } 2106 } 2107 } 2108 2109 /*********************************** 2110 * Turn off `STC.maybescope` for variable `v`. 2111 * 2112 * This exists in order to find where `STC.maybescope` is getting turned off. 2113 * Params: 2114 * v = variable 2115 */ 2116 version (none) 2117 { 2118 public void notMaybeScope(string file = __FILE__, int line = __LINE__)(VarDeclaration v) 2119 { 2120 printf("%.*s(%d): notMaybeScope('%s')\n", cast(int)file.length, file.ptr, line, v.toChars()); 2121 v.storage_class &= ~STC.maybescope; 2122 } 2123 } 2124 else 2125 { 2126 public void notMaybeScope(VarDeclaration v) 2127 { 2128 v.storage_class &= ~STC.maybescope; 2129 } 2130 } 2131 2132 2133 /********************************************** 2134 * Have some variables that are maybescopes that were 2135 * assigned values from other maybescope variables. 2136 * Now that semantic analysis of the function is 2137 * complete, we can finalize this by turning off 2138 * maybescope for array elements that cannot be scope. 2139 * 2140 * $(TABLE2 Scope Table, 2141 * $(THEAD `va`, `v`, =>, `va` , `v` ) 2142 * $(TROW maybe, maybe, =>, scope, scope) 2143 * $(TROW scope, scope, =>, scope, scope) 2144 * $(TROW scope, maybe, =>, scope, scope) 2145 * $(TROW maybe, scope, =>, scope, scope) 2146 * $(TROW - , - , =>, - , - ) 2147 * $(TROW - , maybe, =>, - , - ) 2148 * $(TROW - , scope, =>, error, error) 2149 * $(TROW maybe, - , =>, scope, - ) 2150 * $(TROW scope, - , =>, scope, - ) 2151 * ) 2152 * Params: 2153 * array = array of variables that were assigned to from maybescope variables 2154 */ 2155 public void eliminateMaybeScopes(VarDeclaration[] array) 2156 { 2157 enum log = false; 2158 if (log) printf("eliminateMaybeScopes()\n"); 2159 bool changes; 2160 do 2161 { 2162 changes = false; 2163 foreach (va; array) 2164 { 2165 if (log) printf(" va = %s\n", va.toChars()); 2166 if (!(va.storage_class & (STC.maybescope | STC.scope_))) 2167 { 2168 if (va.maybes) 2169 { 2170 foreach (v; *va.maybes) 2171 { 2172 if (log) printf(" v = %s\n", v.toChars()); 2173 if (v.storage_class & STC.maybescope) 2174 { 2175 // v cannot be scope since it is assigned to a non-scope va 2176 notMaybeScope(v); 2177 if (!(v.storage_class & (STC.ref_ | STC.out_))) 2178 v.storage_class &= ~(STC.return_ | STC.returninferred); 2179 changes = true; 2180 } 2181 } 2182 } 2183 } 2184 } 2185 } while (changes); 2186 } 2187 2188 /************************************************ 2189 * Is type a reference to a mutable value? 2190 * 2191 * This is used to determine if an argument that does not have a corresponding 2192 * Parameter, i.e. a variadic argument, is a pointer to mutable data. 2193 * Params: 2194 * t = type of the argument 2195 * Returns: 2196 * true if it's a pointer (or reference) to mutable data 2197 */ 2198 bool isReferenceToMutable(Type t) 2199 { 2200 t = t.baseElemOf(); 2201 2202 if (!t.isMutable() || 2203 !t.hasPointers()) 2204 return false; 2205 2206 switch (t.ty) 2207 { 2208 case Tpointer: 2209 if (t.nextOf().isTypeFunction()) 2210 break; 2211 goto case; 2212 2213 case Tarray: 2214 case Taarray: 2215 case Tdelegate: 2216 if (t.nextOf().isMutable()) 2217 return true; 2218 break; 2219 2220 case Tclass: 2221 return true; // even if the class fields are not mutable 2222 2223 case Tstruct: 2224 // Have to look at each field 2225 foreach (VarDeclaration v; t.isTypeStruct().sym.fields) 2226 { 2227 if (v.storage_class & STC.ref_) 2228 { 2229 if (v.type.isMutable()) 2230 return true; 2231 } 2232 else if (v.type.isReferenceToMutable()) 2233 return true; 2234 } 2235 break; 2236 2237 default: 2238 assert(0); 2239 } 2240 return false; 2241 } 2242 2243 /**************************************** 2244 * Is parameter a reference to a mutable value? 2245 * 2246 * This is used if an argument has a corresponding Parameter. 2247 * The argument type is necessary if the Parameter is inout. 2248 * Params: 2249 * p = Parameter to check 2250 * t = type of corresponding argument 2251 * Returns: 2252 * true if it's a pointer (or reference) to mutable data 2253 */ 2254 bool isReferenceToMutable(Parameter p, Type t) 2255 { 2256 if (p.isReference()) 2257 { 2258 if (p.type.isConst() || p.type.isImmutable()) 2259 return false; 2260 if (p.type.isWild()) 2261 { 2262 return t.isMutable(); 2263 } 2264 return p.type.isMutable(); 2265 } 2266 return isReferenceToMutable(p.type); 2267 }