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