blob: 9bcd8f5fe45495b046674e9c50c158e5fd3ff066 [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
9#include "compiler/translator/ParseContext.h"
10
Jamie Madill45bcc782016-11-07 13:58:48 -050011namespace sh
12{
13
Olli Etuaho6d40bbd2016-09-30 13:49:38 +010014bool ValidateSwitch::validate(TBasicType switchType,
15 TParseContext *context,
16 TIntermBlock *statementList,
17 const TSourceLoc &loc)
Olli Etuahoac5274d2015-02-20 10:19:08 +020018{
19 ValidateSwitch validate(switchType, context);
20 ASSERT(statementList);
21 statementList->traverse(&validate);
22 return validate.validateInternal(loc);
23}
24
25ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
26 : TIntermTraverser(true, false, true),
27 mSwitchType(switchType),
28 mContext(context),
29 mCaseTypeMismatch(false),
30 mFirstCaseFound(false),
31 mStatementBeforeCase(false),
32 mLastStatementWasCase(false),
33 mControlFlowDepth(0),
34 mCaseInsideControlFlow(false),
35 mDefaultCount(0),
36 mDuplicateCases(false)
37{}
38
39void ValidateSwitch::visitSymbol(TIntermSymbol *)
40{
41 if (!mFirstCaseFound)
42 mStatementBeforeCase = true;
43 mLastStatementWasCase = false;
44}
45
46void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
47{
48 // Conditions of case labels are not traversed, so this is some other constant
49 // Could be just a statement like "0;"
50 if (!mFirstCaseFound)
51 mStatementBeforeCase = true;
52 mLastStatementWasCase = false;
53}
54
55bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
56{
57 if (!mFirstCaseFound)
58 mStatementBeforeCase = true;
59 mLastStatementWasCase = false;
60 return true;
61}
62
63bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
64{
65 if (!mFirstCaseFound)
66 mStatementBeforeCase = true;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030067 mLastStatementWasCase = false;
68 return true;
69}
70
71bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
72{
73 if (!mFirstCaseFound)
74 mStatementBeforeCase = true;
Olli Etuahoac5274d2015-02-20 10:19:08 +020075 mLastStatementWasCase = false;
76 return true;
77}
78
Olli Etuaho57961272016-09-14 13:57:46 +030079bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
Olli Etuahoac5274d2015-02-20 10:19:08 +020080{
81 if (visit == PreVisit)
82 ++mControlFlowDepth;
83 if (visit == PostVisit)
84 --mControlFlowDepth;
85 if (!mFirstCaseFound)
86 mStatementBeforeCase = true;
87 mLastStatementWasCase = false;
88 return true;
89}
90
91bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
92{
93 if (!mFirstCaseFound)
94 mStatementBeforeCase = true;
95 mLastStatementWasCase = false;
96 // Don't go into nested switch statements
97 return false;
98}
99
100bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
101{
102 const char *nodeStr = node->hasCondition() ? "case" : "default";
103 if (mControlFlowDepth > 0)
104 {
105 mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
106 mCaseInsideControlFlow = true;
107 }
108 mFirstCaseFound = true;
109 mLastStatementWasCase = true;
110 if (!node->hasCondition())
111 {
112 ++mDefaultCount;
113 if (mDefaultCount > 1)
114 {
115 mContext->error(node->getLine(), "duplicate default label", nodeStr);
116 }
117 }
118 else
119 {
120 TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
121 if (condition == nullptr)
122 {
123 // This can happen in error cases.
124 return false;
125 }
126 TBasicType conditionType = condition->getBasicType();
127 if (conditionType != mSwitchType)
128 {
129 mContext->error(condition->getLine(),
130 "case label type does not match switch init-expression type", nodeStr);
131 mCaseTypeMismatch = true;
132 }
133
134 if (conditionType == EbtInt)
135 {
136 int iConst = condition->getIConst(0);
137 if (mCasesSigned.find(iConst) != mCasesSigned.end())
138 {
139 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
140 mDuplicateCases = true;
141 }
142 else
143 {
144 mCasesSigned.insert(iConst);
145 }
146 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200147 else if (conditionType == EbtUInt)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200148 {
Olli Etuahoac5274d2015-02-20 10:19:08 +0200149 unsigned int uConst = condition->getUConst(0);
150 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
151 {
152 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
153 mDuplicateCases = true;
154 }
155 else
156 {
157 mCasesUnsigned.insert(uConst);
158 }
Olli Etuahoac5274d2015-02-20 10:19:08 +0200159 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200160 // Other types are possible only in error cases, where the error has already been generated
161 // when parsing the case statement.
Olli Etuahoac5274d2015-02-20 10:19:08 +0200162 }
163 // Don't traverse the condition of the case statement
164 return false;
165}
166
167bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
168{
169 if (getParentNode() != nullptr)
170 {
171 // This is not the statementList node, but some other node.
172 if (!mFirstCaseFound)
173 mStatementBeforeCase = true;
174 mLastStatementWasCase = false;
175 }
176 return true;
177}
178
179bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
180{
181 if (visit == PreVisit)
182 ++mControlFlowDepth;
183 if (visit == PostVisit)
184 --mControlFlowDepth;
185 if (!mFirstCaseFound)
186 mStatementBeforeCase = true;
187 mLastStatementWasCase = false;
188 return true;
189}
190
191bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
192{
193 if (!mFirstCaseFound)
194 mStatementBeforeCase = true;
195 mLastStatementWasCase = false;
196 return true;
197}
198
199bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
200{
201 if (mStatementBeforeCase)
202 {
203 mContext->error(loc,
204 "statement before the first label", "switch");
205 }
206 if (mLastStatementWasCase)
207 {
208 mContext->error(loc,
209 "no statement between the last label and the end of the switch statement", "switch");
210 }
211 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
212 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
213}
Jamie Madill45bcc782016-11-07 13:58:48 -0500214
215} // namespace sh