1
/**
2
 * Perform checks for `nothrow`.
3
 *
4
 * Specification: $(LINK2 https://dlang.org/spec/function.html#nothrow-functions, Nothrow Functions)
5
 *
6
 * Copyright:   Copyright (C) 1999-2020 by The D Language Foundation, All Rights Reserved
7
 * Authors:     $(LINK2 http://www.digitalmars.com, Walter Bright)
8
 * License:     $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9
 * Source:      $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/canthrow.d, _canthrow.d)
10
 * Documentation:  https://dlang.org/phobos/dmd_canthrow.html
11
 * Coverage:    https://codecov.io/gh/dlang/dmd/src/master/src/dmd/canthrow.d
12
 */
13

14
module dmd.canthrow;
15

16
import dmd.aggregate;
17
import dmd.apply;
18
import dmd.arraytypes;
19
import dmd.attrib;
20
import dmd.declaration;
21
import dmd.dstruct;
22
import dmd.dsymbol;
23
import dmd.dtemplate;
24
import dmd.expression;
25
import dmd.func;
26
import dmd.globals;
27
import dmd.init;
28
import dmd.mtype;
29
import dmd.root.rootobject;
30
import dmd.tokens;
31
import dmd.visitor;
32

33
/********************************************
34
 * Returns true if the expression may throw exceptions.
35
 * If 'mustNotThrow' is true, generate an error if it throws
36
 */
37
extern (C++) bool canThrow(Expression e, FuncDeclaration func, bool mustNotThrow)
38
{
39
    //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
40
    // stop walking if we determine this expression can throw
41
    extern (C++) final class CanThrow : StoppableVisitor
42
    {
43
        alias visit = typeof(super).visit;
44
        FuncDeclaration func;
45
        bool mustNotThrow;
46

47
    public:
48 1
        extern (D) this(FuncDeclaration func, bool mustNotThrow)
49
        {
50 1
            this.func = func;
51 1
            this.mustNotThrow = mustNotThrow;
52
        }
53

54
        void checkFuncThrows(Expression e, FuncDeclaration f)
55
        {
56 1
            auto tf = f.type.toBasetype().isTypeFunction();
57 1
            if (tf && !tf.isnothrow)
58
            {
59 1
                if (mustNotThrow)
60
                {
61 1
                    e.error("%s `%s` is not `nothrow`",
62
                        f.kind(), f.toPrettyChars());
63
                }
64 1
                stop = true;  // if any function throws, then the whole expression throws
65
            }
66
        }
67

68
        override void visit(Expression)
69
        {
70
        }
71

72
        override void visit(DeclarationExp de)
73
        {
74 1
            stop = Dsymbol_canThrow(de.declaration, func, mustNotThrow);
75
        }
76

77
        override void visit(CallExp ce)
78
        {
79 1
            if (ce.inDebugStatement)
80 1
                return;
81

82 1
            if (global.errors && !ce.e1.type)
83 0
                return; // error recovery
84
            /* If calling a function or delegate that is typed as nothrow,
85
             * then this expression cannot throw.
86
             * Note that pure functions can throw.
87
             */
88 1
            if (ce.f && ce.f == func)
89 1
                return;
90 1
            Type t = ce.e1.type.toBasetype();
91 1
            auto tf = t.isTypeFunction();
92 1
            if (tf && tf.isnothrow)
93 1
                return;
94
            else
95
            {
96 1
                auto td = t.isTypeDelegate();
97 1
                if (td && td.nextOf().isTypeFunction().isnothrow)
98 1
                    return;
99
            }
100

101 1
            if (ce.f)
102 1
                checkFuncThrows(ce, ce.f);
103 1
            else if (mustNotThrow)
104
            {
105 0
                auto e1 = ce.e1;
106 0
                if (auto pe = e1.isPtrExp())   // print 'fp' if e1 is (*fp)
107 0
                    e1 = pe.e1;
108 0
                ce.error("`%s` is not `nothrow`", e1.toChars());
109
            }
110 1
            stop = true;
111
        }
112

113
        override void visit(NewExp ne)
114
        {
115 1
            if (ne.member)
116
            {
117 1
                if (ne.allocator)
118
                    // https://issues.dlang.org/show_bug.cgi?id=14407
119 0
                    checkFuncThrows(ne, ne.allocator);
120

121
                // See if constructor call can throw
122 1
                checkFuncThrows(ne, ne.member);
123
            }
124
            // regard storage allocation failures as not recoverable
125
        }
126

127
        override void visit(DeleteExp de)
128
        {
129 1
            Type tb = de.e1.type.toBasetype();
130 1
            AggregateDeclaration ad = null;
131 1
            switch (tb.ty)
132
            {
133 1
            case Tclass:
134 1
                ad = tb.isTypeClass().sym;
135 1
                break;
136

137 1
            case Tpointer:
138 1
            case Tarray:
139 1
                auto ts = tb.nextOf().baseElemOf().isTypeStruct();
140 1
                if (!ts)
141 1
                    return;
142 1
                ad = ts.sym;
143 1
                break;
144

145 0
            default:
146 0
                assert(0);  // error should have been detected by semantic()
147
            }
148

149 1
            if (ad.dtor)
150 1
                checkFuncThrows(de, ad.dtor);
151
        }
152

153
        override void visit(AssignExp ae)
154
        {
155
            // blit-init cannot throw
156 1
            if (ae.op == TOK.blit)
157 1
                return;
158
            /* Element-wise assignment could invoke postblits.
159
             */
160 1
            Type t;
161 1
            if (ae.type.toBasetype().ty == Tsarray)
162
            {
163 1
                if (!ae.e2.isLvalue())
164 1
                    return;
165 1
                t = ae.type;
166
            }
167 1
            else if (auto se = ae.e1.isSliceExp())
168 1
                t = se.e1.type;
169
            else
170 1
                return;
171

172 1
            if (auto ts = t.baseElemOf().isTypeStruct())
173 1
                if (auto postblit = ts.sym.postblit)
174 1
                    checkFuncThrows(ae, postblit);
175
        }
176

177
        override void visit(NewAnonClassExp)
178
        {
179 0
            assert(0); // should have been lowered by semantic()
180
        }
181
    }
182

183 1
    scope CanThrow ct = new CanThrow(func, mustNotThrow);
184 1
    return walkPostorder(e, ct);
185
}
186

