d: Merge dmd, druntime d8e3976a58, phobos 7a6e95688
[official-gcc.git] / gcc / d / dmd / blockexit.d
blobd77af7e1c5c4d5b175031cd03b70a3176458c333
1 /**
2 * Find out in what ways control flow can exit a statement block.
4 * Copyright: Copyright (C) 1999-2024 by The D Language Foundation, All Rights Reserved
5 * Authors: $(LINK2 https://www.digitalmars.com, Walter Bright)
6 * License: $(LINK2 https://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.errorsink;
22 import dmd.expression;
23 import dmd.func;
24 import dmd.globals;
25 import dmd.id;
26 import dmd.identifier;
27 import dmd.location;
28 import dmd.mtype;
29 import dmd.statement;
30 import dmd.tokens;
32 /**
33 * BE stands for BlockExit.
35 * It indicates if a statement does transfer control to another block.
36 * A block is a sequence of statements enclosed in { }
38 enum BE : int
40 none = 0,
41 fallthru = 1,
42 throw_ = 2,
43 return_ = 4,
44 goto_ = 8,
45 halt = 0x10,
46 break_ = 0x20,
47 continue_ = 0x40,
48 errthrow = 0x80,
49 any = (fallthru | throw_ | return_ | goto_ | halt),
53 /*********************************************
54 * Determine mask of ways that a statement can exit.
56 * Only valid after semantic analysis.
57 * Params:
58 * s = statement to check for block exit status
59 * func = function that statement s is in
60 * eSink = generate an error if it throws
61 * Returns:
62 * BE.xxxx
64 int blockExit(Statement s, FuncDeclaration func, ErrorSink eSink)
66 int result = BE.none;
68 void visitDefaultCase(Statement s)
70 printf("Statement::blockExit(%p)\n", s);
71 printf("%s\n", s.toChars());
72 assert(0);
75 void visitError(ErrorStatement s)
77 result = BE.none;
80 void visitExp(ExpStatement s)
82 result = BE.fallthru;
83 if (s.exp)
85 if (s.exp.op == EXP.halt)
87 result = BE.halt;
88 return;
90 if (AssertExp a = s.exp.isAssertExp())
92 if (a.e1.toBool().hasValue(false)) // if it's an assert(0)
94 result = BE.halt;
95 return;
98 if (s.exp.type && s.exp.type.toBasetype().isTypeNoreturn())
99 result = BE.halt;
101 result |= canThrow(s.exp, func, eSink);
105 void visitDtorExp(DtorExpStatement s)
107 visitExp(s);
110 void visitMixin(MixinStatement s)
112 assert(global.errors);
113 result = BE.fallthru;
116 void visitCompound(CompoundStatement cs)
118 //printf("CompoundStatement.blockExit(%p) %d result = x%X\n", cs, cs.statements.length, result);
119 result = BE.fallthru;
120 Statement slast = null;
121 foreach (s; *cs.statements)
123 if (s)
125 //printf("result = x%x\n", result);
126 //printf("s: %s\n", s.toChars());
127 if (result & BE.fallthru && slast)
129 slast = slast.last();
130 if (slast && (slast.isCaseStatement() || slast.isDefaultStatement()) && (s.isCaseStatement() || s.isDefaultStatement()))
132 // Allow if last case/default was empty
133 CaseStatement sc = slast.isCaseStatement();
134 DefaultStatement sd = slast.isDefaultStatement();
135 auto sl = (sc ? sc.statement : (sd ? sd.statement : null));
137 if (sl && (!sl.hasCode() || sl.isErrorStatement()))
140 else if (func.getModule().filetype != FileType.c)
142 const(char)* gototype = s.isCaseStatement() ? "case" : "default";
143 // @@@DEPRECATED_2.110@@@ https://issues.dlang.org/show_bug.cgi?id=22999
144 // Deprecated in 2.100
145 // Make an error in 2.110
146 if (sl && sl.isCaseStatement())
147 global.errorSink.deprecation(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype);
148 else
149 global.errorSink.error(s.loc, "switch case fallthrough - use 'goto %s;' if intended", gototype);
154 if ((result & BE.fallthru) || s.comeFrom())
156 result &= ~BE.fallthru;
157 result |= blockExit(s, func, eSink);
159 slast = s;
164 void visitUnrolledLoop(UnrolledLoopStatement uls)
166 result = BE.fallthru;
167 foreach (s; *uls.statements)
169 if (s)
171 int r = blockExit(s, func, eSink);
172 result |= r & ~(BE.break_ | BE.continue_ | BE.fallthru);
173 if ((r & (BE.fallthru | BE.continue_ | BE.break_)) == 0)
174 result &= ~BE.fallthru;
179 void visitScope(ScopeStatement s)
181 //printf("ScopeStatement::blockExit(%p)\n", s.statement);
182 result = blockExit(s.statement, func, eSink);
185 void visitWhile(WhileStatement s)
187 assert(global.errors);
188 result = BE.fallthru;
191 void visitDo(DoStatement s)
193 if (s._body)
195 result = blockExit(s._body, func, eSink);
196 if (result == BE.break_)
198 result = BE.fallthru;
199 return;
201 if (result & BE.continue_)
202 result |= BE.fallthru;
204 else
205 result = BE.fallthru;
206 if (result & BE.fallthru)
208 result |= canThrow(s.condition, func, eSink);
210 if (!(result & BE.break_) && s.condition.toBool().hasValue(true))
211 result &= ~BE.fallthru;
213 result &= ~(BE.break_ | BE.continue_);
216 void visitFor(ForStatement s)
218 result = BE.fallthru;
219 if (s._init)
221 result = blockExit(s._init, func, eSink);
222 if (!(result & BE.fallthru))
223 return;
225 if (s.condition)
227 result |= canThrow(s.condition, func, eSink);
229 const opt = s.condition.toBool();
230 if (opt.hasValue(true))
231 result &= ~BE.fallthru;
232 else if (opt.hasValue(false))
233 return;
235 else
236 result &= ~BE.fallthru; // the body must do the exiting
237 if (s._body)
239 int r = blockExit(s._body, func, eSink);
240 if (r & (BE.break_ | BE.goto_))
241 result |= BE.fallthru;
242 result |= r & ~(BE.fallthru | BE.break_ | BE.continue_);
244 if (s.increment)
245 result |= canThrow(s.increment, func, eSink);
248 void visitForeach(ForeachStatement s)
250 result = BE.fallthru;
251 result |= canThrow(s.aggr, func, eSink);
253 if (s._body)
254 result |= blockExit(s._body, func, eSink) & ~(BE.break_ | BE.continue_);
257 void visitForeachRange(ForeachRangeStatement s)
259 assert(global.errors);
260 result = BE.fallthru;
263 void visitIf(IfStatement s)
265 //printf("IfStatement::blockExit(%p)\n", s);
266 result = BE.none;
267 result |= canThrow(s.condition, func, eSink);
269 const opt = s.condition.toBool();
270 if (opt.hasValue(true))
272 result |= blockExit(s.ifbody, func, eSink);
274 else if (opt.hasValue(false))
276 result |= blockExit(s.elsebody, func, eSink);
278 else
280 result |= blockExit(s.ifbody, func, eSink);
281 result |= blockExit(s.elsebody, func, eSink);
283 //printf("IfStatement::blockExit(%p) = x%x\n", s, result);
286 void visitConditional(ConditionalStatement s)
288 result = blockExit(s.ifbody, func, eSink);
289 if (s.elsebody)
290 result |= blockExit(s.elsebody, func, eSink);
293 void visitPragma(PragmaStatement s)
295 result = BE.fallthru;
298 void visitStaticAssert(StaticAssertStatement s)
300 result = BE.fallthru;
303 void visitSwitch(SwitchStatement s)
305 result = BE.none;
306 result |= canThrow(s.condition, func, eSink);
308 if (s._body)
310 result |= blockExit(s._body, func, eSink);
311 if (result & BE.break_)
313 result |= BE.fallthru;
314 result &= ~BE.break_;
317 else
318 result |= BE.fallthru;
321 void visitCase(CaseStatement s)
323 result = blockExit(s.statement, func, eSink);
326 void visitDefault(DefaultStatement s)
328 result = blockExit(s.statement, func, eSink);
331 void visitGotoDefault(GotoDefaultStatement s)
333 result = BE.goto_;
336 void visitGotoCase(GotoCaseStatement s)
338 result = BE.goto_;
341 void visitSwitchError(SwitchErrorStatement s)
343 // Switch errors are non-recoverable
344 result = BE.halt;
347 void visitReturn(ReturnStatement s)
349 result = BE.return_;
350 if (s.exp)
351 result |= canThrow(s.exp, func, eSink);
354 void visitBreak(BreakStatement s)
356 //printf("BreakStatement::blockExit(%p) = x%x\n", s, s.ident ? BE.goto_ : BE.break_);
357 result = s.ident ? BE.goto_ : BE.break_;
360 void visitContinue(ContinueStatement s)
362 result = s.ident ? BE.continue_ | BE.goto_ : BE.continue_;
365 void visitSynchronized(SynchronizedStatement s)
367 result = blockExit(s._body, func, eSink);
370 void visitWith(WithStatement s)
372 result = BE.none;
373 result |= canThrow(s.exp, func, eSink);
374 result |= blockExit(s._body, func, eSink);
377 void visitTryCatch(TryCatchStatement s)
379 assert(s._body);
380 result = blockExit(s._body, func, null);
382 int catchresult = 0;
383 foreach (c; *s.catches)
385 if (c.type == Type.terror)
386 continue;
388 int cresult = blockExit(c.handler, func, eSink);
390 /* If we're catching Object, then there is no throwing
392 Identifier id = c.type.toBasetype().isClassHandle().ident;
393 if (c.internalCatch && (cresult & BE.fallthru))
395 // https://issues.dlang.org/show_bug.cgi?id=11542
396 // leave blockExit flags of the body
397 cresult &= ~BE.fallthru;
399 else if (id == Id.Object || id == Id.Throwable)
401 result &= ~(BE.throw_ | BE.errthrow);
403 else if (id == Id.Exception)
405 result &= ~BE.throw_;
407 catchresult |= cresult;
409 if (eSink && (result & BE.throw_))
411 // now explain why this is nothrow
412 blockExit(s._body, func, eSink);
414 result |= catchresult;
417 void visitTryFinally(TryFinallyStatement s)
419 result = BE.fallthru;
420 if (s._body)
421 result = blockExit(s._body, func, null);
423 // check finally body as well, it may throw (bug #4082)
424 int finalresult = BE.fallthru;
425 if (s.finalbody)
426 finalresult = blockExit(s.finalbody, func, null);
428 // If either body or finalbody halts
429 if (result == BE.halt)
430 finalresult = BE.none;
431 if (finalresult == BE.halt)
432 result = BE.none;
434 if (eSink)
436 // now explain why this is nothrow
437 if (s._body && (result & BE.throw_))
438 blockExit(s._body, func, eSink);
439 if (s.finalbody && (finalresult & BE.throw_))
440 blockExit(s.finalbody, func, eSink);
443 if (!(finalresult & BE.fallthru))
444 result &= ~BE.fallthru;
445 result |= finalresult & ~BE.fallthru;
448 void visitScopeGuard(ScopeGuardStatement s)
450 // At this point, this statement is just an empty placeholder
451 result = BE.fallthru;
454 void visitThrow(ThrowStatement s)
456 if (s.internalThrow)
458 // https://issues.dlang.org/show_bug.cgi?id=8675
459 // Allow throwing 'Throwable' object even if eSink.
460 result = BE.fallthru;
461 return;
464 result = checkThrow(s.loc, s.exp, func, eSink);
467 void visitGoto(GotoStatement s)
469 //printf("GotoStatement::blockExit(%p)\n", s);
470 result = BE.goto_;
473 void visitLabel(LabelStatement s)
475 //printf("LabelStatement::blockExit(%p)\n", s);
476 result = blockExit(s.statement, func, eSink);
477 if (s.breaks)
478 result |= BE.fallthru;
481 void visitCompoundAsm(CompoundAsmStatement s)
483 // Assume the worst
484 result = BE.fallthru | BE.return_ | BE.goto_ | BE.halt;
485 if (!(s.stc & STC.nothrow_))
487 if(func)
488 func.setThrow(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not");
489 if (eSink)
490 eSink.error(s.loc, "`asm` statement is assumed to throw - mark it with `nothrow` if it does not"); // TODO
491 else
492 result |= BE.throw_;
496 void visitImport(ImportStatement s)
498 result = BE.fallthru;
501 if (!s)
502 return BE.fallthru;
503 mixin VisitStatement!void visit;
504 visit.VisitStatement(s);
505 return result;
509 + Checks whether `throw <exp>` throws an `Exception` or an `Error`
510 + and raises an error if this violates `nothrow`.
512 + Params:
513 + loc = location of the `throw`
514 + exp = expression yielding the throwable
515 + eSink = if !null then inside of a `nothrow` scope
516 + func = function containing the `throw`
518 + Returns: `BE.[err]throw` depending on the type of `exp`
520 BE checkThrow(ref const Loc loc, Expression exp, FuncDeclaration func, ErrorSink eSink)
522 Type t = exp.type.toBasetype();
523 ClassDeclaration cd = t.isClassHandle();
524 assert(cd);
526 if (cd.isErrorException())
528 return BE.errthrow;
530 if (eSink)
531 eSink.error(loc, "`%s` is thrown but not caught", exp.type.toChars());
532 else if (func)
533 func.setThrow(loc, "`%s` is thrown but not caught", exp.type);
535 return BE.throw_;