blob: 6091c72393b32e511909f378875bec690f84c5f3 [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/IntermNode.h"
10#include "compiler/translator/Diagnostics.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;
28 bool visitBinary(Visit, TIntermBinary *) override;
29 bool visitUnary(Visit, TIntermUnary *) override;
30 bool visitTernary(Visit, TIntermTernary *) override;
31 bool visitIfElse(Visit visit, TIntermIfElse *) override;
32 bool visitSwitch(Visit, TIntermSwitch *) override;
33 bool visitCase(Visit, TIntermCase *node) override;
34 bool visitAggregate(Visit, TIntermAggregate *) override;
35 bool visitLoop(Visit visit, TIntermLoop *) override;
36 bool visitBranch(Visit, TIntermBranch *) override;
37
38 private:
39 ValidateSwitch(TBasicType switchType, TDiagnostics *context);
40
41 bool validateInternal(const TSourceLoc &loc);
42
43 TBasicType mSwitchType;
44 TDiagnostics *mDiagnostics;
45 bool mCaseTypeMismatch;
46 bool mFirstCaseFound;
47 bool mStatementBeforeCase;
48 bool mLastStatementWasCase;
49 int mControlFlowDepth;
50 bool mCaseInsideControlFlow;
51 int mDefaultCount;
52 std::set<int> mCasesSigned;
53 std::set<unsigned int> mCasesUnsigned;
54 bool mDuplicateCases;
55};
56
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010057bool ValidateSwitch::validate(TBasicType switchType,
Olli Etuaho7351c2a52016-12-15 18:06:41 +000058 TDiagnostics *diagnostics,
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010059 TIntermBlock *statementList,
60 const TSourceLoc &loc)
Olli Etuahoac5274d2015-02-20 10:19:08 +020061{
Olli Etuaho7351c2a52016-12-15 18:06:41 +000062 ValidateSwitch validate(switchType, diagnostics);
Olli Etuahoac5274d2015-02-20 10:19:08 +020063 ASSERT(statementList);
64 statementList->traverse(&validate);
65 return validate.validateInternal(loc);
66}
67
Olli Etuaho7351c2a52016-12-15 18:06:41 +000068ValidateSwitch::ValidateSwitch(TBasicType switchType, TDiagnostics *diagnostics)
Olli Etuahoac5274d2015-02-20 10:19:08 +020069 : TIntermTraverser(true, false, true),
70 mSwitchType(switchType),
Olli Etuaho7351c2a52016-12-15 18:06:41 +000071 mDiagnostics(diagnostics),
Olli Etuahoac5274d2015-02-20 10:19:08 +020072 mCaseTypeMismatch(false),
73 mFirstCaseFound(false),
74 mStatementBeforeCase(false),
75 mLastStatementWasCase(false),
76 mControlFlowDepth(0),
77 mCaseInsideControlFlow(false),
78 mDefaultCount(0),
79 mDuplicateCases(false)
Jamie Madilld7b1ab52016-12-12 14:42:19 -050080{
81}
Olli Etuahoac5274d2015-02-20 10:19:08 +020082
83void ValidateSwitch::visitSymbol(TIntermSymbol *)
84{
85 if (!mFirstCaseFound)
86 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050087 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020088}
89
90void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
91{
92 // Conditions of case labels are not traversed, so this is some other constant
93 // Could be just a statement like "0;"
94 if (!mFirstCaseFound)
95 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050096 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020097}
98
99bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
100{
101 if (!mFirstCaseFound)
102 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500103 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200104 return true;
105}
106
107bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
108{
109 if (!mFirstCaseFound)
110 mStatementBeforeCase = true;
Olli Etuahod0bad2c2016-09-09 18:01:16 +0300111 mLastStatementWasCase = false;
112 return true;
113}
114
115bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
116{
117 if (!mFirstCaseFound)
118 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500119 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200120 return true;
121}
122
Olli Etuaho57961272016-09-14 13:57:46 +0300123bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200124{
125 if (visit == PreVisit)
126 ++mControlFlowDepth;
127 if (visit == PostVisit)
128 --mControlFlowDepth;
129 if (!mFirstCaseFound)
130 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500131 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200132 return true;
133}
134
135bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
136{
137 if (!mFirstCaseFound)
138 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500139 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200140 // Don't go into nested switch statements
141 return false;
142}
143
144bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
145{
146 const char *nodeStr = node->hasCondition() ? "case" : "default";
147 if (mControlFlowDepth > 0)
148 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000149 mDiagnostics->error(node->getLine(), "label statement nested inside control flow", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200150 mCaseInsideControlFlow = true;
151 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500152 mFirstCaseFound = true;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200153 mLastStatementWasCase = true;
154 if (!node->hasCondition())
155 {
156 ++mDefaultCount;
157 if (mDefaultCount > 1)
158 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000159 mDiagnostics->error(node->getLine(), "duplicate default label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200160 }
161 }
162 else
163 {
164 TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
165 if (condition == nullptr)
166 {
167 // This can happen in error cases.
168 return false;
169 }
170 TBasicType conditionType = condition->getBasicType();
171 if (conditionType != mSwitchType)
172 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000173 mDiagnostics->error(condition->getLine(),
174 "case label type does not match switch init-expression type",
175 nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200176 mCaseTypeMismatch = true;
177 }
178
179 if (conditionType == EbtInt)
180 {
181 int iConst = condition->getIConst(0);
182 if (mCasesSigned.find(iConst) != mCasesSigned.end())
183 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000184 mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200185 mDuplicateCases = true;
186 }
187 else
188 {
189 mCasesSigned.insert(iConst);
190 }
191 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200192 else if (conditionType == EbtUInt)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200193 {
Olli Etuahoac5274d2015-02-20 10:19:08 +0200194 unsigned int uConst = condition->getUConst(0);
195 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
196 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000197 mDiagnostics->error(condition->getLine(), "duplicate case label", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200198 mDuplicateCases = true;
199 }
200 else
201 {
202 mCasesUnsigned.insert(uConst);
203 }
Olli Etuahoac5274d2015-02-20 10:19:08 +0200204 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200205 // Other types are possible only in error cases, where the error has already been generated
206 // when parsing the case statement.
Olli Etuahoac5274d2015-02-20 10:19:08 +0200207 }
208 // Don't traverse the condition of the case statement
209 return false;
210}
211
212bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
213{
214 if (getParentNode() != nullptr)
215 {
216 // This is not the statementList node, but some other node.
217 if (!mFirstCaseFound)
218 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500219 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200220 }
221 return true;
222}
223
224bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
225{
226 if (visit == PreVisit)
227 ++mControlFlowDepth;
228 if (visit == PostVisit)
229 --mControlFlowDepth;
230 if (!mFirstCaseFound)
231 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500232 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200233 return true;
234}
235
236bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
237{
238 if (!mFirstCaseFound)
239 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500240 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200241 return true;
242}
243
244bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
245{
246 if (mStatementBeforeCase)
247 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000248 mDiagnostics->error(loc, "statement before the first label", "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200249 }
250 if (mLastStatementWasCase)
251 {
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000252 mDiagnostics->error(
253 loc, "no statement between the last label and the end of the switch statement",
254 "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200255 }
256 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500257 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200258}
Jamie Madill45bcc782016-11-07 13:58:48 -0500259
Olli Etuaho7351c2a52016-12-15 18:06:41 +0000260} // anonymous namespace
261
262bool ValidateSwitchStatementList(TBasicType switchType,
263 TDiagnostics *diagnostics,
264 TIntermBlock *statementList,
265 const TSourceLoc &loc)
266{
267 return ValidateSwitch::validate(switchType, diagnostics, statementList, loc);
268}
269
Jamie Madill45bcc782016-11-07 13:58:48 -0500270} // namespace sh