Home | History | Annotate | Line # | Download | only in dmd
      1 /**
      2  * Manage flow analysis for constructors.
      3  *
      4  * Copyright:   Copyright (C) 1999-2022 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.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 
     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
     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
     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
    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
    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