Split ternary node class from TIntermSelection

Ternary operator nodes are typed parts of expressions, they always
have two children and the children are also guaranteed to be
TIntermTyped. "If" selection nodes can't be a part of an expression,
they can have either one or two children and the children are code
blocks. Due to all of these differences it makes sense to store these
using two different AST node classes.

BUG=angleproject:1490
TEST=angle_unittests

Change-Id: I913ab1d806e3cdb5c21106f078cc9c0b6c72ac54
Reviewed-on: https://chromium-review.googlesource.com/384512
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 9b124a4..0d4104e 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -292,6 +292,14 @@
         mType.setPrecision(precision);
 }
 
+bool TIntermTernary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+    REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
+    REPLACE_IF_IS(mTrueExpression, TIntermTyped, original, replacement);
+    REPLACE_IF_IS(mFalseExpression, TIntermTyped, original, replacement);
+    return false;
+}
+
 bool TIntermSelection::replaceChildNode(
     TIntermNode *original, TIntermNode *replacement)
 {
@@ -456,20 +464,15 @@
     mOperand = operandCopy;
 }
 
-TIntermSelection::TIntermSelection(const TIntermSelection &node) : TIntermTyped(node)
+TIntermTernary::TIntermTernary(const TIntermTernary &node) : TIntermTyped(node)
 {
-    // Only supported for ternary nodes, not if statements.
-    TIntermTyped *trueTyped  = node.mTrueBlock->getAsTyped();
-    TIntermTyped *falseTyped = node.mFalseBlock->getAsTyped();
-    ASSERT(trueTyped != nullptr);
-    ASSERT(falseTyped != nullptr);
     TIntermTyped *conditionCopy = node.mCondition->deepCopy();
-    TIntermTyped *trueCopy      = trueTyped->deepCopy();
-    TIntermTyped *falseCopy = falseTyped->deepCopy();
+    TIntermTyped *trueCopy      = node.mTrueExpression->deepCopy();
+    TIntermTyped *falseCopy     = node.mFalseExpression->deepCopy();
     ASSERT(conditionCopy != nullptr && trueCopy != nullptr && falseCopy != nullptr);
-    mCondition  = conditionCopy;
-    mTrueBlock  = trueCopy;
-    mFalseBlock = falseCopy;
+    mCondition       = conditionCopy;
+    mTrueExpression  = trueCopy;
+    mFalseExpression = falseCopy;
 }
 
 bool TIntermOperator::isAssignment() const
@@ -692,6 +695,31 @@
     promote();
 }
 
+TIntermTernary::TIntermTernary(TIntermTyped *cond,
+                               TIntermTyped *trueExpression,
+                               TIntermTyped *falseExpression)
+    : TIntermTyped(trueExpression->getType()),
+      mCondition(cond),
+      mTrueExpression(trueExpression),
+      mFalseExpression(falseExpression)
+{
+    getTypePointer()->setQualifier(
+        TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression));
+}
+
+// static
+TQualifier TIntermTernary::DetermineQualifier(TIntermTyped *cond,
+                                              TIntermTyped *trueExpression,
+                                              TIntermTyped *falseExpression)
+{
+    if (cond->getQualifier() == EvqConst && trueExpression->getQualifier() == EvqConst &&
+        falseExpression->getQualifier() == EvqConst)
+    {
+        return EvqConst;
+    }
+    return EvqTemporary;
+}
+
 //
 // Establishes the type of the resultant operation, as well as
 // makes the operator the correct one for the operands.
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 1c32eda..1ff5b57 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -34,6 +34,7 @@
 class TIntermBinary;
 class TIntermUnary;
 class TIntermConstantUnion;
+class TIntermTernary;
 class TIntermSelection;
 class TIntermSwitch;
 class TIntermCase;
@@ -93,6 +94,7 @@
     virtual TIntermAggregate *getAsAggregate() { return 0; }
     virtual TIntermBinary *getAsBinaryNode() { return 0; }
     virtual TIntermUnary *getAsUnaryNode() { return 0; }
+    virtual TIntermTernary *getAsTernaryNode() { return nullptr; }
     virtual TIntermSelection *getAsSelectionNode() { return 0; }
     virtual TIntermSwitch *getAsSwitchNode() { return 0; }
     virtual TIntermCase *getAsCaseNode() { return 0; }
@@ -567,35 +569,53 @@
     TIntermAggregate(const TIntermAggregate &node);  // note: not deleted, just private!
 };
 
