145
|
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 */
|
|
9
|
|
10 #include "statement.h"
|
|
11 #include "declaration.h"
|
|
12 #include "aggregate.h"
|
|
13 #include "id.h"
|
|
14
|
|
15 /* Only valid after semantic analysis
|
|
16 * If 'mustNotThrow' is true, generate an error if it throws
|
|
17 */
|
|
18 int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow)
|
|
19 {
|
|
20 class BlockExit : public Visitor
|
|
21 {
|
|
22 public:
|
|
23 FuncDeclaration *func;
|
|
24 bool mustNotThrow;
|
|
25 int result;
|
|
26
|
|
27 BlockExit(FuncDeclaration *func, bool mustNotThrow)
|
|
28 : func(func), mustNotThrow(mustNotThrow)
|
|
29 {
|
|
30 result = BEnone;
|
|
31 }
|
|
32
|
|
33 void visit(Statement *s)
|
|
34 {
|
|
35 printf("Statement::blockExit(%p)\n", s);
|
|
36 printf("%s\n", s->toChars());
|
|
37 assert(0);
|
|
38 result = BEany;
|
|
39 }
|
|
40
|
|
41 void visit(ErrorStatement *)
|
|
42 {
|
|
43 result = BEany;
|
|
44 }
|
|
45
|
|
46 void visit(ExpStatement *s)
|
|
47 {
|
|
48 result = BEfallthru;
|
|
49 if (s->exp)
|
|
50 {
|
|
51 if (s->exp->op == TOKhalt)
|
|
52 {
|
|
53 result = BEhalt;
|
|
54 return;
|
|
55 }
|
|
56 if (s->exp->op == TOKassert)
|
|
57 {
|
|
58 AssertExp *a = (AssertExp *)s->exp;
|
|
59 if (a->e1->isBool(false)) // if it's an assert(0)
|
|
60 {
|
|
61 result = BEhalt;
|
|
62 return;
|
|
63 }
|
|
64 }
|
|
65 if (canThrow(s->exp, func, mustNotThrow))
|
|
66 result |= BEthrow;
|
|
67 }
|
|
68 }
|
|
69
|
|
70 void visit(CompileStatement *)
|
|
71 {
|
|
72 assert(global.errors);
|
|
73 result = BEfallthru;
|
|
74 }
|
|
75
|
|
76 void visit(CompoundStatement *cs)
|
|
77 {
|
|
78 //printf("CompoundStatement::blockExit(%p) %d result = x%X\n", cs, cs->statements->dim, result);
|
|
79 result = BEfallthru;
|
|
80 Statement *slast = NULL;
|
|
81 for (size_t i = 0; i < cs->statements->dim; i++)
|
|
82 {
|
|
83 Statement *s = (*cs->statements)[i];
|
|
84 if (s)
|
|
85 {
|
|
86 //printf("result = x%x\n", result);
|
|
87 //printf("s: %s\n", s->toChars());
|
|
88 if (result & BEfallthru && slast)
|
|
89 {
|
|
90 slast = slast->last();
|
|
91 if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) &&
|
|
92 (s->isCaseStatement() || s->isDefaultStatement()))
|
|
93 {
|
|
94 // Allow if last case/default was empty
|
|
95 CaseStatement *sc = slast->isCaseStatement();
|
|
96 DefaultStatement *sd = slast->isDefaultStatement();
|
|
97 if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement() || sc->statement->isErrorStatement()))
|
|
98 ;
|
|
99 else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement() || sd->statement->isErrorStatement()))
|
|
100 ;
|
|
101 else
|
|
102 {
|
|
103 const char *gototype = s->isCaseStatement() ? "case" : "default";
|
|
104 s->deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
|
|
105 }
|
|
106 }
|
|
107 }
|
|
108
|
|
109 if (!(result & BEfallthru) && !s->comeFrom())
|
|
110 {
|
|
111 if (blockExit(s, func, mustNotThrow) != BEhalt && s->hasCode())
|
|
112 s->warning("statement is not reachable");
|
|
113 }
|
|
114 else
|
|
115 {
|
|
116 result &= ~BEfallthru;
|
|
117 result |= blockExit(s, func, mustNotThrow);
|
|
118 }
|
|
119 slast = s;
|
|
120 }
|
|
121 }
|
|
122 }
|
|
123
|
|
124 void visit(UnrolledLoopStatement *uls)
|
|
125 {
|
|
126 result = BEfallthru;
|
|
127 for (size_t i = 0; i < uls->statements->dim; i++)
|
|
128 {
|
|
129 Statement *s = (*uls->statements)[i];
|
|
130 if (s)
|
|
131 {
|
|
132 int r = blockExit(s, func, mustNotThrow);
|
|
133 result |= r & ~(BEbreak | BEcontinue | BEfallthru);
|
|
134 if ((r & (BEfallthru | BEcontinue | BEbreak)) == 0)
|
|
135 result &= ~BEfallthru;
|
|
136 }
|
|
137 }
|
|
138 }
|
|
139
|
|
140 void visit(ScopeStatement *s)
|
|
141 {
|
|
142 //printf("ScopeStatement::blockExit(%p)\n", s->statement);
|
|
143 result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru;
|
|
144 }
|
|
145
|
|
146 void visit(WhileStatement *)
|
|
147 {
|
|
148 assert(global.errors);
|
|
149 result = BEfallthru;
|
|
150 }
|
|
151
|
|
152 void visit(DoStatement *s)
|
|
153 {
|
|
154 if (s->_body)
|
|
155 {
|
|
156 result = blockExit(s->_body, func, mustNotThrow);
|
|
157 if (result == BEbreak)
|
|
158 {
|
|
159 result = BEfallthru;
|
|
160 return;
|
|
161 }
|
|
162 if (result & BEcontinue)
|
|
163 result |= BEfallthru;
|
|
164 }
|
|
165 else
|
|
166 result = BEfallthru;
|
|
167 if (result & BEfallthru)
|
|
168 {
|
|
169 if (canThrow(s->condition, func, mustNotThrow))
|
|
170 result |= BEthrow;
|
|
171 if (!(result & BEbreak) && s->condition->isBool(true))
|
|
172 result &= ~BEfallthru;
|
|
173 }
|
|
174 result &= ~(BEbreak | BEcontinue);
|
|
175 }
|
|
176
|
|
177 void visit(ForStatement *s)
|
|
178 {
|
|
179 result = BEfallthru;
|
|
180 if (s->_init)
|
|
181 {
|
|
182 result = blockExit(s->_init, func, mustNotThrow);
|
|
183 if (!(result & BEfallthru))
|
|
184 return;
|
|
185 }
|
|
186 if (s->condition)
|
|
187 {
|
|
188 if (canThrow(s->condition, func, mustNotThrow))
|
|
189 result |= BEthrow;
|
|
190 if (s->condition->isBool(true))
|
|
191 result &= ~BEfallthru;
|
|
192 else if (s->condition->isBool(false))
|
|
193 return;
|
|
194 }
|
|
195 else
|
|
196 result &= ~BEfallthru; // the body must do the exiting
|
|
197 if (s->_body)
|
|
198 {
|
|
199 int r = blockExit(s->_body, func, mustNotThrow);
|
|
200 if (r & (BEbreak | BEgoto))
|
|
201 result |= BEfallthru;
|
|
202 result |= r & ~(BEfallthru | BEbreak | BEcontinue);
|
|
203 }
|
|
204 if (s->increment && canThrow(s->increment, func, mustNotThrow))
|
|
205 result |= BEthrow;
|
|
206 }
|
|
207
|
|
208 void visit(ForeachStatement *s)
|
|
209 {
|
|
210 result = BEfallthru;
|
|
211 if (canThrow(s->aggr, func, mustNotThrow))
|
|
212 result |= BEthrow;
|
|
213 if (s->_body)
|
|
214 result |= blockExit(s->_body, func, mustNotThrow) & ~(BEbreak | BEcontinue);
|
|
215 }
|
|
216
|
|
217 void visit(ForeachRangeStatement *)
|
|
218 {
|
|
219 assert(global.errors);
|
|
220 result = BEfallthru;
|
|
221 }
|
|
222
|
|
223 void visit(IfStatement *s)
|
|
224 {
|
|
225 //printf("IfStatement::blockExit(%p)\n", s);
|
|
226
|
|
227 result = BEnone;
|
|
228 if (canThrow(s->condition, func, mustNotThrow))
|
|
229 result |= BEthrow;
|
|
230 if (s->condition->isBool(true))
|
|
231 {
|
|
232 if (s->ifbody)
|
|
233 result |= blockExit(s->ifbody, func, mustNotThrow);
|
|
234 else
|
|
235 result |= BEfallthru;
|
|
236 }
|
|
237 else if (s->condition->isBool(false))
|
|
238 {
|
|
239 if (s->elsebody)
|
|
240 result |= blockExit(s->elsebody, func, mustNotThrow);
|
|
241 else
|
|
242 result |= BEfallthru;
|
|
243 }
|
|
244 else
|
|
245 {
|
|
246 if (s->ifbody)
|
|
247 result |= blockExit(s->ifbody, func, mustNotThrow);
|
|
248 else
|
|
249 result |= BEfallthru;
|
|
250 if (s->elsebody)
|
|
251 result |= blockExit(s->elsebody, func, mustNotThrow);
|
|
252 else
|
|
253 result |= BEfallthru;
|
|
254 }
|
|
255 //printf("IfStatement::blockExit(%p) = x%x\n", s, result);
|
|
256 }
|
|
257
|
|
258 void visit(ConditionalStatement *s)
|
|
259 {
|
|
260 result = blockExit(s->ifbody, func, mustNotThrow);
|
|
261 if (s->elsebody)
|
|
262 result |= blockExit(s->elsebody, func, mustNotThrow);
|
|
263 }
|
|
264
|
|
265 void visit(PragmaStatement *)
|
|
266 {
|
|
267 result = BEfallthru;
|
|
268 }
|
|
269
|
|
270 void visit(StaticAssertStatement *)
|
|
271 {
|
|
272 result = BEfallthru;
|
|
273 }
|
|
274
|
|
275 void visit(SwitchStatement *s)
|
|
276 {
|
|
277 result = BEnone;
|
|
278 if (canThrow(s->condition, func, mustNotThrow))
|
|
279 result |= BEthrow;
|
|
280 if (s->_body)
|
|
281 {
|
|
282 result |= blockExit(s->_body, func, mustNotThrow);
|
|
283 if (result & BEbreak)
|
|
284 {
|
|
285 result |= BEfallthru;
|
|
286 result &= ~BEbreak;
|
|
287 }
|
|
288 }
|
|
289 else
|
|
290 result |= BEfallthru;
|
|
291 }
|
|
292
|
|
293 void visit(CaseStatement *s)
|
|
294 {
|
|
295 result = blockExit(s->statement, func, mustNotThrow);
|
|
296 }
|
|
297
|
|
298 void visit(DefaultStatement *s)
|
|
299 {
|
|
300 result = blockExit(s->statement, func, mustNotThrow);
|
|
301 }
|
|
302
|
|
303 void visit(GotoDefaultStatement *)
|
|
304 {
|
|
305 result = BEgoto;
|
|
306 }
|
|
307
|
|
308 void visit(GotoCaseStatement *)
|
|
309 {
|
|
310 result = BEgoto;
|
|
311 }
|
|
312
|
|
313 void visit(SwitchErrorStatement *)
|
|
314 {
|
|
315 // Switch errors are non-recoverable
|
|
316 result = BEhalt;
|
|
317 }
|
|
318
|
|
319 void visit(ReturnStatement *s)
|
|
320 {
|
|
321 result = BEreturn;
|
|
322 if (s->exp && canThrow(s->exp, func, mustNotThrow))
|
|
323 result |= BEthrow;
|
|
324 }
|
|
325
|
|
326 void visit(BreakStatement *s)
|
|
327 {
|
|
328 //printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak);
|
|
329 result = s->ident ? BEgoto : BEbreak;
|
|
330 }
|
|
331
|
|
332 void visit(ContinueStatement *s)
|
|
333 {
|
|
334 result = s->ident ? BEgoto : BEcontinue;
|
|
335 }
|
|
336
|
|
337 void visit(SynchronizedStatement *s)
|
|
338 {
|
|
339 result = s->_body ? blockExit(s->_body, func, mustNotThrow) : BEfallthru;
|
|
340 }
|
|
341
|
|
342 void visit(WithStatement *s)
|
|
343 {
|
|
344 result = BEnone;
|
|
345 if (canThrow(s->exp, func, mustNotThrow))
|
|
346 result = BEthrow;
|
|
347 if (s->_body)
|
|
348 result |= blockExit(s->_body, func, mustNotThrow);
|
|
349 else
|
|
350 result |= BEfallthru;
|
|
351 }
|
|
352
|
|
353 void visit(TryCatchStatement *s)
|
|
354 {
|
|
355 assert(s->_body);
|
|
356 result = blockExit(s->_body, func, false);
|
|
357
|
|
358 int catchresult = 0;
|
|
359 for (size_t i = 0; i < s->catches->dim; i++)
|
|
360 {
|
|
361 Catch *c = (*s->catches)[i];
|
|
362 if (c->type == Type::terror)
|
|
363 continue;
|
|
364
|
|
365 int cresult;
|
|
366 if (c->handler)
|
|
367 cresult = blockExit(c->handler, func, mustNotThrow);
|
|
368 else
|
|
369 cresult = BEfallthru;
|
|
370
|
|
371 /* If we're catching Object, then there is no throwing
|
|
372 */
|
|
373 Identifier *id = c->type->toBasetype()->isClassHandle()->ident;
|
|
374 if (c->internalCatch && (cresult & BEfallthru))
|
|
375 {
|
|
376 // Bugzilla 11542: leave blockExit flags of the body
|
|
377 cresult &= ~BEfallthru;
|
|
378 }
|
|
379 else if (id == Id::Object || id == Id::Throwable)
|
|
380 {
|
|
381 result &= ~(BEthrow | BEerrthrow);
|
|
382 }
|
|
383 else if (id == Id::Exception)
|
|
384 {
|
|
385 result &= ~BEthrow;
|
|
386 }
|
|
387 catchresult |= cresult;
|
|
388 }
|
|
389 if (mustNotThrow && (result & BEthrow))
|
|
390 {
|
|
391 // now explain why this is nothrow
|
|
392 blockExit(s->_body, func, mustNotThrow);
|
|
393 }
|
|
394 result |= catchresult;
|
|
395 }
|
|
396
|
|
397 void visit(TryFinallyStatement *s)
|
|
398 {
|
|
399 result = BEfallthru;
|
|
400 if (s->_body)
|
|
401 result = blockExit(s->_body, func, false);
|
|
402
|
|
403 // check finally body as well, it may throw (bug #4082)
|
|
404 int finalresult = BEfallthru;
|
|
405 if (s->finalbody)
|
|
406 finalresult = blockExit(s->finalbody, func, false);
|
|
407
|
|
408 // If either body or finalbody halts
|
|
409 if (result == BEhalt)
|
|
410 finalresult = BEnone;
|
|
411 if (finalresult == BEhalt)
|
|
412 result = BEnone;
|
|
413
|
|
414 if (mustNotThrow)
|
|
415 {
|
|
416 // now explain why this is nothrow
|
|
417 if (s->_body && (result & BEthrow))
|
|
418 blockExit(s->_body, func, mustNotThrow);
|
|
419 if (s->finalbody && (finalresult & BEthrow))
|
|
420 blockExit(s->finalbody, func, mustNotThrow);
|
|
421 }
|
|
422
|
|
423 #if 0
|
|
424 // Bugzilla 13201: Mask to prevent spurious warnings for
|
|
425 // destructor call, exit of synchronized statement, etc.
|
|
426 if (result == BEhalt && finalresult != BEhalt && s->finalbody &&
|
|
427 s->finalbody->hasCode())
|
|
428 {
|
|
429 s->finalbody->warning("statement is not reachable");
|
|
430 }
|
|
431 #endif
|
|
432
|
|
433 if (!(finalresult & BEfallthru))
|
|
434 result &= ~BEfallthru;
|
|
435 result |= finalresult & ~BEfallthru;
|
|
436 }
|
|
437
|
|
438 void visit(OnScopeStatement *)
|
|
439 {
|
|
440 // At this point, this statement is just an empty placeholder
|
|
441 result = BEfallthru;
|
|
442 }
|
|
443
|
|
444 void visit(ThrowStatement *s)
|
|
445 {
|
|
446 if (s->internalThrow)
|
|
447 {
|
|
448 // Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow.
|
|
449 result = BEfallthru;
|
|
450 return;
|
|
451 }
|
|
452
|
|
453 Type *t = s->exp->type->toBasetype();
|
|
454 ClassDeclaration *cd = t->isClassHandle();
|
|
455 assert(cd);
|
|
456
|
|
457 if (cd == ClassDeclaration::errorException ||
|
|
458 ClassDeclaration::errorException->isBaseOf(cd, NULL))
|
|
459 {
|
|
460 result = BEerrthrow;
|
|
461 return;
|
|
462 }
|
|
463 if (mustNotThrow)
|
|
464 s->error("%s is thrown but not caught", s->exp->type->toChars());
|
|
465
|
|
466 result = BEthrow;
|
|
467 }
|
|
468
|
|
469 void visit(GotoStatement *)
|
|
470 {
|
|
471 //printf("GotoStatement::blockExit(%p)\n", s);
|
|
472 result = BEgoto;
|
|
473 }
|
|
474
|
|
475 void visit(LabelStatement *s)
|
|
476 {
|
|
477 //printf("LabelStatement::blockExit(%p)\n", s);
|
|
478 result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru;
|
|
479 if (s->breaks)
|
|
480 result |= BEfallthru;
|
|
481 }
|
|
482
|
|
483 void visit(CompoundAsmStatement *s)
|
|
484 {
|
|
485 if (mustNotThrow && !(s->stc & STCnothrow))
|
|
486 s->deprecation("asm statement is assumed to throw - mark it with 'nothrow' if it does not");
|
|
487
|
|
488 // Assume the worst
|
|
489 result = BEfallthru | BEreturn | BEgoto | BEhalt;
|
|
490 if (!(s->stc & STCnothrow)) result |= BEthrow;
|
|
491 }
|
|
492
|
|
493 void visit(ImportStatement *)
|
|
494 {
|
|
495 result = BEfallthru;
|
|
496 }
|
|
497 };
|
|
498
|
|
499 if (!s)
|
|
500 return BEfallthru;
|
|
501 BlockExit be(func, mustNotThrow);
|
|
502 s->accept(&be);
|
|
503 return be.result;
|
|
504 }
|