From e369bfccfeaa7e86b3fe44186ff42af5e71535a3 Mon Sep 17 00:00:00 2001
From: John Kessenich <cepheus@frii.com>
Date: Wed, 26 Jun 2013 05:54:40 +0000
Subject: [PATCH] Semantic checks for .length(), switch/case/default, and
 multidimensional arrays.

git-svn-id: https://cvs.khronos.org/svn/repos/ogl/trunk/ecosystem/public/sdk/tools/glslang@22175 e7fa87d3-cd2b-0410-9028-fcbf551c1848
---
 Test/120.vert                                 | 30 ++++++++---
 Test/300.frag                                 |  6 +++
 Test/switch.frag                              | 42 +++++++++++++---
 glslang/MachineIndependent/ParseHelper.cpp    | 50 +++++++++++++++++--
 glslang/MachineIndependent/ParseHelper.h      |  5 +-
 glslang/MachineIndependent/ShaderLang.cpp     |  2 +-
 glslang/MachineIndependent/glslang.l          | 37 +-------------
 glslang/MachineIndependent/glslang.y          | 41 ++++++++++-----
 glslang/MachineIndependent/preprocessor/cpp.c |  2 -
 9 files changed, 144 insertions(+), 71 deletions(-)

diff --git a/Test/120.vert b/Test/120.vert
index d0f64e402..be02d66b1 100644
--- a/Test/120.vert
+++ b/Test/120.vert
@@ -1,15 +1,15 @@
 #version 120
 
-in vec4 i;
-out vec4 o;
+in vec4 i;                // ERROR
+out vec4 o;               // ERROR
 
 attribute vec2 attv2;
 attribute vec4 attv4;
 uniform sampler2D s2D;
 invariant varying vec2 centTexCoord;
 invariant gl_Position;
-centroid gl_Position;
-centroid centroid foo;
+centroid gl_Position;     // ERROR
+centroid centroid foo;    // ERROR
 invariant gl_Position, gl_PointSize;
 
 void main()
@@ -22,11 +22,29 @@ void main()
     gl_Position = b[b.length()-1];
 
     float f[];
-    int a = f.length();
+    int a1 = f.length();  // ERROR
+    float f[7];
+    int aa = f.length();
+    int a2 = f.length;    // ERROR
+    int a3 = f.length(a); // ERROR
+    int a4 = f.flizbit;   // ERROR
+    int a4 = f.flizbit(); // ERROR
+    float md[2][4];       // ERROR
+    float[2] md2[4];      // ERROR
+    float[2][4] md3;      // ERROR
+    float md5, md6[2][3]; // ERROR
+    float[2] md4, md7[4]; // ERROR
+    float md9[2][3] = float[2][3](1, 2, 3, 4, 5, 6);  // ERROR
+    float md10, md11[2][3] = float[2][3](1, 2, 3, 4, 5, 6);  // ERROR
 
     gl_PointSize = 3.8;
 }
 
-uniform float initted = 3.4;
+uniform float initted = 3.4;   // okay
 
 const float concall = sin(0.3);
+
+int[2][3] foo(                 // ERROR
+              float[2][3] a,   // ERROR
+              float[2] b[3],   // ERROR
+              float c[2][3]);  // ERROR
diff --git a/Test/300.frag b/Test/300.frag
index 8a185ba48..88539cb59 100644
--- a/Test/300.frag
+++ b/Test/300.frag
@@ -97,5 +97,11 @@ void main()
     atanh(c3D);
 }
 
+uniform multi {
+    int[2] a[3];      // ERROR
+    int[2][3] b;      // ERROR
+    int c[2][3];      // ERROR
+} multiInst[2][3];    // ERROR
+
 float imageBuffer;    // ERROR, reserved
 float uimage2DRect;   // ERROR, reserved
diff --git a/Test/switch.frag b/Test/switch.frag
index d83eac63e..6924b3e91 100644
--- a/Test/switch.frag
+++ b/Test/switch.frag
@@ -8,41 +8,67 @@ void main()
     float f;
     int a[2];
 
