1// Copyright 2016 The SwiftShader Authors. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#include "ValidateSwitch.h"
16
17#include "ParseHelper.h"
18
19bool ValidateSwitch::validate(TBasicType switchType, TParseContext *context,
20 TIntermAggregate *statementList, const TSourceLoc &loc)
21{
22 ValidateSwitch validate(switchType, context);
23 ASSERT(statementList);
24 statementList->traverse(&validate);
25 return validate.validateInternal(loc);
26}
27
28ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
29 : TIntermTraverser(true, false, true),
30 mSwitchType(switchType),
31 mContext(context),
32 mCaseTypeMismatch(false),
33 mFirstCaseFound(false),
34 mStatementBeforeCase(false),
35 mLastStatementWasCase(false),
36 mControlFlowDepth(0),
37 mCaseInsideControlFlow(false),
38 mDefaultCount(0),
39 mDuplicateCases(false)
40{}
41
42void ValidateSwitch::visitSymbol(TIntermSymbol *)
43{
44 if (!mFirstCaseFound)
45 mStatementBeforeCase = true;
46 mLastStatementWasCase = false;
47}
48
49void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
50{
51 // Conditions of case labels are not traversed, so this is some other constant
52 // Could be just a statement like "0;"
53 if (!mFirstCaseFound)
54 mStatementBeforeCase = true;
55 mLastStatementWasCase = false;
56}
57
58bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
59{
60 if (!mFirstCaseFound)
61 mStatementBeforeCase = true;
62 mLastStatementWasCase = false;
63 return true;
64}
65
66bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
67{
68 if (!mFirstCaseFound)
69 mStatementBeforeCase = true;
70 mLastStatementWasCase = false;
71 return true;
72}
73
74bool ValidateSwitch::visitSelection(Visit visit, TIntermSelection *)
75{
76 if (visit == PreVisit)
77 ++mControlFlowDepth;
78 if (visit == PostVisit)
79 --mControlFlowDepth;
80 if (!mFirstCaseFound)
81 mStatementBeforeCase = true;
82 mLastStatementWasCase = false;
83 return true;
84}
85
86bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
87{
88 if (!mFirstCaseFound)
89 mStatementBeforeCase = true;
90 mLastStatementWasCase = false;
91 // Don't go into nested switch statements
92 return false;
93}
94
95bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
96{
97 const char *nodeStr = node->hasCondition() ? "case" : "default";
98 if (mControlFlowDepth > 0)
99 {
100 mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
101 mCaseInsideControlFlow = true;
102 }
103 mFirstCaseFound = true;
104 mLastStatementWasCase = true;
105 if (!node->hasCondition())
106 {
107 ++mDefaultCount;
108 if (mDefaultCount > 1)
109 {
110 mContext->error(node->getLine(), "duplicate default label", nodeStr);
111 }
112 }
113 else
114 {
115 TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
116 if (condition == nullptr)
117 {
118 // This can happen in error cases.
119 return false;
120 }
121 TBasicType conditionType = condition->getBasicType();
122 if (conditionType != mSwitchType)
123 {
124 mContext->error(condition->getLine(),
125 "case label type does not match switch init-expression type", nodeStr);
126 mCaseTypeMismatch = true;
127 }
128
129 if (conditionType == EbtInt)
130 {
131 int iConst = condition->getIConst(0);
132 if (mCasesSigned.find(iConst) != mCasesSigned.end())
133 {
134 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
135 mDuplicateCases = true;
136 }
137 else
138 {
139 mCasesSigned.insert(iConst);
140 }
141 }
142 else if (conditionType == EbtUInt)
143 {
144 unsigned int uConst = condition->getUConst(0);
145 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
146 {
147 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
148 mDuplicateCases = true;
149 }
150 else
151 {
152 mCasesUnsigned.insert(uConst);
153 }
154 }
155 // Other types are possible only in error cases, where the error has already been generated
156 // when parsing the case statement.
157 }
158 // Don't traverse the condition of the case statement
159 return false;
160}
161
162bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
163{
164 if (getParentNode() != nullptr)
165 {
166 // This is not the statementList node, but some other node.
167 if (!mFirstCaseFound)
168 mStatementBeforeCase = true;
169 mLastStatementWasCase = false;
170 }
171 return true;
172}
173
174bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
175{
176 if (visit == PreVisit)
177 ++mControlFlowDepth;
178 if (visit == PostVisit)
179 --mControlFlowDepth;
180 if (!mFirstCaseFound)
181 mStatementBeforeCase = true;
182 mLastStatementWasCase = false;
183 return true;
184}
185
186bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
187{
188 if (!mFirstCaseFound)
189 mStatementBeforeCase = true;
190 mLastStatementWasCase = false;
191 return true;
192}
193
194bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
195{
196 if (mStatementBeforeCase)
197 {
198 mContext->error(loc,
199 "statement before the first label", "switch");
200 }
201 if (mLastStatementWasCase)
202 {
203 mContext->error(loc,
204 "no statement between the last label and the end of the switch statement", "switch");
205 }
206 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
207 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
208}
209