Fix switch statement validation corner cases
The grammar needs to generate AST nodes even for no-op statements,
since they might be the last statement in a switch statement that is
required for switch statement validity. Change the grammar to generate
nodes from empty blocks and empty declarations.
We also need to do some further processing of the AST. This is because
PruneEmptyDeclarations will still remove empty declarations, and at
least the NVIDIA driver GLSL compiler doesn't accept some types of
no-op statements as the last statement inside a switch statement. So
after parsing has finished we do rudimentary dead code elimination to
remove dead cases from the end of switch statements.
BUG=angleproject:2181
TEST=angle_unittests
Change-Id: I586f2e4a3ac2171e65f1f0ccb7a7de220e3cc225
Reviewed-on: https://chromium-review.googlesource.com/712574
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index 99f46b2..d89f3d1 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -29,6 +29,7 @@
#include "compiler/translator/RegenerateStructNames.h"
#include "compiler/translator/RemoveArrayLengthMethod.h"
#include "compiler/translator/RemoveInvariantDeclaration.h"
+#include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
#include "compiler/translator/RemovePow.h"
#include "compiler/translator/RewriteDoWhile.h"
#include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
@@ -374,6 +375,20 @@
if (success && (compileOptions & SH_LIMIT_EXPRESSION_COMPLEXITY))
success = limitExpressionComplexity(root);
+ // Prune empty declarations to work around driver bugs and to keep declaration output
+ // simple. We want to do this before most other operations since TIntermDeclaration nodes
+ // without any children are tricky to work with.
+ if (success)
+ PruneEmptyDeclarations(root);
+
+ // In case the last case inside a switch statement is a certain type of no-op, GLSL
+ // compilers in drivers may not accept it. In this case we clean up the dead code from the
+ // end of switch statements. This is also required because PruneEmptyDeclarations may have
+ // left switch statements that only contained an empty declaration inside the final case in
+ // an invalid state. Relies on that PruneEmptyDeclarations has already been run.
+ if (success)
+ RemoveNoOpCasesFromEndOfSwitchStatements(root, &symbolTable);
+
// Create the function DAG and check there is no recursion
if (success)
success = initCallDag(root);
@@ -392,11 +407,6 @@
if (success && !(compileOptions & SH_DONT_PRUNE_UNUSED_FUNCTIONS))
success = pruneUnusedFunctions(root);
- // Prune empty declarations to work around driver bugs and to keep declaration output
- // simple.
- if (success)
- PruneEmptyDeclarations(root);
-
if (success && shaderVersion >= 310)
{
success = ValidateVaryingLocations(root, &mDiagnostics);
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 925e73a..1d92ee0 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -480,10 +480,11 @@
void TIntermBlock::appendStatement(TIntermNode *statement)
{
- // Declaration nodes with no children can appear if all the declarators just added constants to
- // the symbol table instead of generating code. They're no-ops so they aren't added to blocks.
- if (statement != nullptr && (statement->getAsDeclarationNode() == nullptr ||
- !statement->getAsDeclarationNode()->getSequence()->empty()))
+ // Declaration nodes with no children can appear if it was an empty declaration or if all the
+ // declarators just added constants to the symbol table instead of generating code. We still
+ // need to add the declaration to the AST in that case because it might be relevant to the
+ // validity of switch/case.
+ if (statement != nullptr)
{
mStatements.push_back(statement);
}
@@ -526,6 +527,7 @@
{
REPLACE_IF_IS(mInit, TIntermTyped, original, replacement);
REPLACE_IF_IS(mStatementList, TIntermBlock, original, replacement);
+ ASSERT(mStatementList);
return false;
}
@@ -938,6 +940,28 @@
}
}
+TIntermIfElse::TIntermIfElse(TIntermTyped *cond, TIntermBlock *trueB, TIntermBlock *falseB)
+ : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB)
+{
+ // Prune empty false blocks so that there won't be unnecessary operations done on it.
+ if (mFalseBlock && mFalseBlock->getSequence()->empty())
+ {
+ mFalseBlock = nullptr;
+ }
+}
+
+TIntermSwitch::TIntermSwitch(TIntermTyped *init, TIntermBlock *statementList)
+ : TIntermNode(), mInit(init), mStatementList(statementList)
+{
+ ASSERT(mStatementList);
+}
+
+void TIntermSwitch::setStatementList(TIntermBlock *statementList)
+{
+ ASSERT(statementList);
+ mStatementList = statementList;
+}
+
// static
TQualifier TIntermTernary::DetermineQualifier(TIntermTyped *cond,
TIntermTyped *trueExpression,
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 5dd7da8..ff98447 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -843,10 +843,7 @@
class TIntermIfElse : public TIntermNode
{
public:
- TIntermIfElse(TIntermTyped *cond, TIntermBlock *trueB, TIntermBlock *falseB)
- : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB)
- {
- }
+ TIntermIfElse(TIntermTyped *cond, TIntermBlock *trueB, TIntermBlock *falseB);
void traverse(TIntermTraverser *it) override;
bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
@@ -868,10 +865,7 @@
class TIntermSwitch : public TIntermNode
{
public:
- TIntermSwitch(TIntermTyped *init, TIntermBlock *statementList)
- : TIntermNode(), mInit(init), mStatementList(statementList)
- {
- }
+ TIntermSwitch(TIntermTyped *init, TIntermBlock *statementList);
void traverse(TIntermTraverser *it) override;
bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
@@ -880,7 +874,9 @@
TIntermTyped *getInit() { return mInit; }
TIntermBlock *getStatementList() { return mStatementList; }
- void setStatementList(TIntermBlock *statementList) { mStatementList = statementList; }
+
+ // Must be called with a non-null statementList.
+ void setStatementList(TIntermBlock *statementList);
protected:
TIntermTyped *mInit;
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index e218584..642a371 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -812,16 +812,9 @@
bool TOutputGLSLBase::visitSwitch(Visit visit, TIntermSwitch *node)
{
- if (node->getStatementList())
- {
- writeTriplet(visit, "switch (", ") ", nullptr);
- // The curly braces get written when visiting the statementList aggregate
- }
- else
- {
- // No statementList, so it won't output curly braces
- writeTriplet(visit, "switch (", ") {", "}\n");
- }
+ ASSERT(node->getStatementList());
+ writeTriplet(visit, "switch (", ") ", nullptr);
+ // The curly braces get written when visiting the statementList aggregate
return true;
}
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 1d8d111..4574a8a 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -2135,20 +2135,13 @@
{
TInfoSinkBase &out = getInfoSink();
- if (node->getStatementList())
+ ASSERT(node->getStatementList());
+ if (visit == PreVisit)
{
- if (visit == PreVisit)
- {
- node->setStatementList(RemoveSwitchFallThrough(node->getStatementList()));
- }
- outputTriplet(out, visit, "switch (", ") ", "");
- // The curly braces get written when visiting the statementList block.
+ node->setStatementList(RemoveSwitchFallThrough(node->getStatementList()));
}
- else
- {
- // No statementList, so it won't output curly braces
- outputTriplet(out, visit, "switch (", ") {", "}\n");
- }
+ outputTriplet(out, visit, "switch (", ") ", "");
+ // The curly braces get written when visiting the statementList block.
return true;
}
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index efa9021..8a76153 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -4633,12 +4633,10 @@
return nullptr;
}
- if (statementList)
+ ASSERT(statementList);
+ if (!ValidateSwitchStatementList(switchType, mDiagnostics, statementList, loc))
{
- if (!ValidateSwitchStatementList(switchType, mDiagnostics, statementList, loc))
- {
- return nullptr;
- }
+ return nullptr;
}
TIntermSwitch *node = new TIntermSwitch(init, statementList);
diff --git a/src/compiler/translator/PruneEmptyDeclarations.cpp b/src/compiler/translator/PruneEmptyDeclarations.cpp
index 2175547..c763d2e 100644
--- a/src/compiler/translator/PruneEmptyDeclarations.cpp
+++ b/src/compiler/translator/PruneEmptyDeclarations.cpp
@@ -19,14 +19,14 @@
class PruneEmptyDeclarationsTraverser : private TIntermTraverser
{
public:
- static void apply(TIntermNode *root);
+ static void apply(TIntermBlock *root);
private:
PruneEmptyDeclarationsTraverser();
bool visitDeclaration(Visit, TIntermDeclaration *node) override;
};
-void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root)
+void PruneEmptyDeclarationsTraverser::apply(TIntermBlock *root)
{
PruneEmptyDeclarationsTraverser prune;
root->traverse(&prune);
@@ -62,22 +62,10 @@
else if (sym->getBasicType() != EbtStruct)
{
// Single struct declarations may just declare the struct type and no variables, so
- // they should not be pruned. All other single empty declarations can be pruned
- // entirely. Example of an empty declaration that will be pruned:
- // float;
- TIntermSequence emptyReplacement;
- TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
- // The declaration may be inside a block or in a loop init expression.
- ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
- if (parentAsBlock)
- {
- mMultiReplacements.push_back(
- NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
- }
- else
- {
- queueReplacement(nullptr, OriginalNode::IS_DROPPED);
- }
+ // they should not be pruned. If there are entirely empty non-struct declarations,
+ // they result in TIntermDeclaration nodes without any children in the parsing
+ // stage. This will be handled further down in the code.
+ UNREACHABLE();
}
else if (sym->getType().getQualifier() != EvqGlobal &&
sym->getType().getQualifier() != EvqTemporary)
@@ -102,12 +90,29 @@
}
}
}
+ else
+ {
+ // We have a declaration with no declarators.
+ // The declaration may be either inside a block or in a loop init expression.
+ TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
+ ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
+ if (parentAsBlock)
+ {
+ TIntermSequence emptyReplacement;
+ mMultiReplacements.push_back(
+ NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
+ }
+ else
+ {
+ queueReplacement(nullptr, OriginalNode::IS_DROPPED);
+ }
+ }
return false;
}
} // namespace
-void PruneEmptyDeclarations(TIntermNode *root)
+void PruneEmptyDeclarations(TIntermBlock *root)
{
PruneEmptyDeclarationsTraverser::apply(root);
}
diff --git a/src/compiler/translator/PruneEmptyDeclarations.h b/src/compiler/translator/PruneEmptyDeclarations.h
index f928978..8aa149c 100644
--- a/src/compiler/translator/PruneEmptyDeclarations.h
+++ b/src/compiler/translator/PruneEmptyDeclarations.h
@@ -11,9 +11,9 @@
namespace sh
{
-class TIntermNode;
+class TIntermBlock;
-void PruneEmptyDeclarations(TIntermNode *root);
+void PruneEmptyDeclarations(TIntermBlock *root);
}
#endif // COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
diff --git a/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp b/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp
new file mode 100644
index 0000000..c11dce3
--- /dev/null
+++ b/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.cpp
@@ -0,0 +1,114 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveNoOpCasesFromEndOfSwitchStatements.cpp: Clean up cases from the end of a switch statement
+// that only contain no-ops.
+
+#include "compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h"
+
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermNode_util.h"
+#include "compiler/translator/IntermTraverse.h"
+#include "compiler/translator/SymbolTable.h"
+
+namespace sh
+{
+
+namespace
+{
+
+bool AreEmptyBlocks(TIntermSequence *statements, size_t i);
+
+bool IsEmptyBlock(TIntermNode *node)
+{
+ TIntermBlock *asBlock = node->getAsBlock();
+ if (asBlock)
+ {
+ if (asBlock->getSequence()->empty())
+ {
+ return true;
+ }
+ return AreEmptyBlocks(asBlock->getSequence(), 0u);
+ }
+ // Empty declarations should have already been pruned, otherwise they would need to be handled
+ // here. Note that declarations for struct types do contain a nameless child node.
+ ASSERT(node->getAsDeclarationNode() == nullptr ||
+ !node->getAsDeclarationNode()->getSequence()->empty());
+ return false;
+}
+
+// Return true if all statements in "statements" starting from index i consist only of empty blocks
+// and empty declarations. Returns true also if there are no statements.
+bool AreEmptyBlocks(TIntermSequence *statements, size_t i)
+{
+ for (; i < statements->size(); ++i)
+ {
+ if (!IsEmptyBlock(statements->at(i)))
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+void RemoveNoOpCasesFromEndOfStatementList(TIntermBlock *statementList, TSymbolTable *symbolTable)
+{
+ TIntermSequence *statements = statementList->getSequence();
+
+ bool foundDeadCase = false;
+ do
+ {
+ if (statements->empty())
+ {
+ return;
+ }
+
+ // Find the last case label.
+ size_t i = statements->size();
+ while (i > 0u && !(*statements)[i - 1]->getAsCaseNode())
+ {
+ --i;
+ }
+ // Now i is the index of the first statement following the last label inside the switch
+ // statement.
+ ASSERT(i > 0u);
+
+ foundDeadCase = AreEmptyBlocks(statements, i);
+ if (foundDeadCase)
+ {
+ statements->erase(statements->begin() + (i - 1u), statements->end());
+ }
+ } while (foundDeadCase);
+}
+
+class RemoveNoOpCasesFromEndOfSwitchTraverser : public TIntermTraverser
+{
+ public:
+ RemoveNoOpCasesFromEndOfSwitchTraverser(TSymbolTable *symbolTable)
+ : TIntermTraverser(true, false, false, symbolTable)
+ {
+ }
+
+ bool visitSwitch(Visit visit, TIntermSwitch *node) override;
+};
+
+bool RemoveNoOpCasesFromEndOfSwitchTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+ // Here we may mutate the statement list, but it's safe since traversal has not yet reached
+ // there.
+ RemoveNoOpCasesFromEndOfStatementList(node->getStatementList(), mSymbolTable);
+ // Handle also nested switch statements.
+ return true;
+}
+
+} // anonymous namespace
+
+void RemoveNoOpCasesFromEndOfSwitchStatements(TIntermBlock *root, TSymbolTable *symbolTable)
+{
+ RemoveNoOpCasesFromEndOfSwitchTraverser traverser(symbolTable);
+ root->traverse(&traverser);
+}
+
+} // namespace sh
diff --git a/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h b/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h
new file mode 100644
index 0000000..ebd75eb
--- /dev/null
+++ b/src/compiler/translator/RemoveNoOpCasesFromEndOfSwitchStatements.h
@@ -0,0 +1,21 @@
+//
+// Copyright (c) 2017 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// RemoveNoOpCasesFromEndOfSwitchStatements.h: Clean up cases from the end of a switch statement
+// that only contain no-ops.
+
+#ifndef COMPILER_TRANSLATOR_REMOVENOOPCASESFROMENDOFSWITCHSTATEMENTS_H_
+#define COMPILER_TRANSLATOR_REMOVENOOPCASESFROMENDOFSWITCHSTATEMENTS_H_
+
+namespace sh
+{
+class TIntermBlock;
+class TSymbolTable;
+
+void RemoveNoOpCasesFromEndOfSwitchStatements(TIntermBlock *root, TSymbolTable *symbolTable);
+
+} // namespace sh
+
+#endif // COMPILER_TRANSLATOR_REMOVENOOPCASESFROMENDOFSWITCHSTATEMENTS_H_
diff --git a/src/compiler/translator/ValidateSwitch.cpp b/src/compiler/translator/ValidateSwitch.cpp
index 2c96017..bc9ba34 100644
--- a/src/compiler/translator/ValidateSwitch.cpp
+++ b/src/compiler/translator/ValidateSwitch.cpp
@@ -25,9 +25,12 @@
void visitSymbol(TIntermSymbol *) override;
void visitConstantUnion(TIntermConstantUnion *) override;
+ bool visitDeclaration(Visit, TIntermDeclaration *) override;
+ bool visitBlock(Visit, TIntermBlock *) override;
bool visitBinary(Visit, TIntermBinary *) override;
bool visitUnary(Visit, TIntermUnary *) override;
bool visitTernary(Visit, TIntermTernary *) override;
+ bool visitSwizzle(Visit, TIntermSwizzle *) override;
bool visitIfElse(Visit visit, TIntermIfElse *) override;
bool visitSwitch(Visit, TIntermSwitch *) override;
bool visitCase(Visit, TIntermCase *node) override;
@@ -96,6 +99,25 @@
mLastStatementWasCase = false;
}
+bool ValidateSwitch::visitDeclaration(Visit, TIntermDeclaration *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
+bool ValidateSwitch::visitBlock(Visit, TIntermBlock *)
+{
+ if (getParentNode() != nullptr)
+ {
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ }
+ return true;
+}
+
bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
{
if (!mFirstCaseFound)
@@ -120,6 +142,14 @@
return true;
}
+bool ValidateSwitch::visitSwizzle(Visit, TIntermSwizzle *)
+{
+ if (!mFirstCaseFound)
+ mStatementBeforeCase = true;
+ mLastStatementWasCase = false;
+ return true;
+}
+
bool ValidateSwitch::visitIfElse(Visit visit, TIntermIfElse *)
{
if (visit == PreVisit)
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index 4285e8d..d705b73 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -205,7 +205,7 @@
%type <interm.intermNode> condition conditionopt
%type <interm.intermBlock> translation_unit
%type <interm.intermNode> function_definition statement simple_statement
-%type <interm.intermBlock> statement_list compound_statement compound_statement_no_new_scope
+%type <interm.intermBlock> statement_list compound_statement_with_scope compound_statement_no_new_scope
%type <interm.intermNode> declaration_statement selection_statement expression_statement
%type <interm.intermNode> declaration external_declaration
%type <interm.intermNode> for_init_statement
@@ -1232,8 +1232,8 @@
;
statement
- : compound_statement { $$ = $1; }
- | simple_statement { $$ = $1; }
+ : compound_statement_with_scope { $$ = $1; }
+ | simple_statement { $$ = $1; }
;
// Grammar Note: Labeled statements for SWITCH only; 'goto' is not supported.
@@ -1248,8 +1248,11 @@
| jump_statement { $$ = $1; }
;
-compound_statement
- : LEFT_BRACE RIGHT_BRACE { $$ = 0; }
+compound_statement_with_scope
+ : LEFT_BRACE RIGHT_BRACE {
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
+ }
| LEFT_BRACE { context->symbolTable.push(); } statement_list { context->symbolTable.pop(); } RIGHT_BRACE {
$3->setLine(@$);
$$ = $3;
@@ -1267,9 +1270,10 @@
;
compound_statement_no_new_scope
- // Statement that doesn't create a new scope, for selection_statement, iteration_statement
+ // Statement that doesn't create a new scope for iteration_statement, function definition (scope is created for parameters)
: LEFT_BRACE RIGHT_BRACE {
- $$ = nullptr;
+ $$ = new TIntermBlock();
+ $$->setLine(@$);
}
| LEFT_BRACE statement_list RIGHT_BRACE {
$2->setLine(@$);
@@ -1310,8 +1314,10 @@
}
;
+// Note that we've diverged from the spec grammar here a bit for the sake of simplicity.
+// We're reusing compound_statement_with_scope instead of having separate rules for switch.
switch_statement
- : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement {
+ : SWITCH LEFT_PAREN expression RIGHT_PAREN { context->incrSwitchNestingLevel(); } compound_statement_with_scope {
$$ = context->addSwitch($3, $6, @1);
context->decrSwitchNestingLevel();
}
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 51dd8e5..657ad83 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -766,11 +766,11 @@
1157, 1160, 1163, 1166, 1169, 1172, 1180, 1180, 1183, 1183,
1189, 1192, 1198, 1201, 1208, 1212, 1218, 1221, 1227, 1231,
1235, 1236, 1242, 1243, 1244, 1245, 1246, 1247, 1248, 1252,
- 1253, 1253, 1253, 1260, 1261, 1265, 1265, 1266, 1266, 1271,
- 1274, 1281, 1285, 1292, 1293, 1297, 1303, 1307, 1314, 1314,
- 1321, 1324, 1330, 1334, 1340, 1340, 1345, 1345, 1349, 1349,
- 1357, 1360, 1366, 1369, 1375, 1379, 1386, 1389, 1392, 1395,
- 1398, 1406, 1412, 1418, 1421, 1427, 1427
+ 1256, 1256, 1256, 1263, 1264, 1268, 1268, 1269, 1269, 1274,
+ 1278, 1285, 1289, 1296, 1297, 1301, 1307, 1311, 1320, 1320,
+ 1327, 1330, 1336, 1340, 1346, 1346, 1351, 1351, 1355, 1355,
+ 1363, 1366, 1372, 1375, 1381, 1385, 1392, 1395, 1398, 1401,
+ 1404, 1412, 1418, 1424, 1427, 1433, 1433
};
#endif
@@ -832,7 +832,7 @@
"$@1", "$@2", "struct_declaration_list", "struct_declaration",
"struct_declarator_list", "struct_declarator", "initializer",
"declaration_statement", "statement", "simple_statement",
- "compound_statement", "$@3", "$@4", "statement_no_new_scope",
+ "compound_statement_with_scope", "$@3", "$@4", "statement_no_new_scope",
"statement_with_scope", "$@5", "$@6", "compound_statement_no_new_scope",
"statement_list", "expression_statement", "selection_statement",
"selection_rest_statement", "switch_statement", "$@7", "case_label",
@@ -4559,7 +4559,10 @@
case 249:
- { (yyval.interm.intermBlock) = 0; }
+ {
+ (yyval.interm.intermBlock) = new TIntermBlock();
+ (yyval.interm.intermBlock)->setLine((yyloc));
+ }
break;
@@ -4623,7 +4626,8 @@
case 259:
{
- (yyval.interm.intermBlock) = nullptr;
+ (yyval.interm.intermBlock) = new TIntermBlock();
+ (yyval.interm.intermBlock)->setLine((yyloc));
}
break;