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