1 //
2 // Copyright 2002 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
validate(TBasicType switchType,TDiagnostics * diagnostics,TIntermBlock * statementList,const TSourceLoc & loc)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
ValidateSwitch(TBasicType switchType,TDiagnostics * diagnostics)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
visitSymbol(TIntermSymbol *)89 void ValidateSwitch::visitSymbol(TIntermSymbol *)
90 {
91 if (!mFirstCaseFound)
92 mStatementBeforeCase = true;
93 mLastStatementWasCase = false;
94 }
95
visitConstantUnion(TIntermConstantUnion *)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
visitDeclaration(Visit,TIntermDeclaration *)105 bool ValidateSwitch::visitDeclaration(Visit, TIntermDeclaration *)
106 {
107 if (!mFirstCaseFound)
108 mStatementBeforeCase = true;
109 mLastStatementWasCase = false;
110 return true;
111 }
112
visitBlock(Visit visit,TIntermBlock *)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
visitBinary(Visit,TIntermBinary *)128 bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
129 {
130 if (!mFirstCaseFound)
131 mStatementBeforeCase = true;
132 mLastStatementWasCase = false;
133 return true;
134 }
135
visitUnary(Visit,TIntermUnary *)136 bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
137 {
138 if (!mFirstCaseFound)
139 mStatementBeforeCase = true;
140 mLastStatementWasCase = false;
141 return true;
142 }
143
visitTernary(Visit,TIntermTernary *)144 bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
145 {
146 if (!mFirstCaseFound)
147 mStatementBeforeCase = true;
148 mLastStatementWasCase = false;
149 return true;
150 }
151
visitSwizzle(Visit,TIntermSwizzle *)152 bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
153 {
154 if (!mFirstCaseFound)
155 mStatementBeforeCase = true;
156 mLastStatementWasCase = false;
157 return true;
158 }
159
visitIfElse(Visit visit,TIntermIfElse *)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
visitSwitch(Visit,TIntermSwitch *)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
visitCase(Visit,TIntermCase * node)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
visitAggregate(Visit visit,TIntermAggregate *)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
visitLoop(Visit visit,TIntermLoop *)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
visitBranch(Visit,TIntermBranch *)273 bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
274 {
275 if (!mFirstCaseFound)
276 mStatementBeforeCase = true;
277 mLastStatementWasCase = false;
278 return true;
279 }
280
validateInternal(const TSourceLoc & loc)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
ValidateSwitchStatementList(TBasicType switchType,TDiagnostics * diagnostics,TIntermBlock * statementList,const TSourceLoc & loc)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