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