blob: bc9ba34d423fe94557975c19364a93ac83f363b5 [file] [log] [blame]
Olli Etuahoac5274d2015-02-20 10:19:08 +02001//
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
Olli Etuaho7351c2a52016-12-15 18:06:41 +00009#include "compiler/translator/Diagnostics.h"
Olli Etuahocccf2b02017-07-05 14:50:54 +030010#include "compiler/translator/IntermTraverse.h"
Olli Etuahoac5274d2015-02-20 10:19:08 +020011
Jamie Madill45bcc782016-11-07 13:58:48 -050012namespace sh
13{
14
Olli Etuaho7351c2a52016-12-15 18:06:41 +000015namespace
16{
17
18class ValidateSwitch : public TIntermTraverser
19{
20 public:
21 static bool validate(TBasicType switchType,
22 TDiagnostics *diagnostics,
23 TIntermBlock *statementList,
24 const TSourceLoc &loc);
25
26 void visitSymbol(TIntermSymbol *) override;
27 void visitConstantUnion(TIntermConstantUnion *) override;
Olli Etuaho923ecef2017-10-11 12:01:38 +030028 bool visitDeclaration(Visit, TIntermDeclaration *) override;
29 bool visitBlock(Visit, TIntermBlock *) override;
Olli Etuaho7351c2a52016-12-15 18:06:41 +000030 bool visitBinary(Visit, TIntermBinary *) override;
31 bool visitUnary(Visit, TIntermUnary *) override;
32 bool visitTernary(Visit, TIntermTernary *) override;
Olli Etuaho923ecef2017-10-11 12:01:38 +030033 bool visitSwizzle(Visit, TIntermSwizzle *) override;
Olli Etuaho7351c2a52016-12-15 18:06:41 +000034 bool visitIfElse(Visit visit, TIntermIfElse *) override;
35 bool visitSwitch(Visit, TIntermSwitch *) override;
36 bool visitCase(Visit, TIntermCase *node) override;
37 bool visitAggregate(Visit, TIntermAggregate *) override;
38 bool visitLoop(Visit visit, TIntermLoop *) override;
39 bool visitBranch(Visit, TIntermBranch *) override;
40
41 private:
42 ValidateSwitch(TBasicType switchType, TDiagnostics *context);
43
44 bool validateInternal(const TSourceLoc &loc);
45
46 TBasicType mSwitchType;
47 TDiagnostics *mDiagnostics;
48 bool mCaseTypeMismatch;
49 bool mFirstCaseFound;
50 bool mStatementBeforeCase;
51 bool mLastStatementWasCase;
52 int mControlFlowDepth;
53 bool mCaseInsideControlFlow;
54 int mDefaultCount;
55 std::set<int> mCasesSigned;
56 std::set<unsigned int> mCasesUnsigned;
57 bool mDuplicateCases;
58};
59
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010060bool ValidateSwitch::validate(TBasicType switchType,
Olli Etuaho7351c2a52016-12-15 18:06:41 +000061 TDiagnostics *diagnostics,
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010062 TIntermBlock *statementList,
63 const TSourceLoc &loc)
Olli Etuahoac5274d2015-02-20 10:19:08 +020064{
Olli Etuaho7351c2a52016-12-15 18:06:41 +000065 ValidateSwitch validate(switchType, diagnostics);
Olli Etuahoac5274d2015-02-20 10:19:08 +020066 ASSERT(statementList);
67 statementList->traverse(&validate);
68 return validate.validateInternal(loc);
69}
70
Olli Etuaho7351c2a52016-12-15 18:06:41 +000071ValidateSwitch::ValidateSwitch(TBasicType switchType, TDiagnostics *diagnostics)
Olli Etuahoac5274d2015-02-20 10:19:08 +020072 : TIntermTraverser(true, false, true),
73 mSwitchType(switchType),
Olli Etuaho7351c2a52016-12-15 18:06:41 +000074 mDiagnostics(diagnostics),
Olli Etuahoac5274d2015-02-20 10:19:08 +020075 mCaseTypeMismatch(false),
76 mFirstCaseFound(false),
77 mStatementBeforeCase(false),
78 mLastStatementWasCase(false),
79 mControlFlowDepth(0),
80 mCaseInsideControlFlow(false),
81 mDefaultCount(0),
82 mDuplicateCases(false)
Jamie Madilld7b1ab52016-12-12 14:42:19 -050083{
84}
Olli Etuahoac5274d2015-02-20 10:19:08 +020085
86void ValidateSwitch::visitSymbol(TIntermSymbol *)
87{
88 if (!mFirstCaseFound)
89 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050090 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020091}
92
93void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
94{
95 // Conditions of case labels are not traversed, so this is some other constant
96 // Could be just a statement like "0;"
97 if (!mFirstCaseFound)
98 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050099 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200100}
101
Olli Etuaho923ecef2017-10-11 12:01:38 +0300102bool ValidateSwitch::visitDeclaration(Visit, TIntermDeclaration *)
103{
104 if (!mFirstCaseFound)
105 mStatementBeforeCase = true;
106 mLastStatementWasCase = false;
107 return true;
108}
109
110bool ValidateSwitch::visitBlock(Visit, TIntermBlock *)
111{
112 if (getParentNode() != nullptr)
113 {
114 if (!mFirstCaseFound)
115 mStatementBeforeCase = true;
116 mLastStatementWasCase = false;
117 }
118 return true;
119}
120
Olli Etuahoac5274d2015-02-20 10:19:08 +0200121bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
122{
123 if (!mFirstCaseFound)
124 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500125 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200126 return true;
127}
128
129bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
130{
131 if (!mFirstCaseFound)
132 mStatementBeforeCase = true;
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300133 mLastStatementWasCase = false;
134 return true;
135}
136
137bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
138{
139 if (!mFirstCaseFound)
140 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500141 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200142 return true;
143}
144
Olli Etuaho923ecef2017-10-11 12:01:38 +0300145bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
146{
147 if (!mFirstCaseFound)
148 mStatementBeforeCase = true;
149 mLastStatementWasCase = false;
150 return true;
151}
152
Olli Etuaho57961272016-09-14 13:57:46 +0300153bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200154{
155 if (visit == PreVisit)
156 ++mControlFlowDepth;
157 if (visit == PostVisit)
158 --mControlFlowDepth;
159 if (!mFirstCaseFound)
160 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500161 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200162 return true;
163}
164
165bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
166{
167 if (!mFirstCaseFound)
168 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500169 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200170 // Don't go into nested switch statements
171 return false;
172}
173
174bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
175{
176 const char *nodeStr = node->hasCondition() ? "case" : "default";
177 if (mControlFlowDepth > 0)
178 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000179 mDiagnostics->error(node->getLine(), "label statement nested inside control flow", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200180 mCaseInsideControlFlow = true;
181 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500182 mFirstCaseFound = true;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200183 mLastStatementWasCase = true;
184 if (!node->hasCondition())
185 {
186 ++mDefaultCount;
187 if (mDefaultCount > 1)
188 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000189 mDiagnostics->error(node->getLine(), "duplicate default label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200190 }
191 }
192 else
193 {
194 TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
195 if (condition == nullptr)
196 {
197 // This can happen in error cases.
198 return false;
199 }
200 TBasicType conditionType = condition->getBasicType();
201 if (conditionType != mSwitchType)
202 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000203 mDiagnostics->error(condition->getLine(),
204 "case label type does not match switch init-expression type",
205 nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200206 mCaseTypeMismatch = true;
207 }
208
209 if (conditionType == EbtInt)
210 {
211 int iConst = condition->getIConst(0);
212 if (mCasesSigned.find(iConst) != mCasesSigned.end())
213 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000214 mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200215 mDuplicateCases = true;
216 }
217 else
218 {
219 mCasesSigned.insert(iConst);
220 }
221 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200222 else if (conditionType == EbtUInt)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200223 {
Olli Etuahoac5274d2015-02-20 10:19:08 +0200224 unsigned int uConst = condition->getUConst(0);
225 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
226 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000227 mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200228 mDuplicateCases = true;
229 }
230 else
231 {
232 mCasesUnsigned.insert(uConst);
233 }
Olli Etuahoac5274d2015-02-20 10:19:08 +0200234 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200235 // Other types are possible only in error cases, where the error has already been generated
236 // when parsing the case statement.
Olli Etuahoac5274d2015-02-20 10:19:08 +0200237 }
238 // Don't traverse the condition of the case statement
239 return false;
240}
241
242bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
243{
244 if (getParentNode() != nullptr)
245 {
246 // This is not the statementList node, but some other node.
247 if (!mFirstCaseFound)
248 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500249 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200250 }
251 return true;
252}
253
254bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
255{
256 if (visit == PreVisit)
257 ++mControlFlowDepth;
258 if (visit == PostVisit)
259 --mControlFlowDepth;
260 if (!mFirstCaseFound)
261 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500262 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200263 return true;
264}
265
266bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
267{
268 if (!mFirstCaseFound)
269 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500270 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200271 return true;
272}
273
274bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
275{
276 if (mStatementBeforeCase)
277 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000278 mDiagnostics->error(loc, "statement before the first label", "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200279 }
280 if (mLastStatementWasCase)
281 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000282 mDiagnostics->error(
283 loc, "no statement between the last label and the end of the switch statement",
284 "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200285 }
286 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500287 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200288}
Jamie Madill45bcc782016-11-07 13:58:48 -0500289
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000290} // anonymous namespace
291
292bool ValidateSwitchStatementList(TBasicType switchType,
293 TDiagnostics *diagnostics,
294 TIntermBlock *statementList,
295 const TSourceLoc &loc)
296{
297 return ValidateSwitch::validate(switchType, diagnostics, statementList, loc);
298}
299
Jamie Madill45bcc782016-11-07 13:58:48 -0500300} // namespace sh