1 
2 /* Compiler implementation of the D programming language
3  * Copyright (C) 1999-2021 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 
26 bool Dsymbol_canThrow(Dsymbol *s, FuncDeclaration *func, bool mustNotThrow);
27 bool 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 
canThrow(Expression * e,FuncDeclaration * func,bool mustNotThrow)34 bool 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 
Dsymbol_canThrow(Dsymbol * s,FuncDeclaration * func,bool mustNotThrow)242 bool 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);
254         if (decl && decl->length)
255         {
256             for (size_t i = 0; i < decl->length; 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->length; 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->length; 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