1 /**
2 * Manage flow analysis for constructors.
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/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.globals : Loc;
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
allocFieldinitCtorFlow46 void allocFieldinit(size_t dim)
47 {
48 fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim];
49 }
50
freeFieldinitCtorFlow51 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 */
cloneCtorFlow64 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 */
orCSXCtorFlow74 void orCSX(CSX csx) nothrow pure
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 */
ORCtorFlow86 void OR(const ref CtorFlow ctorflow) pure nothrow
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 */
mergeCallSuper(ref CSX a,const CSX b)112 bool mergeCallSuper(ref CSX a, const CSX b) pure nothrow
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 */
mergeFieldInit(ref CSX a,const CSX b)175 bool mergeFieldInit(ref CSX a, const CSX b) pure nothrow
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 }
225
226