-//
-// For if tests.
-//
-class TIntermSelection : public TIntermTyped
+// For ternary operators like a ? b : c.
+class TIntermTernary : public TIntermTyped
 {
   public:
-    TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB)
-        : TIntermTyped(TType(EbtVoid, EbpUndefined)),
-          mCondition(cond),
-          mTrueBlock(trueB),
-          mFalseBlock(falseB) {}
-    TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB,
-                     const TType &type)
-        : TIntermTyped(type),
-          mCondition(cond),
-          mTrueBlock(trueB),
-          mFalseBlock(falseB) {}
-
-    // Note: only supported for ternary operator nodes.
-    TIntermTyped *deepCopy() const override { return new TIntermSelection(*this); }
+    TIntermTernary(TIntermTyped *cond, TIntermTyped *trueExpression, TIntermTyped *falseExpression);
 
     void traverse(TIntermTraverser *it) override;
     bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
 
-    // Conservatively assume selections have side-effects
-    bool hasSideEffects() const override { return true; }
+    TIntermTyped *getCondition() const { return mCondition; }
+    TIntermTyped *getTrueExpression() const { return mTrueExpression; }
+    TIntermTyped *getFalseExpression() const { return mFalseExpression; }
+    TIntermTernary *getAsTernaryNode() override { return this; }
 
-    bool usesTernaryOperator() const { return getBasicType() != EbtVoid; }
-    TIntermNode *getCondition() const { return mCondition; }
+    TIntermTyped *deepCopy() const override { return new TIntermTernary(*this); }
+
+    bool hasSideEffects() const override
+    {
+        return mCondition->hasSideEffects() || mTrueExpression->hasSideEffects() ||
+               mFalseExpression->hasSideEffects();
+    }
+
+    static TQualifier DetermineQualifier(TIntermTyped *cond,
+                                         TIntermTyped *trueExpression,
+                                         TIntermTyped *falseExpression);
+
+  private:
+    TIntermTernary(const TIntermTernary &node);  // Note: not deleted, just private!
+
+    TIntermTyped *mCondition;
+    TIntermTyped *mTrueExpression;
+    TIntermTyped *mFalseExpression;
+};
+
+// For if tests.
+class TIntermSelection : public TIntermNode
+{
+  public:
+    TIntermSelection(TIntermTyped *cond, TIntermNode *trueB, TIntermNode *falseB)
+        : TIntermNode(), mCondition(cond), mTrueBlock(trueB), mFalseBlock(falseB)
+    {
+    }
+
+    void traverse(TIntermTraverser *it) override;
+    bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+    TIntermTyped *getCondition() const { return mCondition; }
     TIntermNode *getTrueBlock() const { return mTrueBlock; }
     TIntermNode *getFalseBlock() const { return mFalseBlock; }
     TIntermSelection *getAsSelectionNode() override { return this; }
@@ -604,9 +624,6 @@
     TIntermTyped *mCondition;
     TIntermNode *mTrueBlock;
     TIntermNode *mFalseBlock;
-
-  private:
-    TIntermSelection(const TIntermSelection &node);  // Note: not deleted, just private!
 };
 
 //
@@ -692,6 +709,7 @@
     virtual void visitConstantUnion(TIntermConstantUnion *node) {}
     virtual bool visitBinary(Visit visit, TIntermBinary *node) { return true; }
     virtual bool visitUnary(Visit visit, TIntermUnary *node) { return true; }
+    virtual bool visitTernary(Visit visit, TIntermTernary *node) { return true; }
     virtual bool visitSelection(Visit visit, TIntermSelection *node) { return true; }
     virtual bool visitSwitch(Visit visit, TIntermSwitch *node) { return true; }
     virtual bool visitCase(Visit visit, TIntermCase *node) { return true; }
@@ -707,6 +725,7 @@
     virtual void traverseConstantUnion(TIntermConstantUnion *node);
     virtual void traverseBinary(TIntermBinary *node);
     virtual void traverseUnary(TIntermUnary *node);
+    virtual void traverseTernary(TIntermTernary *node);
     virtual void traverseSelection(TIntermSelection *node);
     virtual void traverseSwitch(TIntermSwitch *node);
     virtual void traverseCase(TIntermCase *node);
@@ -994,6 +1013,7 @@
 
     bool visitBinary(Visit, TIntermBinary *) override { return depthCheck(); }
     bool visitUnary(Visit, TIntermUnary *) override { return depthCheck(); }
