Edit

kc3-lang/angle/src/compiler/translator/ValidateSwitch.cpp

Branch :

  • Show log

    Commit

  • Author : Olli Etuaho
    Date : 2015-02-26 17:17:22
    Hash : c8716df9
    Message : Fix an issue with handling an error case inside ValidateSwitch In case a condition with a wrong type is given to a case statement, it generates an error but the compiler recovers. This caused ValidateSwitch to assert. Fix this. BUG=angle:921 Change-Id: I7949798cab923c2b168817471896470c6c611878 Reviewed-on: https://chromium-review.googlesource.com/254080 Reviewed-by: Zhenyao Mo <zmo@chromium.org> Tested-by: Olli Etuaho <oetuaho@nvidia.com> Reviewed-by: Jamie Madill <jmadill@chromium.org>

  • src/compiler/translator/ValidateSwitch.cpp
  • //
    // Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
    // Use of this source code is governed by a BSD-style license that can be
    // found in the LICENSE file.
    //
    
    #include "compiler/translator/ValidateSwitch.h"
    
    #include "compiler/translator/ParseContext.h"
    
    bool ValidateSwitch::validate(TBasicType switchType, TParseContext *context,
        TIntermAggregate *statementList, const TSourceLoc &loc)
    {
        ValidateSwitch validate(switchType, context);
        ASSERT(statementList);
        statementList->traverse(&validate);
        return validate.validateInternal(loc);
    }
    
    ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
        : TIntermTraverser(true, false, true),
          mSwitchType(switchType),
          mContext(context),
          mCaseTypeMismatch(false),
          mFirstCaseFound(false),
          mStatementBeforeCase(false),
          mLastStatementWasCase(false),
          mControlFlowDepth(0),
          mCaseInsideControlFlow(false),
          mDefaultCount(0),
          mDuplicateCases(false)
    {}
    
    void ValidateSwitch::visitSymbol(TIntermSymbol *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
    }
    
    void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
    {
        // Conditions of case labels are not traversed, so this is some other constant
        // Could be just a statement like "0;"
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
    }
    
    bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitSelection(Visit visit, TIntermSelection *)
    {
        if (visit == PreVisit)
            ++mControlFlowDepth;
        if (visit == PostVisit)
            --mControlFlowDepth;
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        // Don't go into nested switch statements
        return false;
    }
    
    bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
    {
        const char *nodeStr = node->hasCondition() ? "case" : "default";
        if (mControlFlowDepth > 0)
        {
            mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
            mCaseInsideControlFlow = true;
        }
        mFirstCaseFound = true;
        mLastStatementWasCase = true;
        if (!node->hasCondition())
        {
            ++mDefaultCount;
            if (mDefaultCount > 1)
            {
                mContext->error(node->getLine(), "duplicate default label", nodeStr);
            }
        }
        else
        {
            TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
            if (condition == nullptr)
            {
                // This can happen in error cases.
                return false;
            }
            TBasicType conditionType = condition->getBasicType();
            if (conditionType != mSwitchType)
            {
                mContext->error(condition->getLine(),
                    "case label type does not match switch init-expression type", nodeStr);
                mCaseTypeMismatch = true;
            }
    
            if (conditionType == EbtInt)
            {
                int iConst = condition->getIConst(0);
                if (mCasesSigned.find(iConst) != mCasesSigned.end())
                {
                    mContext->error(condition->getLine(), "duplicate case label", nodeStr);
                    mDuplicateCases = true;
                }
                else
                {
                    mCasesSigned.insert(iConst);
                }
            }
            else if (conditionType == EbtUInt)
            {
                unsigned int uConst = condition->getUConst(0);
                if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
                {
                    mContext->error(condition->getLine(), "duplicate case label", nodeStr);
                    mDuplicateCases = true;
                }
                else
                {
                    mCasesUnsigned.insert(uConst);
                }
            }
            // Other types are possible only in error cases, where the error has already been generated
            // when parsing the case statement.
        }
        // Don't traverse the condition of the case statement
        return false;
    }
    
    bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
    {
        if (getParentNode() != nullptr)
        {
            // This is not the statementList node, but some other node.
            if (!mFirstCaseFound)
                mStatementBeforeCase = true;
            mLastStatementWasCase = false;
        }
        return true;
    }
    
    bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
    {
        if (visit == PreVisit)
            ++mControlFlowDepth;
        if (visit == PostVisit)
            --mControlFlowDepth;
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
    {
        if (mStatementBeforeCase)
        {
            mContext->error(loc,
                "statement before the first label", "switch");
        }
        if (mLastStatementWasCase)
        {
            mContext->error(loc,
                "no statement between the last label and the end of the switch statement", "switch");
        }
        return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
            !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
    }