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 }