+    bool visitTernary(Visit, TIntermTernary *) override { return depthCheck(); }
     bool visitSelection(Visit, TIntermSelection *) override { return depthCheck(); }
     bool visitAggregate(Visit, TIntermAggregate *) override { return depthCheck(); }
     bool visitLoop(Visit, TIntermLoop *) override { return depthCheck(); }
diff --git a/src/compiler/translator/IntermNodePatternMatcher.cpp b/src/compiler/translator/IntermNodePatternMatcher.cpp
index b614d83..6307563 100644
--- a/src/compiler/translator/IntermNodePatternMatcher.cpp
+++ b/src/compiler/translator/IntermNodePatternMatcher.cpp
@@ -105,14 +105,11 @@
     return false;
 }
 
-bool IntermNodePatternMatcher::match(TIntermSelection *node)
+bool IntermNodePatternMatcher::match(TIntermTernary *node)
 {
     if ((mMask & kUnfoldedShortCircuitExpression) != 0)
     {
-        if (node->usesTernaryOperator())
-        {
-            return true;
-        }
+        return true;
     }
     return false;
 }
diff --git a/src/compiler/translator/IntermNodePatternMatcher.h b/src/compiler/translator/IntermNodePatternMatcher.h
index be6fc61..3e24f85 100644
--- a/src/compiler/translator/IntermNodePatternMatcher.h
+++ b/src/compiler/translator/IntermNodePatternMatcher.h
@@ -14,7 +14,7 @@
 class TIntermAggregate;
 class TIntermBinary;
 class TIntermNode;
-class TIntermSelection;
+class TIntermTernary;
 
 class IntermNodePatternMatcher
 {
@@ -42,7 +42,7 @@
     bool match(TIntermBinary *node, TIntermNode *parentNode, bool isLValueRequiredHere);
 
     bool match(TIntermAggregate *node, TIntermNode *parentNode);
-    bool match(TIntermSelection *node);
+    bool match(TIntermTernary *node);
 
   private:
     const unsigned int mMask;
diff --git a/src/compiler/translator/IntermTraverse.cpp b/src/compiler/translator/IntermTraverse.cpp
index 79cb86d..7adea71 100644
--- a/src/compiler/translator/IntermTraverse.cpp
+++ b/src/compiler/translator/IntermTraverse.cpp
@@ -33,6 +33,11 @@
     it->traverseUnary(this);
 }
 
+void TIntermTernary::traverse(TIntermTraverser *it)
+{
+    it->traverseTernary(this);
+}
+
 void TIntermSelection::traverse(TIntermTraverser *it)
 {
     it->traverseSelection(this);
@@ -567,6 +572,31 @@
 }
 
 //
+// Traverse a ternary node.  Same comments in binary node apply here.
+//
+void TIntermTraverser::traverseTernary(TIntermTernary *node)
+{
+    bool visit = true;
+
+    if (preVisit)
+        visit = visitTernary(PreVisit, node);
+
+    if (visit)
+    {
+        incrementDepth(node);
+        node->getCondition()->traverse(this);
+        if (node->getTrueExpression())
+            node->getTrueExpression()->traverse(this);
+        if (node->getFalseExpression())
+            node->getFalseExpression()->traverse(this);
+        decrementDepth();
+    }
+
+    if (visit && postVisit)
+        visitTernary(PostVisit, node);
+}
+
+//
 // Traverse a selection node.  Same comments in binary node apply here.
 //
 void TIntermTraverser::traverseSelection(TIntermSelection *node)
diff --git a/src/compiler/translator/Intermediate.cpp b/src/compiler/translator/Intermediate.cpp
index 4e2b8b5..53eaa6f 100644
--- a/src/compiler/translator/Intermediate.cpp
+++ b/src/compiler/translator/Intermediate.cpp
@@ -236,43 +236,37 @@
     return commaNode;
 }
 
-//
 // For "?:" test nodes.  There are three children; a condition,
 // a true path, and a false path.  The two paths are specified
 // as separate parameters.
 //
