Remove empty switch statements from translated shaders

The native HLSL compiler does not accept switch statements with an
empty statement list. The simplest way to accommodate this is to
simply remove them from the AST after parsing and some initial
pruning.

This is done by the new RemoveEmptySwitchStatements traverser. It
preserves init statements of switch statements in case they have side
effects. So for example

switch(++i) {}

gets translated to

++i;

BUG=angleproject:2206
TEST=angle_end2end_tests

Change-Id: I550a3c9b010a3566016bdfd93344ac30fd860604
Reviewed-on: https://chromium-review.googlesource.com/742922
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/RemoveEmptySwitchStatements.cpp b/src/compiler/translator/RemoveEmptySwitchStatements.cpp
new file mode 100644
index 0000000..b39c912
--- /dev/null
+++ b/src/compiler/translator/RemoveEmptySwitchStatements.cpp
@@ -0,0 +1,56 @@
+//
+// Copyright (c) 2017 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.
+//
+// RemoveEmptySwitchStatements.cpp: Remove switch statements that have an empty statement list.
+
+#include "compiler/translator/RemoveEmptySwitchStatements.h"
+
+#include "compiler/translator/IntermTraverse.h"
+
+namespace sh
+{
+
+namespace
+{
+
+class RemoveEmptySwitchStatementsTraverser : public TIntermTraverser
+{
+  public:
+    RemoveEmptySwitchStatementsTraverser() : TIntermTraverser(true, false, false) {}
+
+    bool visitSwitch(Visit visit, TIntermSwitch *node);
+};
+
+bool RemoveEmptySwitchStatementsTraverser::visitSwitch(Visit visit, TIntermSwitch *node)
+{
+    if (node->getStatementList()->getSequence()->empty())
+    {
+        // Just output the init statement.
+        if (node->getInit()->hasSideEffects())
+        {
+            queueReplacement(node->getInit(), OriginalNode::IS_DROPPED);
+        }
+        else
+        {
+            TIntermSequence emptyReplacement;
+            ASSERT(getParentNode()->getAsBlock());
+            mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(getParentNode()->getAsBlock(),
+                                                                      node, emptyReplacement));
+        }
+        return false;  // Nothing inside the child nodes to traverse.
+    }
+    return true;
+}
+
+}  // anonymous namespace
+
+void RemoveEmptySwitchStatements(TIntermBlock *root)
+{
+    RemoveEmptySwitchStatementsTraverser traverser;
+    root->traverse(&traverser);
+    traverser.updateTree();
+}
+
+}  // namespace sh