Fold ternary and comma ops only after parsing is done

In case folding a ternary op or a comma op would change the qualifier
of the expression, the folding is deferred to a separate traversal
step.

After this there are no more cases where the type of a TIntermSymbol
node needs to differ from the type of the variable it is referring to.
There are still some cases where some parts of TIntermSymbol type are
changed while keeping the TVariable type the same though, like when
assigning array size to gl_PerVertex nodes or sanitizing qualifiers of
struct declarations.

BUG=angleproject:2267
TEST=angle_unittests, angle_end2end_tests

Change-Id: I1501c8d361f5f765f43ca810d1b7248d9e2c5986
Reviewed-on: https://chromium-review.googlesource.com/850672
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/FoldExpressions.cpp b/src/compiler/translator/FoldExpressions.cpp
new file mode 100644
index 0000000..71e04d0
--- /dev/null
+++ b/src/compiler/translator/FoldExpressions.cpp
@@ -0,0 +1,116 @@
+//
+// Copyright (c) 2018 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.
+//
+// FoldExpressions.cpp: Fold expressions. This may fold expressions so that the qualifier of the
+// folded node differs from the qualifier of the original expression, so it needs to be done after
+// parsing and validation of qualifiers is complete. Expressions that are folded:
+//  1. Ternary ops with a constant condition.
+//  2. Sequence aka comma ops where the left side has no side effects.
+//  3. Any expressions containing any of the above.
+
+#include "compiler/translator/FoldExpressions.h"
+
+#include "compiler/translator/Diagnostics.h"
+#include "compiler/translator/IntermNode.h"
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class FoldExpressionsTraverser : public TIntermTraverser
+{
+  public:
+    FoldExpressionsTraverser(TDiagnostics *diagnostics)
+        : TIntermTraverser(true, false, false), mDiagnostics(diagnostics), mDidReplace(false)
+    {
+    }
+
+    bool didReplace() { return mDidReplace; }
+
+    void nextIteration() { mDidReplace = false; }
+
+  protected:
+    bool visitTernary(Visit visit, TIntermTernary *node) override
+    {
+        TIntermTyped *folded = node->fold(mDiagnostics);
+        if (folded != node)
+        {
+            queueReplacement(folded, OriginalNode::IS_DROPPED);
+            mDidReplace = true;
+            return false;
+        }
+        return true;
+    }
+
+    bool visitAggregate(Visit visit, TIntermAggregate *node) override
+    {
+        TIntermTyped *folded = node->fold(mDiagnostics);
+        if (folded != node)
+        {
+            queueReplacement(folded, OriginalNode::IS_DROPPED);
+            mDidReplace = true;
+            return false;
+        }
+        return true;
+    }
+
+    bool visitBinary(Visit visit, TIntermBinary *node) override
+    {
+        TIntermTyped *folded = node->fold(mDiagnostics);
+        if (folded != node)
+        {
+            queueReplacement(folded, OriginalNode::IS_DROPPED);
+            mDidReplace = true;
+            return false;
+        }
+        return true;
+    }
+
+    bool visitUnary(Visit visit, TIntermUnary *node) override
+    {
+        TIntermTyped *folded = node->fold(mDiagnostics);
+        if (folded != node)
+        {
+            queueReplacement(folded, OriginalNode::IS_DROPPED);
+            mDidReplace = true;
+            return false;
+        }
+        return true;
+    }
+
+    bool visitSwizzle(Visit visit, TIntermSwizzle *node) override
+    {
+        TIntermTyped *folded = node->fold(mDiagnostics);
+        if (folded != node)
+        {
+            queueReplacement(folded, OriginalNode::IS_DROPPED);
+            mDidReplace = true;
+            return false;
+        }
+        return true;
+    }
+
+  private:
+    TDiagnostics *mDiagnostics;
+    bool mDidReplace;
+};
+
+}  // anonymous namespace
+
+void FoldExpressions(TIntermBlock *root, TDiagnostics *diagnostics)
+{
+    FoldExpressionsTraverser traverser(diagnostics);
+    do
+    {
+        traverser.nextIteration();
+        root->traverse(&traverser);
+        traverser.updateTree();
+    } while (traverser.didReplace());
+}
+
+}  // namespace sh