Fix switch statement validation corner cases

The grammar needs to generate AST nodes even for no-op statements,
since they might be the last statement in a switch statement that is
required for switch statement validity. Change the grammar to generate
nodes from empty blocks and empty declarations.

We also need to do some further processing of the AST. This is because
PruneEmptyDeclarations will still remove empty declarations, and at
least the NVIDIA driver GLSL compiler doesn't accept some types of
no-op statements as the last statement inside a switch statement. So
after parsing has finished we do rudimentary dead code elimination to
remove dead cases from the end of switch statements.

BUG=angleproject:2181
TEST=angle_unittests

Change-Id: I586f2e4a3ac2171e65f1f0ccb7a7de220e3cc225
Reviewed-on: https://chromium-review.googlesource.com/712574
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/PruneEmptyDeclarations.cpp b/src/compiler/translator/PruneEmptyDeclarations.cpp
index 2175547..c763d2e 100644
--- a/src/compiler/translator/PruneEmptyDeclarations.cpp
+++ b/src/compiler/translator/PruneEmptyDeclarations.cpp
@@ -19,14 +19,14 @@
 class PruneEmptyDeclarationsTraverser : private TIntermTraverser
 {
   public:
-    static void apply(TIntermNode *root);
+    static void apply(TIntermBlock *root);
 
   private:
     PruneEmptyDeclarationsTraverser();
     bool visitDeclaration(Visit, TIntermDeclaration *node) override;
 };
 
-void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root)
+void PruneEmptyDeclarationsTraverser::apply(TIntermBlock *root)
 {
     PruneEmptyDeclarationsTraverser prune;
     root->traverse(&prune);
@@ -62,22 +62,10 @@
             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;
-                TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
-                // The declaration may be inside a block or in a loop init expression.
-                ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
-                if (parentAsBlock)
-                {
-                    mMultiReplacements.push_back(
-                        NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
-                }
-                else
-                {
-                    queueReplacement(nullptr, OriginalNode::IS_DROPPED);
-                }
+                // they should not be pruned. If there are entirely empty non-struct declarations,
+                // they result in TIntermDeclaration nodes without any children in the parsing
+                // stage. This will be handled further down in the code.
+                UNREACHABLE();
             }
             else if (sym->getType().getQualifier() != EvqGlobal &&
                      sym->getType().getQualifier() != EvqTemporary)
@@ -102,12 +90,29 @@
             }
         }
     }
+    else
+    {
+        // We have a declaration with no declarators.
+        // The declaration may be either inside a block or in a loop init expression.
+        TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
+        ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
+        if (parentAsBlock)
+        {
+            TIntermSequence emptyReplacement;
+            mMultiReplacements.push_back(
+                NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
+        }
+        else
+        {
+            queueReplacement(nullptr, OriginalNode::IS_DROPPED);
+        }
+    }
     return false;
 }
 
 }  // namespace
 
-void PruneEmptyDeclarations(TIntermNode *root)
+void PruneEmptyDeclarations(TIntermBlock *root)
 {
     PruneEmptyDeclarationsTraverser::apply(root);
 }