Edit

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

Branch :

  • Show log

    Commit

  • Author : Jamie Madill
    Date : 2018-11-27 11:34:27
    Hash : b980c563
    Message : Reformat all cpp and h files. This applies git cl format --full to all ANGLE sources. Bug: angleproject:2986 Change-Id: Ib504e618c1589332a37e97696cdc3515d739308f Reviewed-on: https://chromium-review.googlesource.com/c/1351367 Reviewed-by: Jamie Madill <jmadill@chromium.org> Reviewed-by: Shahbaz Youssefi <syoussefi@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/Diagnostics.h"
    #include "compiler/translator/tree_util/IntermTraverse.h"
    
    namespace sh
    {
    
    namespace
    {
    
    const int kMaxAllowedTraversalDepth = 256;
    
    class ValidateSwitch : public TIntermTraverser
    {
      public:
        static bool validate(TBasicType switchType,
                             TDiagnostics *diagnostics,
                             TIntermBlock *statementList,
                             const TSourceLoc &loc);
    
        void visitSymbol(TIntermSymbol *) override;
        void visitConstantUnion(TIntermConstantUnion *) override;
        bool visitDeclaration(Visit, TIntermDeclaration *) override;
        bool visitBlock(Visit visit, TIntermBlock *) override;
        bool visitBinary(Visit, TIntermBinary *) override;
        bool visitUnary(Visit, TIntermUnary *) override;
        bool visitTernary(Visit, TIntermTernary *) override;
        bool visitSwizzle(Visit, TIntermSwizzle *) override;
        bool visitIfElse(Visit visit, TIntermIfElse *) override;
        bool visitSwitch(Visit, TIntermSwitch *) override;
        bool visitCase(Visit, TIntermCase *node) override;
        bool visitAggregate(Visit, TIntermAggregate *) override;
        bool visitLoop(Visit visit, TIntermLoop *) override;
        bool visitBranch(Visit, TIntermBranch *) override;
    
      private:
        ValidateSwitch(TBasicType switchType, TDiagnostics *context);
    
        bool validateInternal(const TSourceLoc &loc);
    
        TBasicType mSwitchType;
        TDiagnostics *mDiagnostics;
        bool mCaseTypeMismatch;
        bool mFirstCaseFound;
        bool mStatementBeforeCase;
        bool mLastStatementWasCase;
        int mControlFlowDepth;
        bool mCaseInsideControlFlow;
        int mDefaultCount;
        std::set<int> mCasesSigned;
        std::set<unsigned int> mCasesUnsigned;
        bool mDuplicateCases;
    };
    
    bool ValidateSwitch::validate(TBasicType switchType,
                                  TDiagnostics *diagnostics,
                                  TIntermBlock *statementList,
                                  const TSourceLoc &loc)
    {
        ValidateSwitch validate(switchType, diagnostics);
        ASSERT(statementList);
        statementList->traverse(&validate);
        return validate.validateInternal(loc);
    }
    
    ValidateSwitch::ValidateSwitch(TBasicType switchType, TDiagnostics *diagnostics)
        : TIntermTraverser(true, false, true, nullptr),
          mSwitchType(switchType),
          mDiagnostics(diagnostics),
          mCaseTypeMismatch(false),
          mFirstCaseFound(false),
          mStatementBeforeCase(false),
          mLastStatementWasCase(false),
          mControlFlowDepth(0),
          mCaseInsideControlFlow(false),
          mDefaultCount(0),
          mDuplicateCases(false)
    {
        setMaxAllowedDepth(kMaxAllowedTraversalDepth);
    }
    
    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::visitDeclaration(Visit, TIntermDeclaration *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitBlock(Visit visit, TIntermBlock *)
    {
        if (getParentNode() != nullptr)
        {
            if (!mFirstCaseFound)
                mStatementBeforeCase = true;
            mLastStatementWasCase = false;
            if (visit == PreVisit)
                ++mControlFlowDepth;
            if (visit == PostVisit)
                --mControlFlowDepth;
        }
        return true;
    }
    
    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::visitTernary(Visit, TIntermTernary *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
    {
        if (!mFirstCaseFound)
            mStatementBeforeCase = true;
        mLastStatementWasCase = false;
        return true;
    }
    
    bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
    {
        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)
        {
            mDiagnostics->error(node->getLine(), "label statement nested inside control flow", nodeStr);
            mCaseInsideControlFlow = true;
        }
        mFirstCaseFound       = true;
        mLastStatementWasCase = true;
        if (!node->hasCondition())
        {
            ++mDefaultCount;
            if (mDefaultCount > 1)
            {
                mDiagnostics->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)
            {
                mDiagnostics->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())
                {
                    mDiagnostics->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())
                {
                    mDiagnostics->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)
        {
            mDiagnostics->error(loc, "statement before the first label", "switch");
        }
        if (mLastStatementWasCase)
        {
            // There have been some differences between versions of GLSL ES specs on whether this should
            // be an error or not, but as of early 2018 the latest discussion is that this is an error
            // also on GLSL ES versions newer than 3.00.
            mDiagnostics->error(
                loc, "no statement between the last label and the end of the switch statement",
                "switch");
        }
        if (getMaxDepth() >= kMaxAllowedTraversalDepth)
        {
            mDiagnostics->error(loc, "too complex expressions inside a switch statement", "switch");
        }
        return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
               !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases &&
               getMaxDepth() < kMaxAllowedTraversalDepth;
    }
    
    }  // anonymous namespace
    
    bool ValidateSwitchStatementList(TBasicType switchType,
                                     TDiagnostics *diagnostics,
                                     TIntermBlock *statementList,
                                     const TSourceLoc &loc)
    {
        return ValidateSwitch::validate(switchType, diagnostics, statementList, loc);
    }
    
    }  // namespace sh