Mercurial > hg > CbC > CbC_gcc
diff gcc/d/dmd/blockexit.c @ 145:1830386684a0
gcc-9.2.0
author | anatofuz |
---|---|
date | Thu, 13 Feb 2020 11:34:05 +0900 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/gcc/d/dmd/blockexit.c Thu Feb 13 11:34:05 2020 +0900 @@ -0,0 +1,504 @@ + +/* Compiler implementation of the D programming language + * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved + * written by Walter Bright + * http://www.digitalmars.com + * Distributed under the Boost Software License, Version 1.0. + * http://www.boost.org/LICENSE_1_0.txt + */ + +#include "statement.h" +#include "declaration.h" +#include "aggregate.h" +#include "id.h" + +/* Only valid after semantic analysis + * If 'mustNotThrow' is true, generate an error if it throws + */ +int blockExit(Statement *s, FuncDeclaration *func, bool mustNotThrow) +{ + class BlockExit : public Visitor + { + public: + FuncDeclaration *func; + bool mustNotThrow; + int result; + + BlockExit(FuncDeclaration *func, bool mustNotThrow) + : func(func), mustNotThrow(mustNotThrow) + { + result = BEnone; + } + + void visit(Statement *s) + { + printf("Statement::blockExit(%p)\n", s); + printf("%s\n", s->toChars()); + assert(0); + result = BEany; + } + + void visit(ErrorStatement *) + { + result = BEany; + } + + void visit(ExpStatement *s) + { + result = BEfallthru; + if (s->exp) + { + if (s->exp->op == TOKhalt) + { + result = BEhalt; + return; + } + if (s->exp->op == TOKassert) + { + AssertExp *a = (AssertExp *)s->exp; + if (a->e1->isBool(false)) // if it's an assert(0) + { + result = BEhalt; + return; + } + } + if (canThrow(s->exp, func, mustNotThrow)) + result |= BEthrow; + } + } + + void visit(CompileStatement *) + { + assert(global.errors); + result = BEfallthru; + } + + void visit(CompoundStatement *cs) + { + //printf("CompoundStatement::blockExit(%p) %d result = x%X\n", cs, cs->statements->dim, result); + result = BEfallthru; + Statement *slast = NULL; + for (size_t i = 0; i < cs->statements->dim; i++) + { + Statement *s = (*cs->statements)[i]; + if (s) + { + //printf("result = x%x\n", result); + //printf("s: %s\n", s->toChars()); + if (result & BEfallthru && slast) + { + slast = slast->last(); + if (slast && (slast->isCaseStatement() || slast->isDefaultStatement()) && + (s->isCaseStatement() || s->isDefaultStatement())) + { + // Allow if last case/default was empty + CaseStatement *sc = slast->isCaseStatement(); + DefaultStatement *sd = slast->isDefaultStatement(); + if (sc && (!sc->statement->hasCode() || sc->statement->isCaseStatement() || sc->statement->isErrorStatement())) + ; + else if (sd && (!sd->statement->hasCode() || sd->statement->isCaseStatement() || sd->statement->isErrorStatement())) + ; + else + { + const char *gototype = s->isCaseStatement() ? "case" : "default"; + s->deprecation("switch case fallthrough - use 'goto %s;' if intended", gototype); + } + } + } + + if (!(result & BEfallthru) && !s->comeFrom()) + { + if (blockExit(s, func, mustNotThrow) != BEhalt && s->hasCode()) + s->warning("statement is not reachable"); + } + else + { + result &= ~BEfallthru; + result |= blockExit(s, func, mustNotThrow); + } + slast = s; + } + } + } + + void visit(UnrolledLoopStatement *uls) + { + result = BEfallthru; + for (size_t i = 0; i < uls->statements->dim; i++) + { + Statement *s = (*uls->statements)[i]; + if (s) + { + int r = blockExit(s, func, mustNotThrow); + result |= r & ~(BEbreak | BEcontinue | BEfallthru); + if ((r & (BEfallthru | BEcontinue | BEbreak)) == 0) + result &= ~BEfallthru; + } + } + } + + void visit(ScopeStatement *s) + { + //printf("ScopeStatement::blockExit(%p)\n", s->statement); + result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru; + } + + void visit(WhileStatement *) + { + assert(global.errors); + result = BEfallthru; + } + + void visit(DoStatement *s) + { + if (s->_body) + { + result = blockExit(s->_body, func, mustNotThrow); + if (result == BEbreak) + { + result = BEfallthru; + return; + } + if (result & BEcontinue) + result |= BEfallthru; + } + else + result = BEfallthru; + if (result & BEfallthru) + { + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (!(result & BEbreak) && s->condition->isBool(true)) + result &= ~BEfallthru; + } + result &= ~(BEbreak | BEcontinue); + } + + void visit(ForStatement *s) + { + result = BEfallthru; + if (s->_init) + { + result = blockExit(s->_init, func, mustNotThrow); + if (!(result & BEfallthru)) + return; + } + if (s->condition) + { + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (s->condition->isBool(true)) + result &= ~BEfallthru; + else if (s->condition->isBool(false)) + return; + } + else + result &= ~BEfallthru; // the body must do the exiting + if (s->_body) + { + int r = blockExit(s->_body, func, mustNotThrow); + if (r & (BEbreak | BEgoto)) + result |= BEfallthru; + result |= r & ~(BEfallthru | BEbreak | BEcontinue); + } + if (s->increment && canThrow(s->increment, func, mustNotThrow)) + result |= BEthrow; + } + + void visit(ForeachStatement *s) + { + result = BEfallthru; + if (canThrow(s->aggr, func, mustNotThrow)) + result |= BEthrow; + if (s->_body) + result |= blockExit(s->_body, func, mustNotThrow) & ~(BEbreak | BEcontinue); + } + + void visit(ForeachRangeStatement *) + { + assert(global.errors); + result = BEfallthru; + } + + void visit(IfStatement *s) + { + //printf("IfStatement::blockExit(%p)\n", s); + + result = BEnone; + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (s->condition->isBool(true)) + { + if (s->ifbody) + result |= blockExit(s->ifbody, func, mustNotThrow); + else + result |= BEfallthru; + } + else if (s->condition->isBool(false)) + { + if (s->elsebody) + result |= blockExit(s->elsebody, func, mustNotThrow); + else + result |= BEfallthru; + } + else + { + if (s->ifbody) + result |= blockExit(s->ifbody, func, mustNotThrow); + else + result |= BEfallthru; + if (s->elsebody) + result |= blockExit(s->elsebody, func, mustNotThrow); + else + result |= BEfallthru; + } + //printf("IfStatement::blockExit(%p) = x%x\n", s, result); + } + + void visit(ConditionalStatement *s) + { + result = blockExit(s->ifbody, func, mustNotThrow); + if (s->elsebody) + result |= blockExit(s->elsebody, func, mustNotThrow); + } + + void visit(PragmaStatement *) + { + result = BEfallthru; + } + + void visit(StaticAssertStatement *) + { + result = BEfallthru; + } + + void visit(SwitchStatement *s) + { + result = BEnone; + if (canThrow(s->condition, func, mustNotThrow)) + result |= BEthrow; + if (s->_body) + { + result |= blockExit(s->_body, func, mustNotThrow); + if (result & BEbreak) + { + result |= BEfallthru; + result &= ~BEbreak; + } + } + else + result |= BEfallthru; + } + + void visit(CaseStatement *s) + { + result = blockExit(s->statement, func, mustNotThrow); + } + + void visit(DefaultStatement *s) + { + result = blockExit(s->statement, func, mustNotThrow); + } + + void visit(GotoDefaultStatement *) + { + result = BEgoto; + } + + void visit(GotoCaseStatement *) + { + result = BEgoto; + } + + void visit(SwitchErrorStatement *) + { + // Switch errors are non-recoverable + result = BEhalt; + } + + void visit(ReturnStatement *s) + { + result = BEreturn; + if (s->exp && canThrow(s->exp, func, mustNotThrow)) + result |= BEthrow; + } + + void visit(BreakStatement *s) + { + //printf("BreakStatement::blockExit(%p) = x%x\n", s, s->ident ? BEgoto : BEbreak); + result = s->ident ? BEgoto : BEbreak; + } + + void visit(ContinueStatement *s) + { + result = s->ident ? BEgoto : BEcontinue; + } + + void visit(SynchronizedStatement *s) + { + result = s->_body ? blockExit(s->_body, func, mustNotThrow) : BEfallthru; + } + + void visit(WithStatement *s) + { + result = BEnone; + if (canThrow(s->exp, func, mustNotThrow)) + result = BEthrow; + if (s->_body) + result |= blockExit(s->_body, func, mustNotThrow); + else + result |= BEfallthru; + } + + void visit(TryCatchStatement *s) + { + assert(s->_body); + result = blockExit(s->_body, func, false); + + int catchresult = 0; + for (size_t i = 0; i < s->catches->dim; i++) + { + Catch *c = (*s->catches)[i]; + if (c->type == Type::terror) + continue; + + int cresult; + if (c->handler) + cresult = blockExit(c->handler, func, mustNotThrow); + else + cresult = BEfallthru; + + /* If we're catching Object, then there is no throwing + */ + Identifier *id = c->type->toBasetype()->isClassHandle()->ident; + if (c->internalCatch && (cresult & BEfallthru)) + { + // Bugzilla 11542: leave blockExit flags of the body + cresult &= ~BEfallthru; + } + else if (id == Id::Object || id == Id::Throwable) + { + result &= ~(BEthrow | BEerrthrow); + } + else if (id == Id::Exception) + { + result &= ~BEthrow; + } + catchresult |= cresult; + } + if (mustNotThrow && (result & BEthrow)) + { + // now explain why this is nothrow + blockExit(s->_body, func, mustNotThrow); + } + result |= catchresult; + } + + void visit(TryFinallyStatement *s) + { + result = BEfallthru; + if (s->_body) + result = blockExit(s->_body, func, false); + + // check finally body as well, it may throw (bug #4082) + int finalresult = BEfallthru; + if (s->finalbody) + finalresult = blockExit(s->finalbody, func, false); + + // If either body or finalbody halts + if (result == BEhalt) + finalresult = BEnone; + if (finalresult == BEhalt) + result = BEnone; + + if (mustNotThrow) + { + // now explain why this is nothrow + if (s->_body && (result & BEthrow)) + blockExit(s->_body, func, mustNotThrow); + if (s->finalbody && (finalresult & BEthrow)) + blockExit(s->finalbody, func, mustNotThrow); + } + + #if 0 + // Bugzilla 13201: Mask to prevent spurious warnings for + // destructor call, exit of synchronized statement, etc. + if (result == BEhalt && finalresult != BEhalt && s->finalbody && + s->finalbody->hasCode()) + { + s->finalbody->warning("statement is not reachable"); + } + #endif + + if (!(finalresult & BEfallthru)) + result &= ~BEfallthru; + result |= finalresult & ~BEfallthru; + } + + void visit(OnScopeStatement *) + { + // At this point, this statement is just an empty placeholder + result = BEfallthru; + } + + void visit(ThrowStatement *s) + { + if (s->internalThrow) + { + // Bugzilla 8675: Allow throwing 'Throwable' object even if mustNotThrow. + result = BEfallthru; + return; + } + + Type *t = s->exp->type->toBasetype(); + ClassDeclaration *cd = t->isClassHandle(); + assert(cd); + + if (cd == ClassDeclaration::errorException || + ClassDeclaration::errorException->isBaseOf(cd, NULL)) + { + result = BEerrthrow; + return; + } + if (mustNotThrow) + s->error("%s is thrown but not caught", s->exp->type->toChars()); + + result = BEthrow; + } + + void visit(GotoStatement *) + { + //printf("GotoStatement::blockExit(%p)\n", s); + result = BEgoto; + } + + void visit(LabelStatement *s) + { + //printf("LabelStatement::blockExit(%p)\n", s); + result = s->statement ? blockExit(s->statement, func, mustNotThrow) : BEfallthru; + if (s->breaks) + result |= BEfallthru; + } + + void visit(CompoundAsmStatement *s) + { + if (mustNotThrow && !(s->stc & STCnothrow)) + s->deprecation("asm statement is assumed to throw - mark it with 'nothrow' if it does not"); + + // Assume the worst + result = BEfallthru | BEreturn | BEgoto | BEhalt; + if (!(s->stc & STCnothrow)) result |= BEthrow; + } + + void visit(ImportStatement *) + { + result = BEfallthru; + } + }; + + if (!s) + return BEfallthru; + BlockExit be(func, mustNotThrow); + s->accept(&be); + return be.result; +}