view gcc/d/dmd/blockexit.c @ 158:494b0b89df80 default tip

...
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Mon, 25 May 2020 18:13:55 +0900
parents 1830386684a0
children
line wrap: on
line source


/* 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;
}