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

46
    void allocFieldinit(size_t dim)
47
    {
48 2
        fieldinit = (cast(FieldInit*)mem.xcalloc(FieldInit.sizeof, dim))[0 .. dim];
49
    }
50

51
    void freeFieldinit()
52
    {
53 2
        if (fieldinit.ptr)
54 2
            mem.xfree(fieldinit.ptr);
55

56 2
        fieldinit = null;
57
    }
58

59
    /***********************
60
     * Create a deep copy of `this`
61
     * Returns:
62
     *  a copy
63
     */
64
    CtorFlow clone()
65
    {
66 2
        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 2
        callSuper |= csx;
77 2
        foreach (ref u; fieldinit)
78 2
            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 2
        callSuper |= ctorflow.callSuper;
89 2
        if (fieldinit.length && ctorflow.fieldinit.length)
90
        {
91 2
            assert(fieldinit.length == ctorflow.fieldinit.length);
92 2
            foreach (i, u; ctorflow.fieldinit)
93
            {
94 2
                auto fi = &fieldinit[i];
95 2
                fi.csx |= u.csx;
96 2
                if (fi.loc is Loc.init)
97 2
                    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 2
    if (b == a)
119 2
        return true;
120

121
    // Have ALL branches called a constructor?
122 2
    const aAll = (a & (CSX.this_ctor | CSX.super_ctor)) != 0;
123 2
    const bAll = (b & (CSX.this_ctor | CSX.super_ctor)) != 0;
124
    // Have ANY branches called a constructor?
125 2
    const aAny = (a & CSX.any_ctor) != 0;
126 2
    const bAny = (b & CSX.any_ctor) != 0;
127
    // Have any branches returned?
128 2
    const aRet = (a & CSX.return_) != 0;
129 2
    const bRet = (b & CSX.return_) != 0;
130
    // Have any branches halted?
131 2
    const aHalt = (a & CSX.halt) != 0;
132 2
    const bHalt = (b & CSX.halt) != 0;
133 2
    if (aHalt && bHalt)
134
    {
135 2
        a = CSX.halt;
136
    }
137 2
    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 2
        return false;
142
    }
143 2
    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 2
        a |= b & (CSX.any_ctor | CSX.label);
149
    }
150 2
    else if (aHalt || aRet && aAll)
151
    {
152 2
        a = cast(CSX)(b | (a & (CSX.any_ctor | CSX.label)));
153
    }
154 2
    else if (aAll != bAll) // both branches must have called ctors, or both not
155 2
        return false;
156
    else
157
    {
158
        // If one returned without a ctor, remember that
159 2
        if (bRet && !bAny)
160 2
            a |= CSX.return_;
161 2
        a |= b & (CSX.any_ctor | CSX.label);
162
    }
163 2
    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 2
    if (b == a)
178 2
        return true;
179

180
    // Have any branches returned?
181 2
    const aRet = (a & CSX.return_) != 0;
182 2
    const bRet = (b & CSX.return_) != 0;
183
    // Have any branches halted?
184 2
    const aHalt = (a & CSX.halt) != 0;
185 2
    const bHalt = (b & CSX.halt) != 0;
186

187 2
    if (aHalt && bHalt)
188
    {
189 0
        a = CSX.halt;
190 0
        return true;
191
    }
192

193
    // The logic here is to prefer the branch that neither halts nor returns.
194 2
    bool ok;
195 2
    if (!bHalt && bRet)
196
    {
197
        // Branch b returns, no merging required.
198 2
        ok = (b & CSX.this_ctor);
199
    }
200 2
    else if (!aHalt && aRet)
201
    {
202
        // Branch a returns, but b doesn't, b takes precedence.
203 0
        ok = (a & CSX.this_ctor);
204 0
        a = b;
205
    }
206 2
    else if (bHalt)
207
    {
208
        // Branch b halts, no merging required.
209 0
        ok = (a & CSX.this_ctor);
210
    }
211 2
    else if (aHalt)
212
    {
213
        // Branch a halts, but b doesn't, b takes precedence.
214 2
        ok = (b & CSX.this_ctor);
215 2
        a = b;
216
    }
217
    else
218
    {
219
        // Neither branch returns nor halts, merge flags.
220 2
        ok = !((a ^ b) & CSX.this_ctor);
221 2
        a |= b;
222
    }
223 2
    return ok;
224
}
225

Read our documentation on viewing source code .

Loading