Keep track of shader built-in declarations separately per ESSL version

The patch decouples the list of unmangled built-ins' names into levels
based on the ESSL version they are from. This is required
because ESSL31 and above built-ins' names should not conflict with
user-declared functions in ESSL3 shaders.

BUG=angleproject:1666
TEST=angle_unittest

Change-Id: I731918a058629c7b6d9d15eb7eac19ce47280315
Reviewed-on: https://chromium-review.googlesource.com/420324
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 64302e4..d9618df 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -2548,9 +2548,11 @@
     TFunction *prevDec =
         static_cast<TFunction *>(symbolTable.find(function->getMangledName(), getShaderVersion()));
 
-    if (getShaderVersion() >= 300 && symbolTable.hasUnmangledBuiltIn(function->getName().c_str()))
+    if (getShaderVersion() >= 300 &&
+        symbolTable.hasUnmangledBuiltInForShaderVersion(function->getName().c_str(),
+                                                        getShaderVersion()))
     {
-        // With ESSL 3.00, names of built-in functions cannot be redeclared as functions.
+        // With ESSL 3.00 and above, names of built-in functions cannot be redeclared as functions.
         // Therefore overloading or redefining builtin functions is an error.
         error(location, "Name of a built-in function cannot be redeclared as function",
               function->getName().c_str());
diff --git a/src/compiler/translator/SymbolTable.cpp b/src/compiler/translator/SymbolTable.cpp
index cfbbbbb..ad4484a 100644
--- a/src/compiler/translator/SymbolTable.cpp
+++ b/src/compiler/translator/SymbolTable.cpp
@@ -248,7 +248,7 @@
 {
     if (ptype1->getBasicType() == EbtGSampler2D)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
         insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
                       TCache::getType(EbtSampler2D), ptype2, ptype3, ptype4, ptype5);
@@ -259,7 +259,7 @@
     }
     else if (ptype1->getBasicType() == EbtGSampler3D)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
         insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
                       TCache::getType(EbtSampler3D), ptype2, ptype3, ptype4, ptype5);
@@ -270,7 +270,7 @@
     }
     else if (ptype1->getBasicType() == EbtGSamplerCube)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
         insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
                       TCache::getType(EbtSamplerCube), ptype2, ptype3, ptype4, ptype5);
@@ -281,7 +281,7 @@
     }
     else if (ptype1->getBasicType() == EbtGSampler2DArray)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         bool gvec4 = (rvalue->getBasicType() == EbtGVec4);
         insertBuiltIn(level, gvec4 ? TCache::getType(EbtFloat, 4) : rvalue, name,
                       TCache::getType(EbtSampler2DArray), ptype2, ptype3, ptype4, ptype5);
@@ -292,7 +292,7 @@
     }
     else if (IsGImage(ptype1->getBasicType()))
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
 
         const TType *floatType    = TCache::getType(EbtFloat, 4);
         const TType *intType      = TCache::getType(EbtInt, 4);
@@ -330,7 +330,7 @@
     else if (IsGenType(rvalue) || IsGenType(ptype1) || IsGenType(ptype2) || IsGenType(ptype3))
     {
         ASSERT(!ptype4 && !ptype5);
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         insertBuiltIn(level, op, ext, SpecificType(rvalue, 1), name, SpecificType(ptype1, 1),
                       SpecificType(ptype2, 1), SpecificType(ptype3, 1));
         insertBuiltIn(level, op, ext, SpecificType(rvalue, 2), name, SpecificType(ptype1, 2),
@@ -343,7 +343,7 @@
     else if (IsVecType(rvalue) || IsVecType(ptype1) || IsVecType(ptype2) || IsVecType(ptype3))
     {
         ASSERT(!ptype4 && !ptype5);
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         insertBuiltIn(level, op, ext, VectorType(rvalue, 2), name, VectorType(ptype1, 2),
                       VectorType(ptype2, 2), VectorType(ptype3, 2));
         insertBuiltIn(level, op, ext, VectorType(rvalue, 3), name, VectorType(ptype1, 3),
@@ -377,7 +377,7 @@
             function->addParameter(TConstParameter(ptype5));
         }
 
-        ASSERT(hasUnmangledBuiltIn(name));
+        ASSERT(hasUnmangledBuiltInAtLevel(name, level));
         insert(level, function);
     }
 }
@@ -407,4 +407,43 @@
     return prec;
 }
 
