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