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
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, 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