Fix ASSERT when rewriting else-if blocks with no else.

The code assumed an else-if would end in an else. We can also save a
few allocations in the cases where there is no else.

BUG=angle:699

Change-Id: I550857366775b4a34aea97e117ef732297d3f448
Reviewed-on: https://chromium-review.googlesource.com/208681
Tested-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Nicolas Capens <capn@chromium.org>
diff --git a/src/compiler/translator/RewriteElseBlocks.cpp b/src/compiler/translator/RewriteElseBlocks.cpp
index 3a32aa1..b03beb5 100644
--- a/src/compiler/translator/RewriteElseBlocks.cpp
+++ b/src/compiler/translator/RewriteElseBlocks.cpp
@@ -14,6 +14,24 @@
 namespace sh
 {
 
+namespace
+{
+
+class ElseBlockRewriter : public TIntermTraverser
+{
+  public:
+    ElseBlockRewriter();
+
+  protected:
+    bool visitAggregate(Visit visit, TIntermAggregate *aggregate);
+
+  private:
+    int mTemporaryIndex;
+    const TType *mFunctionType;
+
+    TIntermNode *rewriteSelection(TIntermSelection *selection);
+};
+
 TIntermSymbol *MakeNewTemporary(const TString &name, TBasicType type)
 {
     TType variableType(type, EbpHigh, EvqInternal);
@@ -83,46 +101,54 @@
 
 TIntermNode *ElseBlockRewriter::rewriteSelection(TIntermSelection *selection)
 {
-    ASSERT(selection->getFalseBlock() != NULL);
+    ASSERT(selection != NULL);
 
     TString temporaryName = "cond_" + str(mTemporaryIndex++);
     TIntermTyped *typedCondition = selection->getCondition()->getAsTyped();
     TType resultType(EbtBool, EbpUndefined);
-    TIntermSymbol *conditionSymbolA = MakeNewTemporary(temporaryName, EbtBool);
-    TIntermSymbol *conditionSymbolB = MakeNewTemporary(temporaryName, EbtBool);
-    TIntermSymbol *conditionSymbolC = MakeNewTemporary(temporaryName, EbtBool);
-    TIntermBinary *storeCondition = MakeNewBinary(EOpInitialize, conditionSymbolA,
+    TIntermSymbol *conditionSymbolInit = MakeNewTemporary(temporaryName, EbtBool);
+    TIntermBinary *storeCondition = MakeNewBinary(EOpInitialize, conditionSymbolInit,
                                                   typedCondition, resultType);
-    TIntermUnary *negatedCondition = MakeNewUnary(EOpLogicalNot, conditionSymbolB);
     TIntermNode *negatedElse = NULL;
 
-    // crbug.com/346463
-    // D3D generates error messages claiming a function has no return value, when rewriting
-    // an if-else clause that returns something non-void in a function. By appending dummy
-    // returns (that are unreachable) we can silence this compile error.
-    if (mFunctionType && mFunctionType->getBasicType() != EbtVoid)
+    TIntermSelection *falseBlock = NULL;
+
+    if (selection->getFalseBlock())
     {
-        TString typeString = mFunctionType->getStruct() ? mFunctionType->getStruct()->name() :
-            mFunctionType->getBasicString();
-        TString rawText = "return (" + typeString + ")0";
-        negatedElse = new TIntermRaw(*mFunctionType, rawText);
+        // crbug.com/346463
+        // D3D generates error messages claiming a function has no return value, when rewriting
+        // an if-else clause that returns something non-void in a function. By appending dummy
+        // returns (that are unreachable) we can silence this compile error.
+        if (mFunctionType && mFunctionType->getBasicType() != EbtVoid)
+        {
+            TString typeString = mFunctionType->getStruct() ? mFunctionType->getStruct()->name() :
+                mFunctionType->getBasicString();
+            TString rawText = "return (" + typeString + ")0";
+            negatedElse = new TIntermRaw(*mFunctionType, rawText);
+        }
+
+        TIntermSymbol *conditionSymbolElse = MakeNewTemporary(temporaryName, EbtBool);
+        TIntermUnary *negatedCondition = MakeNewUnary(EOpLogicalNot, conditionSymbolElse);
+        falseBlock = new TIntermSelection(negatedCondition,
+                                          selection->getFalseBlock(), negatedElse);
     }
 
-    TIntermSelection *falseBlock = new TIntermSelection(negatedCondition,
-                                                        selection->getFalseBlock(), negatedElse);
-    TIntermSelection *newIfElse = new TIntermSelection(conditionSymbolC,
-                                                       selection->getTrueBlock(), falseBlock);
+    TIntermSymbol *conditionSymbolSel = MakeNewTemporary(temporaryName, EbtBool);
+    TIntermSelection *newSelection = new TIntermSelection(conditionSymbolSel,
+                                                          selection->getTrueBlock(), falseBlock);
 
     TIntermAggregate *declaration = new TIntermAggregate(EOpDeclaration);
     declaration->getSequence()->push_back(storeCondition);
 
     TIntermAggregate *block = new TIntermAggregate(EOpSequence);
     block->getSequence()->push_back(declaration);
-    block->getSequence()->push_back(newIfElse);
+    block->getSequence()->push_back(newSelection);
 
     return block;
 }
 
+}
+
 void RewriteElseBlocks(TIntermNode *node)
 {
     ElseBlockRewriter rewriter;
diff --git a/src/compiler/translator/RewriteElseBlocks.h b/src/compiler/translator/RewriteElseBlocks.h
index 172928f..39963d6 100644
--- a/src/compiler/translator/RewriteElseBlocks.h
+++ b/src/compiler/translator/RewriteElseBlocks.h
@@ -15,21 +15,6 @@
 namespace sh
 {
 
-class ElseBlockRewriter : public TIntermTraverser
-{
-  public:
-    ElseBlockRewriter();
-
-  protected:
-    bool visitAggregate(Visit visit, TIntermAggregate *aggregate);
-
-  private:
-    int mTemporaryIndex;
-    const TType *mFunctionType;
-
-    TIntermNode *rewriteSelection(TIntermSelection *selection);
-};
-
 void RewriteElseBlocks(TIntermNode *node);
 
 }
diff --git a/tests/angle_tests/GLSLTest.cpp b/tests/angle_tests/GLSLTest.cpp
index ea04478..ee6735f 100644
--- a/tests/angle_tests/GLSLTest.cpp
+++ b/tests/angle_tests/GLSLTest.cpp
@@ -143,3 +143,65 @@
     GLuint program = compileProgram(vertexShaderSource, fragmentShaderSource);
     EXPECT_NE(0u, program);
 }
+
+TEST_F(GLSLTest, ElseIfRewriting)
+{
+    const std::string &vertexShaderSource =
+        "attribute vec4 a_position;\n"
+        "varying float v;\n"
+        "void main() {\n"
+        "  gl_Position = a_position;\n"
+        "  v = 1.0;\n"
+        "  if (a_position.x <= 0.5) {\n"
+        "    v = 0.0;\n"
+        "  } else if (a_position.x >= 0.5) {\n"
+        "    v = 2.0;\n"
+        "  }\n"
+        "}\n";
+
+    const std::string &fragmentShaderSource =
+        "precision highp float;\n"
+        "varying float v;\n"
+        "void main() {\n"
+        "  vec4 color = vec4(1.0, 0.0, 0.0, 1.0);\n"
+        "  if (v >= 1.0) color = vec4(0.0, 1.0, 0.0, 1.0);\n"
+        "  if (v >= 2.0) color = vec4(0.0, 0.0, 1.0, 1.0);\n"
+        "  gl_FragColor = color;\n"
+        "}\n";
+
+    GLuint program = compileProgram(vertexShaderSource, fragmentShaderSource);
+    ASSERT_NE(0u, program);
+
+    drawQuad(program, "a_position", 0.5f);
+    swapBuffers();
+
+    EXPECT_PIXEL_EQ(0, 0, 255, 0, 0, 255);
+    EXPECT_PIXEL_EQ(getWindowWidth()-1, 0, 0, 255, 0, 255);
+}
+
+TEST_F(GLSLTest, TwoElseIfRewriting)
+{
+    const std::string &vertexShaderSource =
+        "attribute vec4 a_position;\n"
+        "varying float v;\n"
+        "void main() {\n"
+        "  gl_Position = a_position;\n"
+        "  if (a_position.x == 0.0`) {\n"
+        "    v = 1.0;\n"
+        "  } else if (a_position.x > 0.5) {\n"
+        "    v = 0.0;\n"
+        "  } else if (a_position.x > 0.75) {\n"
+        "    v = 0.5;\n"
+        "  }\n"
+        "}\n";
+
+    const std::string &fragmentShaderSource =
+        "precision highp float;\n"
+        "varying float v;\n"
+        "void main() {\n"
+        "  gl_FragColor = vec4(v, 0.0, 0.0, 1.0);\n"
+        "}\n";
+
+    GLuint program = compileProgram(vertexShaderSource, fragmentShaderSource);
+    EXPECT_NE(0u, program);
+}