187
/**************************************
188
 * Does symbol, when initialized, throw?
189
 * Mirrors logic in Dsymbol_toElem().
190
 */
191
private bool Dsymbol_canThrow(Dsymbol s, FuncDeclaration func, bool mustNotThrow)
192
{
193
    int symbolDg(Dsymbol s)
194
    {
195 1
        return Dsymbol_canThrow(s, func, mustNotThrow);
196
    }
197

198
    //printf("Dsymbol_toElem() %s\n", s.toChars());
199 1
    if (auto vd = s.isVarDeclaration())
200
    {
201 1
        s = s.toAlias();
202 1
        if (s != vd)
203 1
            return Dsymbol_canThrow(s, func, mustNotThrow);
204 1
        if (vd.storage_class & STC.manifest)
205
        {
206
        }
207 1
        else if (vd.isStatic() || vd.storage_class & (STC.extern_ | STC.tls | STC.gshared))
208
        {
209
        }
210
        else
211
        {
212 1
            if (vd._init)
213
            {
214 1
                if (auto ie = vd._init.isExpInitializer())
215 1
                    if (canThrow(ie.exp, func, mustNotThrow))
216 1
                        return true;
217
            }
218 1
            if (vd.needsScopeDtor())
219 1
                return canThrow(vd.edtor, func, mustNotThrow);
220
        }
221
    }
222 1
    else if (auto ad = s.isAttribDeclaration())
223
    {
224 1
        return ad.include(null).foreachDsymbol(&symbolDg) != 0;
225
    }
226 1
    else if (auto tm = s.isTemplateMixin())
227
    {
228 0
        return tm.members.foreachDsymbol(&symbolDg) != 0;
229
    }
230 1
    else if (auto td = s.isTupleDeclaration())
231
    {
232 1
        for (size_t i = 0; i < td.objects.dim; i++)
233
        {
234 1
            RootObject o = (*td.objects)[i];
235 1
            if (o.dyncast() == DYNCAST.expression)
236
            {
237 1
                Expression eo = cast(Expression)o;
238 1
                if (auto se = eo.isDsymbolExp())
239
                {
240 1
                    if (Dsymbol_canThrow(se.s, func, mustNotThrow))
241 1
                        return true;
242
                }
243
            }
244
        }
245
    }
246 1
    return false;
247
}

Read our documentation on viewing source code .

Loading