-// Returns the selection node created, or one of trueBlock and falseBlock if the expression could be folded.
-//
-TIntermTyped *TIntermediate::addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
-                                          const TSourceLoc &line)
+// Returns the ternary node created, or one of trueExpression and falseExpression if the expression
+// could be folded.
+TIntermTyped *TIntermediate::AddTernarySelection(TIntermTyped *cond,
+                                                 TIntermTyped *trueExpression,
+                                                 TIntermTyped *falseExpression,
+                                                 const TSourceLoc &line)
 {
-    TQualifier resultQualifier = EvqTemporary;
-    if (cond->getQualifier() == EvqConst && trueBlock->getQualifier() == EvqConst &&
-        falseBlock->getQualifier() == EvqConst)
-    {
-        resultQualifier = EvqConst;
-    }
     // Note that the node resulting from here can be a constant union without being qualified as
     // constant.
     if (cond->getAsConstantUnion())
     {
+        TQualifier resultQualifier =
+            TIntermTernary::DetermineQualifier(cond, trueExpression, falseExpression);
         if (cond->getAsConstantUnion()->getBConst(0))
         {
-            trueBlock->getTypePointer()->setQualifier(resultQualifier);
-            return trueBlock;
+            trueExpression->getTypePointer()->setQualifier(resultQualifier);
+            return trueExpression;
         }
         else
         {
-            falseBlock->getTypePointer()->setQualifier(resultQualifier);
-            return falseBlock;
+            falseExpression->getTypePointer()->setQualifier(resultQualifier);
+            return falseExpression;
         }
     }
 
-    //
-    // Make a selection node.
-    //
-    TIntermSelection *node = new TIntermSelection(cond, trueBlock, falseBlock, trueBlock->getType());
-    node->getTypePointer()->setQualifier(resultQualifier);
+    // Make a ternary node.
+    TIntermTernary *node = new TIntermTernary(cond, trueExpression, falseExpression);
     node->setLine(line);
 
     return node;
diff --git a/src/compiler/translator/Intermediate.h b/src/compiler/translator/Intermediate.h
index c8edb4e..0d75444 100644
--- a/src/compiler/translator/Intermediate.h
+++ b/src/compiler/translator/Intermediate.h
@@ -38,9 +38,11 @@
     TIntermAggregate *makeAggregate(TIntermNode *node, const TSourceLoc &);
     TIntermAggregate *ensureSequence(TIntermNode *node);
     TIntermAggregate *setAggregateOperator(TIntermNode *, TOperator, const TSourceLoc &);
-    TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &);
-    TIntermTyped *addSelection(TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock,
-                               const TSourceLoc &line);
+    TIntermNode *addSelection(TIntermTyped *cond, TIntermNodePair code, const TSourceLoc &line);
+    static TIntermTyped *AddTernarySelection(TIntermTyped *cond,
+                                             TIntermTyped *trueExpression,
+                                             TIntermTyped *falseExpression,
+                                             const TSourceLoc &line);
     TIntermSwitch *addSwitch(
         TIntermTyped *init, TIntermAggregate *statementList, const TSourceLoc &line);
     TIntermCase *addCase(
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 296eef7..77dac74 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -27,11 +27,9 @@
         return (aggregate->getOp() != EOpFunction) &&
                (aggregate->getOp() != EOpSequence);
     }
-    else if (const TIntermSelection *selection = node->getAsSelectionNode())
+    else if (node->getAsSelectionNode())
     {
-        // Ternary operators are usually part of an assignment operator.
-        // This handles those rare cases in which they are all by themselves.
-        return selection->usesTernaryOperator();
+        return false;
     }
     else if (node->getAsLoopNode())
     {
@@ -711,40 +709,40 @@
     return true;
 }
 
