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