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