• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
validate(TBasicType switchType,TParseContext * context,TIntermAggregate * statementList,const TSourceLoc & loc)19 bool 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 
ValidateSwitch(TBasicType switchType,TParseContext * context)28 ValidateSwitch::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 
visitSymbol(TIntermSymbol *)42 void ValidateSwitch::visitSymbol(TIntermSymbol *)
43 {
44 	if (!mFirstCaseFound)
45 		mStatementBeforeCase = true;
46 	mLastStatementWasCase = false;
47 }
48 
visitConstantUnion(TIntermConstantUnion *)49 void 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 
visitBinary(Visit,TIntermBinary *)58 bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
59 {
60 	if (!mFirstCaseFound)
61 		mStatementBeforeCase = true;
62 	mLastStatementWasCase = false;
63 	return true;
64 }
65 
visitUnary(Visit,TIntermUnary *)66 bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
67 {
68 	if (!mFirstCaseFound)
69 		mStatementBeforeCase = true;
70 	mLastStatementWasCase = false;
71 	return true;
72 }
73 
visitSelection(Visit visit,TIntermSelection *)74 bool 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 
visitSwitch(Visit,TIntermSwitch *)86 bool 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 
visitCase(Visit,TIntermCase * node)95 bool 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 
visitAggregate(Visit visit,TIntermAggregate *)162 bool 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 
visitLoop(Visit visit,TIntermLoop *)174 bool 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 
visitBranch(Visit,TIntermBranch *)186 bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
187 {
188 	if (!mFirstCaseFound)
189 		mStatementBeforeCase = true;
190 	mLastStatementWasCase = false;
191 	return true;
192 }
193 
validateInternal(const TSourceLoc & loc)194 bool 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