]>
Commit | Line | Data |
---|---|---|
b4c522fa IB |
1 | |
2 | /* Compiler implementation of the D programming language | |
f3ed896c | 3 | * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved |
b4c522fa IB |
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 | ||
f9ab59ff | 11 | #include "root/dsystem.h" |
b4c522fa IB |
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 | ||
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 | ||
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 | } |