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