|  | // Copyright 2016 The SwiftShader Authors. All Rights Reserved. | 
|  | // | 
|  | // Licensed under the Apache License, Version 2.0 (the "License"); | 
|  | // you may not use this file except in compliance with the License. | 
|  | // You may obtain a copy of the License at | 
|  | // | 
|  | //    http://www.apache.org/licenses/LICENSE-2.0 | 
|  | // | 
|  | // Unless required by applicable law or agreed to in writing, software | 
|  | // distributed under the License is distributed on an "AS IS" BASIS, | 
|  | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
|  | // See the License for the specific language governing permissions and | 
|  | // limitations under the License. | 
|  |  | 
|  | #include "ValidateSwitch.h" | 
|  |  | 
|  | #include "ParseHelper.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; | 
|  | } |