Alexis Hetu | 76a343a | 2015-06-04 17:21:22 -0400 | [diff] [blame] | 1 | //
|
| 2 | // Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
|
| 3 | // Use of this source code is governed by a BSD-style license that can be
|
| 4 | // found in the LICENSE file.
|
| 5 | //
|
| 6 |
|
| 7 | #include "ValidateSwitch.h"
|
| 8 |
|
| 9 | #include "ParseHelper.h"
|
| 10 |
|
| 11 | bool ValidateSwitch::validate(TBasicType switchType, TParseContext *context,
|
| 12 | TIntermAggregate *statementList, const TSourceLoc &loc)
|
| 13 | {
|
| 14 | ValidateSwitch validate(switchType, context);
|
| 15 | ASSERT(statementList);
|
| 16 | statementList->traverse(&validate);
|
| 17 | return validate.validateInternal(loc);
|
| 18 | }
|
| 19 |
|
| 20 | ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
|
| 21 | : TIntermTraverser(true, false, true),
|
| 22 | mSwitchType(switchType),
|
| 23 | mContext(context),
|
| 24 | mCaseTypeMismatch(false),
|
| 25 | mFirstCaseFound(false),
|
| 26 | mStatementBeforeCase(false),
|
| 27 | mLastStatementWasCase(false),
|
| 28 | mControlFlowDepth(0),
|
| 29 | mCaseInsideControlFlow(false),
|
| 30 | mDefaultCount(0),
|
| 31 | mDuplicateCases(false)
|
| 32 | {}
|
| 33 |
|
| 34 | void ValidateSwitch::visitSymbol(TIntermSymbol *)
|
| 35 | {
|
| 36 | if (!mFirstCaseFound)
|
| 37 | mStatementBeforeCase = true;
|
| 38 | mLastStatementWasCase = false;
|
| 39 | }
|
| 40 |
|
| 41 | void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
|
| 42 | {
|
| 43 | // Conditions of case labels are not traversed, so this is some other constant
|
| 44 | // Could be just a statement like "0;"
|
| 45 | if (!mFirstCaseFound)
|
| 46 | mStatementBeforeCase = true;
|
| 47 | mLastStatementWasCase = false;
|
| 48 | }
|
| 49 |
|
| 50 | bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
|
| 51 | {
|
| 52 | if (!mFirstCaseFound)
|
| 53 | mStatementBeforeCase = true;
|
| 54 | mLastStatementWasCase = false;
|
| 55 | return true;
|
| 56 | }
|
| 57 |
|
| 58 | bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
|
| 59 | {
|
| 60 | if (!mFirstCaseFound)
|
| 61 | mStatementBeforeCase = true;
|
| 62 | mLastStatementWasCase = false;
|
| 63 | return true;
|
| 64 | }
|
| 65 |
|
| 66 | bool ValidateSwitch::visitSelection(Visit visit, TIntermSelection *)
|
| 67 | {
|
| 68 | if (visit == PreVisit)
|
| 69 | ++mControlFlowDepth;
|
| 70 | if (visit == PostVisit)
|
| 71 | --mControlFlowDepth;
|
| 72 | if (!mFirstCaseFound)
|
| 73 | mStatementBeforeCase = true;
|
| 74 | mLastStatementWasCase = false;
|
| 75 | return true;
|
| 76 | }
|
| 77 |
|
| 78 | bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
|
| 79 | {
|
| 80 | if (!mFirstCaseFound)
|
| 81 | mStatementBeforeCase = true;
|
| 82 | mLastStatementWasCase = false;
|
| 83 | // Don't go into nested switch statements
|
| 84 | return false;
|
| 85 | }
|
| 86 |
|
| 87 | bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
|
| 88 | {
|
| 89 | const char *nodeStr = node->hasCondition() ? "case" : "default";
|
| 90 | if (mControlFlowDepth > 0)
|
| 91 | {
|
| 92 | mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
|
| 93 | mCaseInsideControlFlow = true;
|
| 94 | }
|
| 95 | mFirstCaseFound = true;
|
| 96 | mLastStatementWasCase = true;
|
| 97 | if (!node->hasCondition())
|
| 98 | {
|
| 99 | ++mDefaultCount;
|
| 100 | if (mDefaultCount > 1)
|
| 101 | {
|
| 102 | mContext->error(node->getLine(), "duplicate default label", nodeStr);
|
| 103 | }
|
| 104 | }
|
| 105 | else
|
| 106 | {
|
| 107 | TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
|
| 108 | if (condition == nullptr)
|
| 109 | {
|
| 110 | // This can happen in error cases.
|
| 111 | return false;
|
| 112 | }
|
| 113 | TBasicType conditionType = condition->getBasicType();
|
| 114 | if (conditionType != mSwitchType)
|
| 115 | {
|
| 116 | mContext->error(condition->getLine(),
|
| 117 | "case label type does not match switch init-expression type", nodeStr);
|
| 118 | mCaseTypeMismatch = true;
|
| 119 | }
|
| 120 |
|
| 121 | if (conditionType == EbtInt)
|
| 122 | {
|
| 123 | int iConst = condition->getIConst(0);
|
| 124 | if (mCasesSigned.find(iConst) != mCasesSigned.end())
|
| 125 | {
|
| 126 | mContext->error(condition->getLine(), "duplicate case label", nodeStr);
|
| 127 | mDuplicateCases = true;
|
| 128 | }
|
| 129 | else
|
| 130 | {
|
| 131 | mCasesSigned.insert(iConst);
|
| 132 | }
|
| 133 | }
|
| 134 | else if (conditionType == EbtUInt)
|
| 135 | {
|
| 136 | unsigned int uConst = condition->getUConst(0);
|
| 137 | if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
|
| 138 | {
|
| 139 | mContext->error(condition->getLine(), "duplicate case label", nodeStr);
|
| 140 | mDuplicateCases = true;
|
| 141 | }
|
| 142 | else
|
| 143 | {
|
| 144 | mCasesUnsigned.insert(uConst);
|
| 145 | }
|
| 146 | }
|
| 147 | // Other types are possible only in error cases, where the error has already been generated
|
| 148 | // when parsing the case statement.
|
| 149 | }
|
| 150 | // Don't traverse the condition of the case statement
|
| 151 | return false;
|
| 152 | }
|
| 153 |
|
| 154 | bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
|
| 155 | {
|
| 156 | if (getParentNode() != nullptr)
|
| 157 | {
|
| 158 | // This is not the statementList node, but some other node.
|
| 159 | if (!mFirstCaseFound)
|
| 160 | mStatementBeforeCase = true;
|
| 161 | mLastStatementWasCase = false;
|
| 162 | }
|
| 163 | return true;
|
| 164 | }
|
| 165 |
|
| 166 | bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
|
| 167 | {
|
| 168 | if (visit == PreVisit)
|
| 169 | ++mControlFlowDepth;
|
| 170 | if (visit == PostVisit)
|
| 171 | --mControlFlowDepth;
|
| 172 | if (!mFirstCaseFound)
|
| 173 | mStatementBeforeCase = true;
|
| 174 | mLastStatementWasCase = false;
|
| 175 | return true;
|
| 176 | }
|
| 177 |
|
| 178 | bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
|
| 179 | {
|
| 180 | if (!mFirstCaseFound)
|
| 181 | mStatementBeforeCase = true;
|
| 182 | mLastStatementWasCase = false;
|
| 183 | return true;
|
| 184 | }
|
| 185 |
|
| 186 | bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
|
| 187 | {
|
| 188 | if (mStatementBeforeCase)
|
| 189 | {
|
| 190 | mContext->error(loc,
|
| 191 | "statement before the first label", "switch");
|
| 192 | }
|
| 193 | if (mLastStatementWasCase)
|
| 194 | {
|
| 195 | mContext->error(loc,
|
| 196 | "no statement between the last label and the end of the switch statement", "switch");
|
| 197 | }
|
| 198 | return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
|
| 199 | !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
|
| 200 | }
|