-    switch(f) { // ERROR
+    switch(f) {      // ERROR
     }
 
-    switch(a) { // ERROR
+    switch(a) {      // ERROR
     }
 
     switch(c)
     {
     }
 
-    switch(c)
+    switch(c)        // ERROR, not enough stuff after last label
     {
-    case 2:    // ERROR, not enough stuff
+    case 2:
     }
 
     switch(c)
     {
-        f = sin(x); // ERRROR
-    case 2:    // ERROR, not enough stuff
+        f = sin(x);   // ERRROR
+    case 2:
         f = cos(x);
         break;
     }
 
     switch (c) {
+    default:
+        break;
     case 1:
         f = sin(x);
         break;
     case 2:
         f = cos(x);
         break;
-    default:
+    default:           // ERROR, 2nd 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);
+    case 1:           // ERROR, 2nd 'case 1'
+        break;
+    case 3.8:         // ERROR, non-int
+        break;
+    case c:           // ERROR, non-constant
+        break;       
+    }
+
+    switch (c) {      // a no-error normal switch
     case 1:
         f = sin(x);
         break;
@@ -60,5 +86,5 @@ void main()
         f = tan(x);
     }
 
-    break; // ERROR
+    break;            // ERROR
 }
diff --git a/glslang/MachineIndependent/ParseHelper.cpp b/glslang/MachineIndependent/ParseHelper.cpp
index 3a3b31af1..51696198e 100644
--- a/glslang/MachineIndependent/ParseHelper.cpp
+++ b/glslang/MachineIndependent/ParseHelper.cpp
@@ -461,10 +461,10 @@ bool TParseContext::lValueErrorCheck(int line, const char* op, TIntermTyped* nod
 // Both test, and if necessary spit out an error, to see if the node is really
 // a constant.
 //
-void TParseContext::constCheck(TIntermTyped* node)
+void TParseContext::constCheck(TIntermTyped* node, const char* token)
 {
     if (node->getQualifier().storage != EvqConst)
-        error(node->getLine(), "constant expression required", "", "");
+        error(node->getLine(), "constant expression required", token, "");
 }
 
 //
@@ -996,6 +996,28 @@ void TParseContext::arraySizeRequiredCheck(int line, int& size)
     }
 }
 
+void TParseContext::arrayDimError(int line)
+{
+    requireProfile(line, (EProfileMask)(ECoreProfileMask | ECompatibilityProfileMask), "arrays of arrays");
+    profileRequires(line, ECoreProfile, 430, 0, "arrays of arrays");
+    profileRequires(line, ECompatibilityProfile, 430, 0, "arrays of arrays");
+}
+
+void TParseContext::arrayDimCheck(int line, TArraySizes sizes1, TArraySizes sizes2)
+{
+    if (sizes1 && sizes2 ||
+        sizes1 && sizes1->size() > 1 ||
+        sizes2 && sizes2->size() > 1)
+        arrayDimError(line);
+}
+
+void TParseContext::arrayDimCheck(int line, const TType* type, TArraySizes sizes2)
+{
+    if (type && type->isArray() && sizes2 ||
+        sizes2 && sizes2->size() > 1)
+        arrayDimError(line);
+}
+
 //
 // Do all the semantic checking for declaring an array, with and 
 // without a size, and make the right changes to the symbol table.
@@ -1582,6 +1604,8 @@ void TParseContext::addBlock(int line, TTypeList& typeList, const TString* insta
         return;
     }
 