+bool TOutputGLSLBase::visitTernary(Visit visit, TIntermTernary *node)
+{
+    TInfoSinkBase &out = objSink();
+    // Notice two brackets at the beginning and end. The outer ones
+    // encapsulate the whole ternary expression. This preserves the
+    // order of precedence when ternary expressions are used in a
+    // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
+    out << "((";
+    node->getCondition()->traverse(this);
+    out << ") ? (";
+    node->getTrueExpression()->traverse(this);
+    out << ") : (";
+    node->getFalseExpression()->traverse(this);
+    out << "))";
+    return false;
+}
+
 bool TOutputGLSLBase::visitSelection(Visit visit, TIntermSelection *node)
 {
     TInfoSinkBase &out = objSink();
 
-    if (node->usesTernaryOperator())
-    {
-        // Notice two brackets at the beginning and end. The outer ones
-        // encapsulate the whole ternary expression. This preserves the
-        // order of precedence when ternary expressions are used in a
-        // compound expression, i.e., c = 2 * (a < b ? 1 : 2).
-        out << "((";
-        node->getCondition()->traverse(this);
-        out << ") ? (";
-        node->getTrueBlock()->traverse(this);
-        out << ") : (";
-        node->getFalseBlock()->traverse(this);
-        out << "))";
-    }
-    else
-    {
-        out << "if (";
-        node->getCondition()->traverse(this);
-        out << ")\n";
+    out << "if (";
+    node->getCondition()->traverse(this);
+    out << ")\n";
 
-        incrementDepth(node);
-        visitCodeBlock(node->getTrueBlock());
+    incrementDepth(node);
+    visitCodeBlock(node->getTrueBlock());
 
-        if (node->getFalseBlock())
-        {
-            out << "else\n";
-            visitCodeBlock(node->getFalseBlock());
-        }
-        decrementDepth();
+    if (node->getFalseBlock())
+    {
+        out << "else\n";
+        visitCodeBlock(node->getFalseBlock());
     }
+    decrementDepth();
     return false;
 }
 
diff --git a/src/compiler/translator/OutputGLSLBase.h b/src/compiler/translator/OutputGLSLBase.h
index 29b7104..8fb92aa 100644
--- a/src/compiler/translator/OutputGLSLBase.h
+++ b/src/compiler/translator/OutputGLSLBase.h
@@ -44,6 +44,7 @@
     void visitConstantUnion(TIntermConstantUnion *node) override;
     bool visitBinary(Visit visit, TIntermBinary *node) override;
     bool visitUnary(Visit visit, TIntermUnary *node) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
     bool visitSelection(Visit visit, TIntermSelection *node) override;
     bool visitSwitch(Visit visit, TIntermSwitch *node) override;
     bool visitCase(Visit visit, TIntermCase *node) override;
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 6391782..9d77da2 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -1456,9 +1456,8 @@
                 // case statements into non-empty case statements, disallowing fall-through from them.
                 // Also no need to output ; after selection (if) statements or sequences. This is done just
                 // for code clarity.
-                TIntermSelection *asSelection = (*sit)->getAsSelectionNode();
-                ASSERT(asSelection == nullptr || !asSelection->usesTernaryOperator());
-                if ((*sit)->getAsCaseNode() == nullptr && asSelection == nullptr && !IsSequence(*sit))
+                if ((*sit)->getAsCaseNode() == nullptr && (*sit)->getAsSelectionNode() == nullptr &&
+                    !IsSequence(*sit))
                     out << ";\n";
             }
 
@@ -1982,11 +1981,18 @@
     }
 }
 
+bool OutputHLSL::visitTernary(Visit, TIntermTernary *)
+{
+    // Ternary ops should have been already converted to something else in the AST. HLSL ternary
+    // operator doesn't short-circuit, so it's not the same as the GLSL ternary operator.
+    UNREACHABLE();
+    return false;
+}
+
 bool OutputHLSL::visitSelection(Visit visit, TIntermSelection *node)
 {
     TInfoSinkBase &out = getInfoSink();
 
-    ASSERT(!node->usesTernaryOperator());
     ASSERT(mInsideFunction);
 
     // D3D errors when there is a gradient operation in a loop in an unflattened if.
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index e5204e4..569b64b 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -58,6 +58,7 @@
     void visitConstantUnion(TIntermConstantUnion*);
     bool visitBinary(Visit visit, TIntermBinary*);
     bool visitUnary(Visit visit, TIntermUnary*);
+    bool visitTernary(Visit visit, TIntermTernary *);
     bool visitSelection(Visit visit, TIntermSelection*);
     bool visitSwitch(Visit visit, TIntermSwitch *);
     bool visitCase(Visit visit, TIntermCase *);
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 9a08ddf..e67f0d0 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -3903,34 +3903,35 @@
 }
 
 TIntermTyped *TParseContext::addTernarySelection(TIntermTyped *cond,
-                                                 TIntermTyped *trueBlock,
-                                                 TIntermTyped *falseBlock,
+                                                 TIntermTyped *trueExpression,
+                                                 TIntermTyped *falseExpression,
                                                  const TSourceLoc &loc)
 {
     checkIsScalarBool(loc, cond);
 
-    if (trueBlock->getType() != falseBlock->getType())
+    if (trueExpression->getType() != falseExpression->getType())
     {
-        binaryOpError(loc, ":", trueBlock->getCompleteString(), falseBlock->getCompleteString());
-        return falseBlock;
+        binaryOpError(loc, ":", trueExpression->getCompleteString(),
+                      falseExpression->getCompleteString());
+        return falseExpression;
     }
     // ESSL1 sections 5.2 and 5.7:
     // ESSL3 section 5.7:
     // Ternary operator is not among the operators allowed for structures/arrays.
-    if (trueBlock->isArray() || trueBlock->getBasicType() == EbtStruct)
+    if (trueExpression->isArray() || trueExpression->getBasicType() == EbtStruct)
     {
         error(loc, "ternary operator is not allowed for structures or arrays", ":");
-        return falseBlock;
+        return falseExpression;
     }
     // WebGL2 section 5.26, the following results in an error:
     // "Ternary operator applied to void, arrays, or structs containing arrays"
-    if (mShaderSpec == SH_WEBGL2_SPEC && trueBlock->getBasicType() == EbtVoid)
+    if (mShaderSpec == SH_WEBGL2_SPEC && trueExpression->getBasicType() == EbtVoid)
     {
         error(loc, "ternary operator is not allowed for void", ":");
-        return falseBlock;
+        return falseExpression;
     }
 
-    return intermediate.addSelection(cond, trueBlock, falseBlock, loc);
+    return TIntermediate::AddTernarySelection(cond, trueExpression, falseExpression, loc);
 }
 
 //
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 9ce37dc..2b0320d 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -360,8 +360,10 @@
                                           const TSourceLoc &loc,
                                           bool *fatalError);
 
