comparison gcc/d/dmd/blockexit.c @ 145:1830386684a0

gcc-9.2.0
author anatofuz
date Thu, 13 Feb 2020 11:34:05 +0900
parents
children
comparison
equal deleted inserted replaced
131:84e7813d76e9 145:1830386684a0
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 }