+    arrayDimCheck(line, arraySizes, 0);
+
     // check for qualifiers and types that don't belong within a block
     for (unsigned int member = 0; member < typeList.size(); ++member) {
         TQualifier memberQualifier = typeList[member].type->getQualifier();
@@ -1774,10 +1798,26 @@ void TParseContext::wrapupSwitchSubsequence(TIntermAggregate* statements, TInter
         statements->setOperator(EOpSequence);
         switchSequence->push_back(statements);
     }
-    if (branchNode)
+    if (branchNode) {
+        // check all previous cases for the same label (or both are 'default')
+        for (unsigned int s = 0; s < switchSequence->size(); ++s) {
+            TIntermBranch* prevBranch = (*switchSequence)[s]->getAsBranchNode();
+            if (prevBranch) {
+                TIntermTyped* prevExpression = prevBranch->getExpression();
+                TIntermTyped* newExpression = branchNode->getAsBranchNode()->getExpression();
+                if (prevExpression == 0 && newExpression == 0)
+                    error(branchNode->getLine(), "duplicate label", "default", "");
+                else if (prevExpression != 0 && 
+                          newExpression != 0 &&
+                         prevExpression->getAsConstantUnion() && 
+                          newExpression->getAsConstantUnion() &&
+                         prevExpression->getAsConstantUnion()->getUnionArrayPointer()->getIConst() == 
+                          newExpression->getAsConstantUnion()->getUnionArrayPointer()->getIConst())
+                    error(branchNode->getLine(), "duplicated value", "case", "");
+            }
+        }
         switchSequence->push_back(branchNode);
-
-    // TODO: semantics: verify no duplicated case values
+    }
 }
 
 //
