Detect when gl_FragData is indexed with != 0 in WebGL 2.0

WebGL 2.0 explicitly specifies it to be an error when gl_FragData is
indexed with anything else than constant zero in spec section
'GLSL ES 1.00 Fragment Shader Output'.

This doesn't apply to WebGL 1.0 or GLES.
dEQP-GLES2.functional.shaders.fragdata* test that dynamic indexing of
gl_FragData is allowed.

TEST=angle_unittests
BUG=angleproject:1210

Change-Id: Ib401242e7867f5e7943456b059dd8e24dc404098
Reviewed-on: https://chromium-review.googlesource.com/312045
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 9676016..6b9ba3f 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -2724,6 +2724,11 @@
                   "array indexes for fragment outputs must be constant integral expressions");
             recover();
         }
+        else if (mShaderSpec == SH_WEBGL2_SPEC && baseExpression->getQualifier() == EvqFragData)
+        {
+            error(location, "", "[", "array index for gl_FragData must be constant zero");
+            recover();
+        }
     }
 
     if (indexConstantUnion)
@@ -2775,7 +2780,29 @@
 
             if (baseExpression->isArray())
             {
-                if (index >= baseExpression->getType().getArraySize())
+                if (baseExpression->getQualifier() == EvqFragData && index > 0)
+                {
+                    if (mShaderSpec == SH_WEBGL2_SPEC)
+                    {
+                        // Error has been already generated if index is not const.
+                        if (indexExpression->getQualifier() == EvqConst)
+                        {
+                            error(location, "", "[",
+                                  "array index for gl_FragData must be constant zero");
+                            recover();
+                        }
+                        safeIndex = 0;
+                    }
+                    else if (!isExtensionEnabled("GL_EXT_draw_buffers"))
+                    {
+                        outOfRangeError(outOfRangeIndexIsError, location, "", "[",
+                                        "array index for gl_FragData must be zero when "
+                                        "GL_EXT_draw_buffers is disabled");
+                        safeIndex = 0;
+                    }
+                }
+                // Only do generic out-of-range check if similar error hasn't already been reported.
+                if (safeIndex < 0 && index >= baseExpression->getType().getArraySize())
                 {
                     std::stringstream extraInfoStream;
                     extraInfoStream << "array index out of range '" << index << "'";
@@ -2783,15 +2810,6 @@
                     outOfRangeError(outOfRangeIndexIsError, location, "", "[", extraInfo.c_str());
                     safeIndex = baseExpression->getType().getArraySize() - 1;
                 }
-                else if (baseExpression->getQualifier() == EvqFragData && index > 0 &&
-                         !isExtensionEnabled("GL_EXT_draw_buffers"))
-                {
-                    outOfRangeError(
-                        outOfRangeIndexIsError, location, "", "[",
-                        "array indexes for gl_FragData must be zero when GL_EXT_draw_buffers is "
-                        "disabled");
-                    safeIndex = 0;
-                }
             }
             else if ((baseExpression->isVector() || baseExpression->isMatrix()) &&
                      baseExpression->getType().getNominalSize() <= index)
diff --git a/src/tests/compiler_tests/MalformedShader_test.cpp b/src/tests/compiler_tests/MalformedShader_test.cpp
index 01f3edd..c75261c 100644
--- a/src/tests/compiler_tests/MalformedShader_test.cpp
+++ b/src/tests/compiler_tests/MalformedShader_test.cpp
@@ -68,6 +68,22 @@
     }
 };
 
+class MalformedWebGL2ShaderTest : public MalformedShaderTest
+{
+  public:
+    MalformedWebGL2ShaderTest() {}
+
+  protected:
+    void SetUp() override
+    {
+        ShBuiltInResources resources;
+        ShInitBuiltInResources(&resources);
+
+        mTranslator = new TranslatorESSL(GL_FRAGMENT_SHADER, SH_WEBGL2_SPEC);
+        ASSERT_TRUE(mTranslator->Init(resources));
+    }
+};
+
 // This is a test for a bug that used to exist in ANGLE:
 // Calling a function with all parameters missing should not succeed.
 TEST_F(MalformedShaderTest, FunctionParameterMismatch)
@@ -1218,3 +1234,22 @@
         FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
     }
 }
+
+// Test that indexing gl_FragData with a non-constant expression is forbidden in WebGL 2.0, even
+// when ANGLE is able to constant fold the index.
+// WebGL 2.0 spec section 'GLSL ES 1.00 Fragment Shader Output'
+TEST_F(MalformedWebGL2ShaderTest, IndexFragDataWithNonConstant)
+{
+    const std::string &shaderString =
+        "precision mediump float;\n"
+        "void main()\n"
+        "{\n"
+        "    for (int i = 0; i < 2; ++i) {\n"
+        "        gl_FragData[true ? 0 : i] = vec4(0.0);\n"
+        "    }\n"
+        "}\n";
+    if (compile(shaderString))
+    {
+        FAIL() << "Shader compilation succeeded, expecting failure " << mInfoLog;
+    }
+}