1 /**
2  * Manage flow analysis for constructors.
3  *
4  * Copyright:   Copyright (C) 1999-2023 by The D Language Foundation, All Rights Reserved
5  * Authors:     $(LINK2 https://www.digitalmars.com, Walter Bright)
6  * License:     $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7  * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d, _ctorflow.d)
8  * Documentation:  https://dlang.org/phobos/dmd_ctorflow.html
9  * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctorflow.d
10  */
11 
12 module dmd.ctorflow;
13 
14 import core.stdc.stdio;
15 
16 import dmd.root.rmem;
17 import dmd.location;
18 
19 enum CSX : ushort
20 {
21     none            = 0,
22     this_ctor       = 0x01,     /// called this()
23     super_ctor      = 0x02,     /// called super()
24     label           = 0x04,     /// seen a label
25     return_         = 0x08,     /// seen a return statement
26     any_ctor        = 0x10,     /// either this() or super() was called
27     halt            = 0x20,     /// assert(0)
28 }
29 
30 /// Individual field in the Ctor with information about its callees and location.
31 struct FieldInit
32 {
33     CSX csx; /// information about the field's callees
34     Loc loc; /// location of the field initialization
35 }
36 
37 /***********
38  * Primitive flow analysis for constructors
39  */
40 struct CtorFlow
41 {
42     CSX callSuper;      /// state of calling other constructors
43 
44     FieldInit[] fieldinit;    /// state of field initializations
45 
46     void allocFieldinit(size_t dim)
47     {
48         fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim];
49     }
50 
51     void freeFieldinit()
52     {
53         if (fieldinit.ptr)
54             mem.xfree(fieldinit.ptr);
55 
56         fieldinit = null;
57     }
58 
59     /***********************
60      * Create a deep copy of `this`
61      * Returns:
62      *  a copy
63      */
64     CtorFlow clone()
65     {
66         return CtorFlow(callSuper, fieldinit.arraydup);
67     }
68 
69     /**********************************
70      * Set CSX bits in flow analysis state
71      * Params:
72      *  csx = bits to set
73      */
74     void orCSX(CSX csx) nothrow pure @safe
75     {
76         callSuper |= csx;
77         foreach (ref u; fieldinit)
78             u.csx |= csx;
79     }
80 
81     /******************************
82      * OR CSX bits to `this`
83      * Params:
84      *  ctorflow = bits to OR in
85      */
86     void OR(const ref CtorFlow ctorflow) pure nothrow @safe
87     {
88         callSuper |= ctorflow.callSuper;
89         if (fieldinit.length && ctorflow.fieldinit.length)
90         {
91             assert(fieldinit.length == ctorflow.fieldinit.length);
92             foreach (i, u; ctorflow.fieldinit)
93             {
94                 auto fi = &fieldinit[i];
95                 fi.csx |= u.csx;
96                 if (fi.loc is Loc.init)
97                     fi.loc = u.loc;
98             }
99         }
100     }
101 }
102 
103 
104 /****************************************
105  * Merge `b` flow analysis results into `a`.
106  * Params:
107  *      a = the path to merge `b` into
108  *      b = the other path
109  * Returns:
110  *      false means one of the paths skips construction
111  */
112 bool mergeCallSuper(ref CSX a, const CSX b) pure nothrow @safe
113 {
114     // This does a primitive flow analysis to support the restrictions
115     // regarding when and how constructors can appear.
116     // It merges the results of two paths.
117     // The two paths are `a` and `b`; the result is merged into `a`.
118     if (b == a)
119         return true;
120 
121     // Have ALL branches called a constructor?
122     const aAll = (a & (CSX.this_ctor | CSX.super_ctor)) != 0;
123     const bAll = (b & (CSX.this_ctor | CSX.super_ctor)) != 0;
124     // Have ANY branches called a constructor?
125     const aAny = (a & CSX.any_ctor) != 0;
126     const bAny = (b & CSX.any_ctor) != 0;
127     // Have any branches returned?
128     const aRet = (a & CSX.return_) != 0;
129     const bRet = (b & CSX.return_) != 0;
130     // Have any branches halted?
131     const aHalt = (a & CSX.halt) != 0;
132     const bHalt = (b & CSX.halt) != 0;
133     if (aHalt && bHalt)
134     {
135         a = CSX.halt;
136     }
137     else if ((!bHalt && bRet && !bAny && aAny) || (!aHalt && aRet && !aAny && bAny))
138     {
139         // If one has returned without a constructor call, there must not
140         // be ctor calls in the other.
141         return false;
142     }
143     else if (bHalt || bRet && bAll)
144     {
145         // If one branch has called a ctor and then exited, anything the
146         // other branch has done is OK (except returning without a
147         // ctor call, but we already checked that).
148         a |= b & (CSX.any_ctor | CSX.label);
149     }
150     else if (aHalt || aRet && aAll)
151     {
152         a = cast(CSX)(b | (a & (CSX.any_ctor | CSX.label)));
153     }
154     else if (aAll != bAll) // both branches must have called ctors, or both not
155         return false;
156     else
157     {
158         // If one returned without a ctor, remember that
159         if (bRet && !bAny)
160             a |= CSX.return_;
161         a |= b & (CSX.any_ctor | CSX.label);
162     }
163     return true;
164 }
165 
166 
167 /****************************************
168  * Merge `b` flow analysis results into `a`.
169  * Params:
170  *      a = the path to merge `b` into
171  *      b = the other path
172  * Returns:
173  *      false means either `a` or `b` skips initialization
174  */
175 bool mergeFieldInit(ref CSX a, const CSX b) pure nothrow @safe
176 {
177     if (b == a)
178         return true;
179 
180     // Have any branches returned?
181     const aRet = (a & CSX.return_) != 0;
182     const bRet = (b & CSX.return_) != 0;
183     // Have any branches halted?
184     const aHalt = (a & CSX.halt) != 0;
185     const bHalt = (b & CSX.halt) != 0;
186 
187     if (aHalt && bHalt)
188     {
189         a = CSX.halt;
190         return true;
191     }
192 
193     // The logic here is to prefer the branch that neither halts nor returns.
194     bool ok;
195     if (!bHalt && bRet)
196     {
197         // Branch b returns, no merging required.
198         ok = (b & CSX.this_ctor);
199     }
200     else if (!aHalt && aRet)
201     {
202         // Branch a returns, but b doesn't, b takes precedence.
203         ok = (a & CSX.this_ctor);
204         a = b;
205     }
206     else if (bHalt)
207     {
208         // Branch b halts, no merging required.
209         ok = (a & CSX.this_ctor);
210     }
211     else if (aHalt)
212     {
213         // Branch a halts, but b doesn't, b takes precedence.
214         ok = (b & CSX.this_ctor);
215         a = b;
216     }
217     else
218     {
219         // Neither branch returns nor halts, merge flags.
220         ok = !((a ^ b) & CSX.this_ctor);
221         a |= b;
222     }
223     return ok;
224 }