blob: dcca7bb2f426f55eab6a1663ef054431463140b6 [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)
Jamie Madilld7b1ab52016-12-12 14:42:19 -050037{
38}
Olli Etuahoac5274d2015-02-20 10:19:08 +020039
40void ValidateSwitch::visitSymbol(TIntermSymbol *)
41{
42 if (!mFirstCaseFound)
43 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050044 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020045}
46
47void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
48{
49 // Conditions of case labels are not traversed, so this is some other constant
50 // Could be just a statement like "0;"
51 if (!mFirstCaseFound)
52 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050053 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020054}
55
56bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
57{
58 if (!mFirstCaseFound)
59 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050060 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020061 return true;
62}
63
64bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
65{
66 if (!mFirstCaseFound)
67 mStatementBeforeCase = true;
Olli Etuahod0bad2c2016-09-09 18:01:16 +030068 mLastStatementWasCase = false;
69 return true;
70}
71
72bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
73{
74 if (!mFirstCaseFound)
75 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050076 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020077 return true;
78}
79
Olli Etuaho57961272016-09-14 13:57:46 +030080bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
Olli Etuahoac5274d2015-02-20 10:19:08 +020081{
82 if (visit == PreVisit)
83 ++mControlFlowDepth;
84 if (visit == PostVisit)
85 --mControlFlowDepth;
86 if (!mFirstCaseFound)
87 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050088 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020089 return true;
90}
91
92bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
93{
94 if (!mFirstCaseFound)
95 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -050096 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +020097 // Don't go into nested switch statements
98 return false;
99}
100
101bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
102{
103 const char *nodeStr = node->hasCondition() ? "case" : "default";
104 if (mControlFlowDepth > 0)
105 {
106 mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
107 mCaseInsideControlFlow = true;
108 }
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500109 mFirstCaseFound = true;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200110 mLastStatementWasCase = true;
111 if (!node->hasCondition())
112 {
113 ++mDefaultCount;
114 if (mDefaultCount > 1)
115 {
116 mContext->error(node->getLine(), "duplicate default label", nodeStr);
117 }
118 }
119 else
120 {
121 TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
122 if (condition == nullptr)
123 {
124 // This can happen in error cases.
125 return false;
126 }
127 TBasicType conditionType = condition->getBasicType();
128 if (conditionType != mSwitchType)
129 {
130 mContext->error(condition->getLine(),
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500131 "case label type does not match switch init-expression type", nodeStr);
Olli Etuahoac5274d2015-02-20 10:19:08 +0200132 mCaseTypeMismatch = true;
133 }
134
135 if (conditionType == EbtInt)
136 {
137 int iConst = condition->getIConst(0);
138 if (mCasesSigned.find(iConst) != mCasesSigned.end())
139 {
140 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
141 mDuplicateCases = true;
142 }
143 else
144 {
145 mCasesSigned.insert(iConst);
146 }
147 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200148 else if (conditionType == EbtUInt)
Olli Etuahoac5274d2015-02-20 10:19:08 +0200149 {
Olli Etuahoac5274d2015-02-20 10:19:08 +0200150 unsigned int uConst = condition->getUConst(0);
151 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
152 {
153 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
154 mDuplicateCases = true;
155 }
156 else
157 {
158 mCasesUnsigned.insert(uConst);
159 }
Olli Etuahoac5274d2015-02-20 10:19:08 +0200160 }
Olli Etuahoc8716df2015-02-26 17:17:22 +0200161 // Other types are possible only in error cases, where the error has already been generated
162 // when parsing the case statement.
Olli Etuahoac5274d2015-02-20 10:19:08 +0200163 }
164 // Don't traverse the condition of the case statement
165 return false;
166}
167
168bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
169{
170 if (getParentNode() != nullptr)
171 {
172 // This is not the statementList node, but some other node.
173 if (!mFirstCaseFound)
174 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500175 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200176 }
177 return true;
178}
179
180bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
181{
182 if (visit == PreVisit)
183 ++mControlFlowDepth;
184 if (visit == PostVisit)
185 --mControlFlowDepth;
186 if (!mFirstCaseFound)
187 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500188 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200189 return true;
190}
191
192bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
193{
194 if (!mFirstCaseFound)
195 mStatementBeforeCase = true;
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500196 mLastStatementWasCase = false;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200197 return true;
198}
199
200bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
201{
202 if (mStatementBeforeCase)
203 {
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500204 mContext->error(loc, "statement before the first label", "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200205 }
206 if (mLastStatementWasCase)
207 {
208 mContext->error(loc,
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500209 "no statement between the last label and the end of the switch statement",
210 "switch");
Olli Etuahoac5274d2015-02-20 10:19:08 +0200211 }
212 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
Jamie Madilld7b1ab52016-12-12 14:42:19 -0500213 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
Olli Etuahoac5274d2015-02-20 10:19:08 +0200214}
Jamie Madill45bcc782016-11-07 13:58:48 -0500215
216} // namespace sh