d: Merge upstream dmd 3982604c5, druntime bc58b1e9, phobos 12329adb6.
[official-gcc.git] / gcc / d / dmd / blockexit.d
blob5945644482a281ecefbfb69ab644963ba7d08fb8
1 /**
2 * Find out in what ways control flow can exit a statement block.
4 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
7 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/blockexit.d, _blockexit.d)
8 * Documentation: https://dlang.org/phobos/dmd_blockexit.html
9 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/blockexit.d
12 module dmd.blockexit;
14 import core.stdc.stdio;
16 import dmd.arraytypes;
17 import dmd.astenums;
18 import dmd.canthrow;
19 import dmd.dclass;
20 import dmd.declaration;
21 import dmd.expression;
22 import dmd.func;
23 import dmd.globals;
24 import dmd.id;
25 import dmd.identifier;
26 import dmd.mtype;
27 import dmd.statement;
28 import dmd.tokens;
29 import dmd.visitor;
31 /**
32 * BE stands for BlockExit.
34 * It indicates if a statement does transfer control to another block.
35 * A block is a sequence of statements enclosed in { }
37 enum BE : int
39 none = 0,
40 fallthru = 1,
41 throw_ = 2,
42 return_ = 4,
43 goto_ = 8,
44 halt = 0x10,
45 break_ = 0x20,
46 continue_ = 0x40,
47 errthrow = 0x80,
48 any = (fallthru | throw_ | return_ | goto_ | halt),
52 /*********************************************
53 * Determine mask of ways that a statement can exit.
55 * Only valid after semantic analysis.
56 * Params:
57 * s = statement to check for block exit status
58 * func = function that statement s is in
59 * mustNotThrow = generate an error if it throws
60 * Returns:
61 * BE.xxxx
63 int blockExit(Statement s, FuncDeclaration func, bool mustNotThrow)
65 extern (C++) final class BlockExit : Visitor
67 alias visit = Visitor.visit;
68 public:
69 FuncDeclaration func;
70 bool mustNotThrow;
71 int result;
73 extern (D) this(FuncDeclaration func, bool mustNotThrow)
75 this.func = func;
76 this.mustNotThrow = mustNotThrow;
77 result = BE.none;
80 override void visit(Statement s)
82 printf("Statement::blockExit(%p)\n", s);
83 printf("%s\n", s.toChars());
84 assert(0);
87 override void visit(ErrorStatement s)
89 result = BE.none;
92 override void visit(ExpStatement s)
94 result = BE.fallthru;
95 if (s.exp)
97 if (s.exp.op == EXP.halt)
99 result = BE.halt;
100 return;
102 if (s.exp.op == EXP.assert_)
104 AssertExp a = cast(AssertExp)s.exp;
105 if (a.e1.toBool().hasValue(false)) // if it's an assert(0)
107 result = BE.halt;
108 return;
111 if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn())
112 result = BE.halt;
113 if (canThrow(s.exp, func, mustNotThrow))
114 result |= BE.throw_;
118 override void visit(CompileStatement s)
120 assert(global.errors);
121 result = BE.fallthru;
124 override void visit(CompoundStatement cs)
126 //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.dim, result);
127 result = BE.fallthru;
128 Statement slast = null;
129 foreach (s; *cs.statements)
131 if (s)
133 //printf("result = x%x\n", result);
134 //printf("s: %s\n", s.toChars());
135 if (result & BE.fallthru && slast)
137 slast = slast.last();
138 if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement()))
140 // Allow if last case/default was empty
141 CaseStatement sc = slast.isCaseStatement();
142 DefaultStatement sd = slast.isDefaultStatement();
143 if (sc && (!sc.statement.hasCode() || sc.statement.isCaseStatement() || sc.statement.isErrorStatement()))
146 else if (sd && (!sd.statement.hasCode() || sd.statement.isCaseStatement() || sd.statement.isErrorStatement()))
149 else if (!func.getModule().isCFile)
151 const(char)* gototype = s.isCaseStatement() ? "case" : "default";
152 s.deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype);
157 if (!(result & BE.fallthru) && !s.comeFrom())
159 if (blockExit(s, func, mustNotThrow) != BE.halt && s.hasCode() &&
160 s.loc != Loc.initial) // don't emit warning for generated code
161 s.warning("statement is not reachable");
163 else
165 result &= ~BE.fallthru;
166 result |= blockExit(s, func, mustNotThrow);
168 slast = s;
173 override void visit(UnrolledLoopStatement uls)
175 result = BE.fallthru;
176 foreach (s; *uls.statements)
178 if (s)
180 int r = blockExit(s, func, mustNotThrow);
181 result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru);
182 if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0)
183 result &= ~BE.fallthru;
188 override void visit(ScopeStatement s)
190 //printf("ScopeStatement::blockExit(%p)\n", s.statement);
191 result = blockExit(s.statement, func, mustNotThrow);
194 override void visit(WhileStatement s)
196 assert(global.errors);
197 result = BE.fallthru;
200 override void visit(DoStatement s)
202 if (s._body)
204 result = blockExit(s._body, func, mustNotThrow);
205 if (result == BE.break_)
207 result = BE.fallthru;
208 return;
210 if (result & BE.continue_)
211 result |= BE.fallthru;
213 else
214 result = BE.fallthru;
215 if (result & BE.fallthru)
217 if (canThrow(s.condition, func, mustNotThrow))
218 result |= BE.throw_;
219 if (!(result & BE.break_) && s.condition.toBool().hasValue(true))
220 result &= ~BE.fallthru;
222 result &= ~(BE.break_ | BE.continue_);
225 override void visit(ForStatement s)
227 result = BE.fallthru;
228 if (s._init)
230 result = blockExit(s._init, func, mustNotThrow);
231 if (!(result & BE.fallthru))
232 return;
234 if (s.condition)
236 if (canThrow(s.condition, func, mustNotThrow))
237 result |= BE.throw_;
238 const opt = s.condition.toBool();
239 if (opt.hasValue(true))
240 result &= ~BE.fallthru;
241 else if (opt.hasValue(false))
242 return;
244 else
245 result &= ~BE.fallthru; // the body must do the exiting
246 if (s._body)
248 int r = blockExit(s._body, func, mustNotThrow);
249 if (r & (BE.break_ | BE.goto_))
250 result |= BE.fallthru;
251 result |= r & ~(BE.fallthru | BE.break_ | BE.continue_);
253 if (s.increment && canThrow(s.increment, func, mustNotThrow))
254 result |= BE.throw_;
257 override void visit(ForeachStatement s)
259 result = BE.fallthru;
260 if (canThrow(s.aggr, func, mustNotThrow))
261 result |= BE.throw_;
262 if (s._body)
263 result |= blockExit(s._body, func, mustNotThrow) & ~(BE.break_ | BE.continue_);
266 override void visit(ForeachRangeStatement s)
268 assert(global.errors);
269 result = BE.fallthru;
272 override void visit(IfStatement s)
274 //printf("IfStatement::blockExit(%p)\n", s);
275 result = BE.none;
276 if (canThrow(s.condition, func, mustNotThrow))
277 result |= BE.throw_;
279 const opt = s.condition.toBool();
280 if (opt.hasValue(true))
282 result |= blockExit(s.ifbody, func, mustNotThrow);
284 else if (opt.hasValue(false))
286 result |= blockExit(s.elsebody, func, mustNotThrow);
288 else
290 result |= blockExit(s.ifbody, func, mustNotThrow);
291 result |= blockExit(s.elsebody, func, mustNotThrow);
293 //printf("IfStatement::blockExit(%p) = x%x\n", s, result);
296 override void visit(ConditionalStatement s)
298 result = blockExit(s.ifbody, func, mustNotThrow);
299 if (s.elsebody)
300 result |= blockExit(s.elsebody, func, mustNotThrow);
303 override void visit(PragmaStatement s)
305 result = BE.fallthru;
308 override void visit(StaticAssertStatement s)
310 result = BE.fallthru;
313 override void visit(SwitchStatement s)
315 result = BE.none;
316 if (canThrow(s.condition, func, mustNotThrow))
317 result |= BE.throw_;
318 if (s._body)
320 result |= blockExit(s._body, func, mustNotThrow);
321 if (result & BE.break_)
323 result |= BE.fallthru;
324 result &= ~BE.break_;
327 else
328 result |= BE.fallthru;
331 override void visit(CaseStatement s)
333 result = blockExit(s.statement, func, mustNotThrow);
336 override void visit(DefaultStatement s)
338 result = blockExit(s.statement, func, mustNotThrow);
341 override void visit(GotoDefaultStatement s)
343 result = BE.goto_;
346 override void visit(GotoCaseStatement s)
348 result = BE.goto_;
351 override void visit(SwitchErrorStatement s)
353 // Switch errors are non-recoverable
354 result = BE.halt;
357 override void visit(ReturnStatement s)
359 result = BE.return_;
360 if (s.exp && canThrow(s.exp, func, mustNotThrow))
361 result |= BE.throw_;
364 override void visit(BreakStatement s)
366 //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_);
367 result = s.ident ? BE.goto_ : BE.break_;
370 override void visit(ContinueStatement s)
372 result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_;
375 override void visit(SynchronizedStatement s)
377 result = blockExit(s._body, func, mustNotThrow);
380 override void visit(WithStatement s)
382 result = BE.none;
383 if (canThrow(s.exp, func, mustNotThrow))
384 result = BE.throw_;
385 result |= blockExit(s._body, func, mustNotThrow);
388 override void visit(TryCatchStatement s)
390 assert(s._body);
391 result = blockExit(s._body, func, false);
393 int catchresult = 0;
394 foreach (c; *s.catches)
396 if (c.type == Type.terror)
397 continue;
399 int cresult = blockExit(c.handler, func, mustNotThrow);
401 /* If we're catching Object, then there is no throwing
403 Identifier id = c.type.toBasetype().isClassHandle().ident;
404 if (c.internalCatch && (cresult & BE.fallthru))
406 // https://issues.dlang.org/show_bug.cgi?id=11542
407 // leave blockExit flags of the body
408 cresult &= ~BE.fallthru;
410 else if (id == Id.Object || id == Id.Throwable)
412 result &= ~(BE.throw_ | BE.errthrow);
414 else if (id == Id.Exception)
416 result &= ~BE.throw_;
418 catchresult |= cresult;
420 if (mustNotThrow && (result & BE.throw_))
422 // now explain why this is nothrow
423 blockExit(s._body, func, mustNotThrow);
425 result |= catchresult;
428 override void visit(TryFinallyStatement s)
430 result = BE.fallthru;
431 if (s._body)
432 result = blockExit(s._body, func, false);
434 // check finally body as well, it may throw (bug #4082)
435 int finalresult = BE.fallthru;
436 if (s.finalbody)
437 finalresult = blockExit(s.finalbody, func, false);
439 // If either body or finalbody halts
440 if (result == BE.halt)
441 finalresult = BE.none;
442 if (finalresult == BE.halt)
443 result = BE.none;
445 if (mustNotThrow)
447 // now explain why this is nothrow
448 if (s._body && (result & BE.throw_))
449 blockExit(s._body, func, mustNotThrow);
450 if (s.finalbody && (finalresult & BE.throw_))
451 blockExit(s.finalbody, func, mustNotThrow);
454 version (none)
456 // https://issues.dlang.org/show_bug.cgi?id=13201
457 // Mask to prevent spurious warnings for
458 // destructor call, exit of synchronized statement, etc.
459 if (result == BE.halt && finalresult != BE.halt && s.finalbody && s.finalbody.hasCode())
461 s.finalbody.warning("statement is not reachable");
465 if (!(finalresult & BE.fallthru))
466 result &= ~BE.fallthru;
467 result |= finalresult & ~BE.fallthru;
470 override void visit(ScopeGuardStatement s)
472 // At this point, this statement is just an empty placeholder
473 result = BE.fallthru;
476 override void visit(ThrowStatement s)
478 if (s.internalThrow)
480 // https://issues.dlang.org/show_bug.cgi?id=8675
481 // Allow throwing 'Throwable' object even if mustNotThrow.
482 result = BE.fallthru;
483 return;
486 Type t = s.exp.type.toBasetype();
487 ClassDeclaration cd = t.isClassHandle();
488 assert(cd);
490 if (cd == ClassDeclaration.errorException || ClassDeclaration.errorException.isBaseOf(cd, null))
492 result = BE.errthrow;
493 return;
495 if (mustNotThrow)
496 s.error("`%s` is thrown but not caught", s.exp.type.toChars());
498 result = BE.throw_;
501 override void visit(GotoStatement s)
503 //printf("GotoStatement::blockExit(%p)\n", s);
504 result = BE.goto_;
507 override void visit(LabelStatement s)
509 //printf("LabelStatement::blockExit(%p)\n", s);
510 result = blockExit(s.statement, func, mustNotThrow);
511 if (s.breaks)
512 result |= BE.fallthru;
515 override void visit(CompoundAsmStatement s)
517 // Assume the worst
518 result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt;
519 if (!(s.stc & STC.nothrow_))
521 if (mustNotThrow && !(s.stc & STC.nothrow_))
522 s.deprecation("`asm` statement is assumed to throw - mark it with `nothrow` if it does not");
523 else
524 result |= BE.throw_;
528 override void visit(ImportStatement s)
530 result = BE.fallthru;
534 if (!s)
535 return BE.fallthru;
536 scope BlockExit be = new BlockExit(func, mustNotThrow);
537 s.accept(be);
538 return be.result;