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
12namespace sh
13{
14
15namespace
16{
17
18const int kMaxAllowedTraversalDepth = 256;
19
20class 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
62bool 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
73ValidateSwitch::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
89void ValidateSwitch::visitSymbol(TIntermSymbol *)
90{
91 if (!mFirstCaseFound)
92 mStatementBeforeCase = true;
93 mLastStatementWasCase = false;
94}
95
96void 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
105bool ValidateSwitch::visitDeclaration(Visit, TIntermDeclaration *)
106{
107 if (!mFirstCaseFound)
108 mStatementBeforeCase = true;
109 mLastStatementWasCase = false;
110 return true;
111}
112
113bool 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
128bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
129{
130 if (!mFirstCaseFound)
131 mStatementBeforeCase = true;
132 mLastStatementWasCase = false;
133 return true;
134}
135
136bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
137{
138 if (!mFirstCaseFound)
139 mStatementBeforeCase = true;
140 mLastStatementWasCase = false;
141 return true;
142}
143
144bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
145{
146 if (!mFirstCaseFound)
147 mStatementBeforeCase = true;
148 mLastStatementWasCase = false;
149 return true;
150}
151
152bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
153{
154 if (!mFirstCaseFound)
155 mStatementBeforeCase = true;
156 mLastStatementWasCase = false;
157 return true;
158}
159
160bool 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
172bool 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
181bool 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
249bool 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
261bool 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
273bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
274{
275 if (!mFirstCaseFound)
276 mStatementBeforeCase = true;
277 mLastStatementWasCase = false;
278 return true;
279}
280
281bool 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
307bool 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