blob: 8b6fa72f4178cf26efb072583ae8b3f98e833a8e [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 Etuahoc26214d2018-03-16 10:43:11 +020010#include "compiler/translator/tree_util/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;
Olli Etuahofd643282018-04-04 16:12:09 +030029 bool visitBlock(Visit 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:
Olli Etuahod05f9642018-03-05 12:13:26 +020042 ValidateSwitch(TBasicType switchType, TDiagnostics *context);
Olli Etuaho7351c2a52016-12-15 18:06:41 +000043
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 Etuahod05f9642018-03-05 12:13:26 +020065 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 Etuahod05f9642018-03-05 12:13:26 +020071ValidateSwitch::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
Olli Etuahofd643282018-04-04 16:12:09 +0300110bool ValidateSwitch::visitBlock(Visit visit, TIntermBlock *)
Olli Etuaho923ecef2017-10-11 12:01:38 +0300111{
112 if (getParentNode() != nullptr)
113 {
114 if (!mFirstCaseFound)
115 mStatementBeforeCase = true;
116 mLastStatementWasCase = false;
Olli Etuahofd643282018-04-04 16:12:09 +0300117 if (visit == PreVisit)
118 ++mControlFlowDepth;
119 if (visit == PostVisit)
120 --mControlFlowDepth;
Olli Etuaho923ecef2017-10-11 12:01:38 +0300121 }
122 return true;
123}
124
Olli Etuahoac5274d2015-02-20 10:19:08 +0200125bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
126{
127 if (!mFirstCaseFound)
128 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500129 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200130 return true;
131}
132
133bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
134{
135 if (!mFirstCaseFound)
136 mStatementBeforeCase = true;
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300137 mLastStatementWasCase = false;
138 return true;
139}
140
141bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
142{
143 if (!mFirstCaseFound)
144 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500145 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200146 return true;
147}
148
Olli Etuaho923ecef2017-10-11 12:01:38 +0300149bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
150{
151 if (!mFirstCaseFound)
152 mStatementBeforeCase = true;
153 mLastStatementWasCase = false;
154 return true;
155}
156
Olli Etuaho57961272016-09-14 13:57:46 +0300157bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200158{
159 if (visit == PreVisit)
160 ++mControlFlowDepth;
161 if (visit == PostVisit)
162 --mControlFlowDepth;
163 if (!mFirstCaseFound)
164 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500165 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200166 return true;
167}
168
169bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
170{
171 if (!mFirstCaseFound)
172 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500173 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200174 // Don't go into nested switch statements
175 return false;
176}
177
178bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
179{
180 const char *nodeStr = node->hasCondition() ? "case" : "default";
181 if (mControlFlowDepth > 0)
182 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000183 mDiagnostics->error(node->getLine(), "label statement nested inside control flow", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200184 mCaseInsideControlFlow = true;
185 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500186 mFirstCaseFound = true;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200187 mLastStatementWasCase = true;
188 if (!node->hasCondition())
189 {
190 ++mDefaultCount;
191 if (mDefaultCount > 1)
192 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000193 mDiagnostics->error(node->getLine(), "duplicate default label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200194 }
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 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000207 mDiagnostics->error(condition->getLine(),
208 "case label type does not match switch init-expression type",
209 nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200210 mCaseTypeMismatch = true;
211 }
212
213 if (conditionType == EbtInt)
214 {
215 int iConst = condition->getIConst(0);
216 if (mCasesSigned.find(iConst) != mCasesSigned.end())
217 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000218 mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200219 mDuplicateCases = true;
220 }
221 else
222 {
223 mCasesSigned.insert(iConst);
224 }
225 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200226 else if (conditionType == EbtUInt)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200227 {
Olli Etuahoac5274d2015-02-20 10:19:08 +0200228 unsigned int uConst = condition->getUConst(0);
229 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
230 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000231 mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200232 mDuplicateCases = true;
233 }
234 else
235 {
236 mCasesUnsigned.insert(uConst);
237 }
Olli Etuahoac5274d2015-02-20 10:19:08 +0200238 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200239 // Other types are possible only in error cases, where the error has already been generated
240 // when parsing the case statement.
Olli Etuahoac5274d2015-02-20 10:19:08 +0200241 }
242 // Don't traverse the condition of the case statement
243 return false;
244}
245
246bool 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;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500253 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200254 }
255 return true;
256}
257
258bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
259{
260 if (visit == PreVisit)
261 ++mControlFlowDepth;
262 if (visit == PostVisit)
263 --mControlFlowDepth;
264 if (!mFirstCaseFound)
265 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500266 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200267 return true;
268}
269
270bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
271{
272 if (!mFirstCaseFound)
273 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500274 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200275 return true;
276}
277
278bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
279{
280 if (mStatementBeforeCase)
281 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000282 mDiagnostics->error(loc, "statement before the first label", "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200283 }
284 if (mLastStatementWasCase)
285 {
Olli Etuahod05f9642018-03-05 12:13:26 +0200286 // There have been some differences between versions of GLSL ES specs on whether this should
287 // be an error or not, but as of early 2018 the latest discussion is that this is an error
288 // also on GLSL ES versions newer than 3.00.
289 mDiagnostics->error(
290 loc, "no statement between the last label and the end of the switch statement",
291 "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200292 }
Olli Etuahod05f9642018-03-05 12:13:26 +0200293 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500294 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200295}
Jamie Madill45bcc782016-11-07 13:58:48 -0500296
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000297} // anonymous namespace
298
299bool ValidateSwitchStatementList(TBasicType switchType,
300 TDiagnostics *diagnostics,
301 TIntermBlock *statementList,
302 const TSourceLoc &loc)
303{
Olli Etuahod05f9642018-03-05 12:13:26 +0200304 return ValidateSwitch::validate(switchType, diagnostics, statementList, loc);
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000305}
306
Jamie Madill45bcc782016-11-07 13:58:48 -0500307} // namespace sh