Mercurial > hg > CbC > CbC_gcc
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 } |