1 /** 2 * Flow analysis for Ownership/Borrowing 3 * 4 * Copyright: Copyright (C) 1999-2019 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/ob.d, _ob.d) 8 * Documentation: https://dlang.org/phobos/dmd_escape.html 9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ob.d 10 */ 11 12 module dmd.ob; 13 14 import core.stdc.stdio : printf; 15 import core.stdc.stdlib; 16 import core.stdc..string; 17 18 import dmd.root.array; 19 import dmd.root.rootobject; 20 import dmd.root.rmem; 21 22 import dmd.aggregate; 23 import dmd.apply; 24 import dmd.arraytypes; 25 import dmd.declaration; 26 import dmd.dscope; 27 import dmd.dsymbol; 28 import dmd.dtemplate; 29 import dmd.errors; 30 import dmd.escape; 31 import dmd.expression; 32 import dmd.foreachvar; 33 import dmd.func; 34 import dmd.globals; 35 import dmd.identifier; 36 import dmd.init; 37 import dmd.mtype; 38 import dmd.printast; 39 import dmd.statement; 40 import dmd.stmtstate; 41 import dmd.tokens; 42 import dmd.visitor; 43 44 import dmd.root.bitarray; 45 import dmd.root.outbuffer; 46 47 /********************************** 48 * Perform ownership/borrowing checks for funcdecl. 49 * Does not modify the AST, just checks for errors. 50 */ 51 52 void oblive(FuncDeclaration funcdecl) 53 { 54 //printf("oblive() %s\n", funcdecl.toChars()); 55 //printf("fbody: %s\n", funcdecl.fbody.toChars()); 56 ObState obstate; 57 58 /* Build the flow graph 59 */ 60 setLabelStatementExtraFields(funcdecl.labtab); 61 toObNodes(obstate.nodes, funcdecl.fbody); 62 insertFinallyBlockCalls(obstate.nodes); 63 insertFinallyBlockGotos(obstate.nodes); 64 removeUnreachable(obstate.nodes); 65 computePreds(obstate.nodes); 66 67 numberNodes(obstate.nodes); 68 //foreach (ob; obstate.nodes) ob.print(); 69 70 collectVars(funcdecl, obstate.vars); 71 allocStates(obstate); 72 doDataFlowAnalysis(obstate); 73 74 checkObErrors(obstate); 75 } 76 77 alias ObNodes = Array!(ObNode*); 78 79 alias StmtState = dmd.stmtstate.StmtState!ObNode; 80 81 /******************************************* 82 * Collect the state information. 83 */ 84 struct ObState 85 { 86 ObNodes nodes; 87 VarDeclarations vars; 88 89 Array!size_t varStack; /// temporary storage 90 Array!bool mutableStack; /// parallel to varStack[], is type mutable? 91 92 PtrVarState[] varPool; /// memory pool 93 94 ~this() 95 { 96 mem.xfree(varPool.ptr); 97 } 98 } 99 100 /*********************************************** 101 * A node in the function's expression graph, and its edges to predecessors and successors. 102 */ 103 struct ObNode 104 { 105 Expression exp; /// expression for the node 106 ObNodes preds; /// predecessors 107 ObNodes succs; /// successors 108 ObNode* tryBlock; /// try-finally block we're inside 109 ObType obtype; 110 uint index; /// index of this in obnodes 111 112 PtrVarState[] gen; /// new states generated for this node 113 PtrVarState[] input; /// variable states on entry to exp 114 PtrVarState[] output; /// variable states on exit to exp 115 116 this(ObNode* tryBlock) 117 { 118 this.tryBlock = tryBlock; 119 } 120 121 void print() 122 { 123 printf("%d: %s %s\n", index, obtype.toString.ptr, exp ? exp.toChars() : "-"); 124 printf(" preds: "); 125 foreach (ob; preds) 126 printf(" %d", ob.index); 127 printf("\n succs: "); 128 foreach (ob; succs) 129 printf(" %d", ob.index); 130 printf("\n\n"); 131 } 132 } 133 134 135 enum ObType : ubyte 136 { 137 goto_, /// goto one of the succs[] 138 return_, /// returns from function 139 retexp, /// returns expression from function 140 throw_, /// exits with throw 141 exit, /// exits program 142 try_, 143 finally_, 144 fend, 145 } 146 147 string toString(ObType obtype) 148 { 149 return obtype == ObType.goto_ ? "goto " : 150 obtype == ObType.return_ ? "ret " : 151 obtype == ObType.retexp ? "retexp" : 152 obtype == ObType.throw_ ? "throw" : 153 obtype == ObType.exit ? "exit" : 154 obtype == ObType.try_ ? "try" : 155 obtype == ObType.finally_ ? "finally" : 156 obtype == ObType.fend ? "fend" : 157 "---"; 158 } 159 160 /*********** 161 Pointer variable states: 162 163 Initial state is not known; ignore for now 164 165 Undefined not in a usable state 166 167 T* p = void; 168 169 Owner mutable pointer 170 171 T* p = initializer; 172 173 Borrowed scope mutable pointer, borrowed from [p] 174 175 T* p = initializer; 176 scope T* b = p; 177 178 Readonly scope const pointer, copied from [p] 179 180 T* p = initializer; 181 scope const(T)* cp = p; 182 183 Examples: 184 185 T* p = initializer; // p is owner 186 T** pp = &p; // pp borrows from p 187 188 T* p = initialize; // p is owner 189 T* q = p; // transfer: q is owner, p is undefined 190 */ 191 192 enum PtrState : ubyte 193 { 194 Initial, Undefined, Owner, Borrowed, Readonly 195 } 196 197 /************ 198 */ 199 const(char)* toChars(PtrState state) 200 { 201 return toString(state).ptr; 202 } 203 204 string toString(PtrState state) 205 { 206 return ["Initial", "Undefined", "Owner", "Borrowed", "Readonly"][state]; 207 } 208 209 /****** 210 * Carries the state of a pointer variable. 211 */ 212 struct PtrVarState 213 { 214 BitArray deps; /// dependencies 215 PtrState state; /// state the pointer variable is in 216 217 void opAssign(const ref PtrVarState pvs) 218 { 219 state = pvs.state; 220 deps = pvs.deps; 221 } 222 223 /* Combine `this` and `pvs` into `this`, 224 * on the idea that the `this` and the `pvs` paths 225 * are being merged 226 * Params: 227 * pvs = path to be merged with `this` 228 */ 229 void combine(ref PtrVarState pvs, size_t vi, PtrVarState[] gen) 230 { 231 static uint X(PtrState x1, PtrState x2) { return x1 * (PtrState.max + 1) + x2; } 232 233 with (PtrState) 234 { 235 switch (X(state, pvs.state)) 236 { 237 case X(Initial, Initial): 238 break; 239 240 case X(Initial, Owner ): 241 case X(Initial, Borrowed ): 242 case X(Initial, Readonly ): 243 // Transfer state to `this` 244 state = pvs.state; 245 deps = pvs.deps; 246 break; 247 248 case X(Owner, Initial): 249 case X(Borrowed, Initial): 250 case X(Readonly, Initial): 251 break; 252 253 case X(Undefined, Initial): 254 case X(Undefined, Undefined): 255 case X(Undefined, Owner ): 256 case X(Undefined, Borrowed ): 257 case X(Undefined, Readonly ): 258 break; 259 260 case X(Owner , Owner ): 261 break; 262 263 case X(Borrowed , Borrowed): 264 case X(Readonly , Readonly): 265 deps.or(pvs.deps); 266 break; 267 268 default: 269 makeUndefined(vi, gen); 270 break; 271 } 272 } 273 } 274 275 bool opEquals(const ref PtrVarState pvs) const 276 { 277 return state == pvs.state && 278 deps == pvs.deps; 279 } 280 281 /*********************** 282 */ 283 void print(VarDeclaration[] vars) 284 { 285 string s = toString(state); 286 printf("%.*s [", cast(int)s.length, s.ptr); 287 assert(vars.length == deps.length); 288 OutBuffer buf; 289 depsToBuf(buf, vars); 290 string t = buf[]; 291 printf("%.*s]\n", cast(int)t.length, t.ptr); 292 } 293 294 /***************************** 295 * Produce a user-readable comma separated string of the 296 * dependencies. 297 * Params: 298 * buf = write resulting string here 299 * vars = array from which to get the variable names 300 */ 301 void depsToBuf(ref OutBuffer buf, const VarDeclaration[] vars) 302 { 303 bool any = false; 304 foreach (i; 0 .. deps.length) 305 { 306 if (deps[i]) 307 { 308 if (any) 309 buf.writestring(", "); 310 buf.writestring(vars[i].toString()); 311 any = true; 312 } 313 } 314 } 315 } 316 317 318 /***************************************** 319 * Set the `.extra` field for LabelStatements in labtab[]. 320 */ 321 void setLabelStatementExtraFields(DsymbolTable labtab) 322 { 323 if (labtab) 324 foreach (keyValue; labtab.tab.asRange) 325 { 326 //printf(" KV: %s = %s\n", keyValue.key.toChars(), keyValue.value.toChars()); 327 auto label = cast(LabelDsymbol)keyValue.value; 328 if (label.statement) 329 label.statement.extra = cast(void*) new ObNode(null); 330 } 331 } 332 333 /***************************************** 334 * Convert statement into ObNodes. 335 */ 336 337 void toObNodes(ref ObNodes obnodes, Statement s) 338 { 339 ObNode* curblock = new ObNode(null); 340 obnodes.push(curblock); 341 342 void visit(Statement s, StmtState* stmtstate) 343 { 344 if (!s) 345 return; 346 347 ObNode* newNode() 348 { 349 return new ObNode(stmtstate.tryBlock); 350 } 351 352 ObNode* nextNodeIs(ObNode* ob) 353 { 354 obnodes.push(ob); 355 curblock = ob; 356 return ob; 357 } 358 359 ObNode* nextNode() 360 { 361 return nextNodeIs(newNode()); 362 } 363 364 ObNode* gotoNextNodeIs(ObNode* ob) 365 { 366 obnodes.push(ob); 367 curblock.succs.push(ob); 368 curblock = ob; 369 return ob; 370 } 371 372 // block_goto(blx, BCgoto, null) 373 ObNode* gotoNextNode() 374 { 375 return gotoNextNodeIs(newNode()); 376 } 377 378 /*** 379 * Doing a goto to dest 380 */ 381 ObNode* gotoDest(ObNode* dest) 382 { 383 curblock.succs.push(dest); 384 return nextNode(); 385 } 386 387 void visitExp(ExpStatement s) 388 { 389 curblock.obtype = ObType.goto_; 390 curblock.exp = s.exp; 391 gotoNextNode(); 392 } 393 394 void visitDtorExp(DtorExpStatement s) 395 { 396 visitExp(s); 397 } 398 399 void visitCompound(CompoundStatement s) 400 { 401 if (s.statements) 402 { 403 foreach (s2; *s.statements) 404 { 405 visit(s2, stmtstate); 406 } 407 } 408 } 409 410 void visitCompoundDeclaration(CompoundDeclarationStatement s) 411 { 412 visitCompound(s); 413 } 414 415 void visitUnrolledLoop(UnrolledLoopStatement s) 416 { 417 StmtState mystate = StmtState(stmtstate, s); 418 mystate.breakBlock = newNode(); 419 420 gotoNextNode(); 421 422 foreach (s2; *s.statements) 423 { 424 if (s2) 425 { 426 mystate.contBlock = newNode(); 427 428 visit(s2, &mystate); 429 430 gotoNextNodeIs(mystate.contBlock); 431 } 432 } 433 434 gotoNextNodeIs(mystate.breakBlock); 435 } 436 437 void visitScope(ScopeStatement s) 438 { 439 if (s.statement) 440 { 441 StmtState mystate = StmtState(stmtstate, s); 442 443 if (mystate.prev.ident) 444 mystate.ident = mystate.prev.ident; 445 446 visit(s.statement, &mystate); 447 448 if (mystate.breakBlock) 449 gotoNextNodeIs(mystate.breakBlock); 450 } 451 } 452 453 void visitDo(DoStatement s) 454 { 455 StmtState mystate = StmtState(stmtstate, s); 456 mystate.breakBlock = newNode(); 457 mystate.contBlock = newNode(); 458 459 auto bpre = curblock; 460 461 auto ob = newNode(); 462 obnodes.push(ob); 463 curblock.succs.push(ob); 464 curblock = ob; 465 bpre.succs.push(curblock); 466 467 mystate.contBlock.succs.push(curblock); 468 mystate.contBlock.succs.push(mystate.breakBlock); 469 470 visit(s._body, &mystate); 471 472 gotoNextNodeIs(mystate.contBlock); 473 mystate.contBlock.exp = s.condition; 474 nextNodeIs(mystate.breakBlock); 475 } 476 477 void visitFor(ForStatement s) 478 { 479 //printf("visit(ForStatement)) %u..%u\n", s.loc.linnum, s.endloc.linnum); 480 StmtState mystate = StmtState(stmtstate, s); 481 mystate.breakBlock = newNode(); 482 mystate.contBlock = newNode(); 483 484 visit(s._init, &mystate); 485 486 auto bcond = gotoNextNode(); 487 mystate.contBlock.succs.push(bcond); 488 489 if (s.condition) 490 { 491 bcond.exp = s.condition; 492 auto ob = newNode(); 493 obnodes.push(ob); 494 bcond.succs.push(ob); 495 bcond.succs.push(mystate.breakBlock); 496 curblock = ob; 497 } 498 else 499 { /* No conditional, it's a straight goto 500 */ 501 bcond.exp = s.condition; 502 bcond.succs.push(nextNode()); 503 } 504 505 visit(s._body, &mystate); 506 /* End of the body goes to the continue block 507 */ 508 curblock.succs.push(mystate.contBlock); 509 nextNodeIs(mystate.contBlock); 510 511 if (s.increment) 512 curblock.exp = s.increment; 513 514 /* The 'break' block follows the for statement. 515 */ 516 nextNodeIs(mystate.breakBlock); 517 } 518 519 void visitIf(IfStatement s) 520 { 521 StmtState mystate = StmtState(stmtstate, s); 522 523 // bexit is the block that gets control after this IfStatement is done 524 auto bexit = mystate.breakBlock ? mystate.breakBlock : newNode(); 525 526 curblock.exp = s.condition; 527 528 auto bcond = curblock; 529 gotoNextNode(); 530 531 visit(s.ifbody, &mystate); 532 curblock.succs.push(bexit); 533 534 if (s.elsebody) 535 { 536 bcond.succs.push(nextNode()); 537 538 visit(s.elsebody, &mystate); 539 540 gotoNextNodeIs(bexit); 541 } 542 else 543 { 544 bcond.succs.push(bexit); 545 nextNodeIs(bexit); 546 } 547 } 548 549 void visitSwitch(SwitchStatement s) 550 { 551 StmtState mystate = StmtState(stmtstate, s); 552 553 mystate.switchBlock = curblock; 554 555 /* Block for where "break" goes to 556 */ 557 mystate.breakBlock = newNode(); 558 559 /* Block for where "default" goes to. 560 * If there is a default statement, then that is where default goes. 561 * If not, then do: 562 * default: break; 563 * by making the default block the same as the break block. 564 */ 565 mystate.defaultBlock = s.sdefault ? newNode() : mystate.breakBlock; 566 567 const numcases = s.cases ? s.cases.dim : 0; 568 569 /* allocate a block for each case 570 */ 571 if (numcases) 572 foreach (cs; *s.cases) 573 { 574 cs.extra = cast(void*)newNode(); 575 } 576 577 curblock.exp = s.condition; 578 579 if (s.hasVars) 580 { /* Generate a sequence of if-then-else blocks for the cases. 581 */ 582 if (numcases) 583 foreach (cs; *s.cases) 584 { 585 auto ecase = newNode(); 586 obnodes.push(ecase); 587 ecase.exp = cs.exp; 588 curblock.succs.push(ecase); 589 590 auto cn = cast(ObNode*)cs.extra; 591 ecase.succs.push(cn); 592 ecase.succs.push(nextNode()); 593 } 594 595 /* The final 'else' clause goes to the default 596 */ 597 curblock.succs.push(mystate.defaultBlock); 598 nextNode(); 599 600 visit(s._body, &mystate); 601 602 /* Have the end of the switch body fall through to the block 603 * following the switch statement. 604 */ 605 gotoNextNodeIs(mystate.breakBlock); 606 return; 607 } 608 609 auto ob = newNode(); 610 obnodes.push(ob); 611 curblock = ob; 612 613 mystate.switchBlock.succs.push(mystate.defaultBlock); 614 615 visit(s._body, &mystate); 616 617 /* Have the end of the switch body fall through to the block 618 * following the switch statement. 619 */ 620 gotoNextNodeIs(mystate.breakBlock); 621 } 622 623 void visitCase(CaseStatement s) 624 { 625 auto cb = cast(ObNode*)s.extra; 626 cb.tryBlock = stmtstate.tryBlock; 627 auto bsw = stmtstate.getSwitchBlock(); 628 bsw.succs.push(cb); 629 gotoNextNodeIs(cb); 630 631 visit(s.statement, stmtstate); 632 } 633 634 void visitDefault(DefaultStatement s) 635 { 636 auto bdefault = stmtstate.getDefaultBlock; 637 bdefault.tryBlock = stmtstate.tryBlock; 638 gotoNextNodeIs(bdefault); 639 visit(s.statement, stmtstate); 640 } 641 642 void visitGotoDefault(GotoDefaultStatement s) 643 { 644 gotoDest(stmtstate.getDefaultBlock); 645 } 646 647 void visitGotoCase(GotoCaseStatement s) 648 { 649 gotoDest(cast(ObNode*)s.cs.extra); 650 } 651 652 void visitSwitchError(SwitchErrorStatement s) 653 { 654 curblock.obtype = ObType.throw_; 655 curblock.exp = s.exp; 656 657 nextNode(); 658 } 659 660 void visitReturn(ReturnStatement s) 661 { 662 //printf("visitReturn() %s\n", s.toChars()); 663 curblock.obtype = s.exp && s.exp.type.toBasetype().ty != Tvoid 664 ? ObType.retexp 665 : ObType.return_; 666 curblock.exp = s.exp; 667 668 nextNode(); 669 } 670 671 void visitBreak(BreakStatement s) 672 { 673 gotoDest(stmtstate.getBreakBlock(s.ident)); 674 } 675 676 void visitContinue(ContinueStatement s) 677 { 678 gotoDest(stmtstate.getContBlock(s.ident)); 679 } 680 681 void visitWith(WithStatement s) 682 { 683 visit(s._body, stmtstate); 684 } 685 686 void visitTryCatch(TryCatchStatement s) 687 { 688 /* tryblock 689 * body 690 * breakBlock 691 * catches 692 * breakBlock2 693 */ 694 695 StmtState mystate = StmtState(stmtstate, s); 696 mystate.breakBlock = newNode(); 697 698 auto tryblock = gotoNextNode(); 699 700 visit(s._body, &mystate); 701 702 gotoNextNodeIs(mystate.breakBlock); 703 704 // create new break block that follows all the catches 705 auto breakBlock2 = newNode(); 706 707 gotoDest(breakBlock2); 708 709 foreach (cs; *s.catches) 710 { 711 /* Each catch block is a successor to tryblock 712 * and the last block of try body 713 */ 714 StmtState catchState = StmtState(stmtstate, s); 715 716 auto bcatch = curblock; 717 tryblock.succs.push(bcatch); 718 mystate.breakBlock.succs.push(bcatch); 719 720 nextNode(); 721 722 visit(cs.handler, &catchState); 723 724 gotoDest(breakBlock2); 725 } 726 727 curblock.succs.push(breakBlock2); 728 obnodes.push(breakBlock2); 729 curblock = breakBlock2; 730 } 731 732 void visitTryFinally(TryFinallyStatement s) 733 { 734 /* Build this: 735 * 1 goto [2] 736 * 2 try_ [3] [5] [7] 737 * 3 body 738 * 4 goto [8] 739 * 5 finally_ [6] 740 * 6 finalbody 741 * 7 fend [8] 742 * 8 lastblock 743 */ 744 745 StmtState bodyState = StmtState(stmtstate, s); 746 747 auto b2 = gotoNextNode(); 748 b2.obtype = ObType.try_; 749 bodyState.tryBlock = b2; 750 751 gotoNextNode(); 752 753 visit(s._body, &bodyState); 754 755 auto b4 = gotoNextNode(); 756 757 auto b5 = newNode(); 758 b5.obtype = ObType.finally_; 759 nextNodeIs(b5); 760 gotoNextNode(); 761 762 StmtState finallyState = StmtState(stmtstate, s); 763 visit(s.finalbody, &finallyState); 764 765 auto b7 = gotoNextNode(); 766 b7.obtype = ObType.fend; 767 768 auto b8 = gotoNextNode(); 769 770 b2.succs.push(b5); 771 b2.succs.push(b7); 772 773 b4.succs.push(b8); 774 } 775 776 void visitThrow(ThrowStatement s) 777 { 778 curblock.obtype = ObType.throw_; 779 curblock.exp = s.exp; 780 nextNode(); 781 } 782 783 void visitGoto(GotoStatement s) 784 { 785 gotoDest(cast(ObNode*)s.label.statement.extra); 786 } 787 788 void visitLabel(LabelStatement s) 789 { 790 StmtState mystate = StmtState(stmtstate, s); 791 mystate.ident = s.ident; 792 793 auto ob = cast(ObNode*)s.extra; 794 ob.tryBlock = mystate.tryBlock; 795 visit(s.statement, &mystate); 796 } 797 798 final switch (s.stmt) 799 { 800 case STMT.Exp: visitExp(s.isExpStatement()); break; 801 case STMT.DtorExp: visitDtorExp(s.isDtorExpStatement()); break; 802 case STMT.Compound: visitCompound(s.isCompoundStatement()); break; 803 case STMT.CompoundDeclaration: visitCompoundDeclaration(s.isCompoundDeclarationStatement()); break; 804 case STMT.UnrolledLoop: visitUnrolledLoop(s.isUnrolledLoopStatement()); break; 805 case STMT.Scope: visitScope(s.isScopeStatement()); break; 806 case STMT.Do: visitDo(s.isDoStatement()); break; 807 case STMT.For: visitFor(s.isForStatement()); break; 808 case STMT.If: visitIf(s.isIfStatement()); break; 809 case STMT.Switch: visitSwitch(s.isSwitchStatement()); break; 810 case STMT.Case: visitCase(s.isCaseStatement()); break; 811 case STMT.Default: visitDefault(s.isDefaultStatement()); break; 812 case STMT.GotoDefault: visitGotoDefault(s.isGotoDefaultStatement()); break; 813 case STMT.GotoCase: visitGotoCase(s.isGotoCaseStatement()); break; 814 case STMT.SwitchError: visitSwitchError(s.isSwitchErrorStatement()); break; 815 case STMT.Return: visitReturn(s.isReturnStatement()); break; 816 case STMT.Break: visitBreak(s.isBreakStatement()); break; 817 case STMT.Continue: visitContinue(s.isContinueStatement()); break; 818 case STMT.With: visitWith(s.isWithStatement()); break; 819 case STMT.TryCatch: visitTryCatch(s.isTryCatchStatement()); break; 820 case STMT.TryFinally: visitTryFinally(s.isTryFinallyStatement()); break; 821 case STMT.Throw: visitThrow(s.isThrowStatement()); break; 822 case STMT.Goto: visitGoto(s.isGotoStatement()); break; 823 case STMT.Label: visitLabel(s.isLabelStatement()); break; 824 825 case STMT.CompoundAsm: 826 case STMT.Asm: 827 case STMT.InlineAsm: 828 case STMT.GccAsm: 829 830 case STMT.Pragma: 831 case STMT.Import: 832 case STMT.ScopeGuard: 833 case STMT.Error: 834 break; // ignore these 835 836 case STMT.Foreach: 837 case STMT.ForeachRange: 838 case STMT.Debug: 839 case STMT.CaseRange: 840 case STMT.StaticForeach: 841 case STMT.StaticAssert: 842 case STMT.Conditional: 843 case STMT.While: 844 case STMT.Forwarding: 845 case STMT.Compile: 846 case STMT.Peel: 847 case STMT.Synchronized: 848 debug printf("s: %s\n", s.toChars()); 849 assert(0); // should have been rewritten 850 } 851 } 852 853 StmtState stmtstate; 854 visit(s, &stmtstate); 855 curblock.obtype = ObType.return_; 856 857 static if (0) 858 { 859 printf("toObNodes()\n"); 860 printf("------- before ----------\n"); 861 numberNodes(obnodes); 862 foreach (ob; obnodes) ob.print(); 863 printf("-------------------------\n"); 864 } 865 866 assert(stmtstate.breakBlock is null); 867 assert(stmtstate.contBlock is null); 868 assert(stmtstate.switchBlock is null); 869 assert(stmtstate.defaultBlock is null); 870 assert(stmtstate.tryBlock is null); 871 } 872 873 /*************************************************** 874 * Insert finally block calls when doing a goto from 875 * inside a try block to outside. 876 * Done after blocks are generated because then we know all 877 * the edges of the graph, but before the pred's are computed. 878 * Params: 879 * obnodes = graph of the function 880 */ 881 882 void insertFinallyBlockCalls(ref ObNodes obnodes) 883 { 884 ObNode* bcret = null; 885 ObNode* bcretexp = null; 886 887 enum log = false; 888 889 static if (log) 890 { 891 printf("insertFinallyBlockCalls()\n"); 892 printf("------- before ----------\n"); 893 numberNodes(obnodes); 894 foreach (ob; obnodes) ob.print(); 895 printf("-------------------------\n"); 896 } 897 898 foreach (ob; obnodes) 899 { 900 if (!ob.tryBlock) 901 continue; 902 903 switch (ob.obtype) 904 { 905 case ObType.return_: 906 // Rewrite into a ObType.goto_ => ObType.return_ 907 if (!bcret) 908 { 909 bcret = new ObNode(); 910 bcret.obtype = ob.obtype; 911 } 912 ob.obtype = ObType.goto_; 913 ob.succs.push(bcret); 914 goto case_goto; 915 916 case ObType.retexp: 917 // Rewrite into a ObType.goto_ => ObType.retexp 918 if (!bcretexp) 919 { 920 bcretexp = new ObNode(); 921 bcretexp.obtype = ob.obtype; 922 } 923 ob.obtype = ObType.goto_; 924 ob.succs.push(bcretexp); 925 goto case_goto; 926 927 case ObType.goto_: 928 if (ob.succs.length != 1) 929 break; 930 931 case_goto: 932 { 933 auto target = ob.succs[0]; // destination of goto 934 ob.succs.setDim(0); 935 auto lasttry = target.tryBlock; 936 auto blast = ob; 937 for (auto bt = ob.tryBlock; bt != lasttry; bt = bt.tryBlock) 938 { 939 assert(bt.obtype == ObType.try_); 940 auto bf = bt.succs[1]; 941 assert(bf.obtype == ObType.finally_); 942 auto bfend = bt.succs[2]; 943 assert(bfend.obtype == ObType.fend); 944 945 if (!blast.succs.contains(bf.succs[0])) 946 blast.succs.push(bf.succs[0]); 947 948 blast = bfend; 949 } 950 if (!blast.succs.contains(target)) 951 blast.succs.push(target); 952 953 break; 954 } 955 956 default: 957 break; 958 } 959 } 960 if (bcret) 961 obnodes.push(bcret); 962 if (bcretexp) 963 obnodes.push(bcretexp); 964 965 static if (log) 966 { 967 printf("------- after ----------\n"); 968 numberNodes(obnodes); 969 foreach (ob; obnodes) ob.print(); 970 printf("-------------------------\n"); 971 } 972 } 973 974 /*************************************************** 975 * Remove try-finally scaffolding. 976 * Params: 977 * obnodes = nodes for the function 978 */ 979 980 void insertFinallyBlockGotos(ref ObNodes obnodes) 981 { 982 /* Remove all the try_, finally_, lpad and ret nodes. 983 * Actually, just make them into no-ops. 984 */ 985 foreach (ob; obnodes) 986 { 987 ob.tryBlock = null; 988 switch (ob.obtype) 989 { 990 case ObType.try_: 991 ob.obtype = ObType.goto_; 992 ob.succs.remove(2); // remove fend 993 ob.succs.remove(1); // remove finally_ 994 break; 995 996 case ObType.finally_: 997 ob.obtype = ObType.goto_; 998 break; 999 1000 case ObType.fend: 1001 ob.obtype = ObType.goto_; 1002 break; 1003 1004 default: 1005 break; 1006 } 1007 } 1008 } 1009 1010 /********************************* 1011 * Set the `index` field of each ObNode 1012 * to its index in the `obnodes[]` array. 1013 */ 1014 void numberNodes(ref ObNodes obnodes) 1015 { 1016 //printf("numberNodes()\n"); 1017 foreach (i, ob; obnodes) 1018 { 1019 //printf("ob = %d, %p\n", i, ob); 1020 ob.index = cast(uint)i; 1021 } 1022 1023 // Verify that nodes do not appear more than once in obnodes[] 1024 debug 1025 foreach (i, ob; obnodes) 1026 { 1027 assert(ob.index == cast(uint)i); 1028 } 1029 } 1030 1031 1032 /********************************* 1033 * Remove unreachable nodes and compress 1034 * them out of obnodes[]. 1035 * Params: 1036 * obnodes = array of nodes 1037 */ 1038 void removeUnreachable(ref ObNodes obnodes) 1039 { 1040 if (!obnodes.length) 1041 return; 1042 1043 /* Mark all nodes as unreachable, 1044 * temporarilly reusing ObNode.index 1045 */ 1046 foreach (ob; obnodes) 1047 ob.index = 0; 1048 1049 /* Recursively mark ob and all its successors as reachable 1050 */ 1051 static void mark(ObNode* ob) 1052 { 1053 ob.index = 1; 1054 foreach (succ; ob.succs) 1055 { 1056 if (!succ.index) 1057 mark(succ); 1058 } 1059 } 1060 1061 mark(obnodes[0]); // first node is entry point 1062 1063 /* Remove unreachable nodes by shifting the remainder left 1064 */ 1065 size_t j = 1; 1066 foreach (i; 1 .. obnodes.length) 1067 { 1068 if (obnodes[i].index) 1069 { 1070 if (i != j) 1071 obnodes[j] = obnodes[i]; 1072 ++j; 1073 } 1074 else 1075 { 1076 obnodes[i].destroy(); 1077 } 1078 } 1079 obnodes.setDim(j); 1080 } 1081 1082 1083 1084 /************************************* 1085 * Compute predecessors. 1086 */ 1087 void computePreds(ref ObNodes obnodes) 1088 { 1089 foreach (ob; obnodes) 1090 { 1091 foreach (succ; ob.succs) 1092 { 1093 succ.preds.push(ob); 1094 } 1095 } 1096 } 1097 1098 /******************************* 1099 * Are we interested in tracking variable `v`? 1100 */ 1101 bool isTrackableVar(VarDeclaration v) 1102 { 1103 //printf("isTrackableVar() %s\n", v.toChars()); 1104 auto tb = v.type.toBasetype(); 1105 1106 /* Assume class references are managed by the GC, 1107 * don't need to track them 1108 */ 1109 if (tb.ty == Tclass) 1110 return false; 1111 1112 /* Assume types with a destructor are doing their own tracking, 1113 * such as being a ref counted type 1114 */ 1115 if (v.needsScopeDtor()) 1116 return false; 1117 1118 /* Not tracking function parameters that are not mutable 1119 */ 1120 if (v.storage_class & STC.parameter && !tb.hasPointersToMutableFields()) 1121 return false; 1122 1123 /* Not tracking global variables 1124 */ 1125 return !v.isDataseg(); 1126 } 1127 1128 /******************************* 1129 * Are we interested in tracking this expression? 1130 * Returns: 1131 * variable if so, null if not 1132 */ 1133 VarDeclaration isTrackableVarExp(Expression e) 1134 { 1135 if (auto ve = e.isVarExp()) 1136 { 1137 if (auto v = ve.var.isVarDeclaration()) 1138 if (isTrackableVar(v)) 1139 return v; 1140 } 1141 return null; 1142 } 1143 1144 1145 /************** 1146 * Find the pointer variable declarations in this function, 1147 * and fill `vars` with them. 1148 * Params: 1149 * funcdecl = function we are in 1150 * vars = array to fill in 1151 */ 1152 void collectVars(FuncDeclaration funcdecl, out VarDeclarations vars) 1153 { 1154 enum log = false; 1155 if (log) 1156 printf("----------------collectVars()---------------\n"); 1157 1158 if (funcdecl.parameters) 1159 foreach (v; (*funcdecl.parameters)[]) 1160 { 1161 if (isTrackableVar(v)) 1162 vars.push(v); 1163 } 1164 1165 void dgVar(VarDeclaration v) 1166 { 1167 if (isTrackableVar(v)) 1168 vars.push(v); 1169 } 1170 1171 void dgExp(Expression e) 1172 { 1173 foreachVar(e, &dgVar); 1174 } 1175 1176 foreachExpAndVar(funcdecl.fbody, &dgExp, &dgVar); 1177 1178 static if (log) 1179 { 1180 foreach (i, v; vars[]) 1181 { 1182 printf("vars[%d] = %s\n", cast(int)i, v.toChars()); 1183 } 1184 } 1185 } 1186 1187 /*********************************** 1188 * Allocate BitArrays in PtrVarState. 1189 * Can be allocated much more efficiently by subdividing a single 1190 * large array of bits 1191 */ 1192 void allocDeps(PtrVarState[] pvss) 1193 { 1194 //printf("allocDeps()\n"); 1195 foreach (ref pvs; pvss) 1196 { 1197 pvs.deps.length = pvss.length; 1198 } 1199 } 1200 1201 1202 /************************************** 1203 * Allocate state variables foreach node. 1204 */ 1205 void allocStates(ref ObState obstate) 1206 { 1207 //printf("---------------allocStates()------------------\n"); 1208 const vlen = obstate.vars.length; 1209 PtrVarState* p = cast(PtrVarState*) mem.xcalloc(obstate.nodes.length * 3 * vlen, PtrVarState.sizeof); 1210 obstate.varPool = p[0 .. obstate.nodes.length * 3 * vlen]; 1211 foreach (i, ob; obstate.nodes) 1212 { 1213 //printf(" [%d]\n", cast(int)i); 1214 // ob.kill.length = obstate.vars.length; 1215 // ob.comb.length = obstate.vars.length; 1216 ob.gen = p[0 .. vlen]; p += vlen; 1217 ob.input = p[0 .. vlen]; p += vlen; 1218 ob.output = p[0 .. vlen]; p += vlen; 1219 1220 allocDeps(ob.gen); 1221 allocDeps(ob.input); 1222 allocDeps(ob.output); 1223 } 1224 } 1225 1226 /****************************** 1227 * Does v meet the definiton of a `Borrowed` pointer? 1228 * Returns: 1229 * true if it does 1230 */ 1231 bool isBorrowedPtr(VarDeclaration v) 1232 { 1233 return v.isScope() && !v.isowner && v.type.nextOf().isMutable(); 1234 } 1235 1236 /****************************** 1237 * Does v meet the definiton of a `Readonly` pointer? 1238 * Returns: 1239 * true if it does 1240 */ 1241 bool isReadonlyPtr(VarDeclaration v) 1242 { 1243 return v.isScope() && !v.type.nextOf().isMutable(); 1244 } 1245 1246 /*************************************** 1247 * Compute the gen vector for ob. 1248 */ 1249 void genKill(ref ObState obstate, ObNode* ob) 1250 { 1251 enum log = false; 1252 if (log) 1253 printf("-----------computeGenKill()-----------\n"); 1254 1255 /*************** 1256 * Assigning result of expression `e` to variable `v`. 1257 */ 1258 void dgWriteVar(ObNode* ob, VarDeclaration v, Expression e, bool initializer) 1259 { 1260 if (log) 1261 printf("dgWriteVar() %s := %s %d\n", v.toChars(), e.toChars(), initializer); 1262 const vi = obstate.vars.find(v); 1263 assert(vi != size_t.max); 1264 PtrVarState* pvs = &ob.gen[vi]; 1265 readVar(ob, vi, true, ob.gen); 1266 if (e) 1267 { 1268 if (isBorrowedPtr(v)) 1269 pvs.state = PtrState.Borrowed; 1270 else if (isReadonlyPtr(v)) 1271 pvs.state = PtrState.Readonly; 1272 else 1273 pvs.state = PtrState.Owner; 1274 pvs.deps.zero(); 1275 1276 EscapeByResults er; 1277 escapeByValue(e, &er, true); 1278 bool any = false; // if any variables are assigned to v 1279 1280 void by(VarDeclaration r) 1281 { 1282 const ri = obstate.vars.find(r); 1283 if (ri != size_t.max && ri != vi) 1284 { 1285 pvs.deps[ri] = true; // v took from r 1286 auto pvsr = &ob.gen[ri]; 1287 any = true; 1288 1289 if (isBorrowedPtr(v)) 1290 { 1291 // v is borrowing from r 1292 pvs.state = PtrState.Borrowed; 1293 } 1294 else if (isReadonlyPtr(v)) 1295 { 1296 pvs.state = PtrState.Readonly; 1297 } 1298 else 1299 { 1300 // move r to v, which "consumes" r 1301 pvsr.state = PtrState.Undefined; 1302 pvsr.deps.zero(); 1303 } 1304 } 1305 } 1306 1307 foreach (VarDeclaration v2; er.byvalue) 1308 by(v2); 1309 foreach (VarDeclaration v2; er.byref) 1310 by(v2); 1311 1312 /* Make v an Owner for initializations like: 1313 * scope v = malloc(); 1314 */ 1315 if (initializer && !any && isBorrowedPtr(v)) 1316 { 1317 v.isowner = true; 1318 pvs.state = PtrState.Owner; 1319 } 1320 } 1321 else 1322 { 1323 if (isBorrowedPtr(v)) 1324 pvs.state = PtrState.Borrowed; 1325 else if (isReadonlyPtr(v)) 1326 pvs.state = PtrState.Readonly; 1327 else 1328 pvs.state = PtrState.Owner; 1329 pvs.deps.zero(); 1330 } 1331 } 1332 1333 void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) 1334 { 1335 if (log) 1336 printf("dgReadVar() %s %d\n", v.toChars(), mutable); 1337 const vi = obstate.vars.find(v); 1338 assert(vi != size_t.max); 1339 readVar(ob, vi, mutable, ob.gen); 1340 } 1341 1342 void foreachExp(ObNode* ob, Expression e) 1343 { 1344 extern (C++) final class ExpWalker : Visitor 1345 { 1346 alias visit = typeof(super).visit; 1347 extern (D) void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar; 1348 extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar; 1349 ObNode* ob; 1350 ObState* obstate; 1351 1352 extern (D) this(void delegate(ObNode*, VarDeclaration, Expression, bool) dgWriteVar, 1353 void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable) dgReadVar, 1354 ObNode* ob, ref ObState obstate) 1355 { 1356 this.dgWriteVar = dgWriteVar; 1357 this.dgReadVar = dgReadVar; 1358 this.ob = ob; 1359 this.obstate = &obstate; 1360 } 1361 1362 override void visit(Expression e) 1363 { 1364 //printf("[%s] %s: %s\n", e.loc.toChars(), Token.toChars(e.op), e.toChars()); 1365 //assert(0); 1366 } 1367 1368 void visitAssign(AssignExp ae, bool initializer) 1369 { 1370 ae.e2.accept(this); 1371 if (auto ve = ae.e1.isVarExp()) 1372 { 1373 if (auto v = ve.var.isVarDeclaration()) 1374 if (isTrackableVar(v)) 1375 dgWriteVar(ob, v, ae.e2, initializer); 1376 } 1377 else 1378 ae.e1.accept(this); 1379 } 1380 1381 override void visit(AssignExp ae) 1382 { 1383 visitAssign(ae, false); 1384 } 1385 1386 override void visit(DeclarationExp e) 1387 { 1388 void Dsymbol_visit(Dsymbol s) 1389 { 1390 if (auto vd = s.isVarDeclaration()) 1391 { 1392 s = s.toAlias(); 1393 if (s != vd) 1394 return Dsymbol_visit(s); 1395 if (!isTrackableVar(vd)) 1396 return; 1397 1398 if (!(vd._init && vd._init.isVoidInitializer())) 1399 { 1400 auto ei = vd._init ? vd._init.isExpInitializer() : null; 1401 if (ei) 1402 visitAssign(cast(AssignExp)ei.exp, true); 1403 else 1404 dgWriteVar(ob, vd, null, false); 1405 } 1406 } 1407 else if (auto td = s.isTupleDeclaration()) 1408 { 1409 foreach (o; *td.objects) 1410 { 1411 if (auto eo = o.isExpression()) 1412 { 1413 if (auto se = eo.isDsymbolExp()) 1414 { 1415 Dsymbol_visit(se.s); 1416 } 1417 } 1418 } 1419 } 1420 } 1421 1422 Dsymbol_visit(e.declaration); 1423 } 1424 1425 override void visit(VarExp ve) 1426 { 1427 //printf("VarExp: %s\n", ve.toChars()); 1428 if (auto v = ve.var.isVarDeclaration()) 1429 if (isTrackableVar(v)) 1430 { 1431 dgReadVar(ve.loc, ob, v, isMutableRef(ve.type)); 1432 } 1433 } 1434 1435 override void visit(CallExp ce) 1436 { 1437 //printf("CallExp() %s\n", ce.toChars()); 1438 ce.e1.accept(this); 1439 auto t = ce.e1.type.toBasetype(); 1440 auto tf = t.isTypeFunction(); 1441 if (!tf) 1442 { 1443 assert(t.ty == Tdelegate); 1444 tf = t.nextOf().isTypeFunction(); 1445 assert(tf); 1446 } 1447 1448 // j=1 if _arguments[] is first argument 1449 const int j = tf.isDstyleVariadic(); 1450 bool hasOut; 1451 const varStackSave = obstate.varStack.length; 1452 1453 foreach (const i, arg; (*ce.arguments)[]) 1454 { 1455 if (i - j < tf.parameterList.length && 1456 i >= j) 1457 { 1458 Parameter p = tf.parameterList[i - j]; 1459 auto pt = p.type.toBasetype(); 1460 1461 EscapeByResults er; 1462 escapeByValue(arg, &er, true); 1463 1464 if (!(p.storageClass & STC.out_ && arg.isVarExp())) 1465 arg.accept(this); 1466 1467 void by(VarDeclaration v) 1468 { 1469 if (!isTrackableVar(v)) 1470 return; 1471 1472 const vi = obstate.vars.find(v); 1473 if (vi == size_t.max) 1474 return; 1475 1476 if (p.storageClass & STC.out_) 1477 { 1478 /// initialize 1479 hasOut = true; 1480 makeUndefined(vi, ob.gen); 1481 } 1482 else if (p.storageClass & STC.scope_) 1483 { 1484 // borrow 1485 obstate.varStack.push(vi); 1486 obstate.mutableStack.push(isMutableRef(pt)); 1487 } 1488 else 1489 { 1490 // move (i.e. consume arg) 1491 makeUndefined(vi, ob.gen); 1492 } 1493 } 1494 1495 foreach (VarDeclaration v2; er.byvalue) 1496 by(v2); 1497 foreach (VarDeclaration v2; er.byref) 1498 by(v2); 1499 } 1500 else // variadic args 1501 { 1502 arg.accept(this); 1503 1504 EscapeByResults er; 1505 escapeByValue(arg, &er, true); 1506 1507 void byv(VarDeclaration v) 1508 { 1509 if (!isTrackableVar(v)) 1510 return; 1511 1512 const vi = obstate.vars.find(v); 1513 if (vi == size_t.max) 1514 return; 1515 1516 obstate.varStack.push(vi); 1517 obstate.mutableStack.push(isMutableRef(arg.type)); 1518 1519 // move (i.e. consume arg) 1520 makeUndefined(vi, ob.gen); 1521 } 1522 1523 foreach (VarDeclaration v2; er.byvalue) 1524 byv(v2); 1525 foreach (VarDeclaration v2; er.byref) 1526 byv(v2); 1527 } 1528 } 1529 1530 /* Do a dummy 'read' of each variable passed to the function, 1531 * to detect O/B errors 1532 */ 1533 assert(obstate.varStack.length == obstate.mutableStack.length); 1534 foreach (i; varStackSave .. obstate.varStack.length) 1535 { 1536 const vi = obstate.varStack[i]; 1537 // auto pvs = &ob.gen[vi]; 1538 auto v = obstate.vars[vi]; 1539 //if (pvs.state == PtrState.Undefined) 1540 //v.error(ce.loc, "is Undefined, cannot pass to function"); 1541 1542 dgReadVar(ce.loc, ob, v, obstate.mutableStack[i]); 1543 } 1544 1545 /* Pop off stack all variables for this function call 1546 */ 1547 obstate.varStack.setDim(varStackSave); 1548 obstate.mutableStack.setDim(varStackSave); 1549 1550 if (hasOut) 1551 // Initialization of out's only happens after the function call 1552 foreach (const i, arg; (*ce.arguments)[]) 1553 { 1554 if (i - j < tf.parameterList.length && 1555 i >= j) 1556 { 1557 Parameter p = tf.parameterList[i - j]; 1558 if (p.storageClass & STC.out_) 1559 { 1560 if (auto v = isTrackableVarExp(arg)) 1561 dgWriteVar(ob, v, null, true); 1562 } 1563 } 1564 } 1565 } 1566 1567 override void visit(SymOffExp e) 1568 { 1569 if (auto v = e.var.isVarDeclaration()) 1570 if (isTrackableVar(v)) 1571 { 1572 dgReadVar(e.loc, ob, v, isMutableRef(e.type)); 1573 } 1574 } 1575 1576 override void visit(LogicalExp e) 1577 { 1578 e.e1.accept(this); 1579 1580 const vlen = obstate.vars.length; 1581 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof); 1582 PtrVarState[] gen1 = p[0 .. vlen]; 1583 foreach (i, ref pvs; gen1) 1584 { 1585 pvs = ob.gen[i]; 1586 } 1587 1588 e.e2.accept(this); 1589 1590 // Merge gen1 into ob.gen 1591 foreach (i; 0 .. vlen) 1592 { 1593 ob.gen[i].combine(gen1[i], i, ob.gen); 1594 } 1595 1596 mem.xfree(p); // should free .deps too 1597 } 1598 1599 override void visit(CondExp e) 1600 { 1601 e.econd.accept(this); 1602 1603 const vlen = obstate.vars.length; 1604 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof); 1605 PtrVarState[] gen1 = p[0 .. vlen]; 1606 foreach (i, ref pvs; gen1) 1607 { 1608 pvs = ob.gen[i]; 1609 } 1610 1611 e.e1.accept(this); 1612 1613 // Swap gen1 with ob.gen 1614 foreach (i; 0 .. vlen) 1615 { 1616 gen1[i].deps.swap(ob.gen[i].deps); 1617 const state = gen1[i].state; 1618 gen1[i].state = ob.gen[i].state; 1619 ob.gen[i].state = state; 1620 } 1621 1622 e.e2.accept(this); 1623 1624 /* xxx1 is the state from expression e1, ob.xxx is the state from e2. 1625 * Merge xxx1 into ob.xxx to get the state from `e`. 1626 */ 1627 foreach (i; 0 .. vlen) 1628 { 1629 ob.gen[i].combine(gen1[i], i, ob.gen); 1630 } 1631 1632 mem.xfree(p); // should free .deps too 1633 } 1634 1635 override void visit(AddrExp e) 1636 { 1637 /* Taking the address of struct literal is normally not 1638 * allowed, but CTFE can generate one out of a new expression, 1639 * but it'll be placed in static data so no need to check it. 1640 */ 1641 if (e.e1.op != TOK.structLiteral) 1642 e.e1.accept(this); 1643 } 1644 1645 override void visit(UnaExp e) 1646 { 1647 e.e1.accept(this); 1648 } 1649 1650 override void visit(BinExp e) 1651 { 1652 e.e1.accept(this); 1653 e.e2.accept(this); 1654 } 1655 1656 override void visit(ArrayLiteralExp e) 1657 { 1658 Type tb = e.type.toBasetype(); 1659 if (tb.ty == Tsarray || tb.ty == Tarray) 1660 { 1661 if (e.basis) 1662 e.basis.accept(this); 1663 foreach (el; *e.elements) 1664 { 1665 if (el) 1666 el.accept(this); 1667 } 1668 } 1669 } 1670 1671 override void visit(StructLiteralExp e) 1672 { 1673 if (e.elements) 1674 { 1675 foreach (ex; *e.elements) 1676 { 1677 if (ex) 1678 ex.accept(this); 1679 } 1680 } 1681 } 1682 1683 override void visit(AssocArrayLiteralExp e) 1684 { 1685 if (e.keys) 1686 { 1687 foreach (i, key; *e.keys) 1688 { 1689 if (key) 1690 key.accept(this); 1691 if (auto value = (*e.values)[i]) 1692 value.accept(this); 1693 } 1694 } 1695 } 1696 1697 override void visit(NewExp e) 1698 { 1699 if (e.arguments) 1700 { 1701 foreach (ex; *e.arguments) 1702 { 1703 if (ex) 1704 ex.accept(this); 1705 } 1706 } 1707 } 1708 1709 override void visit(SliceExp e) 1710 { 1711 e.e1.accept(this); 1712 if (e.lwr) 1713 e.lwr.accept(this); 1714 if (e.upr) 1715 e.upr.accept(this); 1716 } 1717 } 1718 1719 if (e) 1720 { 1721 scope ExpWalker ew = new ExpWalker(&dgWriteVar, &dgReadVar, ob, obstate); 1722 e.accept(ew); 1723 } 1724 } 1725 1726 foreachExp(ob, ob.exp); 1727 } 1728 1729 /*************************************** 1730 * Determine the state of a variable based on 1731 * its type and storage class. 1732 */ 1733 PtrState toPtrState(VarDeclaration v) 1734 { 1735 /* pointer to mutable: Owner 1736 * pointer to mutable, scope: Borrowed 1737 * pointer to const: Owner 1738 * pointer to const, scope: Readonly 1739 * ref: Borrowed 1740 * const ref: Readonly 1741 */ 1742 1743 auto t = v.type; 1744 if (v.isRef()) 1745 { 1746 return t.hasMutableFields() ? PtrState.Borrowed : PtrState.Readonly; 1747 } 1748 if (v.isScope()) 1749 { 1750 return t.hasPointersToMutableFields() ? PtrState.Borrowed : PtrState.Readonly; 1751 } 1752 else 1753 return PtrState.Owner; 1754 } 1755 1756 /********************************** 1757 * Does type `t` contain any pointers to mutable? 1758 */ 1759 bool hasPointersToMutableFields(Type t) 1760 { 1761 auto tb = t.toBasetype(); 1762 if (!tb.isMutable()) 1763 return false; 1764 if (auto tsa = tb.isTypeSArray()) 1765 { 1766 return tsa.nextOf().hasPointersToMutableFields(); 1767 } 1768 if (auto ts = tb.isTypeStruct()) 1769 { 1770 foreach (v; ts.sym.fields) 1771 { 1772 if (v.isRef()) 1773 { 1774 if (v.type.hasMutableFields()) 1775 return true; 1776 } 1777 else if (v.type.hasPointersToMutableFields()) 1778 return true; 1779 } 1780 return false; 1781 } 1782 auto tbn = tb.nextOf(); 1783 return tbn && tbn.hasMutableFields(); 1784 } 1785 1786 /******************************** 1787 * Does type `t` have any mutable fields? 1788 */ 1789 bool hasMutableFields(Type t) 1790 { 1791 auto tb = t.toBasetype(); 1792 if (!tb.isMutable()) 1793 return false; 1794 if (auto tsa = tb.isTypeSArray()) 1795 { 1796 return tsa.nextOf().hasMutableFields(); 1797 } 1798 if (auto ts = tb.isTypeStruct()) 1799 { 1800 foreach (v; ts.sym.fields) 1801 { 1802 if (v.type.hasMutableFields()) 1803 return true; 1804 } 1805 return false; 1806 } 1807 return true; 1808 } 1809 1810 1811 1812 /*************************************** 1813 * Do the data flow analysis (i.e. compute the input[] 1814 * and output[] vectors for each ObNode). 1815 */ 1816 void doDataFlowAnalysis(ref ObState obstate) 1817 { 1818 enum log = false; 1819 if (log) 1820 { 1821 printf("-----------------doDataFlowAnalysis()-------------------------\n"); 1822 foreach (ob; obstate.nodes) ob.print(); 1823 printf("------------------------------------------\n"); 1824 } 1825 1826 if (!obstate.nodes.length) 1827 return; 1828 1829 auto startnode = obstate.nodes[0]; 1830 assert(startnode.preds.length == 0); 1831 1832 /* Set opening state `input[]` for first node 1833 */ 1834 foreach (i, ref ps; startnode.input) 1835 { 1836 auto v = obstate.vars[i]; 1837 auto state = toPtrState(v); 1838 if (v.isParameter()) 1839 { 1840 if (v.isOut()) 1841 state = PtrState.Undefined; 1842 else 1843 state = PtrState.Owner; 1844 } 1845 else 1846 state = PtrState.Undefined; 1847 ps.state = state; 1848 ps.deps.zero(); 1849 startnode.gen[i] = ps; 1850 } 1851 1852 /* Set all output[]s to Initial 1853 */ 1854 foreach (ob; obstate.nodes[]) 1855 { 1856 foreach (ref ps; ob.output) 1857 { 1858 ps.state = PtrState.Initial; 1859 ps.deps.zero(); 1860 } 1861 } 1862 1863 const vlen = obstate.vars.length; 1864 PtrVarState pvs; 1865 pvs.deps.length = vlen; 1866 int counter = 0; 1867 bool changes; 1868 do 1869 { 1870 changes = false; 1871 assert(++counter <= 1000); // should converge, but don't hang if it doesn't 1872 foreach (ob; obstate.nodes[]) 1873 { 1874 /* Construct ob.gen[] by combining the .output[]s of each ob.preds[] 1875 * and set ob.input[] to the same state 1876 */ 1877 if (ob != startnode) 1878 { 1879 assert(ob.preds.length); 1880 1881 foreach (i; 0 .. vlen) 1882 { 1883 ob.gen[i] = ob.preds[0].output[i]; 1884 } 1885 1886 foreach (j; 1 .. ob.preds.length) 1887 { 1888 foreach (i; 0 .. vlen) 1889 { 1890 ob.gen[i].combine(ob.preds[j].output[i], i, ob.gen); 1891 } 1892 } 1893 1894 /* Set ob.input[] to ob.gen[], 1895 * if any changes were made we'll have to do another iteration 1896 */ 1897 foreach (i; 0 .. vlen) 1898 { 1899 if (ob.gen[i] != ob.input[i]) 1900 { 1901 ob.input[i] = ob.gen[i]; 1902 changes = true; 1903 } 1904 } 1905 } 1906 1907 /* Compute gen[] for node ob 1908 */ 1909 genKill(obstate, ob); 1910 1911 foreach (i; 0 .. vlen) 1912 { 1913 if (ob.gen[i] != ob.output[i]) 1914 { 1915 ob.output[i] = ob.gen[i]; 1916 changes = true; 1917 } 1918 } 1919 } 1920 } while (changes); 1921 1922 static if (log) 1923 { 1924 foreach (obi, ob; obstate.nodes) 1925 { 1926 printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr); 1927 printf(" input:\n"); 1928 foreach (i, ref pvs2; ob.input[]) 1929 { 1930 printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]); 1931 } 1932 1933 printf(" gen:\n"); 1934 foreach (i, ref pvs2; ob.gen[]) 1935 { 1936 printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]); 1937 } 1938 printf(" output:\n"); 1939 foreach (i, ref pvs2; ob.output[]) 1940 { 1941 printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]); 1942 } 1943 } 1944 printf("\n"); 1945 } 1946 } 1947 1948 1949 /*************************************** 1950 * Check for Ownership/Borrowing errors. 1951 */ 1952 void checkObErrors(ref ObState obstate) 1953 { 1954 enum log = false; 1955 if (log) 1956 printf("------------checkObErrors()----------\n"); 1957 1958 void dgWriteVar(ObNode* ob, PtrVarState[] cpvs, VarDeclaration v, Expression e) 1959 { 1960 if (log) printf("dgWriteVar(v:%s, e:%s)\n", v.toChars(), e ? e.toChars() : "null"); 1961 const vi = obstate.vars.find(v); 1962 assert(vi != size_t.max); 1963 PtrVarState* pvs = &cpvs[vi]; 1964 readVar(ob, vi, true, cpvs); 1965 if (e) 1966 { 1967 if (isBorrowedPtr(v)) 1968 pvs.state = PtrState.Borrowed; 1969 else if (isReadonlyPtr(v)) 1970 pvs.state = PtrState.Readonly; 1971 else 1972 pvs.state = PtrState.Owner; 1973 pvs.deps.zero(); 1974 1975 EscapeByResults er; 1976 escapeByValue(e, &er, true); 1977 1978 void by(VarDeclaration r) // `v` = `r` 1979 { 1980 //printf(" by(%s)\n", r.toChars()); 1981 const ri = obstate.vars.find(r); 1982 if (ri == size_t.max) 1983 return; 1984 1985 with (PtrState) 1986 { 1987 pvs.deps[ri] = true; // v took from r 1988 auto pvsr = &cpvs[ri]; 1989 1990 if (pvsr.state == Undefined) 1991 { 1992 v.error(e.loc, "is reading from `%s` which is Undefined", r.toChars()); 1993 } 1994 else if (isBorrowedPtr(v)) // v is going to borrow from r 1995 { 1996 if (pvsr.state == Readonly) 1997 v.error(e.loc, "is borrowing from `%s` which is Readonly", r.toChars()); 1998 1999 pvs.state = Borrowed; 2000 } 2001 else if (isReadonlyPtr(v)) 2002 { 2003 pvs.state = Readonly; 2004 } 2005 else 2006 { 2007 // move from r to v 2008 pvsr.state = Undefined; 2009 pvsr.deps.zero(); 2010 } 2011 } 2012 } 2013 2014 foreach (VarDeclaration v2; er.byvalue) 2015 by(v2); 2016 foreach (VarDeclaration v2; er.byref) 2017 by(v2); 2018 } 2019 else 2020 { 2021 if (isBorrowedPtr(v)) 2022 pvs.state = PtrState.Borrowed; 2023 else if (isReadonlyPtr(v)) 2024 pvs.state = PtrState.Readonly; 2025 else 2026 pvs.state = PtrState.Owner; 2027 pvs.deps.zero(); 2028 } 2029 } 2030 2031 void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen) 2032 { 2033 if (log) printf("dgReadVar() %s\n", v.toChars()); 2034 const vi = obstate.vars.find(v); 2035 assert(vi != size_t.max); 2036 auto pvs = &gen[vi]; 2037 if (pvs.state == PtrState.Undefined) 2038 v.error(loc, "has undefined state and cannot be read"); 2039 2040 readVar(ob, vi, mutable, gen); 2041 } 2042 2043 void foreachExp(ObNode* ob, Expression e, PtrVarState[] cpvs) 2044 { 2045 extern (C++) final class ExpWalker : Visitor 2046 { 2047 alias visit = typeof(super).visit; 2048 extern (D) void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar; 2049 extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar; 2050 PtrVarState[] cpvs; 2051 ObNode* ob; 2052 ObState* obstate; 2053 2054 extern (D) this(void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar, 2055 void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar, 2056 PtrVarState[] cpvs, ObNode* ob, ref ObState obstate) 2057 { 2058 this.dgReadVar = dgReadVar; 2059 this.dgWriteVar = dgWriteVar; 2060 this.cpvs = cpvs; 2061 this.ob = ob; 2062 this.obstate = &obstate; 2063 } 2064 2065 override void visit(Expression) 2066 { 2067 } 2068 2069 override void visit(DeclarationExp e) 2070 { 2071 void Dsymbol_visit(Dsymbol s) 2072 { 2073 if (auto vd = s.isVarDeclaration()) 2074 { 2075 s = s.toAlias(); 2076 if (s != vd) 2077 return Dsymbol_visit(s); 2078 if (!isTrackableVar(vd)) 2079 return; 2080 2081 if (vd._init && vd._init.isVoidInitializer()) 2082 return; 2083 2084 auto ei = vd._init ? vd._init.isExpInitializer() : null; 2085 if (ei) 2086 { 2087 auto e = ei.exp; 2088 if (auto ae = e.isConstructExp()) 2089 e = ae.e2; 2090 dgWriteVar(ob, cpvs, vd, e); 2091 } 2092 else 2093 dgWriteVar(ob, cpvs, vd, null); 2094 } 2095 else if (auto td = s.isTupleDeclaration()) 2096 { 2097 foreach (o; *td.objects) 2098 { 2099 if (auto eo = o.isExpression()) 2100 { 2101 if (auto se = eo.isDsymbolExp()) 2102 { 2103 Dsymbol_visit(se.s); 2104 } 2105 } 2106 } 2107 } 2108 } 2109 2110 Dsymbol_visit(e.declaration); 2111 } 2112 2113 override void visit(AssignExp ae) 2114 { 2115 ae.e2.accept(this); 2116 if (auto ve = ae.e1.isVarExp()) 2117 { 2118 if (auto v = ve.var.isVarDeclaration()) 2119 if (isTrackableVar(v)) 2120 dgWriteVar(ob, cpvs, v, ae.e2); 2121 } 2122 else 2123 ae.e1.accept(this); 2124 } 2125 2126 override void visit(VarExp ve) 2127 { 2128 //printf("VarExp: %s\n", ve.toChars()); 2129 if (auto v = ve.var.isVarDeclaration()) 2130 if (isTrackableVar(v)) 2131 { 2132 dgReadVar(ve.loc, ob, v, isMutableRef(ve.type), cpvs); 2133 } 2134 } 2135 2136 override void visit(CallExp ce) 2137 { 2138 //printf("CallExp(%s)\n", ce.toChars()); 2139 ce.e1.accept(this); 2140 auto t = ce.e1.type.toBasetype(); 2141 auto tf = t.isTypeFunction(); 2142 if (!tf) 2143 { 2144 assert(t.ty == Tdelegate); 2145 tf = t.nextOf().isTypeFunction(); 2146 assert(tf); 2147 } 2148 2149 // j=1 if _arguments[] is first argument 2150 const int j = tf.isDstyleVariadic(); 2151 bool hasOut; 2152 const varStackSave = obstate.varStack.length; 2153 2154 foreach (const i, arg; (*ce.arguments)[]) 2155 { 2156 if (i - j < tf.parameterList.length && 2157 i >= j) 2158 { 2159 Parameter p = tf.parameterList[i - j]; 2160 auto pt = p.type.toBasetype(); 2161 2162 if (!(p.storageClass & STC.out_ && arg.isVarExp())) 2163 arg.accept(this); 2164 2165 EscapeByResults er; 2166 escapeByValue(arg, &er, true); 2167 2168 void by(VarDeclaration v) 2169 { 2170 if (!isTrackableVar(v)) 2171 return; 2172 2173 const vi = obstate.vars.find(v); 2174 if (vi == size_t.max) 2175 return; 2176 2177 auto pvs = &cpvs[vi]; 2178 2179 if (p.storageClass & STC.out_) 2180 { 2181 /// initialize 2182 hasOut = true; 2183 makeUndefined(vi, cpvs); 2184 } 2185 else if (p.storageClass & STC.scope_) 2186 { 2187 // borrow 2188 obstate.varStack.push(vi); 2189 obstate.mutableStack.push(isMutableRef(pt)); 2190 } 2191 else 2192 { 2193 // move (i.e. consume arg) 2194 if (pvs.state != PtrState.Owner) 2195 v.error(arg.loc, "is not Owner, cannot consume its value"); 2196 makeUndefined(vi, cpvs); 2197 } 2198 } 2199 2200 foreach (VarDeclaration v2; er.byvalue) 2201 by(v2); 2202 foreach (VarDeclaration v2; er.byref) 2203 by(v2); 2204 } 2205 else // variadic args 2206 { 2207 arg.accept(this); 2208 2209 EscapeByResults er; 2210 escapeByValue(arg, &er, true); 2211 2212 void byv(VarDeclaration v) 2213 { 2214 if (!isTrackableVar(v)) 2215 return; 2216 2217 const vi = obstate.vars.find(v); 2218 if (vi == size_t.max) 2219 return; 2220 2221 auto pvs = &cpvs[vi]; 2222 obstate.varStack.push(vi); 2223 obstate.mutableStack.push(isMutableRef(arg.type)); 2224 2225 // move (i.e. consume arg) 2226 if (pvs.state != PtrState.Owner) 2227 v.error(arg.loc, "is not Owner, cannot consume its value"); 2228 makeUndefined(vi, cpvs); 2229 } 2230 2231 foreach (VarDeclaration v2; er.byvalue) 2232 byv(v2); 2233 foreach (VarDeclaration v2; er.byref) 2234 byv(v2); 2235 } 2236 } 2237 2238 /* Do a dummy 'read' of each variable passed to the function, 2239 * to detect O/B errors 2240 */ 2241 assert(obstate.varStack.length == obstate.mutableStack.length); 2242 foreach (i; varStackSave .. obstate.varStack.length) 2243 { 2244 const vi = obstate.varStack[i]; 2245 auto pvs = &cpvs[vi]; 2246 auto v = obstate.vars[vi]; 2247 //if (pvs.state == PtrState.Undefined) 2248 //v.error(ce.loc, "is Undefined, cannot pass to function"); 2249 2250 dgReadVar(ce.loc, ob, v, obstate.mutableStack[i], cpvs); 2251 2252 if (pvs.state == PtrState.Owner) 2253 { 2254 for (size_t k = i + 1; k < obstate.varStack.length;++k) 2255 { 2256 const vk = obstate.varStack[k]; 2257 if (vk == vi) 2258 { 2259 if (obstate.mutableStack[vi] || obstate.mutableStack[vk]) 2260 { 2261 v.error(ce.loc, "is passed as Owner more than once"); 2262 break; // no need to continue 2263 } 2264 } 2265 } 2266 } 2267 } 2268 2269 /* Pop off stack all variables for this function call 2270 */ 2271 obstate.varStack.setDim(varStackSave); 2272 obstate.mutableStack.setDim(varStackSave); 2273 2274 if (hasOut) 2275 // Initialization of out's only happens after the function call 2276 foreach (const i, arg; (*ce.arguments)[]) 2277 { 2278 if (i - j < tf.parameterList.length && 2279 i >= j) 2280 { 2281 Parameter p = tf.parameterList[i - j]; 2282 if (p.storageClass & STC.out_) 2283 { 2284 if (auto v = isTrackableVarExp(arg)) 2285 { 2286 dgWriteVar(ob, cpvs, v, null); 2287 } 2288 } 2289 } 2290 } 2291 } 2292 2293 override void visit(SymOffExp e) 2294 { 2295 if (auto v = e.var.isVarDeclaration()) 2296 if (isTrackableVar(v)) 2297 { 2298 dgReadVar(e.loc, ob, v, isMutableRef(e.type), cpvs); 2299 } 2300 } 2301 2302 override void visit(LogicalExp e) 2303 { 2304 e.e1.accept(this); 2305 2306 const vlen = obstate.vars.length; 2307 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof); 2308 PtrVarState[] out1 = p[0 .. vlen]; 2309 foreach (i, ref pvs; out1) 2310 { 2311 pvs = cpvs[i]; 2312 } 2313 2314 e.e2.accept(this); 2315 2316 // Merge out1 into cpvs 2317 foreach (i; 0 .. vlen) 2318 { 2319 cpvs[i].combine(out1[i], i, cpvs); 2320 } 2321 2322 mem.xfree(p); // should free .deps too 2323 } 2324 2325 override void visit(CondExp e) 2326 { 2327 e.econd.accept(this); 2328 2329 const vlen = obstate.vars.length; 2330 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof); 2331 PtrVarState[] out1 = p[0 .. vlen]; 2332 foreach (i, ref pvs; out1) 2333 { 2334 pvs = cpvs[i]; 2335 } 2336 2337 e.e1.accept(this); 2338 2339 // Swap out1 with cpvs 2340 foreach (i; 0 .. vlen) 2341 { 2342 out1[i].deps.swap(cpvs[i].deps); 2343 const state = out1[i].state; 2344 out1[i].state = cpvs[i].state; 2345 cpvs[i].state = state; 2346 } 2347 2348 e.e2.accept(this); 2349 2350 // Merge out1 into cpvs 2351 foreach (i; 0 .. vlen) 2352 { 2353 cpvs[i].combine(out1[i], i, cpvs); 2354 } 2355 2356 mem.xfree(p); // should free .deps too 2357 } 2358 2359 override void visit(AddrExp e) 2360 { 2361 /* Taking the address of struct literal is normally not 2362 * allowed, but CTFE can generate one out of a new expression, 2363 * but it'll be placed in static data so no need to check it. 2364 */ 2365 if (e.e1.op != TOK.structLiteral) 2366 e.e1.accept(this); 2367 } 2368 2369 override void visit(UnaExp e) 2370 { 2371 e.e1.accept(this); 2372 } 2373 2374 override void visit(BinExp e) 2375 { 2376 e.e1.accept(this); 2377 e.e2.accept(this); 2378 } 2379 2380 override void visit(ArrayLiteralExp e) 2381 { 2382 Type tb = e.type.toBasetype(); 2383 if (tb.ty == Tsarray || tb.ty == Tarray) 2384 { 2385 if (e.basis) 2386 e.basis.accept(this); 2387 foreach (el; *e.elements) 2388 { 2389 if (el) 2390 el.accept(this); 2391 } 2392 } 2393 } 2394 2395 override void visit(StructLiteralExp e) 2396 { 2397 if (e.elements) 2398 { 2399 foreach (ex; *e.elements) 2400 { 2401 if (ex) 2402 ex.accept(this); 2403 } 2404 } 2405 } 2406 2407 override void visit(AssocArrayLiteralExp e) 2408 { 2409 if (e.keys) 2410 { 2411 foreach (i, key; *e.keys) 2412 { 2413 if (key) 2414 key.accept(this); 2415 if (auto value = (*e.values)[i]) 2416 value.accept(this); 2417 } 2418 } 2419 } 2420 2421 override void visit(NewExp e) 2422 { 2423 if (e.arguments) 2424 { 2425 foreach (ex; *e.arguments) 2426 { 2427 if (ex) 2428 ex.accept(this); 2429 } 2430 } 2431 } 2432 2433 override void visit(SliceExp e) 2434 { 2435 e.e1.accept(this); 2436 if (e.lwr) 2437 e.lwr.accept(this); 2438 if (e.upr) 2439 e.upr.accept(this); 2440 } 2441 } 2442 2443 if (e) 2444 { 2445 scope ExpWalker ew = new ExpWalker(&dgReadVar, &dgWriteVar, cpvs, ob, obstate); 2446 e.accept(ew); 2447 } 2448 } 2449 2450 const vlen = obstate.vars.length; 2451 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof); 2452 PtrVarState[] cpvs = p[0 .. vlen]; 2453 foreach (ref pvs; cpvs) 2454 pvs.deps.length = vlen; 2455 2456 foreach (obi, ob; obstate.nodes) 2457 { 2458 static if (log) 2459 { 2460 printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr); 2461 printf(" input:\n"); 2462 foreach (i, ref pvs; ob.input[]) 2463 { 2464 printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]); 2465 } 2466 } 2467 2468 /* Combine the .output[]s of each ob.preds[] looking for errors 2469 */ 2470 if (obi) // skip startnode 2471 { 2472 assert(ob.preds.length); 2473 2474 foreach (i; 0 .. vlen) 2475 { 2476 ob.gen[i] = ob.preds[0].output[i]; 2477 } 2478 2479 foreach (j; 1 .. ob.preds.length) 2480 { 2481 foreach (i; 0 .. vlen) 2482 { 2483 auto pvs1 = &ob.gen[i]; 2484 auto pvs2 = &ob.preds[j].output[i]; 2485 const s1 = pvs1.state; 2486 const s2 = pvs2.state; 2487 if (s1 != s2 && (s1 == PtrState.Owner || s2 == PtrState.Owner)) 2488 { 2489 auto v = obstate.vars[i]; 2490 v.error(ob.exp ? ob.exp.loc : v.loc, "is both %s and %s", s1.toChars(), s2.toChars()); 2491 } 2492 pvs1.combine(*pvs2, i, ob.gen); 2493 } 2494 } 2495 } 2496 2497 /* Prolly should use gen[] instead of cpvs[], or vice versa 2498 */ 2499 foreach (i, ref pvs; ob.input) 2500 { 2501 cpvs[i] = pvs; 2502 } 2503 2504 foreachExp(ob, ob.exp, cpvs); 2505 2506 static if (log) 2507 { 2508 printf(" cpvs:\n"); 2509 foreach (i, ref pvs; cpvs[]) 2510 { 2511 printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]); 2512 } 2513 printf(" output:\n"); 2514 foreach (i, ref pvs; ob.output[]) 2515 { 2516 printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]); 2517 } 2518 } 2519 2520 if (ob.obtype == ObType.retexp) 2521 { 2522 EscapeByResults er; 2523 escapeByValue(ob.exp, &er, true); 2524 2525 void by(VarDeclaration r) // `r` is the rvalue 2526 { 2527 const ri = obstate.vars.find(r); 2528 if (ri == size_t.max) 2529 return; 2530 with (PtrState) 2531 { 2532 auto pvsr = &ob.output[ri]; 2533 switch (pvsr.state) 2534 { 2535 case Undefined: 2536 r.error(ob.exp.loc, "is returned but is Undefined"); 2537 break; 2538 2539 case Owner: 2540 pvsr.state = Undefined; // returning a pointer "consumes" it 2541 break; 2542 2543 case Borrowed: 2544 case Readonly: 2545 break; 2546 2547 default: 2548 assert(0); 2549 } 2550 } 2551 } 2552 2553 foreach (VarDeclaration v2; er.byvalue) 2554 by(v2); 2555 foreach (VarDeclaration v2; er.byref) 2556 by(v2); 2557 } 2558 2559 if (ob.obtype == ObType.return_ || ob.obtype == ObType.retexp) 2560 { 2561 foreach (i, ref pvs; ob.output[]) 2562 { 2563 //printf("%s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]); 2564 if (pvs.state == PtrState.Owner) 2565 { 2566 auto v = obstate.vars[i]; 2567 if (v.type.hasPointers()) 2568 v.error(v.loc, "is left dangling at return"); 2569 } 2570 } 2571 } 2572 } 2573 } 2574 2575 2576 /*************************************************** 2577 * Read from variable vi. 2578 * The beginning of the 'scope' of a variable is when it is first read. 2579 * Hence, when a read is done, instead of when assignment to the variable is done, the O/B rules are enforced. 2580 * (Also called "non-lexical scoping".) 2581 */ 2582 void readVar(ObNode* ob, const size_t vi, bool mutable, PtrVarState[] gen) 2583 { 2584 //printf("readVar(v%d)\n", cast(int)vi); 2585 auto pvso = &gen[vi]; 2586 switch (pvso.state) 2587 { 2588 case PtrState.Owner: 2589 //printf("t: %s\n", t.toChars()); 2590 if (mutable) // if mutable read 2591 { 2592 makeChildrenUndefined(vi, gen); 2593 } 2594 else // const read 2595 { 2596 // If there's a Borrow child, set that to Undefined 2597 foreach (di; 0 .. gen.length) 2598 { 2599 auto pvsd = &gen[di]; 2600 if (pvsd.deps[vi] && pvsd.state == PtrState.Borrowed) // if di borrowed vi 2601 { 2602 makeUndefined(di, gen); 2603 } 2604 } 2605 } 2606 break; 2607 2608 case PtrState.Borrowed: 2609 /* All children become Undefined 2610 */ 2611 makeChildrenUndefined(vi, gen); 2612 break; 2613 2614 case PtrState.Readonly: 2615 break; 2616 2617 case PtrState.Undefined: 2618 break; 2619 2620 default: 2621 break; 2622 } 2623 } 2624 2625 /******************** 2626 * Recursively make Undefined all who list vi as a dependency 2627 */ 2628 void makeChildrenUndefined(size_t vi, PtrVarState[] gen) 2629 { 2630 //printf("makeChildrenUndefined(%d)\n", vi); 2631 foreach (di; 0 .. gen.length) 2632 { 2633 if (gen[di].deps[vi]) // if di depends on vi 2634 { 2635 if (gen[di].state != PtrState.Undefined) 2636 { 2637 gen[di].state = PtrState.Undefined; // set this first to avoid infinite recursion 2638 makeChildrenUndefined(di, gen); 2639 gen[di].deps.zero(); 2640 } 2641 } 2642 } 2643 } 2644 2645 2646 /******************** 2647 * Recursively make Undefined vi undefined and all who list vi as a dependency 2648 */ 2649 void makeUndefined(size_t vi, PtrVarState[] gen) 2650 { 2651 auto pvs = &gen[vi]; 2652 pvs.state = PtrState.Undefined; // set this first to avoid infinite recursion 2653 makeChildrenUndefined(vi, gen); 2654 pvs.deps.zero(); 2655 } 2656 2657 /************************* 2658 * Is type `t` a reference to a const or a reference to a mutable? 2659 */ 2660 bool isMutableRef(Type t) 2661 { 2662 auto tb = t.toBasetype(); 2663 return (tb.nextOf() ? tb.nextOf() : tb).isMutable(); 2664 }