+void TSymbolTable::insertUnmangledBuiltInName(const char *name, ESymbolLevel level)
+{
+    ASSERT(level >= 0 && level < static_cast<ESymbolLevel>(table.size()));
+    table[level]->insertUnmangledBuiltInName(std::string(name));
+}
+
+bool TSymbolTable::hasUnmangledBuiltInAtLevel(const char *name, ESymbolLevel level)
+{
+    ASSERT(level >= 0 && level < static_cast<ESymbolLevel>(table.size()));
+    return table[level]->hasUnmangledBuiltIn(std::string(name));
+}
+
+bool TSymbolTable::hasUnmangledBuiltInForShaderVersion(const char *name, int shaderVersion)
+{
+    ASSERT(static_cast<ESymbolLevel>(table.size()) > LAST_BUILTIN_LEVEL);
+
+    for (int level = LAST_BUILTIN_LEVEL; level >= 0; --level)
+    {
+        if (level == ESSL3_1_BUILTINS && shaderVersion != 310)
+        {
+            --level;
+        }
+        if (level == ESSL3_BUILTINS && shaderVersion < 300)
+        {
+            --level;
+        }
+        if (level == ESSL1_BUILTINS && shaderVersion != 100)
+        {
+            --level;
+        }
+
+        if (table[level]->hasUnmangledBuiltIn(name))
+        {
+            return true;
+        }
+    }
+    return false;
+}
+
 }  // namespace sh
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index fc498f8..d0792c5 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -242,10 +242,23 @@
 
     void setGlobalInvariant(bool invariant) { mGlobalInvariant = invariant; }
 
+    void insertUnmangledBuiltInName(const std::string &name)
+    {
+        mUnmangledBuiltInNames.insert(name);
+    }
+
+    bool hasUnmangledBuiltIn(const std::string &name)
+    {
+        return mUnmangledBuiltInNames.count(name) > 0;
+    }
+
   protected:
     tLevel level;
     std::set<std::string> mInvariantVaryings;
     bool mGlobalInvariant;
+
+  private:
+    std::set<std::string> mUnmangledBuiltInNames;
 };
 
 // Define ESymbolLevel as int rather than an enum since level can go
@@ -360,7 +373,7 @@
                        const TType *ptype4 = 0,
                        const TType *ptype5 = 0)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         insertBuiltIn(level, EOpNull, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
     }
 
@@ -374,7 +387,7 @@
                        const TType *ptype4 = 0,
                        const TType *ptype5 = 0)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         insertBuiltIn(level, EOpNull, ext, rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
     }
 
@@ -388,7 +401,7 @@
                        const TType *ptype4 = 0,
                        const TType *ptype5 = 0)
     {
-        insertUnmangledBuiltIn(name);
+        insertUnmangledBuiltInName(name, level);
         insertBuiltIn(level, op, "", rvalue, name, ptype1, ptype2, ptype3, ptype4, ptype5);
     }
 
@@ -452,26 +465,22 @@
 
     static int nextUniqueId() { return ++uniqueIdCounter; }
 
-    bool hasUnmangledBuiltIn(const char *name)
-    {
-        return mUnmangledBuiltinNames.count(std::string(name)) > 0;
-    }
+    // Checks whether there is a built-in accessible by a shader with the specified version.
+    bool hasUnmangledBuiltInForShaderVersion(const char *name, int shaderVersion);
 
   private:
     ESymbolLevel currentLevel() const { return static_cast<ESymbolLevel>(table.size() - 1); }
 
-    // Used to insert unmangled functions to check redeclaration of built-ins in ESSL 3.00.
-    void insertUnmangledBuiltIn(const char *name)
-    {
-        mUnmangledBuiltinNames.insert(std::string(name));
-    }
+    // Used to insert unmangled functions to check redeclaration of built-ins in ESSL 3.00 and
+    // above.
+    void insertUnmangledBuiltInName(const char *name, ESymbolLevel level);
+
+    bool hasUnmangledBuiltInAtLevel(const char *name, ESymbolLevel level);
 
     std::vector<TSymbolTableLevel *> table;
     typedef TMap<TBasicType, TPrecision> PrecisionStackLevel;
     std::vector<PrecisionStackLevel *> precisionStack;
 
-    std::set<std::string> mUnmangledBuiltinNames;
-
     static int uniqueIdCounter;
 };
 
diff --git a/src/tests/compiler_tests/MalformedShader_test.cpp b/src/tests/compiler_tests/MalformedShader_test.cpp
index b9f9c49..47aa4e3 100644
--- a/src/tests/compiler_tests/MalformedShader_test.cpp
+++ b/src/tests/compiler_tests/MalformedShader_test.cpp
@@ -3328,4 +3328,22 @@
     {
         FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
     }
+}
+
+// Declaring a function with the same name as a built-in from a higher ESSL version should not cause
+// a redeclaration error.
+TEST_F(MalformedShaderTest, BuiltinESSL31FunctionDeclaredInESSL30Shader)
+{
+    const std::string &shaderString =
+        "#version 300 es\n"
+        "precision mediump float;\n"
+        "void imageSize() {}\n"
+        "void main() {\n"
+        "   imageSize();\n"
+        "}\n";
+
+    if (!compile(shaderString))
+    {
+        FAIL() << "Shader compilation failed, expecting success " << mInfoLog;
+    }
 }
\ No newline at end of file