| /** | |
| * Manage flow analysis for constructors. | |
| * | |
| * Copyright: Copyright (C) 1999-2022 by The D Language Foundation, All Rights Reserved | |
| * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright) | |
| * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) | |
| * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/ctorflow.d, _ctorflow.d) | |
| * Documentation: https://dlang.org/phobos/dmd_ctorflow.html | |
| * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/ctorflow.d | |
| */ | |
| module dmd.ctorflow; | |
| import core.stdc.stdio; | |
| import dmd.root.rmem; | |
| import dmd.globals : Loc; | |
| enum CSX : ushort | |
| { | |
| none = 0, | |
| this_ctor = 0x01, /// called this() | |
| super_ctor = 0x02, /// called super() | |
| label = 0x04, /// seen a label | |
| return_ = 0x08, /// seen a return statement | |
| any_ctor = 0x10, /// either this() or super() was called | |
| halt = 0x20, /// assert(0) | |
| } | |
| /// Individual field in the Ctor with information about its callees and location. | |
| struct FieldInit | |
| { | |
| CSX csx; /// information about the field's callees | |
| Loc loc; /// location of the field initialization | |
| } | |
| /*********** | |
| * Primitive flow analysis for constructors | |
| */ | |
| struct CtorFlow | |
| { | |
| CSX callSuper; /// state of calling other constructors | |
| FieldInit[] fieldinit; /// state of field initializations | |
| void allocFieldinit(size_t dim) | |
| { | |
| fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim]; | |
| } | |
| void freeFieldinit() | |
| { | |
| if (fieldinit.ptr) | |
| mem.xfree(fieldinit.ptr); | |
| fieldinit = null; | |
| } | |
| /*********************** | |
| * Create a deep copy of `this` | |
| * Returns: | |
| * a copy | |
| */ | |
| CtorFlow clone() | |
| { | |
| return CtorFlow(callSuper, fieldinit.arraydup); | |
| } | |
| /********************************** | |
| * Set CSX bits in flow analysis state | |
| * Params: | |
| * csx = bits to set | |
| */ | |
| void orCSX(CSX csx) nothrow pure | |
| { | |
| callSuper |= csx; | |
| foreach (ref u; fieldinit) | |
| u.csx |= csx; | |
| } | |
| /****************************** | |
| * OR CSX bits to `this` | |
| * Params: | |
| * ctorflow = bits to OR in | |
| */ | |
| void OR(const ref CtorFlow ctorflow) pure nothrow | |
| { | |
| callSuper |= ctorflow.callSuper; | |
| if (fieldinit.length && ctorflow.fieldinit.length) | |
| { | |
| assert(fieldinit.length == ctorflow.fieldinit.length); | |
| foreach (i, u; ctorflow.fieldinit) | |
| { | |
| auto fi = &fieldinit[i]; | |
| fi.csx |= u.csx; | |
| if (fi.loc is Loc.init) | |
| fi.loc = u.loc; | |
| } | |
| } | |
| } | |
| } | |
| /**************************************** | |
| * Merge `b` flow analysis results into `a`. | |
| * Params: | |
| * a = the path to merge `b` into | |
| * b = the other path | |
| * Returns: | |
| * false means one of the paths skips construction | |
| */ | |
| bool mergeCallSuper(ref CSX a, const CSX b) pure nothrow | |
| { | |
| // This does a primitive flow analysis to support the restrictions | |
| // regarding when and how constructors can appear. | |
| // It merges the results of two paths. | |
| // The two paths are `a` and `b`; the result is merged into `a`. | |
| if (b == a) | |
| return true; | |
| // Have ALL branches called a constructor? | |
| const aAll = (a & (CSX.this_ctor | CSX.super_ctor)) != 0; | |
| const bAll = (b & (CSX.this_ctor | CSX.super_ctor)) != 0; | |
| // Have ANY branches called a constructor? | |
| const aAny = (a & CSX.any_ctor) != 0; | |
| const bAny = (b & CSX.any_ctor) != 0; | |
| // Have any branches returned? | |
| const aRet = (a & CSX.return_) != 0; | |
| const bRet = (b & CSX.return_) != 0; | |
| // Have any branches halted? | |
| const aHalt = (a & CSX.halt) != 0; | |
| const bHalt = (b & CSX.halt) != 0; | |
| if (aHalt && bHalt) | |
| { | |
| a = CSX.halt; | |
| } | |
| else if ((!bHalt && bRet && !bAny && aAny) || (!aHalt && aRet && !aAny && bAny)) | |
| { | |
| // If one has returned without a constructor call, there must not | |
| // be ctor calls in the other. | |
| return false; | |
| } | |
| else if (bHalt || bRet && bAll) | |
| { | |
| // If one branch has called a ctor and then exited, anything the | |
| // other branch has done is OK (except returning without a | |
| // ctor call, but we already checked that). | |
| a |= b & (CSX.any_ctor | CSX.label); | |
| } | |
| else if (aHalt || aRet && aAll) | |
| { | |
| a = cast(CSX)(b | (a & (CSX.any_ctor | CSX.label))); | |
| } | |
| else if (aAll != bAll) // both branches must have called ctors, or both not | |
| return false; | |
| else | |
| { | |
| // If one returned without a ctor, remember that | |
| if (bRet && !bAny) | |
| a |= CSX.return_; | |
| a |= b & (CSX.any_ctor | CSX.label); | |
| } | |
| return true; | |
| } | |
| /**************************************** | |
| * Merge `b` flow analysis results into `a`. | |
| * Params: | |
| * a = the path to merge `b` into | |
| * b = the other path | |
| * Returns: | |
| * false means either `a` or `b` skips initialization | |
| */ | |
| bool mergeFieldInit(ref CSX a, const CSX b) pure nothrow | |
| { | |
| if (b == a) | |
| return true; | |
| // Have any branches returned? | |
| const aRet = (a & CSX.return_) != 0; | |
| const bRet = (b & CSX.return_) != 0; | |
| // Have any branches halted? | |
| const aHalt = (a & CSX.halt) != 0; | |
| const bHalt = (b & CSX.halt) != 0; | |
| if (aHalt && bHalt) | |
| { | |
| a = CSX.halt; | |
| return true; | |
| } | |
| // The logic here is to prefer the branch that neither halts nor returns. | |
| bool ok; | |
| if (!bHalt && bRet) | |
| { | |
| // Branch b returns, no merging required. | |
| ok = (b & CSX.this_ctor); | |
| } | |
| else if (!aHalt && aRet) | |
| { | |
| // Branch a returns, but b doesn't, b takes precedence. | |
| ok = (a & CSX.this_ctor); | |
| a = b; | |
| } | |
| else if (bHalt) | |
| { | |
| // Branch b halts, no merging required. | |
| ok = (a & CSX.this_ctor); | |
| } | |
| else if (aHalt) | |
| { | |
| // Branch a halts, but b doesn't, b takes precedence. | |
| ok = (b & CSX.this_ctor); | |
| a = b; | |
| } | |
| else | |
| { | |
| // Neither branch returns nor halts, merge flags. | |
| ok = !((a ^ b) & CSX.this_ctor); | |
| a |= b; | |
| } | |
| return ok; | |
| } |