diff --git a/Test/switch.frag b/Test/switch.frag new file mode 100644 index 00000000..690562a2 --- /dev/null +++ b/Test/switch.frag @@ -0,0 +1,64 @@ +#version 300 es + +uniform int c, d; +in float x; + +void main() +{ + float f; + int a[2]; + + switch(f) { // ERROR + } + + switch(a) { // ERROR + } + + switch(c) + { + } + + switch(c) + { + case 2: // ERROR, not enough stuff + } + + switch(c) + { + f = sin(x); // ERRROR + case 2: // ERROR, not enough stuff + f = cos(x); + break; + } + + switch (c) { + case 1: + f = sin(x); + break; + case 2: + f = cos(x); + break; + default: + f = tan(x); + } + + switch (c) { + case 1: + f = sin(x); + break; + case 2: + switch (d) { + case 1: + f = x * x * x; + break; + case 2: + f = x * x; + break; + } + break; + default: + f = tan(x); + } + + break; // ERROR +} diff --git a/Test/testlist b/Test/testlist index 36c3bc9f..3127f9ba 100644 --- a/Test/testlist +++ b/Test/testlist @@ -33,3 +33,4 @@ constFold.frag errors.frag forwardRef.frag uint.frag +switch.frag diff --git a/glslang/Include/intermediate.h b/glslang/Include/intermediate.h index 69229ce9..d35ae8f8 100644 --- a/glslang/Include/intermediate.h +++ b/glslang/Include/intermediate.h @@ -217,6 +217,8 @@ enum TOperator { EOpReturn, EOpBreak, EOpContinue, + EOpCase, + EOpDefault, // // Constructors @@ -297,6 +299,8 @@ class TIntermUnary; class TIntermBinary; class TIntermConstantUnion; class TIntermSelection; +class TIntermSwitch; +class TIntermBranch; class TIntermTyped; class TIntermMethod; class TIntermSymbol; @@ -319,8 +323,10 @@ public: virtual TIntermUnary* getAsUnaryNode() { return 0; } virtual TIntermBinary* getAsBinaryNode() { return 0; } virtual TIntermSelection* getAsSelectionNode() { return 0; } + virtual TIntermSwitch* getAsSwitchNode() { return 0; } virtual TIntermMethod* getAsMethodNode() { return 0; } virtual TIntermSymbol* getAsSymbolNode() { return 0; } + virtual TIntermBranch* getAsBranchNode() { return 0; } virtual ~TIntermNode() { } protected: TSourceLoc line; @@ -383,19 +389,20 @@ protected: }; // -// Handle break, continue, return, and kill. +// Handle case, break, continue, return, and kill. // class TIntermBranch : public TIntermNode { public: TIntermBranch(TOperator op, TIntermTyped* e) : flowOp(op), expression(e) { } + virtual TIntermBranch* getAsBranchNode() { return this; } virtual void traverse(TIntermTraverser*); TOperator getFlowOp() { return flowOp; } TIntermTyped* getExpression() { return expression; } protected: TOperator flowOp; - TIntermTyped* expression; // non-zero except for "return exp;" statements + TIntermTyped* expression; }; // @@ -534,7 +541,7 @@ protected: }; // -// For if tests. Simplified since there is no switch statement. +// For if tests. // class TIntermSelection : public TIntermTyped { public: @@ -553,6 +560,24 @@ protected: TIntermNode* falseBlock; }; +// +// For switch statements. Designed use is that a switch will have sequence of nodes +// that are either case/default nodes or a *single* node that represents all the code +// in between (if any) consecutive case/defaults. So, a traversal need only deal with +// 0 or 1 nodes per case/default statement. +// +class TIntermSwitch : public TIntermAggregate { +public: + TIntermSwitch(TIntermTyped* cond, TIntermAggregate* b) : condition(cond), body(b) { } + virtual void traverse(TIntermTraverser*); + virtual TIntermNode* getCondition() const { return condition; } + virtual TIntermAggregate* getBody() const { return body; } + virtual TIntermSwitch* getAsSwitchNode() { return this; } +protected: + TIntermTyped* condition; + TIntermAggregate* body; +}; + // // For traversing the tree. User should derive from this, // put their traversal specific data in it, and then pass @@ -587,6 +612,7 @@ public: bool (*visitAggregate)(bool preVisit, TIntermAggregate*, TIntermTraverser*); bool (*visitLoop)(bool preVisit, TIntermLoop*, TIntermTraverser*); bool (*visitBranch)(bool preVisit, TIntermBranch*, TIntermTraverser*); + bool (*visitSwitch)(bool preVisit, TIntermSwitch*, TIntermTraverser*); int depth; bool preVisit; diff --git a/glslang/MachineIndependent/IntermTraverse.cpp b/glslang/MachineIndependent/IntermTraverse.cpp index eb87bf68..8b8d1886 100644 --- a/glslang/MachineIndependent/IntermTraverse.cpp +++ b/glslang/MachineIndependent/IntermTraverse.cpp @@ -176,13 +176,13 @@ void TIntermSelection::traverse(TIntermTraverser* it) if (it->rightToLeft) { if (falseBlock) falseBlock->traverse(it); - if (trueBlock) - trueBlock->traverse(it); + if (trueBlock) + trueBlock->traverse(it); condition->traverse(it); } else { condition->traverse(it); - if (trueBlock) - trueBlock->traverse(it); + if (trueBlock) + trueBlock->traverse(it); if (falseBlock) falseBlock->traverse(it); } @@ -210,11 +210,11 @@ void TIntermLoop::traverse(TIntermTraverser* it) terminal->traverse(it); if (body) body->traverse(it); - if (test) - test->traverse(it); + if (test) + test->traverse(it); } else { - if (test) - test->traverse(it); + if (test) + test->traverse(it); if (body) body->traverse(it); if (terminal) @@ -247,3 +247,28 @@ void TIntermBranch::traverse(TIntermTraverser* it) it->visitBranch(false, this, it); } +// +// Traverse a switch node. +// +void TIntermSwitch::traverse(TIntermTraverser* it) +{ + bool visit = true; + + if (it->preVisit && it->visitSwitch) + visit = it->visitSwitch(true, this, it); + + if (visit) { + ++it->depth; + if (it->rightToLeft) { + body->traverse(it); + condition->traverse(it); + } else { + condition->traverse(it); + body->traverse(it); + } + --it->depth; + } + + if (visit && it->postVisit && it->visitSwitch) + it->visitSwitch(false, this, it); +} diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp index b247cbf8..033a8c1c 100644 --- a/glslang/MachineIndependent/ParseHelper.cpp +++ b/glslang/MachineIndependent/ParseHelper.cpp @@ -44,7 +44,7 @@ TParseContext::TParseContext(TSymbolTable& symt, TIntermediate& interm, int v, E bool fc, EShMessages m) : intermediate(interm), symbolTable(symt), infoSink(is), language(L), treeRoot(0), recoveredFromError(false), numErrors(0), lexAfterType(false), loopNestingLevel(0), - switchNestingLevel(0), inTypeParen(false), + inTypeParen(false), version(v), profile(p), forwardCompatible(fc), messages(m), contextPragma(true, false) { @@ -1574,6 +1574,58 @@ void TParseContext::addBlock(int line, TPublicType& publicType, const TString& b } } +void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode) +{ + auto switchSequence = switchSequenceStack.back(); + + if (statements) { + if (switchSequence->size() == 0) { + error(statements->getLine(), "cannot have statements before first case/default label", "switch", ""); + recover(); + } + statements->setOperator(EOpSequence); + switchSequence->push_back(statements); + } + if (branchNode) + switchSequence->push_back(branchNode); +} + +TIntermNode* TParseContext::addSwitch(int line, TIntermTyped* expression, TIntermAggregate* lastStatements) +{ + profileRequires(line, EEsProfile, 300, 0, "switch statements"); + profileRequires(line, ENoProfile, 130, 0, "switch statements"); + + wrapupSwitchSubsequence(lastStatements, 0); + + if (expression == 0 || + expression->getBasicType() != EbtInt && expression->getBasicType() != EbtUint || + expression->getType().isArray() || expression->getType().isMatrix() || expression->getType().isVector()) { + error(line, "condition must be a scalar integer expression", "switch", ""); + recover(); + } + + // If there is nothing to do, drop the switch but still execute the expression + auto switchSequence = switchSequenceStack.back(); + if (switchSequence->size() == 0) + return expression; + + if (lastStatements == 0) { + error(line, "last case/default label must be followed by statements", "switch", ""); + recover(); + + return expression; + } + + TIntermAggregate* body = new TIntermAggregate(EOpSequence); + body->getSequence() = *switchSequenceStack.back(); + body->setLine(line); + + TIntermSwitch* switchNode = new TIntermSwitch(expression, body); + switchNode->setLine(line); + + return switchNode; +} + void TParseContext::updateDefaults(int line, const TPublicType& publicType, const TString* id) { bool cantHaveId = false; diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h index 66c75ec7..10b31357 100644 --- a/glslang/MachineIndependent/ParseHelper.h +++ b/glslang/MachineIndependent/ParseHelper.h @@ -78,7 +78,7 @@ struct TParseContext { int numErrors; bool lexAfterType; // true if we've recognized a type, so can only be looking for an identifier int loopNestingLevel; // 0 if outside all loops - int switchNestingLevel; // 0 if outside all switch statements + TList switchSequenceStack; // case, node, case, case, node, ...; ensure only one node between cases; stack of them for nesting bool inTypeParen; // true if in parentheses, looking only for an identifier const TType* currentFunctionType; // the return type of the function that's currently being parsed bool functionReturnsValue; // true if a non-void function has a return @@ -143,6 +143,8 @@ struct TParseContext { TIntermTyped* constructStruct(TIntermNode*, const TType&, int, TSourceLoc); TIntermTyped* constructBuiltIn(const TType&, TOperator, TIntermNode*, TSourceLoc, bool subset); void addBlock(int line, TPublicType& qualifier, const TString& blockName, TTypeList& typeList, const TString* instanceName = 0, TArraySizes arraySizes = 0); + void wrapupSwitchSubsequence(TIntermAggregate* statements, TIntermNode* branchNode); + TIntermNode* addSwitch(int line, TIntermTyped* expression, TIntermAggregate* body); void updateDefaults(int line, const TPublicType&, const TString* id); TIntermTyped* addConstVectorNode(TVectorFields&, TIntermTyped*, TSourceLoc); TIntermTyped* addConstMatrixNode(int , TIntermTyped*, TSourceLoc); diff --git a/glslang/MachineIndependent/RemoveTree.cpp b/glslang/MachineIndependent/RemoveTree.cpp index 196279e3..e4f4dc66 100644 --- a/glslang/MachineIndependent/RemoveTree.cpp +++ b/glslang/MachineIndependent/RemoveTree.cpp @@ -71,6 +71,13 @@ bool RemoveSelection(bool /*preVisit*/ , TIntermSelection* node, TIntermTravers return true; } +bool RemoveSwitch(bool /*preVisit*/ , TIntermSwitch* node, TIntermTraverser*) +{ + delete node; + + return true; +} + void RemoveConstantUnion(TIntermConstantUnion* node, TIntermTraverser*) { delete node; @@ -89,6 +96,7 @@ void RemoveAllTreeNodes(TIntermNode* root) it.visitSelection = RemoveSelection; it.visitSymbol = RemoveSymbol; it.visitUnary = RemoveUnary; + it.visitSwitch = RemoveSwitch; it.preVisit = false; it.postVisit = true; diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp index ea4a31de..fb891729 100644 --- a/glslang/MachineIndependent/ShaderLang.cpp +++ b/glslang/MachineIndependent/ShaderLang.cpp @@ -131,9 +131,9 @@ bool InitializeSymbolTable(TBuiltInStrings* BuiltInStrings, int version, EProfil builtInShaders[0] = (*i).c_str(); builtInLengths[0] = (int) (*i).size(); - if (PaParseStrings(const_cast(builtInShaders), builtInLengths, 1, parseContext, 0) != 0) { infoSink.info.message(EPrefixInternalError, "Unable to parse built-ins"); + printf("Unable to parse built-ins\n"); return false; } diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y index 1bf9c001..4a7f950e 100644 --- a/glslang/MachineIndependent/glslang.y +++ b/glslang/MachineIndependent/glslang.y @@ -188,9 +188,9 @@ extern void yyerror(const char*); %type translation_unit function_definition %type statement simple_statement -%type statement_list compound_statement +%type statement_list switch_statement_list compound_statement %type declaration_statement selection_statement expression_statement -%type switch_statement case_label switch_statement_list +%type switch_statement case_label %type declaration external_declaration %type for_init_statement compound_statement_no_new_scope %type selection_rest_statement for_rest_statement @@ -2735,9 +2735,19 @@ compound_statement_no_new_scope statement_list : statement { $$ = parseContext.intermediate.makeAggregate($1, 0); + if ($1 && $1->getAsBranchNode() && ($1->getAsBranchNode()->getFlowOp() == EOpCase || + $1->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence(0, $1); + $$ = 0; // start a fresh subsequence for what's after this case + } } | statement_list statement { - $$ = parseContext.intermediate.growAggregate($1, $2, 0); + if ($2 && $2->getAsBranchNode() && ($2->getAsBranchNode()->getFlowOp() == EOpCase || + $2->getAsBranchNode()->getFlowOp() == EOpDefault)) { + parseContext.wrapupSwitchSubsequence($1, $2); + $$ = 0; // start a fresh subsequence for what's after this case + } else + $$ = parseContext.intermediate.growAggregate($1, $2, 0); } ; @@ -2787,14 +2797,20 @@ condition ; switch_statement - : SWITCH LEFT_PAREN expression RIGHT_PAREN { ++parseContext.switchNestingLevel; } LEFT_BRACE switch_statement_list RIGHT_BRACE { - $$ = 0; - --parseContext.switchNestingLevel; + : SWITCH LEFT_PAREN expression RIGHT_PAREN { + // start new switch sequence on the switch stack + parseContext.switchSequenceStack.push_back(new TIntermSequence); + } + LEFT_BRACE switch_statement_list RIGHT_BRACE { + $$ = parseContext.addSwitch($1.line, $3, $7); + delete parseContext.switchSequenceStack.back(); + parseContext.switchSequenceStack.pop_back(); } ; switch_statement_list : /* nothing */ { + $$ = 0; } | statement_list { $$ = $1; @@ -2803,10 +2819,10 @@ switch_statement_list case_label : CASE expression COLON { - $$ = 0; + $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.line); } | DEFAULT COLON { - $$ = 0; + $$ = parseContext.intermediate.addBranch(EOpDefault, $1.line); } ; @@ -2881,7 +2897,7 @@ jump_statement $$ = parseContext.intermediate.addBranch(EOpContinue, $1.line); } | BREAK SEMICOLON { - if (parseContext.loopNestingLevel + parseContext.switchNestingLevel <= 0) { + if (parseContext.loopNestingLevel + parseContext.switchSequenceStack.size() <= 0) { parseContext.error($1.line, "break statement only allowed in switch and loops", "", ""); parseContext.recover(); } diff --git a/glslang/MachineIndependent/intermOut.cpp b/glslang/MachineIndependent/intermOut.cpp index ace81542..980f1089 100644 --- a/glslang/MachineIndependent/intermOut.cpp +++ b/glslang/MachineIndependent/intermOut.cpp @@ -503,6 +503,8 @@ bool OutputBranch(bool /* previsit*/, TIntermBranch* node, TIntermTraverser* it) case EOpBreak: out.debug << "Branch: Break"; break; case EOpContinue: out.debug << "Branch: Continue"; break; case EOpReturn: out.debug << "Branch: Return"; break; + case EOpCase: out.debug << "case: "; break; + case EOpDefault: out.debug << "default: "; break; default: out.debug << "Branch: Unknown Branch"; break; } @@ -517,6 +519,30 @@ bool OutputBranch(bool /* previsit*/, TIntermBranch* node, TIntermTraverser* it) return false; } +bool OutputSwitch(bool /* preVisit */, TIntermSwitch* node, TIntermTraverser* it) +{ + TOutputTraverser* oit = static_cast(it); + TInfoSink& out = oit->infoSink; + + OutputTreeText(out, node, oit->depth); + out.debug << "switch\n"; + + OutputTreeText(out, node, oit->depth); + out.debug << "condition\n"; + ++oit->depth; + node->getCondition()->traverse(it); + + --oit->depth; + OutputTreeText(out, node, oit->depth); + out.debug << "body\n"; + ++oit->depth; + node->getBody()->traverse(it); + + --oit->depth; + + return false; +} + // // This function is the one to call externally to start the traversal. // Individual functions can be initialized to 0 to skip processing of that @@ -537,6 +563,7 @@ void TIntermediate::outputTree(TIntermNode* root) it.visitUnary = OutputUnary; it.visitLoop = OutputLoop; it.visitBranch = OutputBranch; + it.visitSwitch = OutputSwitch; root->traverse(&it); }