]> git.ipfire.org Git - thirdparty/gcc.git/blame - gcc/d/dmd/ob.d
d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[thirdparty/gcc.git] / gcc / d / dmd / ob.d
CommitLineData
5fee5ec3
IB
1/**
2 * Flow analysis for Ownership/Borrowing
3 *
4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/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;
38import dmd.mtype;
39import dmd.printast;
40import dmd.statement;
41import dmd.stmtstate;
42import dmd.tokens;
43import dmd.visitor;
44
45import dmd.root.bitarray;
0fb57034 46import dmd.common.outbuffer;
5fee5ec3
IB
47
48/**********************************
49 * Perform ownership/borrowing checks for funcdecl.
50 * Does not modify the AST, just checks for errors.
51 */
52
53void 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
78alias ObNodes = Array!(ObNode*);
79
80alias StmtState = dmd.stmtstate.StmtState!ObNode;
81
82/*******************************************
83 * Collect the state information.
84 */
85struct 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 */
104struct 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)
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
136enum 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
148string toString(ObType obtype)
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
193enum PtrState : ubyte
194{
195 Initial, Undefined, Owner, Borrowed, Readonly
196}
197
198/************
199 */
200const(char)* toChars(PtrState state)
201{
202 return toString(state).ptr;
203}
204
205string toString(PtrState state)
206{
207 return ["Initial", "Undefined", "Owner", "Borrowed", "Readonly"][state];
208}
209
210/******
211 * Carries the state of a pointer variable.
212 */
213struct 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 */
322void 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
338void 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.dim : 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.Compile:
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
883void 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
981void 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 */
1015void numberNodes(ref ObNodes obnodes)
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 */
1039void 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 */
1088void 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 */
1102bool 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 */
1134VarDeclaration 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 */
1153void 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 */
1193void 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 */
1206void 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 */
1232bool 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 */
1242bool isReadonlyPtr(VarDeclaration v)
1243{
1244 return v.isScope() && !v.type.nextOf().isMutable();
1245}
1246
1247/***************************************
1248 * Compute the gen vector for ob.
1249 */
1250void 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)
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 {
9c7d5e88 1365 //printf("[%s] %s: %s\n", e.loc.toChars(), EXPtoString(e.op).ptr, e.toChars());
5fee5ec3
IB
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 foreach (o; *td.objects)
1411 {
1412 if (auto eo = o.isExpression())
1413 {
1414 if (auto se = eo.isDsymbolExp())
1415 {
1416 Dsymbol_visit(se.s);
1417 }
1418 }
1419 }
1420 }
1421 }
1422
1423 Dsymbol_visit(e.declaration);
1424 }
1425
1426 override void visit(VarExp ve)
1427 {
1428 //printf("VarExp: %s\n", ve.toChars());
1429 if (auto v = ve.var.isVarDeclaration())
1430 if (isTrackableVar(v))
1431 {
1432 dgReadVar(ve.loc, ob, v, isMutableRef(ve.type));
1433 }
1434 }
1435
1436 override void visit(CallExp ce)
1437 {
1438 //printf("CallExp() %s\n", ce.toChars());
1439 ce.e1.accept(this);
1440 auto t = ce.e1.type.toBasetype();
1441 auto tf = t.isTypeFunction();
1442 if (!tf)
1443 {
1444 assert(t.ty == Tdelegate);
1445 tf = t.nextOf().isTypeFunction();
1446 assert(tf);
1447 }
1448
1449 // j=1 if _arguments[] is first argument
1450 const int j = tf.isDstyleVariadic();
1451 bool hasOut;
1452 const varStackSave = obstate.varStack.length;
1453
1454 foreach (const i, arg; (*ce.arguments)[])
1455 {
1456 if (i - j < tf.parameterList.length &&
1457 i >= j)
1458 {
1459 Parameter p = tf.parameterList[i - j];
1460 auto pt = p.type.toBasetype();
1461
1462 EscapeByResults er;
1463 escapeByValue(arg, &er, true);
1464
1465 if (!(p.storageClass & STC.out_ && arg.isVarExp()))
1466 arg.accept(this);
1467
1468 void by(VarDeclaration v)
1469 {
1470 if (!isTrackableVar(v))
1471 return;
1472
1473 const vi = obstate.vars.find(v);
1474 if (vi == size_t.max)
1475 return;
1476
1477 if (p.storageClass & STC.out_)
1478 {
1479 /// initialize
1480 hasOut = true;
1481 makeUndefined(vi, ob.gen);
1482 }
1483 else if (p.storageClass & STC.scope_)
1484 {
1485 // borrow
1486 obstate.varStack.push(vi);
1487 obstate.mutableStack.push(isMutableRef(pt));
1488 }
1489 else
1490 {
1491 // move (i.e. consume arg)
1492 makeUndefined(vi, ob.gen);
1493 }
1494 }
1495
1496 foreach (VarDeclaration v2; er.byvalue)
1497 by(v2);
1498 foreach (VarDeclaration v2; er.byref)
1499 by(v2);
1500 }
1501 else // variadic args
1502 {
1503 arg.accept(this);
1504
1505 EscapeByResults er;
1506 escapeByValue(arg, &er, true);
1507
1508 void byv(VarDeclaration v)
1509 {
1510 if (!isTrackableVar(v))
1511 return;
1512
1513 const vi = obstate.vars.find(v);
1514 if (vi == size_t.max)
1515 return;
1516
1517 if (tf.parameterList.stc & STC.scope_)
1518 {
1519 // borrow
1520 obstate.varStack.push(vi);
1521 obstate.mutableStack.push(isMutableRef(arg.type) &&
1522 !(tf.parameterList.stc & (STC.const_ | STC.immutable_)));
1523 }
1524 else
1525 // move (i.e. consume arg)
1526 makeUndefined(vi, ob.gen);
1527 }
1528
1529 foreach (VarDeclaration v2; er.byvalue)
1530 byv(v2);
1531 foreach (VarDeclaration v2; er.byref)
1532 byv(v2);
1533 }
1534 }
1535
1536 /* Do a dummy 'read' of each variable passed to the function,
1537 * to detect O/B errors
1538 */
1539 assert(obstate.varStack.length == obstate.mutableStack.length);
1540 foreach (i; varStackSave .. obstate.varStack.length)
1541 {
1542 const vi = obstate.varStack[i];
1543 // auto pvs = &ob.gen[vi];
1544 auto v = obstate.vars[vi];
1545 //if (pvs.state == PtrState.Undefined)
1546 //v.error(ce.loc, "is Undefined, cannot pass to function");
1547
1548 dgReadVar(ce.loc, ob, v, obstate.mutableStack[i]);
1549 }
1550
1551 /* Pop off stack all variables for this function call
1552 */
1553 obstate.varStack.setDim(varStackSave);
1554 obstate.mutableStack.setDim(varStackSave);
1555
1556 if (hasOut)
1557 // Initialization of out's only happens after the function call
1558 foreach (const i, arg; (*ce.arguments)[])
1559 {
1560 if (i - j < tf.parameterList.length &&
1561 i >= j)
1562 {
1563 Parameter p = tf.parameterList[i - j];
1564 if (p.storageClass & STC.out_)
1565 {
1566 if (auto v = isTrackableVarExp(arg))
1567 dgWriteVar(ob, v, null, true);
1568 }
1569 }
1570 }
1571 }
1572
1573 override void visit(SymOffExp e)
1574 {
1575 if (auto v = e.var.isVarDeclaration())
1576 if (isTrackableVar(v))
1577 {
1578 dgReadVar(e.loc, ob, v, isMutableRef(e.type));
1579 }
1580 }
1581
1582 override void visit(LogicalExp e)
1583 {
1584 e.e1.accept(this);
1585
1586 const vlen = obstate.vars.length;
1587 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
1588 PtrVarState[] gen1 = p[0 .. vlen];
1589 foreach (i, ref pvs; gen1)
1590 {
1591 pvs = ob.gen[i];
1592 }
1593
1594 e.e2.accept(this);
1595
1596 // Merge gen1 into ob.gen
1597 foreach (i; 0 .. vlen)
1598 {
1599 ob.gen[i].combine(gen1[i], i, ob.gen);
1600 }
1601
1602 mem.xfree(p); // should free .deps too
1603 }
1604
1605 override void visit(CondExp e)
1606 {
1607 e.econd.accept(this);
1608
1609 const vlen = obstate.vars.length;
1610 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
1611 PtrVarState[] gen1 = p[0 .. vlen];
1612 foreach (i, ref pvs; gen1)
1613 {
1614 pvs = ob.gen[i];
1615 }
1616
1617 e.e1.accept(this);
1618
1619 // Swap gen1 with ob.gen
1620 foreach (i; 0 .. vlen)
1621 {
1622 gen1[i].deps.swap(ob.gen[i].deps);
1623 const state = gen1[i].state;
1624 gen1[i].state = ob.gen[i].state;
1625 ob.gen[i].state = state;
1626 }
1627
1628 e.e2.accept(this);
1629
1630 /* xxx1 is the state from expression e1, ob.xxx is the state from e2.
1631 * Merge xxx1 into ob.xxx to get the state from `e`.
1632 */
1633 foreach (i; 0 .. vlen)
1634 {
1635 ob.gen[i].combine(gen1[i], i, ob.gen);
1636 }
1637
1638 mem.xfree(p); // should free .deps too
1639 }
1640
1641 override void visit(AddrExp e)
1642 {
1643 /* Taking the address of struct literal is normally not
1644 * allowed, but CTFE can generate one out of a new expression,
1645 * but it'll be placed in static data so no need to check it.
1646 */
9c7d5e88 1647 if (e.e1.op != EXP.structLiteral)
5fee5ec3
IB
1648 e.e1.accept(this);
1649 }
1650
1651 override void visit(UnaExp e)
1652 {
1653 e.e1.accept(this);
1654 }
1655
1656 override void visit(BinExp e)
1657 {
1658 e.e1.accept(this);
1659 e.e2.accept(this);
1660 }
1661
1662 override void visit(ArrayLiteralExp e)
1663 {
1664 Type tb = e.type.toBasetype();
1665 if (tb.ty == Tsarray || tb.ty == Tarray)
1666 {
1667 if (e.basis)
1668 e.basis.accept(this);
1669 foreach (el; *e.elements)
1670 {
1671 if (el)
1672 el.accept(this);
1673 }
1674 }
1675 }
1676
1677 override void visit(StructLiteralExp e)
1678 {
1679 if (e.elements)
1680 {
1681 foreach (ex; *e.elements)
1682 {
1683 if (ex)
1684 ex.accept(this);
1685 }
1686 }
1687 }
1688
1689 override void visit(AssocArrayLiteralExp e)
1690 {
1691 if (e.keys)
1692 {
1693 foreach (i, key; *e.keys)
1694 {
1695 if (key)
1696 key.accept(this);
1697 if (auto value = (*e.values)[i])
1698 value.accept(this);
1699 }
1700 }
1701 }
1702
1703 override void visit(NewExp e)
1704 {
1705 if (e.arguments)
1706 {
1707 foreach (ex; *e.arguments)
1708 {
1709 if (ex)
1710 ex.accept(this);
1711 }
1712 }
1713 }
1714
1715 override void visit(SliceExp e)
1716 {
1717 e.e1.accept(this);
1718 if (e.lwr)
1719 e.lwr.accept(this);
1720 if (e.upr)
1721 e.upr.accept(this);
1722 }
1723 }
1724
1725 if (e)
1726 {
1727 scope ExpWalker ew = new ExpWalker(&dgWriteVar, &dgReadVar, ob, obstate);
1728 e.accept(ew);
1729 }
1730 }
1731
1732 foreachExp(ob, ob.exp);
1733}
1734
1735/***************************************
1736 * Determine the state of a variable based on
1737 * its type and storage class.
1738 */
1739PtrState toPtrState(VarDeclaration v)
1740{
1741 /* pointer to mutable: Owner
1742 * pointer to mutable, scope: Borrowed
1743 * pointer to const: Owner
1744 * pointer to const, scope: Readonly
1745 * ref: Borrowed
1746 * const ref: Readonly
1747 */
1748
1749 auto t = v.type;
1750 if (v.isRef())
1751 {
1752 return t.hasMutableFields() ? PtrState.Borrowed : PtrState.Readonly;
1753 }
1754 if (v.isScope())
1755 {
1756 return t.hasPointersToMutableFields() ? PtrState.Borrowed : PtrState.Readonly;
1757 }
1758 else
1759 return PtrState.Owner;
1760}
1761
1762/**********************************
1763 * Does type `t` contain any pointers to mutable?
1764 */
1765bool hasPointersToMutableFields(Type t)
1766{
1767 auto tb = t.toBasetype();
1768 if (!tb.isMutable())
1769 return false;
1770 if (auto tsa = tb.isTypeSArray())
1771 {
1772 return tsa.nextOf().hasPointersToMutableFields();
1773 }
1774 if (auto ts = tb.isTypeStruct())
1775 {
1776 foreach (v; ts.sym.fields)
1777 {
1778 if (v.isRef())
1779 {
1780 if (v.type.hasMutableFields())
1781 return true;
1782 }
1783 else if (v.type.hasPointersToMutableFields())
1784 return true;
1785 }
1786 return false;
1787 }
1788 auto tbn = tb.nextOf();
1789 return tbn && tbn.hasMutableFields();
1790}
1791
1792/********************************
1793 * Does type `t` have any mutable fields?
1794 */
1795bool hasMutableFields(Type t)
1796{
1797 auto tb = t.toBasetype();
1798 if (!tb.isMutable())
1799 return false;
1800 if (auto tsa = tb.isTypeSArray())
1801 {
1802 return tsa.nextOf().hasMutableFields();
1803 }
1804 if (auto ts = tb.isTypeStruct())
1805 {
1806 foreach (v; ts.sym.fields)
1807 {
1808 if (v.type.hasMutableFields())
1809 return true;
1810 }
1811 return false;
1812 }
1813 return true;
1814}
1815
1816
1817
1818/***************************************
1819 * Do the data flow analysis (i.e. compute the input[]
1820 * and output[] vectors for each ObNode).
1821 */
1822void doDataFlowAnalysis(ref ObState obstate)
1823{
1824 enum log = false;
1825 if (log)
1826 {
1827 printf("-----------------doDataFlowAnalysis()-------------------------\n");
1828 foreach (ob; obstate.nodes) ob.print();
1829 printf("------------------------------------------\n");
1830 }
1831
1832 if (!obstate.nodes.length)
1833 return;
1834
1835 auto startnode = obstate.nodes[0];
1836 assert(startnode.preds.length == 0);
1837
1838 /* Set opening state `input[]` for first node
1839 */
1840 foreach (i, ref ps; startnode.input)
1841 {
1842 auto v = obstate.vars[i];
1843 auto state = toPtrState(v);
1844 if (v.isParameter())
1845 {
1846 if (v.isOut())
1847 state = PtrState.Undefined;
1848 else if (v.isBorrowedPtr())
1849 state = PtrState.Borrowed;
1850 else
1851 state = PtrState.Owner;
1852 }
1853 else
1854 state = PtrState.Undefined;
1855 ps.state = state;
1856 ps.deps.zero();
1857 startnode.gen[i] = ps;
1858 }
1859
1860 /* Set all output[]s to Initial
1861 */
1862 foreach (ob; obstate.nodes[])
1863 {
1864 foreach (ref ps; ob.output)
1865 {
1866 ps.state = PtrState.Initial;
1867 ps.deps.zero();
1868 }
1869 }
1870
1871 const vlen = obstate.vars.length;
1872 PtrVarState pvs;
1873 pvs.deps.length = vlen;
1874 int counter = 0;
1875 bool changes;
1876 do
1877 {
1878 changes = false;
1879 assert(++counter <= 1000); // should converge, but don't hang if it doesn't
1880 foreach (ob; obstate.nodes[])
1881 {
1882 /* Construct ob.gen[] by combining the .output[]s of each ob.preds[]
1883 * and set ob.input[] to the same state
1884 */
1885 if (ob != startnode)
1886 {
1887 assert(ob.preds.length);
1888
1889 foreach (i; 0 .. vlen)
1890 {
1891 ob.gen[i] = ob.preds[0].output[i];
1892 }
1893
1894 foreach (j; 1 .. ob.preds.length)
1895 {
1896 foreach (i; 0 .. vlen)
1897 {
1898 ob.gen[i].combine(ob.preds[j].output[i], i, ob.gen);
1899 }
1900 }
1901
1902 /* Set ob.input[] to ob.gen[],
1903 * if any changes were made we'll have to do another iteration
1904 */
1905 foreach (i; 0 .. vlen)
1906 {
1907 if (ob.gen[i] != ob.input[i])
1908 {
1909 ob.input[i] = ob.gen[i];
1910 changes = true;
1911 }
1912 }
1913 }
1914
1915 /* Compute gen[] for node ob
1916 */
1917 genKill(obstate, ob);
1918
1919 foreach (i; 0 .. vlen)
1920 {
1921 if (ob.gen[i] != ob.output[i])
1922 {
1923 ob.output[i] = ob.gen[i];
1924 changes = true;
1925 }
1926 }
1927 }
1928 } while (changes);
1929
1930 static if (log)
1931 {
1932 foreach (obi, ob; obstate.nodes)
1933 {
1934 printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr);
1935 printf(" input:\n");
1936 foreach (i, ref pvs2; ob.input[])
1937 {
1938 printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]);
1939 }
1940
1941 printf(" gen:\n");
1942 foreach (i, ref pvs2; ob.gen[])
1943 {
1944 printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]);
1945 }
1946 printf(" output:\n");
1947 foreach (i, ref pvs2; ob.output[])
1948 {
1949 printf(" %s: ", obstate.vars[i].toChars()); pvs2.print(obstate.vars[]);
1950 }
1951 }
1952 printf("\n");
1953 }
1954}
1955
1956
1957/***************************************
1958 * Check for Ownership/Borrowing errors.
1959 */
1960void checkObErrors(ref ObState obstate)
1961{
1962 enum log = false;
1963 if (log)
1964 printf("------------checkObErrors()----------\n");
1965
1966 void dgWriteVar(ObNode* ob, PtrVarState[] cpvs, VarDeclaration v, Expression e)
1967 {
1968 if (log) printf("dgWriteVar(v:%s, e:%s)\n", v.toChars(), e ? e.toChars() : "null");
1969 const vi = obstate.vars.find(v);
1970 assert(vi != size_t.max);
1971 PtrVarState* pvs = &cpvs[vi];
1972 readVar(ob, vi, true, cpvs);
1973 if (e)
1974 {
1975 if (isBorrowedPtr(v))
1976 pvs.state = PtrState.Borrowed;
1977 else if (isReadonlyPtr(v))
1978 pvs.state = PtrState.Readonly;
1979 else
1980 pvs.state = PtrState.Owner;
1981 pvs.deps.zero();
1982
1983 EscapeByResults er;
1984 escapeByValue(e, &er, true);
1985
1986 void by(VarDeclaration r) // `v` = `r`
1987 {
1988 //printf(" by(%s)\n", r.toChars());
1989 const ri = obstate.vars.find(r);
1990 if (ri == size_t.max)
1991 return;
1992
1993 with (PtrState)
1994 {
1995 pvs.deps[ri] = true; // v took from r
1996 auto pvsr = &cpvs[ri];
1997
1998 if (pvsr.state == Undefined)
1999 {
2000 v.error(e.loc, "is reading from `%s` which is Undefined", r.toChars());
2001 }
2002 else if (isBorrowedPtr(v)) // v is going to borrow from r
2003 {
2004 if (pvsr.state == Readonly)
2005 v.error(e.loc, "is borrowing from `%s` which is Readonly", r.toChars());
2006
2007 pvs.state = Borrowed;
2008 }
2009 else if (isReadonlyPtr(v))
2010 {
2011 pvs.state = Readonly;
2012 }
2013 else
2014 {
2015 // move from r to v
2016 pvsr.state = Undefined;
2017 pvsr.deps.zero();
2018 }
2019 }
2020 }
2021
2022 foreach (VarDeclaration v2; er.byvalue)
2023 by(v2);
2024 foreach (VarDeclaration v2; er.byref)
2025 by(v2);
2026 }
2027 else
2028 {
2029 if (isBorrowedPtr(v))
2030 pvs.state = PtrState.Borrowed;
2031 else if (isReadonlyPtr(v))
2032 pvs.state = PtrState.Readonly;
2033 else
2034 pvs.state = PtrState.Owner;
2035 pvs.deps.zero();
2036 }
2037 }
2038
2039 void dgReadVar(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[] gen)
2040 {
2041 if (log) printf("dgReadVar() %s\n", v.toChars());
2042 const vi = obstate.vars.find(v);
2043 assert(vi != size_t.max);
2044 auto pvs = &gen[vi];
2045 if (pvs.state == PtrState.Undefined)
2046 v.error(loc, "has undefined state and cannot be read");
2047
2048 readVar(ob, vi, mutable, gen);
2049 }
2050
2051 void foreachExp(ObNode* ob, Expression e, PtrVarState[] cpvs)
2052 {
2053 extern (C++) final class ExpWalker : Visitor
2054 {
2055 alias visit = typeof(super).visit;
2056 extern (D) void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar;
2057 extern (D) void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar;
2058 PtrVarState[] cpvs;
2059 ObNode* ob;
2060 ObState* obstate;
2061
2062 extern (D) this(void delegate(const ref Loc loc, ObNode* ob, VarDeclaration v, bool mutable, PtrVarState[]) dgReadVar,
2063 void delegate(ObNode*, PtrVarState[], VarDeclaration, Expression) dgWriteVar,
2064 PtrVarState[] cpvs, ObNode* ob, ref ObState obstate)
2065 {
2066 this.dgReadVar = dgReadVar;
2067 this.dgWriteVar = dgWriteVar;
2068 this.cpvs = cpvs;
2069 this.ob = ob;
2070 this.obstate = &obstate;
2071 }
2072
2073 override void visit(Expression)
2074 {
2075 }
2076
2077 override void visit(DeclarationExp e)
2078 {
2079 void Dsymbol_visit(Dsymbol s)
2080 {
2081 if (auto vd = s.isVarDeclaration())
2082 {
2083 s = s.toAlias();
2084 if (s != vd)
2085 return Dsymbol_visit(s);
2086 if (!isTrackableVar(vd))
2087 return;
2088
2089 if (vd._init && vd._init.isVoidInitializer())
2090 return;
2091
2092 auto ei = vd._init ? vd._init.isExpInitializer() : null;
2093 if (ei)
2094 {
2095 auto e = ei.exp;
2096 if (auto ae = e.isConstructExp())
2097 e = ae.e2;
2098 dgWriteVar(ob, cpvs, vd, e);
2099 }
2100 else
2101 dgWriteVar(ob, cpvs, vd, null);
2102 }
2103 else if (auto td = s.isTupleDeclaration())
2104 {
2105 foreach (o; *td.objects)
2106 {
2107 if (auto eo = o.isExpression())
2108 {
2109 if (auto se = eo.isDsymbolExp())
2110 {
2111 Dsymbol_visit(se.s);
2112 }
2113 }
2114 }
2115 }
2116 }
2117
2118 Dsymbol_visit(e.declaration);
2119 }
2120
2121 override void visit(AssignExp ae)
2122 {
2123 ae.e2.accept(this);
2124 if (auto ve = ae.e1.isVarExp())
2125 {
2126 if (auto v = ve.var.isVarDeclaration())
2127 if (isTrackableVar(v))
2128 dgWriteVar(ob, cpvs, v, ae.e2);
2129 }
2130 else
2131 ae.e1.accept(this);
2132 }
2133
2134 override void visit(VarExp ve)
2135 {
2136 //printf("VarExp: %s\n", ve.toChars());
2137 if (auto v = ve.var.isVarDeclaration())
2138 if (isTrackableVar(v))
2139 {
2140 dgReadVar(ve.loc, ob, v, isMutableRef(ve.type), cpvs);
2141 }
2142 }
2143
2144 override void visit(CallExp ce)
2145 {
2146 //printf("CallExp(%s)\n", ce.toChars());
2147 ce.e1.accept(this);
2148 auto t = ce.e1.type.toBasetype();
2149 auto tf = t.isTypeFunction();
2150 if (!tf)
2151 {
2152 assert(t.ty == Tdelegate);
2153 tf = t.nextOf().isTypeFunction();
2154 assert(tf);
2155 }
2156
2157 // j=1 if _arguments[] is first argument
2158 const int j = tf.isDstyleVariadic();
2159 bool hasOut;
2160 const varStackSave = obstate.varStack.length;
2161
2162 foreach (const i, arg; (*ce.arguments)[])
2163 {
2164 if (i - j < tf.parameterList.length &&
2165 i >= j)
2166 {
2167 Parameter p = tf.parameterList[i - j];
2168 auto pt = p.type.toBasetype();
2169
2170 if (!(p.storageClass & STC.out_ && arg.isVarExp()))
2171 arg.accept(this);
2172
2173 EscapeByResults er;
2174 escapeByValue(arg, &er, true);
2175
2176 void by(VarDeclaration v)
2177 {
2178 if (!isTrackableVar(v))
2179 return;
2180
2181 const vi = obstate.vars.find(v);
2182 if (vi == size_t.max)
2183 return;
2184
2185 auto pvs = &cpvs[vi];
2186
2187 if (p.storageClass & STC.out_)
2188 {
2189 /// initialize
2190 hasOut = true;
2191 makeUndefined(vi, cpvs);
2192 }
2193 else if (p.storageClass & STC.scope_)
2194 {
2195 // borrow
2196 obstate.varStack.push(vi);
2197 obstate.mutableStack.push(isMutableRef(pt));
2198 }
2199 else
2200 {
2201 // move (i.e. consume arg)
2202 if (pvs.state != PtrState.Owner)
2203 v.error(arg.loc, "is not Owner, cannot consume its value");
2204 makeUndefined(vi, cpvs);
2205 }
2206 }
2207
2208 foreach (VarDeclaration v2; er.byvalue)
2209 by(v2);
2210 foreach (VarDeclaration v2; er.byref)
2211 by(v2);
2212 }
2213 else // variadic args
2214 {
2215 arg.accept(this);
2216
2217 EscapeByResults er;
2218 escapeByValue(arg, &er, true);
2219
2220 void byv(VarDeclaration v)
2221 {
2222 if (!isTrackableVar(v))
2223 return;
2224
2225 const vi = obstate.vars.find(v);
2226 if (vi == size_t.max)
2227 return;
2228
2229 auto pvs = &cpvs[vi];
2230
2231 if (tf.parameterList.stc & STC.scope_)
2232 {
2233 // borrow
2234 obstate.varStack.push(vi);
2235 obstate.mutableStack.push(isMutableRef(arg.type) &&
2236 !(tf.parameterList.stc & (STC.const_ | STC.immutable_)));
2237 }
2238 else
2239 {
2240 // move (i.e. consume arg)
2241 if (pvs.state != PtrState.Owner)
2242 v.error(arg.loc, "is not Owner, cannot consume its value");
2243 makeUndefined(vi, cpvs);
2244 }
2245 }
2246
2247 foreach (VarDeclaration v2; er.byvalue)
2248 byv(v2);
2249 foreach (VarDeclaration v2; er.byref)
2250 byv(v2);
2251 }
2252 }
2253
2254 /* Do a dummy 'read' of each variable passed to the function,
2255 * to detect O/B errors
2256 */
2257 assert(obstate.varStack.length == obstate.mutableStack.length);
2258 foreach (i; varStackSave .. obstate.varStack.length)
2259 {
2260 const vi = obstate.varStack[i];
2261 auto pvs = &cpvs[vi];
2262 auto v = obstate.vars[vi];
2263 //if (pvs.state == PtrState.Undefined)
2264 //v.error(ce.loc, "is Undefined, cannot pass to function");
2265
2266 dgReadVar(ce.loc, ob, v, obstate.mutableStack[i], cpvs);
2267
2268 if (pvs.state == PtrState.Owner)
2269 {
2270 for (size_t k = i + 1; k < obstate.varStack.length;++k)
2271 {
2272 const vk = obstate.varStack[k];
2273 if (vk == vi)
2274 {
2275 if (obstate.mutableStack[vi] || obstate.mutableStack[vk])
2276 {
2277 v.error(ce.loc, "is passed as Owner more than once");
2278 break; // no need to continue
2279 }
2280 }
2281 }
2282 }
2283 }
2284
2285 /* Pop off stack all variables for this function call
2286 */
2287 obstate.varStack.setDim(varStackSave);
2288 obstate.mutableStack.setDim(varStackSave);
2289
2290 if (hasOut)
2291 // Initialization of out's only happens after the function call
2292 foreach (const i, arg; (*ce.arguments)[])
2293 {
2294 if (i - j < tf.parameterList.length &&
2295 i >= j)
2296 {
2297 Parameter p = tf.parameterList[i - j];
2298 if (p.storageClass & STC.out_)
2299 {
2300 if (auto v = isTrackableVarExp(arg))
2301 {
2302 dgWriteVar(ob, cpvs, v, null);
2303 }
2304 }
2305 }
2306 }
2307 }
2308
2309 override void visit(SymOffExp e)
2310 {
2311 if (auto v = e.var.isVarDeclaration())
2312 if (isTrackableVar(v))
2313 {
2314 dgReadVar(e.loc, ob, v, isMutableRef(e.type), cpvs);
2315 }
2316 }
2317
2318 override void visit(LogicalExp e)
2319 {
2320 e.e1.accept(this);
2321
2322 const vlen = obstate.vars.length;
2323 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
2324 PtrVarState[] out1 = p[0 .. vlen];
2325 foreach (i, ref pvs; out1)
2326 {
2327 pvs = cpvs[i];
2328 }
2329
2330 e.e2.accept(this);
2331
2332 // Merge out1 into cpvs
2333 foreach (i; 0 .. vlen)
2334 {
2335 cpvs[i].combine(out1[i], i, cpvs);
2336 }
2337
2338 mem.xfree(p); // should free .deps too
2339 }
2340
2341 override void visit(CondExp e)
2342 {
2343 e.econd.accept(this);
2344
2345 const vlen = obstate.vars.length;
2346 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
2347 PtrVarState[] out1 = p[0 .. vlen];
2348 foreach (i, ref pvs; out1)
2349 {
2350 pvs = cpvs[i];
2351 }
2352
2353 e.e1.accept(this);
2354
2355 // Swap out1 with cpvs
2356 foreach (i; 0 .. vlen)
2357 {
2358 out1[i].deps.swap(cpvs[i].deps);
2359 const state = out1[i].state;
2360 out1[i].state = cpvs[i].state;
2361 cpvs[i].state = state;
2362 }
2363
2364 e.e2.accept(this);
2365
2366 // Merge out1 into cpvs
2367 foreach (i; 0 .. vlen)
2368 {
2369 cpvs[i].combine(out1[i], i, cpvs);
2370 }
2371
2372 mem.xfree(p); // should free .deps too
2373 }
2374
2375 override void visit(AddrExp e)
2376 {
2377 /* Taking the address of struct literal is normally not
2378 * allowed, but CTFE can generate one out of a new expression,
2379 * but it'll be placed in static data so no need to check it.
2380 */
9c7d5e88 2381 if (e.e1.op != EXP.structLiteral)
5fee5ec3
IB
2382 e.e1.accept(this);
2383 }
2384
2385 override void visit(UnaExp e)
2386 {
2387 e.e1.accept(this);
2388 }
2389
2390 override void visit(BinExp e)
2391 {
2392 e.e1.accept(this);
2393 e.e2.accept(this);
2394 }
2395
2396 override void visit(ArrayLiteralExp e)
2397 {
2398 Type tb = e.type.toBasetype();
2399 if (tb.ty == Tsarray || tb.ty == Tarray)
2400 {
2401 if (e.basis)
2402 e.basis.accept(this);
2403 foreach (el; *e.elements)
2404 {
2405 if (el)
2406 el.accept(this);
2407 }
2408 }
2409 }
2410
2411 override void visit(StructLiteralExp e)
2412 {
2413 if (e.elements)
2414 {
2415 foreach (ex; *e.elements)
2416 {
2417 if (ex)
2418 ex.accept(this);
2419 }
2420 }
2421 }
2422
2423 override void visit(AssocArrayLiteralExp e)
2424 {
2425 if (e.keys)
2426 {
2427 foreach (i, key; *e.keys)
2428 {
2429 if (key)
2430 key.accept(this);
2431 if (auto value = (*e.values)[i])
2432 value.accept(this);
2433 }
2434 }
2435 }
2436
2437 override void visit(NewExp e)
2438 {
2439 if (e.arguments)
2440 {
2441 foreach (ex; *e.arguments)
2442 {
2443 if (ex)
2444 ex.accept(this);
2445 }
2446 }
2447 }
2448
2449 override void visit(SliceExp e)
2450 {
2451 e.e1.accept(this);
2452 if (e.lwr)
2453 e.lwr.accept(this);
2454 if (e.upr)
2455 e.upr.accept(this);
2456 }
2457 }
2458
2459 if (e)
2460 {
2461 scope ExpWalker ew = new ExpWalker(&dgReadVar, &dgWriteVar, cpvs, ob, obstate);
2462 e.accept(ew);
2463 }
2464 }
2465
2466 const vlen = obstate.vars.length;
2467 auto p = cast(PtrVarState*)mem.xcalloc(vlen, PtrVarState.sizeof);
2468 PtrVarState[] cpvs = p[0 .. vlen];
2469 foreach (ref pvs; cpvs)
2470 pvs.deps.length = vlen;
2471
2472 foreach (obi, ob; obstate.nodes)
2473 {
2474 static if (log)
2475 {
2476 printf("%d: %s\n", obi, ob.exp ? ob.exp.toChars() : "".ptr);
2477 printf(" input:\n");
2478 foreach (i, ref pvs; ob.input[])
2479 {
2480 printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
2481 }
2482 }
2483
2484 /* Combine the .output[]s of each ob.preds[] looking for errors
2485 */
2486 if (obi) // skip startnode
2487 {
2488 assert(ob.preds.length);
2489
2490 foreach (i; 0 .. vlen)
2491 {
2492 ob.gen[i] = ob.preds[0].output[i];
2493 }
2494
2495 foreach (j; 1 .. ob.preds.length)
2496 {
2497 foreach (i; 0 .. vlen)
2498 {
2499 auto pvs1 = &ob.gen[i];
2500 auto pvs2 = &ob.preds[j].output[i];
2501 const s1 = pvs1.state;
2502 const s2 = pvs2.state;
2503 if (s1 != s2 && (s1 == PtrState.Owner || s2 == PtrState.Owner))
2504 {
2505 auto v = obstate.vars[i];
2506 v.error(ob.exp ? ob.exp.loc : v.loc, "is both %s and %s", s1.toChars(), s2.toChars());
2507 }
2508 pvs1.combine(*pvs2, i, ob.gen);
2509 }
2510 }
2511 }
2512
2513 /* Prolly should use gen[] instead of cpvs[], or vice versa
2514 */
2515 foreach (i, ref pvs; ob.input)
2516 {
2517 cpvs[i] = pvs;
2518 }
2519
2520 foreachExp(ob, ob.exp, cpvs);
2521
2522 static if (log)
2523 {
2524 printf(" cpvs:\n");
2525 foreach (i, ref pvs; cpvs[])
2526 {
2527 printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
2528 }
2529 printf(" output:\n");
2530 foreach (i, ref pvs; ob.output[])
2531 {
2532 printf(" %s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
2533 }
2534 }
2535
2536 if (ob.obtype == ObType.retexp)
2537 {
2538 EscapeByResults er;
2539 escapeByValue(ob.exp, &er, true);
2540
2541 void by(VarDeclaration r) // `r` is the rvalue
2542 {
2543 const ri = obstate.vars.find(r);
2544 if (ri == size_t.max)
2545 return;
2546 with (PtrState)
2547 {
2548 auto pvsr = &ob.output[ri];
2549 switch (pvsr.state)
2550 {
2551 case Undefined:
2552 r.error(ob.exp.loc, "is returned but is Undefined");
2553 break;
2554
2555 case Owner:
2556 pvsr.state = Undefined; // returning a pointer "consumes" it
2557 break;
2558
2559 case Borrowed:
2560 case Readonly:
2561 break;
2562
2563 default:
2564 assert(0);
2565 }
2566 }
2567 }
2568
2569 foreach (VarDeclaration v2; er.byvalue)
2570 by(v2);
2571 foreach (VarDeclaration v2; er.byref)
2572 by(v2);
2573 }
2574
2575 if (ob.obtype == ObType.return_ || ob.obtype == ObType.retexp)
2576 {
2577 foreach (i, ref pvs; ob.output[])
2578 {
2579 //printf("%s: ", obstate.vars[i].toChars()); pvs.print(obstate.vars[]);
2580 if (pvs.state == PtrState.Owner)
2581 {
2582 auto v = obstate.vars[i];
2583 if (v.type.hasPointers())
2584 v.error(v.loc, "is left dangling at return");
2585 }
2586 }
2587 }
2588 }
2589}
2590
2591
2592/***************************************************
2593 * Read from variable vi.
2594 * The beginning of the 'scope' of a variable is when it is first read.
2595 * Hence, when a read is done, instead of when assignment to the variable is done, the O/B rules are enforced.
2596 * (Also called "non-lexical scoping".)
2597 */
2598void readVar(ObNode* ob, const size_t vi, bool mutable, PtrVarState[] gen)
2599{
2600 //printf("readVar(v%d)\n", cast(int)vi);
2601 auto pvso = &gen[vi];
2602 switch (pvso.state)
2603 {
2604 case PtrState.Owner:
2605 //printf("t: %s\n", t.toChars());
2606 if (mutable) // if mutable read
2607 {
2608 makeChildrenUndefined(vi, gen);
2609 }
2610 else // const read
2611 {
2612 // If there's a Borrow child, set that to Undefined
2613 foreach (di; 0 .. gen.length)
2614 {
2615 auto pvsd = &gen[di];
2616 if (pvsd.deps[vi] && pvsd.state == PtrState.Borrowed) // if di borrowed vi
2617 {
2618 makeUndefined(di, gen);
2619 }
2620 }
2621 }
2622 break;
2623
2624 case PtrState.Borrowed:
2625 /* All children become Undefined
2626 */
2627 makeChildrenUndefined(vi, gen);
2628 break;
2629
2630 case PtrState.Readonly:
2631 break;
2632
2633 case PtrState.Undefined:
2634 break;
2635
2636 default:
2637 break;
2638 }
2639}
2640
2641/********************
2642 * Recursively make Undefined all who list vi as a dependency
2643 */
2644void makeChildrenUndefined(size_t vi, PtrVarState[] gen)
2645{
2646 //printf("makeChildrenUndefined(%d)\n", vi);
2647 foreach (di; 0 .. gen.length)
2648 {
2649 if (gen[di].deps[vi]) // if di depends on vi
2650 {
2651 if (gen[di].state != PtrState.Undefined)
2652 {
2653 gen[di].state = PtrState.Undefined; // set this first to avoid infinite recursion
2654 makeChildrenUndefined(di, gen);
2655 gen[di].deps.zero();
2656 }
2657 }
2658 }
2659}
2660
2661
2662/********************
2663 * Recursively make Undefined vi undefined and all who list vi as a dependency
2664 */
2665void makeUndefined(size_t vi, PtrVarState[] gen)
2666{
2667 auto pvs = &gen[vi];
2668 pvs.state = PtrState.Undefined; // set this first to avoid infinite recursion
2669 makeChildrenUndefined(vi, gen);
2670 pvs.deps.zero();
2671}
2672
2673/*************************
2674 * Is type `t` a reference to a const or a reference to a mutable?
2675 */
2676bool isMutableRef(Type t)
2677{
2678 auto tb = t.toBasetype();
2679 return (tb.nextOf() ? tb.nextOf() : tb).isMutable();
2680}