Put each array declarator into a separate declaration in HLSL output

Since HLSL doesn't support arrays as l-values, HLSL output needs to split
declarators that initialize arrays to variable declaration and assignment
implemented via a function call. To prepare for this, it is necessary that each
declarator has its own declaration.

BUG=angleproject:941
TEST=angle_end2end_tests, WebGL conformance tests

Change-Id: I43dee487578561c01dbde90c2f55a93dda2f057a
Reviewed-on: https://chromium-review.googlesource.com/266001
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Zhenyao Mo <zmo@chromium.org>
Tested-by: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/SeparateDeclarations.cpp b/src/compiler/translator/SeparateDeclarations.cpp
new file mode 100644
index 0000000..7ea1ce0
--- /dev/null
+++ b/src/compiler/translator/SeparateDeclarations.cpp
@@ -0,0 +1,86 @@
+//
+// 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 SeparateArrayDeclarations function processes declarations that contain array declarators. Each declarator in
+// such declarations gets its own declaration.
+// This is useful as an intermediate step when initialization needs to be separated from declaration.
+// Example:
+//     int a[1] = int[1](1), b[1] = int[1](2);
+// gets transformed when run through this class into the AST equivalent of:
+//     int a[1] = int[1](1);
+//     int b[1] = int[1](2);
+
+#include "compiler/translator/SeparateDeclarations.h"
+
+#include "compiler/translator/IntermNode.h"
+
+namespace
+{
+
+class SeparateDeclarations : private TIntermTraverser
+{
+  public:
+    static void apply(TIntermNode *root);
+  private:
+    SeparateDeclarations();
+    bool visitAggregate(Visit, TIntermAggregate *node) override;
+};
+
+void SeparateDeclarations::apply(TIntermNode *root)
+{
+    SeparateDeclarations separateDecl;
+    root->traverse(&separateDecl);
+    separateDecl.updateTree();
+}
+
+SeparateDeclarations::SeparateDeclarations()
+    : TIntermTraverser(true, false, false)
+{
+}
+
+bool SeparateDeclarations::visitAggregate(Visit, TIntermAggregate *node)
+{
+    if (node->getOp() == EOpDeclaration)
+    {
+        TIntermSequence *sequence = node->getSequence();
+        bool sequenceContainsArrays = false;
+        for (size_t ii = 0; ii < sequence->size(); ++ii)
+        {
+            TIntermTyped *typed = sequence->at(ii)->getAsTyped();
+            if (typed != nullptr && typed->isArray())
+            {
+                sequenceContainsArrays = true;
+                break;
+            }
+        }
+        if (sequence->size() > 1 && sequenceContainsArrays)
+        {
+            TIntermAggregate *parentAgg = getParentNode()->getAsAggregate();
+            ASSERT(parentAgg != nullptr);
+
+            TIntermSequence replacementDeclarations;
+            for (size_t ii = 0; ii < sequence->size(); ++ii)
+            {
+                TIntermAggregate *replacementDeclaration = new TIntermAggregate;
+
+                replacementDeclaration->setOp(EOpDeclaration);
+                replacementDeclaration->getSequence()->push_back(sequence->at(ii));
+                replacementDeclaration->setLine(sequence->at(ii)->getLine());
+                replacementDeclarations.push_back(replacementDeclaration);
+            }
+
+            mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(parentAgg, node, replacementDeclarations));
+        }
+        return false;
+    }
+    return true;
+}
+
+} // namespace
+
+void SeparateArrayDeclarations(TIntermNode *root)
+{
+    SeparateDeclarations::apply(root);
+}