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