diff --git a/glslang/MachineIndependent/ParseHelper.h b/glslang/MachineIndependent/ParseHelper.h
index efaa6c6ae..bdb866d53 100644
--- a/glslang/MachineIndependent/ParseHelper.h
+++ b/glslang/MachineIndependent/ParseHelper.h
@@ -113,13 +113,16 @@ struct TParseContext {
     void binaryOpError(int line, const char* op, TString left, TString right);
     void variableCheck(TIntermTyped*& nodePtr);
     bool lValueErrorCheck(int line, const char* op, TIntermTyped*);
-    void constCheck(TIntermTyped* node);
+    void constCheck(TIntermTyped* node, const char* token);
     void integerCheck(TIntermTyped* node, const char* token);
     void globalCheck(int line, bool global, const char* token);
     bool constructorError(int line, TIntermNode*, TFunction&, TOperator, TType&);
     void arraySizeCheck(int line, TIntermTyped* expr, int& size);
     bool arrayQualifierError(int line, const TPublicType&);
     void arraySizeRequiredCheck(int line, int& size);
+    void arrayDimError(int line);
+    void arrayDimCheck(int line, TArraySizes sizes1, TArraySizes sizes2);
+    void arrayDimCheck(int line, const TType*, TArraySizes);
     void arrayCheck(int line, TString& identifier, const TPublicType&, TVariable*& variable);
     bool insertBuiltInArrayAtGlobalLevel();
     bool voidErrorCheck(int, const TString&, const TPublicType&);
diff --git a/glslang/MachineIndependent/ShaderLang.cpp b/glslang/MachineIndependent/ShaderLang.cpp
index 0c1bd9a35..bf5a00f2e 100644
--- a/glslang/MachineIndependent/ShaderLang.cpp
+++ b/glslang/MachineIndependent/ShaderLang.cpp
@@ -82,7 +82,7 @@ const int VersionCount = 12;
 // Each has a different set of built-ins, and we want to preserve that from
 // compile to compile.
 //
-// TODO: quality: thread safety: ensure the built-in symbol table levels are reado only.
+// TODO: quality: thread safety: ensure the built-in symbol table levels are read only.
 TSymbolTable* SharedSymbolTables[VersionCount][EProfileCount][EShLangCount] = {};
 
 TPoolAllocator* PerProcessGPA = 0;
diff --git a/glslang/MachineIndependent/glslang.l b/glslang/MachineIndependent/glslang.l
index 14a7ab934..0845f8ae0 100644
--- a/glslang/MachineIndependent/glslang.l
+++ b/glslang/MachineIndependent/glslang.l
@@ -554,8 +554,6 @@ int yy_input(char* buf, int max_size);
 {D}+"."{D}*({E})?{LF} { pyylval->lex.line = yylineno; pyylval->lex.d = atof(yytext); return DOUBLECONSTANT; }
 "."{D}+({E})?{LF}     { pyylval->lex.line = yylineno; pyylval->lex.d = atof(yytext); return DOUBLECONSTANT; }
 
-"/*"            {  int ret = PaParseComment(pyylval->lex.line, parseContext); if (!ret) return ret; }
-
 "+="            {  pyylval->lex.line = yylineno; return ADD_ASSIGN; }
 "-="            {  pyylval->lex.line = yylineno; return SUB_ASSIGN; }
 "*="            {  pyylval->lex.line = yylineno; return MUL_ASSIGN; }
@@ -667,7 +665,7 @@ int PaParseStrings(char* argv[], int strLen[], int argc, TParseContext& parseCon
     char *writeablePreamble = 0;
     if (preamble) {
         // preAmble could be a hard-coded string; make writable copy
-        // TODO: CPP: make it not need writable strings
+        // TODO: efficiency PP: make it not need writable strings
         int size = strlen(preamble) + 1;
         writeablePreamble = new char[size];
         memcpy(writeablePreamble, preamble, size);
@@ -689,7 +687,7 @@ int PaParseStrings(char* argv[], int strLen[], int argc, TParseContext& parseCon
     cpp->notAVersionToken = 0;
     yylineno   = 1;
 
-    // TODO: CPP: a shader containing nothing but white space and comments is valid, even though it has no parse tokens
+    // TODO: desktop PP: a shader containing nothing but white space and comments is valid, even though it has no parse tokens
     int len = 0;
     while (argv[0][len] == ' '  ||
            argv[0][len] == '\t' ||
@@ -886,37 +884,6 @@ int Pa2ndGenerationImage(TParseContext& pc, int line, const char* text, YYSTYPE*
     return PaIdentOrType(text, pc, pyylval);
 }
 
-// TODO: CPP: is this dead code?  Combine with other comment parsers.
-int PaParseComment(int& lineno, TParseContext& parseContextLocal)
-{
-    int transitionFlag = 0;
-    int nextChar;
-
-    while (transitionFlag != 2) {
-        nextChar = yyinput();
-        if (nextChar == '\n')
-             lineno++;
-        switch (nextChar) {
-        case '*' :
-            transitionFlag = 1;
-            break;
-        case '/' :  /* if star is the previous character, then it is the end of comment */
-            if (transitionFlag == 1) {
-                return 1 ;
-            }
-            break;
-        case EOF :
-            parseContextLocal.error(yylineno, "End of shader found before end of comment.", "", "", "");
-
-            return YY_NULL;
-        default :  /* Any other character will be a part of the comment */
-            transitionFlag = 0;
-        }
-    }
-
-    return 1;
-}
-
 extern "C" {
 
 void ShPpDebugLogMsg(const char *msg)
diff --git a/glslang/MachineIndependent/glslang.y b/glslang/MachineIndependent/glslang.y
index 3164ced0d..cad4fbdd5 100644
--- a/glslang/MachineIndependent/glslang.y
+++ b/glslang/MachineIndependent/glslang.y
@@ -326,7 +326,6 @@ postfix_expression
             TType newType($1->getType());
             newType.dereference();
             $$->setType(newType);
-            // TODO: testing: write a set of dereference tests
         }
     }
     | function_call {
@@ -340,8 +339,6 @@ postfix_expression
             // we later see the function calling syntax.  Save away the name for now.
             //
 
-            // TODO: semantics: if next token is not "(", then this is an error
-
             if (*$3.string == "length") {
                 parseContext.profileRequires($3.line, ENoProfile, 120, "GL_3DL_array_objects", ".length");
                 $$ = parseContext.intermediate.addMethod($1, TType(EbtInt), $3.string, $2.line);
@@ -453,7 +450,8 @@ function_call
         TFunction* fnCall = $1.function;
         TOperator op = fnCall->getBuiltInOp();
         if (op == EOpArrayLength) {
-            // TODO: semantics: check for no arguments to .length()
+            if (fnCall->getParamCount() > 0)
+                parseContext.error($1.line, "method does not accept any arguments", fnCall->getName().c_str(), "");
             int length;
             if ($1.intermNode->getAsTyped() == 0 || ! $1.intermNode->getAsTyped()->getType().isArray() || $1.intermNode->getAsTyped()->getType().getArraySize() == 0) {
                 parseContext.error($1.line, "", fnCall->getName().c_str(), "array must be declared with a size before using this method");
@@ -502,8 +500,8 @@ function_call
                     $$ = parseContext.intermediate.addBuiltInFunctionCall(op, fnCandidate->getParamCount() == 1, $1.intermNode, fnCandidate->getReturnType());
                     if ($$ == 0)  {
                         parseContext.error($1.intermNode->getLine(), " wrong operand type", "Internal Error",
-                            "built in unary operator function.  Type: %s",
-                            static_cast<TIntermTyped*>($1.intermNode)->getCompleteString().c_str());
+                                           "built in unary operator function.  Type: %s",
+                                           static_cast<TIntermTyped*>($1.intermNode)->getCompleteString().c_str());
                         YYERROR;
                     }
                 } else {
@@ -754,7 +752,7 @@ function_identifier
                 TFunction *function = new TFunction(&symbol->getName(), TType(EbtVoid));
                 $$.function = function;
             } else
-                parseContext.error($1->getLine(), "function call, method or subroutine call expected", "", "");
+                parseContext.error($1->getLine(), "function call, method, or subroutine call expected", "", "");
         }
 
         if ($$.function == 0) {
@@ -769,6 +767,8 @@ unary_expression
     : postfix_expression {
         parseContext.variableCheck($1);
         $$ = $1;
+        if (TIntermMethod* method = $1->getAsMethodNode())
+            parseContext.error($1->getLine(), "incomplete method syntax", method->getMethodName().c_str(), "");
     }
     | INC_OP unary_expression {
         parseContext.lValueErrorCheck($1.line, "++", $2);
@@ -1086,7 +1086,7 @@ expression
 
 constant_expression
     : conditional_expression {
-        parseContext.constCheck($1);
+        parseContext.constCheck($1, "");
         $$ = $1;
     }
     ;
@@ -1277,6 +1277,7 @@ parameter_declarator
             parseContext.profileRequires($1.line, EEsProfile, 300, 0, "arrayed type");
             parseContext.arraySizeRequiredCheck($1.line, $1.arraySizes->front());
         }
+        parseContext.arrayDimCheck($2.line, $1.arraySizes, $3.arraySizes);
 
         parseContext.arraySizeRequiredCheck($3.line, $3.arraySizes->front());
         parseContext.reservedErrorCheck($2.line, *$2.string);
@@ -1346,7 +1347,8 @@ init_declarator_list
         parseContext.nonInitConstCheck($3.line, *$3.string, $1.type);
         if (parseContext.profile == EEsProfile)
             parseContext.arraySizeRequiredCheck($4.line, $4.arraySizes->front());
-        
+        parseContext.arrayDimCheck($3.line, $1.type.arraySizes, $4.arraySizes);
+
         $$ = $1;
 
         if (! parseContext.arrayQualifierError($4.line, $1.type)) {
@@ -1363,6 +1365,7 @@ init_declarator_list
             $1.type.arraySizes = $4.arraySizes;
             parseContext.arrayCheck($4.line, *$3.string, $1.type, variable);
         }
+        parseContext.arrayDimCheck($3.line, $1.type.arraySizes, $4.arraySizes);
 
         parseContext.profileRequires($5.line, ENoProfile, 120, "GL_3DL_array_objects", "initializer");
 
@@ -1414,7 +1417,8 @@ single_declaration
         $$.intermAggregate = 0;
         parseContext.nonInitConstCheck($2.line, *$2.string, $1);        
         if (parseContext.profile == EEsProfile)
-            parseContext.arraySizeRequiredCheck($3.line, $3.arraySizes->front());
+            parseContext.arraySizeRequiredCheck($3.line, $3.arraySizes->front());        
+        parseContext.arrayDimCheck($2.line, $1.arraySizes, $3.arraySizes);
 
         $$.type = $1;
 
@@ -1425,7 +1429,9 @@ single_declaration
         }
         parseContext.updateTypedDefaults($2.line, $$.type.qualifier, $2.string);
     }
-    | fully_specified_type IDENTIFIER array_specifier EQUAL initializer {
+    | fully_specified_type IDENTIFIER array_specifier EQUAL initializer {        
+        parseContext.arrayDimCheck($3.line, $1.arraySizes, $3.arraySizes);
+
         $$.intermAggregate = 0;
         $$.type = $1;
 
@@ -1736,6 +1742,7 @@ type_specifier
         $$.qualifier.precision = parseContext.getDefaultPrecision($$);
     }
     | type_specifier_nonarray array_specifier {        
+        parseContext.arrayDimCheck($2.line, $2.arraySizes, 0);
         $$ = $1;
         $$.qualifier.precision = parseContext.getDefaultPrecision($$);
         $$.arraySizes = $2.arraySizes;
@@ -2477,8 +2484,10 @@ struct_declaration
         parseContext.voidErrorCheck($1.line, (*$2)[0].type->getFieldName(), $1);
         parseContext.precisionQualifierCheck($1.line, $1);
 
-        for (unsigned int i = 0; i < $$->size(); ++i)
+        for (unsigned int i = 0; i < $$->size(); ++i) {
+            parseContext.arrayDimCheck($1.line, (*$$)[i].type, $1.arraySizes);
             (*$$)[i].type->mergeType($1);
+        }
     }
     | type_qualifier type_specifier struct_declarator_list SEMICOLON {
         if ($2.arraySizes) {
@@ -2494,8 +2503,10 @@ struct_declaration
         parseContext.mergeQualifiers($2.line, $2.qualifier, $1.qualifier, true);
         parseContext.precisionQualifierCheck($2.line, $2);
 
-        for (unsigned int i = 0; i < $$->size(); ++i)
+        for (unsigned int i = 0; i < $$->size(); ++i) {
+            parseContext.arrayDimCheck($1.line, (*$$)[i].type, $2.arraySizes);
             (*$$)[i].type->mergeType($2);
+        }
     }
     ;
 
@@ -2518,6 +2529,8 @@ struct_declarator
     | IDENTIFIER array_specifier {        
         if (parseContext.profile == EEsProfile)
             parseContext.arraySizeRequiredCheck($2.line, $2.arraySizes->front());
+        parseContext.arrayDimCheck($1.line, $2.arraySizes, 0);
+
         $$.type = new TType(EbtVoid);
         $$.line = $1.line;
         $$.type->setFieldName(*$1.string);
@@ -2678,6 +2691,8 @@ switch_statement_list
 
 case_label
     : CASE expression COLON {
+        parseContext.constCheck($2, "case");
+        parseContext.integerCheck($2, "case");
         $$ = parseContext.intermediate.addBranch(EOpCase, $2, $1.line);
     }
     | DEFAULT COLON {
diff --git a/glslang/MachineIndependent/preprocessor/cpp.c b/glslang/MachineIndependent/preprocessor/cpp.c
index 51872a01e..86a28fb57 100644
--- a/glslang/MachineIndependent/preprocessor/cpp.c
+++ b/glslang/MachineIndependent/preprocessor/cpp.c
@@ -78,8 +78,6 @@ NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 // cpp.c
 //
 
-// TODO: CPP handle escaped newlines in a // style comment, correctly done in ConsumeWhitespaceComment()
-
 #define _CRT_SECURE_NO_WARNINGS
 
 #include <stdarg.h>
-- 
GitLab