1
2/* Compiler implementation of the D programming language
3 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
4 * written by Walter Bright
5 * http://www.digitalmars.com
6 * Distributed under the Boost Software License, Version 1.0.
7 * http://www.boost.org/LICENSE_1_0.txt
8 * https://github.com/D-Programming-Language/dmd/blob/master/src/canthrow.c
9 */
10
11#include "root/dsystem.h"
12
13#include "mars.h"
14#include "init.h"
15#include "expression.h"
16#include "template.h"
17#include "statement.h"
18#include "mtype.h"
19#include "utf.h"
20#include "declaration.h"
21#include "aggregate.h"
22#include "scope.h"
23#include "attrib.h"
24#include "tokens.h"
25
26bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow);
27bool walkPostorder(Expression *e, StoppableVisitor *v);
28
29/********************************************
30 * Returns true if the expression may throw exceptions.
31 * If 'mustNotThrow' is true, generate an error if it throws
32 */
33
34bool canThrow(Expression *e, FuncDeclaration *func, bool mustNotThrow)
35{
36    //printf("Expression::canThrow(%d) %s\n", mustNotThrow, toChars());
37
38    // stop walking if we determine this expression can throw
39    class CanThrow : public StoppableVisitor
40    {
41        FuncDeclaration *func;
42        bool mustNotThrow;
43
44    public:
45        CanThrow(FuncDeclaration *func, bool mustNotThrow)
46            : func(func), mustNotThrow(mustNotThrow)
47        {
48        }
49
50        void visit(Expression *)
51        {
52        }
53
54        void visit(DeclarationExp *de)
55        {
56            stop = Dsymbol_canThrow(de->declaration, func, mustNotThrow);
57        }
58
59        void visit(CallExp *ce)
60        {
61            if (global.errors && !ce->e1->type)
62                return;                       // error recovery
63
64            /* If calling a function or delegate that is typed as nothrow,
65             * then this expression cannot throw.
66             * Note that pure functions can throw.
67             */
68            Type *t = ce->e1->type->toBasetype();
69            if (ce->f && ce->f == func)
70                return;
71            if (t->ty == Tfunction && ((TypeFunction *)t)->isnothrow)
72                return;
73            if (t->ty == Tdelegate && ((TypeFunction *)((TypeDelegate *)t)->next)->isnothrow)
74                return;
75
76            if (mustNotThrow)
77            {
78                if (ce->f)
79                {
80                    ce->error("%s '%s' is not nothrow",
81                        ce->f->kind(), ce->f->toPrettyChars());
82                }
83                else
84                {
85                    Expression *e1 = ce->e1;
86                    if (e1->op == TOKstar)   // print 'fp' if e1 is (*fp)
87                        e1 = ((PtrExp *)e1)->e1;
88                    ce->error("'%s' is not nothrow", e1->toChars());
89                }
90            }
91            stop = true;
92        }
93
94        void visit(NewExp *ne)
95        {
96            if (ne->member)
97            {
98                if (ne->allocator)
99                {
100                    // Bugzilla 14407
101                    Type *t = ne->allocator->type->toBasetype();
102                    if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
103                    {
104                        if (mustNotThrow)
105                        {
106                            ne->error("%s '%s' is not nothrow",
107                                ne->allocator->kind(), ne->allocator->toPrettyChars());
108                        }
109                        stop = true;
110                    }
111                }
112                // See if constructor call can throw
113                Type *t = ne->member->type->toBasetype();
114                if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
115                {
116                    if (mustNotThrow)
117                    {
118                        ne->error("%s '%s' is not nothrow",
119                            ne->member->kind(), ne->member->toPrettyChars());
120                    }
121                    stop = true;
122                }
123            }
124            // regard storage allocation failures as not recoverable
125        }
126
127        void visit(DeleteExp *de)
128        {
129            Type *tb = de->e1->type->toBasetype();
130            AggregateDeclaration *ad = NULL;
131            switch (tb->ty)
132            {
133            case Tclass:
134                ad = ((TypeClass *)tb)->sym;
135                break;
136
137            case Tpointer:
138                tb = ((TypePointer *)tb)->next->toBasetype();
139                if (tb->ty == Tstruct)
140                    ad = ((TypeStruct *)tb)->sym;
141                break;
142
143            case Tarray:
144            {
145                Type *tv = tb->nextOf()->baseElemOf();
146                if (tv->ty == Tstruct)
147                {
148                    ad = ((TypeStruct *)tv)->sym;
149                    break;
150                }
151            }
152
153            default:
154                break;
155            }
156            if (!ad)
157                return;
158
159            if (ad->dtor)
160            {
161                Type *t = ad->dtor->type->toBasetype();
162                if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
163                {
164                    if (mustNotThrow)
165                    {
166                        de->error("%s '%s' is not nothrow",
167                            ad->dtor->kind(), ad->dtor->toPrettyChars());
168                    }
169                    stop = true;
170                }
171            }
172            if (ad->aggDelete && tb->ty != Tarray)
173            {
174                Type *t = ad->aggDelete->type;
175                if (t->ty == Tfunction && !((TypeFunction *)t)->isnothrow)
176                {
177                    if (mustNotThrow)
178                    {
179                        de->error("%s '%s' is not nothrow",
180                            ad->aggDelete->kind(), ad->aggDelete->toPrettyChars());
181                    }
182                    stop = true;
183                }
184            }
185        }
186
187        void visit(AssignExp *ae)
188        {
189            // blit-init cannot throw
190            if (ae->op == TOKblit)
191                return;
192
193            /* Element-wise assignment could invoke postblits.
194             */
195            Type *t;
196            if (ae->type->toBasetype()->ty == Tsarray)
197            {
198                if (!ae->e2->isLvalue())
199                    return;
200                t = ae->type;
201            }
202            else if (ae->e1->op == TOKslice)
203                t = ((SliceExp *)ae->e1)->e1->type;
204            else
205                return;
206
207            Type *tv = t->baseElemOf();
208            if (tv->ty != Tstruct)
209                return;
210            StructDeclaration *sd = ((TypeStruct *)tv)->sym;
211            if (!sd->postblit || sd->postblit->type->ty != Tfunction)
212                return;
213
214            if (((TypeFunction *)sd->postblit->type)->isnothrow)
215                ;
216            else
217            {
218                if (mustNotThrow)
219                {
220                    ae->error("%s '%s' is not nothrow",
221                        sd->postblit->kind(), sd->postblit->toPrettyChars());
222                }
223                stop = true;
224            }
225        }
226
227        void visit(NewAnonClassExp *)
228        {
229            assert(0);          // should have been lowered by semantic()
230        }
231    };
232
233    CanThrow ct(func, mustNotThrow);
234    return walkPostorder(e, &ct);
235}
236
237/**************************************
238 * Does symbol, when initialized, throw?
239 * Mirrors logic in Dsymbol_toElem().
240 */
241
242bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow)
243{
244    AttribDeclaration *ad;
245    VarDeclaration *vd;
246    TemplateMixin *tm;
247    TupleDeclaration *td;
248
249    //printf("Dsymbol_toElem() %s\n", s->toChars());
250    ad = s->isAttribDeclaration();
251    if (ad)
252    {
253        Dsymbols *decl = ad->include(NULL, NULL);
254        if (decl && decl->dim)
255        {
256            for (size_t i = 0; i < decl->dim; i++)
257            {
258                s = (*decl)[i];
259                if (Dsymbol_canThrow(s, func, mustNotThrow))
260                    return true;
261            }
262        }
263    }
264    else if ((vd = s->isVarDeclaration()) != NULL)
265    {
266        s = s->toAlias();
267        if (s != vd)
268            return Dsymbol_canThrow(s, func, mustNotThrow);
269        if (vd->storage_class & STCmanifest)
270            ;
271        else if (vd->isStatic() || vd->storage_class & (STCextern | STCtls | STCgshared))
272            ;
273        else
274        {
275            if (vd->_init)
276            {
277                ExpInitializer *ie = vd->_init->isExpInitializer();
278                if (ie && canThrow(ie->exp, func, mustNotThrow))
279                    return true;
280            }
281            if (vd->needsScopeDtor())
282                return canThrow(vd->edtor, func, mustNotThrow);
283        }
284    }
285    else if ((tm = s->isTemplateMixin()) != NULL)
286    {
287        //printf("%s\n", tm->toChars());
288        if (tm->members)
289        {
290            for (size_t i = 0; i < tm->members->dim; i++)
291            {
292                Dsymbol *sm = (*tm->members)[i];
293                if (Dsymbol_canThrow(sm, func, mustNotThrow))
294                    return true;
295            }
296        }
297    }
298    else if ((td = s->isTupleDeclaration()) != NULL)
299    {
300        for (size_t i = 0; i < td->objects->dim; i++)
301        {
302            RootObject *o = (*td->objects)[i];
303            if (o->dyncast() == DYNCAST_EXPRESSION)
304            {
305                Expression *eo = (Expression *)o;
306                if (eo->op == TOKdsymbol)
307                {
308                    DsymbolExp *se = (DsymbolExp *)eo;
309                    if (Dsymbol_canThrow(se->s, func, mustNotThrow))
310                        return true;
311                }
312            }
313        }
314    }
315    return false;
316}
317