Prune empty declarations from the AST

Empty declarations in ESSL shaders currently result in errors on several
platforms. Prune empty declarations that are not struct or interface block
declarations from the AST.

TEST=WebGL conformance tests, angle_unittests, angle_end2end_tests
BUG=angleproject:980

Change-Id: I9e3abf8134e6dfed0253cc83f69ce0ee92b0e0e7
Reviewed-on: https://chromium-review.googlesource.com/266841
Reviewed-by: Olli Etuaho <oetuaho@nvidia.com>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index ad3dfc5..f5650e1 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -11,6 +11,7 @@
 #include "compiler/translator/InitializeParseContext.h"
 #include "compiler/translator/InitializeVariables.h"
 #include "compiler/translator/ParseContext.h"
+#include "compiler/translator/PruneEmptyDeclarations.h"
 #include "compiler/translator/RegenerateStructNames.h"
 #include "compiler/translator/RenameFunction.h"
 #include "compiler/translator/ScalarizeVecAndMatConstructorArgs.h"
@@ -249,6 +250,10 @@
         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 == 300 && shaderType == GL_FRAGMENT_SHADER)
             success = validateOutputs(root);
 
diff --git a/src/compiler/translator/PruneEmptyDeclarations.cpp b/src/compiler/translator/PruneEmptyDeclarations.cpp
new file mode 100644
index 0000000..ef62dbf
--- /dev/null
+++ b/src/compiler/translator/PruneEmptyDeclarations.cpp
@@ -0,0 +1,81 @@
+//
+// Copyright (c) 2002-2015 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.
+//
+// The PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST.
+
+#include "compiler/translator/PruneEmptyDeclarations.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace
+{
+
+class PruneEmptyDeclarationsTraverser : private TIntermTraverser
+{
+  public:
+    static void apply(TIntermNode *root);
+  private:
+    PruneEmptyDeclarationsTraverser();
+    bool visitAggregate(Visit, TIntermAggregate *node) override;
+};
+
+void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root)
+{
+    PruneEmptyDeclarationsTraverser prune;
+    root->traverse(&prune);
+    prune.updateTree();
+}
+
+PruneEmptyDeclarationsTraverser::PruneEmptyDeclarationsTraverser()
+    : TIntermTraverser(true, false, false)
+{
+}
+
+bool PruneEmptyDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node)
+{
+    if (node->getOp() == EOpDeclaration)
+    {
+        TIntermSequence *sequence = node->getSequence();
+        if (sequence->size() >= 1)
+        {
+            TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
+            // Prune declarations without a variable name, unless it's an interface block declaration.
+            if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock())
+            {
+                if (sequence->size() > 1)
+                {
+                    // Generate a replacement that will remove the empty declarator in the beginning of a declarator
+                    // list. Example of a declaration that will be changed:
+                    // float, a;
+                    // will be changed to
+                    // float a;
+                    // This applies also to struct declarations.
+                    TIntermSequence emptyReplacement;
+                    mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
+                }
+                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;
+                    TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
+                    ASSERT(parentAgg != nullptr);
+                    mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, emptyReplacement));
+                }
+            }
+        }
+        return false;
+    }
+    return true;
+}
+
+} // namespace
+
+void PruneEmptyDeclarations(TIntermNode *root)
+{
+    PruneEmptyDeclarationsTraverser::apply(root);
+}
diff --git a/src/compiler/translator/PruneEmptyDeclarations.h b/src/compiler/translator/PruneEmptyDeclarations.h
new file mode 100644
index 0000000..122e830
--- /dev/null
+++ b/src/compiler/translator/PruneEmptyDeclarations.h
@@ -0,0 +1,15 @@
+//
+// Copyright (c) 2002-2015 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.
+//
+// The PruneEmptyDeclarations function prunes unnecessary empty declarations and declarators from the AST.
+
+#ifndef COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
+#define COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_
+
+class TIntermNode;
+
+void PruneEmptyDeclarations(TIntermNode *root);
+
+#endif  // COMPILER_TRANSLATOR_PRUNEEMPTYDECLARATIONS_H_