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  */
blockExit(Statement * s,FuncDeclaration * func,bool mustNotThrow)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     BlockExit be(func, mustNotThrow);
500     s->accept(&be);
501     return be.result;
502 }
503