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 "compiler/translator/ValidateSwitch.h" |
8 | |
9 | #include "compiler/translator/Diagnostics.h" |
10 | #include "compiler/translator/tree_util/IntermTraverse.h" |
11 | |
12 | namespace sh |
13 | { |
14 | |
15 | namespace |
16 | { |
17 | |
18 | const int kMaxAllowedTraversalDepth = 256; |
19 | |
20 | class ValidateSwitch : public TIntermTraverser |
21 | { |
22 | public: |
23 | static bool validate(TBasicType switchType, |
24 | TDiagnostics *diagnostics, |
25 | TIntermBlock *statementList, |
26 | const TSourceLoc &loc); |
27 | |
28 | void visitSymbol(TIntermSymbol *) override; |
29 | void visitConstantUnion(TIntermConstantUnion *) override; |
30 | bool visitDeclaration(Visit, TIntermDeclaration *) override; |
31 | bool visitBlock(Visit visit, TIntermBlock *) override; |
32 | bool visitBinary(Visit, TIntermBinary *) override; |
33 | bool visitUnary(Visit, TIntermUnary *) override; |
34 | bool visitTernary(Visit, TIntermTernary *) override; |
35 | bool visitSwizzle(Visit, TIntermSwizzle *) override; |
36 | bool visitIfElse(Visit visit, TIntermIfElse *) override; |
37 | bool visitSwitch(Visit, TIntermSwitch *) override; |
38 | bool visitCase(Visit, TIntermCase *node) override; |
39 | bool visitAggregate(Visit, TIntermAggregate *) override; |
40 | bool visitLoop(Visit visit, TIntermLoop *) override; |
41 | bool visitBranch(Visit, TIntermBranch *) override; |
42 | |
43 | private: |
44 | ValidateSwitch(TBasicType switchType, TDiagnostics *context); |
45 | |
46 | bool validateInternal(const TSourceLoc &loc); |
47 | |
48 | TBasicType mSwitchType; |
49 | TDiagnostics *mDiagnostics; |
50 | bool mCaseTypeMismatch; |
51 | bool mFirstCaseFound; |
52 | bool mStatementBeforeCase; |
53 | bool mLastStatementWasCase; |
54 | int mControlFlowDepth; |
55 | bool mCaseInsideControlFlow; |
56 | int mDefaultCount; |
57 | std::set<int> mCasesSigned; |
58 | std::set<unsigned int> mCasesUnsigned; |
59 | bool mDuplicateCases; |
60 | }; |
61 | |
62 | bool ValidateSwitch::validate(TBasicType switchType, |
63 | TDiagnostics *diagnostics, |
64 | TIntermBlock *statementList, |
65 | const TSourceLoc &loc) |
66 | { |
67 | ValidateSwitch validate(switchType, diagnostics); |
68 | ASSERT(statementList); |
69 | statementList->traverse(&validate); |
70 | return validate.validateInternal(loc); |
71 | } |
72 | |
73 | ValidateSwitch::ValidateSwitch(TBasicType switchType, TDiagnostics *diagnostics) |
74 | : TIntermTraverser(true, false, true, nullptr), |
75 | mSwitchType(switchType), |
76 | mDiagnostics(diagnostics), |
77 | mCaseTypeMismatch(false), |
78 | mFirstCaseFound(false), |
79 | mStatementBeforeCase(false), |
80 | mLastStatementWasCase(false), |
81 | mControlFlowDepth(0), |
82 | mCaseInsideControlFlow(false), |
83 | mDefaultCount(0), |
84 | mDuplicateCases(false) |
85 | { |
86 | setMaxAllowedDepth(kMaxAllowedTraversalDepth); |
87 | } |
88 | |
89 | void ValidateSwitch::visitSymbol(TIntermSymbol *) |
90 | { |
91 | if (!mFirstCaseFound) |
92 | mStatementBeforeCase = true; |
93 | mLastStatementWasCase = false; |
94 | } |
95 | |
96 | void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *) |
97 | { |
98 | // Conditions of case labels are not traversed, so this is some other constant |
99 | // Could be just a statement like "0;" |
100 | if (!mFirstCaseFound) |
101 | mStatementBeforeCase = true; |
102 | mLastStatementWasCase = false; |
103 | } |
104 | |
105 | bool ValidateSwitch::visitDeclaration(Visit, TIntermDeclaration *) |
106 | { |
107 | if (!mFirstCaseFound) |
108 | mStatementBeforeCase = true; |
109 | mLastStatementWasCase = false; |
110 | return true; |
111 | } |
112 | |
113 | bool ValidateSwitch::visitBlock(Visit visit, TIntermBlock *) |
114 | { |
115 | if (getParentNode() != nullptr) |
116 | { |
117 | if (!mFirstCaseFound) |
118 | mStatementBeforeCase = true; |
119 | mLastStatementWasCase = false; |
120 | if (visit == PreVisit) |
121 | ++mControlFlowDepth; |
122 | if (visit == PostVisit) |
123 | --mControlFlowDepth; |
124 | } |
125 | return true; |
126 | } |
127 | |
128 | bool ValidateSwitch::visitBinary(Visit, TIntermBinary *) |
129 | { |
130 | if (!mFirstCaseFound) |
131 | mStatementBeforeCase = true; |
132 | mLastStatementWasCase = false; |
133 | return true; |
134 | } |
135 | |
136 | bool ValidateSwitch::visitUnary(Visit, TIntermUnary *) |
137 | { |
138 | if (!mFirstCaseFound) |
139 | mStatementBeforeCase = true; |
140 | mLastStatementWasCase = false; |
141 | return true; |
142 | } |
143 | |
144 | bool ValidateSwitch::visitTernary(Visit, TIntermTernary *) |
145 | { |
146 | if (!mFirstCaseFound) |
147 | mStatementBeforeCase = true; |
148 | mLastStatementWasCase = false; |
149 | return true; |
150 | } |
151 | |
152 | bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *) |
153 | { |
154 | if (!mFirstCaseFound) |
155 | mStatementBeforeCase = true; |
156 | mLastStatementWasCase = false; |
157 | return true; |
158 | } |
159 | |
160 | bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *) |
161 | { |
162 | if (visit == PreVisit) |
163 | ++mControlFlowDepth; |
164 | if (visit == PostVisit) |
165 | --mControlFlowDepth; |
166 | if (!mFirstCaseFound) |
167 | mStatementBeforeCase = true; |
168 | mLastStatementWasCase = false; |
169 | return true; |
170 | } |
171 | |
172 | bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *) |
173 | { |
174 | if (!mFirstCaseFound) |
175 | mStatementBeforeCase = true; |
176 | mLastStatementWasCase = false; |
177 | // Don't go into nested switch statements |
178 | return false; |
179 | } |
180 | |
181 | bool ValidateSwitch::visitCase(Visit, TIntermCase *node) |
182 | { |
183 | const char *nodeStr = node->hasCondition() ? "case" : "default" ; |
184 | if (mControlFlowDepth > 0) |
185 | { |
186 | mDiagnostics->error(node->getLine(), "label statement nested inside control flow" , nodeStr); |
187 | mCaseInsideControlFlow = true; |
188 | } |
189 | mFirstCaseFound = true; |
190 | mLastStatementWasCase = true; |
191 | if (!node->hasCondition()) |
192 | { |
193 | ++mDefaultCount; |
194 | if (mDefaultCount > 1) |
195 | { |
196 | mDiagnostics->error(node->getLine(), "duplicate default label" , nodeStr); |
197 | } |
198 | } |
199 | else |
200 | { |
201 | TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion(); |
202 | if (condition == nullptr) |
203 | { |
204 | // This can happen in error cases. |
205 | return false; |
206 | } |
207 | TBasicType conditionType = condition->getBasicType(); |
208 | if (conditionType != mSwitchType) |
209 | { |
210 | mDiagnostics->error(condition->getLine(), |
211 | "case label type does not match switch init-expression type" , |
212 | nodeStr); |
213 | mCaseTypeMismatch = true; |
214 | } |
215 | |
216 | if (conditionType == EbtInt) |
217 | { |
218 | int iConst = condition->getIConst(0); |
219 | if (mCasesSigned.find(iConst) != mCasesSigned.end()) |
220 | { |
221 | mDiagnostics->error(condition->getLine(), "duplicate case label" , nodeStr); |
222 | mDuplicateCases = true; |
223 | } |
224 | else |
225 | { |
226 | mCasesSigned.insert(iConst); |
227 | } |
228 | } |
229 | else if (conditionType == EbtUInt) |
230 | { |
231 | unsigned int uConst = condition->getUConst(0); |
232 | if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end()) |
233 | { |
234 | mDiagnostics->error(condition->getLine(), "duplicate case label" , nodeStr); |
235 | mDuplicateCases = true; |
236 | } |
237 | else |
238 | { |
239 | mCasesUnsigned.insert(uConst); |
240 | } |
241 | } |
242 | // Other types are possible only in error cases, where the error has already been generated |
243 | // when parsing the case statement. |
244 | } |
245 | // Don't traverse the condition of the case statement |
246 | return false; |
247 | } |
248 | |
249 | bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *) |
250 | { |
251 | if (getParentNode() != nullptr) |
252 | { |
253 | // This is not the statementList node, but some other node. |
254 | if (!mFirstCaseFound) |
255 | mStatementBeforeCase = true; |
256 | mLastStatementWasCase = false; |
257 | } |
258 | return true; |
259 | } |
260 | |
261 | bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *) |
262 | { |
263 | if (visit == PreVisit) |
264 | ++mControlFlowDepth; |
265 | if (visit == PostVisit) |
266 | --mControlFlowDepth; |
267 | if (!mFirstCaseFound) |
268 | mStatementBeforeCase = true; |
269 | mLastStatementWasCase = false; |
270 | return true; |
271 | } |
272 | |
273 | bool ValidateSwitch::visitBranch(Visit, TIntermBranch *) |
274 | { |
275 | if (!mFirstCaseFound) |
276 | mStatementBeforeCase = true; |
277 | mLastStatementWasCase = false; |
278 | return true; |
279 | } |
280 | |
281 | bool ValidateSwitch::validateInternal(const TSourceLoc &loc) |
282 | { |
283 | if (mStatementBeforeCase) |
284 | { |
285 | mDiagnostics->error(loc, "statement before the first label" , "switch" ); |
286 | } |
287 | if (mLastStatementWasCase) |
288 | { |
289 | // There have been some differences between versions of GLSL ES specs on whether this should |
290 | // be an error or not, but as of early 2018 the latest discussion is that this is an error |
291 | // also on GLSL ES versions newer than 3.00. |
292 | mDiagnostics->error( |
293 | loc, "no statement between the last label and the end of the switch statement" , |
294 | "switch" ); |
295 | } |
296 | if (getMaxDepth() >= kMaxAllowedTraversalDepth) |
297 | { |
298 | mDiagnostics->error(loc, "too complex expressions inside a switch statement" , "switch" ); |
299 | } |
300 | return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow && |
301 | !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases && |
302 | getMaxDepth() < kMaxAllowedTraversalDepth; |
303 | } |
304 | |
305 | } // anonymous namespace |
306 | |
307 | bool ValidateSwitchStatementList(TBasicType switchType, |
308 | TDiagnostics *diagnostics, |
309 | TIntermBlock *statementList, |
310 | const TSourceLoc &loc) |
311 | { |
312 | return ValidateSwitch::validate(switchType, diagnostics, statementList, loc); |
313 | } |
314 | |
315 | } // namespace sh |
316 | |