-    TIntermTyped *addTernarySelection(
-        TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &line);
+    TIntermTyped *addTernarySelection(TIntermTyped *cond,
+                                      TIntermTyped *trueExpression,
+                                      TIntermTyped *falseExpression,
+                                      const TSourceLoc &line);
 
     // TODO(jmadill): make these private
     TIntermediate intermediate;  // to build a parse tree
diff --git a/src/compiler/translator/RemoveSwitchFallThrough.cpp b/src/compiler/translator/RemoveSwitchFallThrough.cpp
index b278b53..191586e 100644
--- a/src/compiler/translator/RemoveSwitchFallThrough.cpp
+++ b/src/compiler/translator/RemoveSwitchFallThrough.cpp
@@ -62,6 +62,13 @@
     return false;
 }
 
+bool RemoveSwitchFallThrough::visitTernary(Visit, TIntermTernary *node)
+{
+    mPreviousCase->getSequence()->push_back(node);
+    mLastStatementWasBreak = false;
+    return false;
+}
+
 bool RemoveSwitchFallThrough::visitSelection(Visit, TIntermSelection *node)
 {
     mPreviousCase->getSequence()->push_back(node);
diff --git a/src/compiler/translator/RemoveSwitchFallThrough.h b/src/compiler/translator/RemoveSwitchFallThrough.h
index db86993..3182797 100644
--- a/src/compiler/translator/RemoveSwitchFallThrough.h
+++ b/src/compiler/translator/RemoveSwitchFallThrough.h
@@ -23,6 +23,7 @@
     void visitConstantUnion(TIntermConstantUnion *node) override;
     bool visitBinary(Visit, TIntermBinary *node) override;
     bool visitUnary(Visit, TIntermUnary *node) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
     bool visitSelection(Visit visit, TIntermSelection *node) override;
     bool visitSwitch(Visit, TIntermSwitch *node) override;
     bool visitCase(Visit, TIntermCase *node) override;
diff --git a/src/compiler/translator/SimplifyLoopConditions.cpp b/src/compiler/translator/SimplifyLoopConditions.cpp
index 61a68fc..da8da48 100644
--- a/src/compiler/translator/SimplifyLoopConditions.cpp
+++ b/src/compiler/translator/SimplifyLoopConditions.cpp
@@ -36,7 +36,7 @@
 
     bool visitBinary(Visit visit, TIntermBinary *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
-    bool visitSelection(Visit visit, TIntermSelection *node) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
 
     void nextIteration();
     bool foundLoopToChange() const { return mFoundLoopToChange; }
@@ -100,14 +100,14 @@
     return !mFoundLoopToChange;
 }
 
-bool SimplifyLoopConditionsTraverser::visitSelection(Visit visit, TIntermSelection *node)
+bool SimplifyLoopConditionsTraverser::visitTernary(Visit visit, TIntermTernary *node)
 {
     if (mFoundLoopToChange)
         return false;
 
     // Don't traverse ternary operators outside loop conditions.
     if (!mInsideLoopConditionOrExpression)
-        return !node->usesTernaryOperator();
+        return false;
 
     mFoundLoopToChange = mConditionsToSimplify.match(node);
     return !mFoundLoopToChange;
diff --git a/src/compiler/translator/SplitSequenceOperator.cpp b/src/compiler/translator/SplitSequenceOperator.cpp
index 45fc8dc..ff6443d 100644
--- a/src/compiler/translator/SplitSequenceOperator.cpp
+++ b/src/compiler/translator/SplitSequenceOperator.cpp
@@ -26,7 +26,7 @@
 
     bool visitBinary(Visit visit, TIntermBinary *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
-    bool visitSelection(Visit visit, TIntermSelection *node) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
 
     void nextIteration();
     bool foundExpressionToSplit() const { return mFoundExpressionToSplit; }
@@ -123,7 +123,7 @@
     return true;
 }
 
-bool SplitSequenceOperatorTraverser::visitSelection(Visit visit, TIntermSelection *node)
+bool SplitSequenceOperatorTraverser::visitTernary(Visit visit, TIntermTernary *node)
 {
     if (mFoundExpressionToSplit)
         return false;
diff --git a/src/compiler/translator/UnfoldShortCircuitAST.cpp b/src/compiler/translator/UnfoldShortCircuitAST.cpp
index 3ef555f..e57681e 100644
--- a/src/compiler/translator/UnfoldShortCircuitAST.cpp
+++ b/src/compiler/translator/UnfoldShortCircuitAST.cpp
@@ -10,32 +10,30 @@
 {
 
 // "x || y" is equivalent to "x ? true : y".
-TIntermSelection *UnfoldOR(TIntermTyped *x, TIntermTyped *y)
+TIntermTernary *UnfoldOR(TIntermTyped *x, TIntermTyped *y)
 {
-    const TType boolType(EbtBool, EbpUndefined);
     TConstantUnion *u = new TConstantUnion;
     u->setBConst(true);
     TIntermConstantUnion *trueNode = new TIntermConstantUnion(
         u, TType(EbtBool, EbpUndefined, EvqConst, 1));
-    return new TIntermSelection(x, trueNode, y, boolType);
+    return new TIntermTernary(x, trueNode, y);
 }
 
 // "x && y" is equivalent to "x ? y : false".
-TIntermSelection *UnfoldAND(TIntermTyped *x, TIntermTyped *y)
+TIntermTernary *UnfoldAND(TIntermTyped *x, TIntermTyped *y)
 {
-    const TType boolType(EbtBool, EbpUndefined);
     TConstantUnion *u = new TConstantUnion;
     u->setBConst(false);
     TIntermConstantUnion *falseNode = new TIntermConstantUnion(
         u, TType(EbtBool, EbpUndefined, EvqConst, 1));
-    return new TIntermSelection(x, y, falseNode, boolType);
+    return new TIntermTernary(x, y, falseNode);
 }
 
 }  // namespace anonymous
 
 bool UnfoldShortCircuitAST::visitBinary(Visit visit, TIntermBinary *node)
 {
-    TIntermSelection *replacement = NULL;
+    TIntermTernary *replacement = nullptr;
 
     switch (node->getOp())
     {
diff --git a/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/src/compiler/translator/UnfoldShortCircuitToIf.cpp
index cdac70a..6503194 100644
--- a/src/compiler/translator/UnfoldShortCircuitToIf.cpp
+++ b/src/compiler/translator/UnfoldShortCircuitToIf.cpp
@@ -23,7 +23,7 @@
     UnfoldShortCircuitTraverser();
 
     bool visitBinary(Visit visit, TIntermBinary *node) override;
-    bool visitSelection(Visit visit, TIntermSelection *node) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
 
     void nextIteration();
     bool foundShortCircuit() const { return mFoundShortCircuit; }
@@ -118,7 +118,7 @@
     }
 }
 
-bool UnfoldShortCircuitTraverser::visitSelection(Visit visit, TIntermSelection *node)
+bool UnfoldShortCircuitTraverser::visitTernary(Visit visit, TIntermTernary *node)
 {
     if (mFoundShortCircuit)
         return false;
@@ -131,8 +131,6 @@
 
     mFoundShortCircuit = true;
 
-    ASSERT(node->usesTernaryOperator());
-
     // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
     TIntermSequence insertions;
 
@@ -142,11 +140,11 @@
     insertions.push_back(tempDeclaration);
 
     TIntermAggregate *trueBlock   = new TIntermAggregate(EOpSequence);
-    TIntermBinary *trueAssignment = createTempAssignment(node->getTrueBlock()->getAsTyped());
+    TIntermBinary *trueAssignment = createTempAssignment(node->getTrueExpression());
     trueBlock->getSequence()->push_back(trueAssignment);
 
     TIntermAggregate *falseBlock   = new TIntermAggregate(EOpSequence);
-    TIntermBinary *falseAssignment = createTempAssignment(node->getFalseBlock()->getAsTyped());
+    TIntermBinary *falseAssignment = createTempAssignment(node->getFalseExpression());
     falseBlock->getSequence()->push_back(falseAssignment);
 
     TIntermSelection *ifNode =
diff --git a/src/compiler/translator/ValidateSwitch.cpp b/src/compiler/translator/ValidateSwitch.cpp
index 9a4ed33..9fe9b5d 100644
--- a/src/compiler/translator/ValidateSwitch.cpp
+++ b/src/compiler/translator/ValidateSwitch.cpp
@@ -59,6 +59,14 @@
 {
     if (!mFirstCaseFound)
         mStatementBeforeCase = true;
+    mLastStatementWasCase    = false;
+    return true;
+}
+
+bool ValidateSwitch::visitTernary(Visit, TIntermTernary *)
+{
+    if (!mFirstCaseFound)
+        mStatementBeforeCase = true;
     mLastStatementWasCase = false;
     return true;
 }
diff --git a/src/compiler/translator/ValidateSwitch.h b/src/compiler/translator/ValidateSwitch.h
index ddbefc5..b20ee75 100644
--- a/src/compiler/translator/ValidateSwitch.h
+++ b/src/compiler/translator/ValidateSwitch.h
@@ -23,6 +23,7 @@
     void visitConstantUnion(TIntermConstantUnion *) override;
     bool visitBinary(Visit, TIntermBinary *) override;
     bool visitUnary(Visit, TIntermUnary *) override;
+    bool visitTernary(Visit, TIntermTernary *) override;
     bool visitSelection(Visit visit, TIntermSelection *) override;
     bool visitSwitch(Visit, TIntermSwitch *) override;
     bool visitCase(Visit, TIntermCase *node) override;
diff --git a/src/compiler/translator/intermOut.cpp b/src/compiler/translator/intermOut.cpp
index 6dca547..942eadc 100644
--- a/src/compiler/translator/intermOut.cpp
+++ b/src/compiler/translator/intermOut.cpp
@@ -44,10 +44,12 @@
     void visitConstantUnion(TIntermConstantUnion *) override;
     bool visitBinary(Visit visit, TIntermBinary *) override;
     bool visitUnary(Visit visit, TIntermUnary *) override;
+    bool visitTernary(Visit visit, TIntermTernary *node) override;
     bool visitSelection(Visit visit, TIntermSelection *) override;
     bool visitAggregate(Visit visit, TIntermAggregate *) override;
     bool visitLoop(Visit visit, TIntermLoop *) override;
     bool visitBranch(Visit visit, TIntermBranch *) override;
+    // TODO: Add missing visit functions
 };
 
 //
@@ -457,14 +459,46 @@
     return true;
 }
 
+bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
+{
+    TInfoSinkBase &out = sink;
+
+    OutputTreeText(out, node, mDepth);
+
+    out << "Ternary selection";
+    out << " (" << node->getCompleteString() << ")\n";
+
+    ++mDepth;
+
+    OutputTreeText(sink, node, mDepth);
+    out << "Condition\n";
+    node->getCondition()->traverse(this);
+
+    OutputTreeText(sink, node, mDepth);
+    if (node->getTrueExpression())
+    {
+        out << "true case\n";
+        node->getTrueExpression()->traverse(this);
+    }
+    if (node->getFalseExpression())
+    {
+        OutputTreeText(sink, node, mDepth);
+        out << "false case\n";
+        node->getFalseExpression()->traverse(this);
+    }
+
+    --mDepth;
+
+    return false;
+}
+
 bool TOutputTraverser::visitSelection(Visit visit, TIntermSelection *node)
 {
     TInfoSinkBase &out = sink;
 
     OutputTreeText(out, node, mDepth);
 
-    out << "Test condition and select";
-    out << " (" << node->getCompleteString() << ")\n";
+    out << "If test\n";
 
     ++mDepth;