Clean up unary and aggregate math folding

Prefer asserts instead of adding internal errors to the compiler log
when types of arguments are not as expected or the folding function
runs into an operation it can't handle. Neither of these cases should
be possible, the checks for correct argument types are solid at this
point.

In the future, when new built-in functions are added, constant folding
support for them should be added as well.

foldUnaryWithDifferentReturnType and foldUnaryWithSameReturnType are
renamed to foldUnaryNonComponentWise and foldUnaryComponentWise
respectively. These names better reflect what these functions are
doing.

The info sink member is removed from TIntermediate, since TDiagnostics
is now passed into the functions that may generate warnings instead.

BUG=angleproject:1490
TEST=angle_unittests

Change-Id: I6a08abbe29cf23f3a318032fdc46dd3dbaf4410e
Reviewed-on: https://chromium-review.googlesource.com/377959
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/Compiler.cpp b/src/compiler/translator/Compiler.cpp
index aba357c..1f1f831 100644
--- a/src/compiler/translator/Compiler.cpp
+++ b/src/compiler/translator/Compiler.cpp
@@ -219,8 +219,7 @@
         ++firstSource;
     }
 
-    TIntermediate intermediate(infoSink);
-    TParseContext parseContext(symbolTable, extensionBehavior, intermediate, shaderType, shaderSpec,
+    TParseContext parseContext(symbolTable, extensionBehavior, shaderType, shaderSpec,
                                compileOptions, true, infoSink, getResources());
 
     parseContext.setFragmentPrecisionHighOnESSL1(fragmentPrecisionHigh);
@@ -254,7 +253,7 @@
         mComputeShaderLocalSize         = parseContext.getComputeShaderLocalSize();
 
         root = parseContext.getTreeRoot();
-        root = intermediate.postProcess(root);
+        root = TIntermediate::PostProcess(root);
 
         // Highp might have been auto-enabled based on shader version
         fragmentPrecisionHigh = parseContext.getFragmentPrecisionHigh();
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index b00022a..7588fbe 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -44,13 +44,14 @@
     return constUnion;
 }
 
-void UndefinedConstantFoldingError(const TSourceLoc &loc, TOperator op, TBasicType basicType,
-                                   TInfoSink &infoSink, TConstantUnion *result)
+void UndefinedConstantFoldingError(const TSourceLoc &loc,
+                                   TOperator op,
+                                   TBasicType basicType,
+                                   TDiagnostics *diagnostics,
+                                   TConstantUnion *result)
 {
-    std::stringstream constantFoldingErrorStream;
-    constantFoldingErrorStream << "'" << GetOperatorString(op)
-                               << "' operation result is undefined for the values passed in";
-    infoSink.info.message(EPrefixWarning, loc, constantFoldingErrorStream.str().c_str());
+    diagnostics->warning(loc, "operation result is undefined for the values passed in",
+                         GetOperatorString(op), "");
 
     switch (basicType)
     {
@@ -856,7 +857,7 @@
     return CreateFoldedNode(constArray, this, resultQualifier);
 }
 
-TIntermTyped *TIntermUnary::fold(TInfoSink &infoSink)
+TIntermTyped *TIntermUnary::fold(TDiagnostics *diagnostics)
 {
     TIntermConstantUnion *operandConstant = mOperand->getAsConstantUnion();
     if (operandConstant == nullptr)
@@ -879,11 +880,11 @@
       case EOpUnpackUnorm2x16:
       case EOpPackHalf2x16:
       case EOpUnpackHalf2x16:
-        constArray = operandConstant->foldUnaryWithDifferentReturnType(mOp, infoSink);
-        break;
+          constArray = operandConstant->foldUnaryNonComponentWise(mOp);
+          break;
       default:
-        constArray = operandConstant->foldUnaryWithSameReturnType(mOp, infoSink);
-        break;
+          constArray = operandConstant->foldUnaryComponentWise(mOp, diagnostics);
+          break;
     }
 
     // Nodes may be constant folded without being qualified as constant.
@@ -891,7 +892,7 @@
     return CreateFoldedNode(constArray, this, resultQualifier);
 }
 
-TIntermTyped *TIntermAggregate::fold(TInfoSink &infoSink)
+TIntermTyped *TIntermAggregate::fold(TDiagnostics *diagnostics)
 {
     // Make sure that all params are constant before actual constant folding.
     for (auto *param : *getSequence())
@@ -903,9 +904,9 @@
     }
     TConstantUnion *constArray = nullptr;
     if (isConstructor())
-        constArray = TIntermConstantUnion::FoldAggregateConstructor(this, infoSink);
+        constArray = TIntermConstantUnion::FoldAggregateConstructor(this);
     else
-        constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, infoSink);
+        constArray = TIntermConstantUnion::FoldAggregateBuiltIn(this, diagnostics);
 
     // Nodes may be constant folded without being qualified as constant.
     TQualifier resultQualifier = areChildrenConstQualified() ? EvqConst : EvqTemporary;
@@ -925,10 +926,7 @@
     const TConstantUnion *leftArray  = getUnionArrayPointer();
     const TConstantUnion *rightArray = rightNode->getUnionArrayPointer();
 
-    if (!leftArray)
-        return nullptr;
-    if (!rightArray)
-        return nullptr;
+    ASSERT(leftArray && rightArray);
 
     size_t objectSize = getType().getObjectSize();
 
@@ -1224,29 +1222,22 @@
     return resultArray;
 }
 
-//
-// The fold functions see if an operation on a constant can be done in place,
-// without generating run-time code.
-//
-// Returns the constant value to keep using or nullptr.
-//
-TConstantUnion *TIntermConstantUnion::foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink)
+// The fold functions do operations on a constant at GLSL compile time, without generating run-time
+// code. Returns the constant value to keep using. Nullptr should not be returned.
+TConstantUnion *TIntermConstantUnion::foldUnaryNonComponentWise(TOperator op)
 {
-    //
-    // Do operations where the return type has a different number of components compared to the operand type.
-    //
+    // Do operations where the return type may have a different number of components compared to the
+    // operand type.
 
     const TConstantUnion *operandArray = getUnionArrayPointer();
-    if (!operandArray)
-        return nullptr;
+    ASSERT(operandArray);
 
     size_t objectSize = getType().getObjectSize();
     TConstantUnion *resultArray = nullptr;
     switch (op)
     {
-      case EOpAny:
-        if (getType().getBasicType() == EbtBool)
-        {
+        case EOpAny:
+            ASSERT(getType().getBasicType() == EbtBool);
             resultArray = new TConstantUnion();
             resultArray->setBConst(false);
             for (size_t i = 0; i < objectSize; i++)
@@ -1258,16 +1249,9 @@
                 }
             }
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpAll:
-        if (getType().getBasicType() == EbtBool)
-        {
+        case EOpAll:
+            ASSERT(getType().getBasicType() == EbtBool);
             resultArray = new TConstantUnion();
             resultArray->setBConst(true);
             for (size_t i = 0; i < objectSize; i++)
@@ -1279,89 +1263,55 @@
                 }
             }
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpLength:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpLength:
+            ASSERT(getType().getBasicType() == EbtFloat);
             resultArray = new TConstantUnion();
             resultArray->setFConst(VectorLength(operandArray, objectSize));
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpTranspose:
-        if (getType().getBasicType() == EbtFloat)
+        case EOpTranspose:
         {
+            ASSERT(getType().getBasicType() == EbtFloat);
             resultArray = new TConstantUnion[objectSize];
             angle::Matrix<float> result =
                 GetMatrix(operandArray, getType().getRows(), getType().getCols()).transpose();
             SetUnionArrayFromMatrix(result, resultArray);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpDeterminant:
-        if (getType().getBasicType() == EbtFloat)
+        case EOpDeterminant:
         {
+            ASSERT(getType().getBasicType() == EbtFloat);
             unsigned int size = getType().getNominalSize();
             ASSERT(size >= 2 && size <= 4);
             resultArray = new TConstantUnion();
             resultArray->setFConst(GetMatrix(operandArray, size).determinant());
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpInverse:
-        if (getType().getBasicType() == EbtFloat)
+        case EOpInverse:
         {
+            ASSERT(getType().getBasicType() == EbtFloat);
             unsigned int size = getType().getNominalSize();
             ASSERT(size >= 2 && size <= 4);
-            resultArray = new TConstantUnion[objectSize];
+            resultArray                 = new TConstantUnion[objectSize];
             angle::Matrix<float> result = GetMatrix(operandArray, size).inverse();
             SetUnionArrayFromMatrix(result, resultArray);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpPackSnorm2x16:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpPackSnorm2x16:
+            ASSERT(getType().getBasicType() == EbtFloat);
             ASSERT(getType().getNominalSize() == 2);
             resultArray = new TConstantUnion();
-            resultArray->setUConst(gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+            resultArray->setUConst(
+                gl::packSnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpUnpackSnorm2x16:
-        if (getType().getBasicType() == EbtUInt)
+        case EOpUnpackSnorm2x16:
         {
+            ASSERT(getType().getBasicType() == EbtUInt);
             resultArray = new TConstantUnion[2];
             float f1, f2;
             gl::unpackSnorm2x16(operandArray[0].getUConst(), &f1, &f2);
@@ -1369,29 +1319,18 @@
             resultArray[1].setFConst(f2);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpPackUnorm2x16:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpPackUnorm2x16:
+            ASSERT(getType().getBasicType() == EbtFloat);
             ASSERT(getType().getNominalSize() == 2);
             resultArray = new TConstantUnion();
-            resultArray->setUConst(gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+            resultArray->setUConst(
+                gl::packUnorm2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpUnpackUnorm2x16:
-        if (getType().getBasicType() == EbtUInt)
+        case EOpUnpackUnorm2x16:
         {
+            ASSERT(getType().getBasicType() == EbtUInt);
             resultArray = new TConstantUnion[2];
             float f1, f2;
             gl::unpackUnorm2x16(operandArray[0].getUConst(), &f1, &f2);
@@ -1399,29 +1338,18 @@
             resultArray[1].setFConst(f2);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpPackHalf2x16:
-        if (getType().getBasicType() == EbtFloat)
-        {
+        case EOpPackHalf2x16:
+            ASSERT(getType().getBasicType() == EbtFloat);
             ASSERT(getType().getNominalSize() == 2);
             resultArray = new TConstantUnion();
-            resultArray->setUConst(gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
+            resultArray->setUConst(
+                gl::packHalf2x16(operandArray[0].getFConst(), operandArray[1].getFConst()));
             break;
-        }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
 
-      case EOpUnpackHalf2x16:
-        if (getType().getBasicType() == EbtUInt)
+        case EOpUnpackHalf2x16:
         {
+            ASSERT(getType().getBasicType() == EbtUInt);
             resultArray = new TConstantUnion[2];
             float f1, f2;
             gl::unpackHalf2x16(operandArray[0].getUConst(), &f1, &f2);
@@ -1429,29 +1357,24 @@
             resultArray[1].setFConst(f2);
             break;
         }
-        else
-        {
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
-        }
-        break;
 
-      default:
-        break;
+        default:
+            UNREACHABLE();
+            break;
     }
 
     return resultArray;
 }
 
-TConstantUnion *TIntermConstantUnion::foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::foldUnaryComponentWise(TOperator op,
+                                                             TDiagnostics *diagnostics)
 {
-    //
-    // Do unary operations where the return type is the same as operand type.
-    //
+    // Do unary operations where each component of the result is computed based on the corresponding
+    // component of the operand. Also folds normalize, though the divisor in that case takes all
+    // components into account.
 
     const TConstantUnion *operandArray = getUnionArrayPointer();
-    if (!operandArray)
-        return nullptr;
+    ASSERT(operandArray);
 
     size_t objectSize = getType().getObjectSize();
 
@@ -1460,243 +1383,213 @@
     {
         switch(op)
         {
-          case EOpNegative:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
-                resultArray[i].setFConst(-operandArray[i].getFConst());
+            case EOpNegative:
+                switch (getType().getBasicType())
+                {
+                    case EbtFloat:
+                        resultArray[i].setFConst(-operandArray[i].getFConst());
+                        break;
+                    case EbtInt:
+                        resultArray[i].setIConst(-operandArray[i].getIConst());
+                        break;
+                    case EbtUInt:
+                        resultArray[i].setUConst(static_cast<unsigned int>(
+                            -static_cast<int>(operandArray[i].getUConst())));
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              case EbtInt:
-                resultArray[i].setIConst(-operandArray[i].getIConst());
-                break;
-              case EbtUInt:
-                resultArray[i].setUConst(static_cast<unsigned int>(
-                    -static_cast<int>(operandArray[i].getUConst())));
-                break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpPositive:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
-                resultArray[i].setFConst(operandArray[i].getFConst());
+            case EOpPositive:
+                switch (getType().getBasicType())
+                {
+                    case EbtFloat:
+                        resultArray[i].setFConst(operandArray[i].getFConst());
+                        break;
+                    case EbtInt:
+                        resultArray[i].setIConst(operandArray[i].getIConst());
+                        break;
+                    case EbtUInt:
+                        resultArray[i].setUConst(static_cast<unsigned int>(
+                            static_cast<int>(operandArray[i].getUConst())));
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              case EbtInt:
-                resultArray[i].setIConst(operandArray[i].getIConst());
-                break;
-              case EbtUInt:
-                resultArray[i].setUConst(static_cast<unsigned int>(
-                    static_cast<int>(operandArray[i].getUConst())));
-                break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpLogicalNot:
-            // this code is written for possible future use,
-            // will not get executed currently
-            switch (getType().getBasicType())
-            {
-              case EbtBool:
-                resultArray[i].setBConst(!operandArray[i].getBConst());
+            case EOpLogicalNot:
+                switch (getType().getBasicType())
+                {
+                    case EbtBool:
+                        resultArray[i].setBConst(!operandArray[i].getBConst());
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpBitwiseNot:
-            switch (getType().getBasicType())
-            {
-              case EbtInt:
-                resultArray[i].setIConst(~operandArray[i].getIConst());
+            case EOpBitwiseNot:
+                switch (getType().getBasicType())
+                {
+                    case EbtInt:
+                        resultArray[i].setIConst(~operandArray[i].getIConst());
+                        break;
+                    case EbtUInt:
+                        resultArray[i].setUConst(~operandArray[i].getUConst());
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
+                }
                 break;
-              case EbtUInt:
-                resultArray[i].setUConst(~operandArray[i].getUConst());
-                break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpRadians:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpRadians:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setFConst(kDegreesToRadiansMultiplier * operandArray[i].getFConst());
                 break;
-            }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpDegrees:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpDegrees:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setFConst(kRadiansToDegreesMultiplier * operandArray[i].getFConst());
                 break;
-            }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpSin:
-            if (!foldFloatTypeUnary(operandArray[i], &sinf, infoSink, &resultArray[i]))
-               return nullptr;
-            break;
-
-          case EOpCos:
-            if (!foldFloatTypeUnary(operandArray[i], &cosf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpTan:
-            if (!foldFloatTypeUnary(operandArray[i], &tanf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAsin:
-            // For asin(x), results are undefined if |x| > 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &asinf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAcos:
-            // For acos(x), results are undefined if |x| > 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) > 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &acosf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAtan:
-            if (!foldFloatTypeUnary(operandArray[i], &atanf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpSinh:
-            if (!foldFloatTypeUnary(operandArray[i], &sinhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpCosh:
-            if (!foldFloatTypeUnary(operandArray[i], &coshf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpTanh:
-            if (!foldFloatTypeUnary(operandArray[i], &tanhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAsinh:
-            if (!foldFloatTypeUnary(operandArray[i], &asinhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAcosh:
-            // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &acoshf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAtanh:
-            // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && fabsf(operandArray[i].getFConst()) >= 1.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &atanhf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
-
-          case EOpAbs:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
-                resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
+            case EOpSin:
+                foldFloatTypeUnary(operandArray[i], &sinf, &resultArray[i]);
                 break;
-              case EbtInt:
-                resultArray[i].setIConst(abs(operandArray[i].getIConst()));
-                break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpSign:
-            switch (getType().getBasicType())
-            {
-              case EbtFloat:
+            case EOpCos:
+                foldFloatTypeUnary(operandArray[i], &cosf, &resultArray[i]);
+                break;
+
+            case EOpTan:
+                foldFloatTypeUnary(operandArray[i], &tanf, &resultArray[i]);
+                break;
+
+            case EOpAsin:
+                // For asin(x), results are undefined if |x| > 1, we are choosing to set result to
+                // 0.
+                if (fabsf(operandArray[i].getFConst()) > 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &asinf, &resultArray[i]);
+                break;
+
+            case EOpAcos:
+                // For acos(x), results are undefined if |x| > 1, we are choosing to set result to
+                // 0.
+                if (fabsf(operandArray[i].getFConst()) > 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &acosf, &resultArray[i]);
+                break;
+
+            case EOpAtan:
+                foldFloatTypeUnary(operandArray[i], &atanf, &resultArray[i]);
+                break;
+
+            case EOpSinh:
+                foldFloatTypeUnary(operandArray[i], &sinhf, &resultArray[i]);
+                break;
+
+            case EOpCosh:
+                foldFloatTypeUnary(operandArray[i], &coshf, &resultArray[i]);
+                break;
+
+            case EOpTanh:
+                foldFloatTypeUnary(operandArray[i], &tanhf, &resultArray[i]);
+                break;
+
+            case EOpAsinh:
+                foldFloatTypeUnary(operandArray[i], &asinhf, &resultArray[i]);
+                break;
+
+            case EOpAcosh:
+                // For acosh(x), results are undefined if x < 1, we are choosing to set result to 0.
+                if (operandArray[i].getFConst() < 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &acoshf, &resultArray[i]);
+                break;
+
+            case EOpAtanh:
+                // For atanh(x), results are undefined if |x| >= 1, we are choosing to set result to
+                // 0.
+                if (fabsf(operandArray[i].getFConst()) >= 1.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &atanhf, &resultArray[i]);
+                break;
+
+            case EOpAbs:
+                switch (getType().getBasicType())
                 {
-                    float fConst = operandArray[i].getFConst();
-                    float fResult = 0.0f;
-                    if (fConst > 0.0f)
-                        fResult = 1.0f;
-                    else if (fConst < 0.0f)
-                        fResult = -1.0f;
-                    resultArray[i].setFConst(fResult);
+                    case EbtFloat:
+                        resultArray[i].setFConst(fabsf(operandArray[i].getFConst()));
+                        break;
+                    case EbtInt:
+                        resultArray[i].setIConst(abs(operandArray[i].getIConst()));
+                        break;
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
                 }
                 break;
-              case EbtInt:
+
+            case EOpSign:
+                switch (getType().getBasicType())
                 {
-                    int iConst = operandArray[i].getIConst();
-                    int iResult = 0;
-                    if (iConst > 0)
-                        iResult = 1;
-                    else if (iConst < 0)
-                        iResult = -1;
-                    resultArray[i].setIConst(iResult);
+                    case EbtFloat:
+                    {
+                        float fConst  = operandArray[i].getFConst();
+                        float fResult = 0.0f;
+                        if (fConst > 0.0f)
+                            fResult = 1.0f;
+                        else if (fConst < 0.0f)
+                            fResult = -1.0f;
+                        resultArray[i].setFConst(fResult);
+                        break;
+                    }
+                    case EbtInt:
+                    {
+                        int iConst  = operandArray[i].getIConst();
+                        int iResult = 0;
+                        if (iConst > 0)
+                            iResult = 1;
+                        else if (iConst < 0)
+                            iResult = -1;
+                        resultArray[i].setIConst(iResult);
+                        break;
+                    }
+                    default:
+                        UNREACHABLE();
+                        return nullptr;
                 }
                 break;
-              default:
-                infoSink.info.message(
-                    EPrefixInternalError, getLine(),
-                    "Unary operation not folded into constant");
-                return nullptr;
-            }
-            break;
 
-          case EOpFloor:
-            if (!foldFloatTypeUnary(operandArray[i], &floorf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpFloor:
+                foldFloatTypeUnary(operandArray[i], &floorf, &resultArray[i]);
+                break;
 
-          case EOpTrunc:
-            if (!foldFloatTypeUnary(operandArray[i], &truncf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpTrunc:
+                foldFloatTypeUnary(operandArray[i], &truncf, &resultArray[i]);
+                break;
 
-          case EOpRound:
-            if (!foldFloatTypeUnary(operandArray[i], &roundf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpRound:
+                foldFloatTypeUnary(operandArray[i], &roundf, &resultArray[i]);
+                break;
 
-          case EOpRoundEven:
-            if (getType().getBasicType() == EbtFloat)
+            case EOpRoundEven:
             {
+                ASSERT(getType().getBasicType() == EbtFloat);
                 float x = operandArray[i].getFConst();
                 float result;
                 float fractPart = modff(x, &result);
@@ -1707,197 +1600,151 @@
                 resultArray[i].setFConst(result);
                 break;
             }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpCeil:
-            if (!foldFloatTypeUnary(operandArray[i], &ceilf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpCeil:
+                foldFloatTypeUnary(operandArray[i], &ceilf, &resultArray[i]);
+                break;
 
-          case EOpFract:
-            if (getType().getBasicType() == EbtFloat)
+            case EOpFract:
             {
+                ASSERT(getType().getBasicType() == EbtFloat);
                 float x = operandArray[i].getFConst();
                 resultArray[i].setFConst(x - floorf(x));
                 break;
             }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpIsNan:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpIsNan:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setBConst(gl::isNaN(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpIsInf:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpIsInf:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setBConst(gl::isInf(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpFloatBitsToInt:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpFloatBitsToInt:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setIConst(gl::bitCast<int32_t>(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpFloatBitsToUint:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpFloatBitsToUint:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 resultArray[i].setUConst(gl::bitCast<uint32_t>(operandArray[0].getFConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpIntBitsToFloat:
-            if (getType().getBasicType() == EbtInt)
-            {
+            case EOpIntBitsToFloat:
+                ASSERT(getType().getBasicType() == EbtInt);
                 resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getIConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpUintBitsToFloat:
-            if (getType().getBasicType() == EbtUInt)
-            {
+            case EOpUintBitsToFloat:
+                ASSERT(getType().getBasicType() == EbtUInt);
                 resultArray[i].setFConst(gl::bitCast<float>(operandArray[0].getUConst()));
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpExp:
-            if (!foldFloatTypeUnary(operandArray[i], &expf, infoSink, &resultArray[i]))
-              return nullptr;
-            break;
+            case EOpExp:
+                foldFloatTypeUnary(operandArray[i], &expf, &resultArray[i]);
+                break;
 
-          case EOpLog:
-            // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpLog:
+                // For log(x), results are undefined if x <= 0, we are choosing to set result to 0.
+                if (operandArray[i].getFConst() <= 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+                break;
 
-          case EOpExp2:
-            if (!foldFloatTypeUnary(operandArray[i], &exp2f, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpExp2:
+                foldFloatTypeUnary(operandArray[i], &exp2f, &resultArray[i]);
+                break;
 
-          case EOpLog2:
-            // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
-            // And log2f is not available on some plarforms like old android, so just using log(x)/log(2) here.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &logf, infoSink, &resultArray[i]))
-                return nullptr;
-            else
-                resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
-            break;
+            case EOpLog2:
+                // For log2(x), results are undefined if x <= 0, we are choosing to set result to 0.
+                // And log2f is not available on some plarforms like old android, so just using
+                // log(x)/log(2) here.
+                if (operandArray[i].getFConst() <= 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                {
+                    foldFloatTypeUnary(operandArray[i], &logf, &resultArray[i]);
+                    resultArray[i].setFConst(resultArray[i].getFConst() / logf(2.0f));
+                }
+                break;
 
-          case EOpSqrt:
-            // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() < 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
-                return nullptr;
-            break;
+            case EOpSqrt:
+                // For sqrt(x), results are undefined if x < 0, we are choosing to set result to 0.
+                if (operandArray[i].getFConst() < 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                    foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+                break;
 
-          case EOpInverseSqrt:
-            // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
-            // so getting the square root first using builtin function sqrt() and then taking its inverse.
-            // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set result to 0.
-            if (getType().getBasicType() == EbtFloat && operandArray[i].getFConst() <= 0.0f)
-                UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink, &resultArray[i]);
-            else if (!foldFloatTypeUnary(operandArray[i], &sqrtf, infoSink, &resultArray[i]))
-                return nullptr;
-            else
-                resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
-            break;
+            case EOpInverseSqrt:
+                // There is no stdlib built-in function equavalent for GLES built-in inversesqrt(),
+                // so getting the square root first using builtin function sqrt() and then taking
+                // its inverse.
+                // Also, for inversesqrt(x), results are undefined if x <= 0, we are choosing to set
+                // result to 0.
+                if (operandArray[i].getFConst() <= 0.0f)
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
+                else
+                {
+                    foldFloatTypeUnary(operandArray[i], &sqrtf, &resultArray[i]);
+                    resultArray[i].setFConst(1.0f / resultArray[i].getFConst());
+                }
+                break;
 
-          case EOpVectorLogicalNot:
-            if (getType().getBasicType() == EbtBool)
-            {
+            case EOpVectorLogicalNot:
+                ASSERT(getType().getBasicType() == EbtBool);
                 resultArray[i].setBConst(!operandArray[i].getBConst());
                 break;
-            }
-            infoSink.info.message(
-                EPrefixInternalError, getLine(),
-                "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpNormalize:
-            if (getType().getBasicType() == EbtFloat)
+            case EOpNormalize:
             {
-                float x = operandArray[i].getFConst();
+                ASSERT(getType().getBasicType() == EbtFloat);
+                float x      = operandArray[i].getFConst();
                 float length = VectorLength(operandArray, objectSize);
                 if (length)
                     resultArray[i].setFConst(x / length);
                 else
-                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(), infoSink,
-                                                  &resultArray[i]);
+                    UndefinedConstantFoldingError(getLine(), op, getType().getBasicType(),
+                                                  diagnostics, &resultArray[i]);
                 break;
             }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          case EOpDFdx:
-          case EOpDFdy:
-          case EOpFwidth:
-            if (getType().getBasicType() == EbtFloat)
-            {
+            case EOpDFdx:
+            case EOpDFdy:
+            case EOpFwidth:
+                ASSERT(getType().getBasicType() == EbtFloat);
                 // Derivatives of constant arguments should be 0.
                 resultArray[i].setFConst(0.0f);
                 break;
-            }
-            infoSink.info.message(EPrefixInternalError, getLine(), "Unary operation not folded into constant");
-            return nullptr;
 
-          default:
-            return nullptr;
+            default:
+                return nullptr;
         }
     }
 
     return resultArray;
 }
 
-bool TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc,
-                                              TInfoSink &infoSink, TConstantUnion *result) const
+void TIntermConstantUnion::foldFloatTypeUnary(const TConstantUnion &parameter,
+                                              FloatTypeUnaryFunc builtinFunc,
+                                              TConstantUnion *result) const
 {
     ASSERT(builtinFunc);
 
-    if (getType().getBasicType() == EbtFloat)
-    {
-        result->setFConst(builtinFunc(parameter.getFConst()));
-        return true;
-    }
-
-    infoSink.info.message(
-        EPrefixInternalError, getLine(),
-        "Unary operation not folded into constant");
-    return false;
+    ASSERT(getType().getBasicType() == EbtFloat);
+    result->setFConst(builtinFunc(parameter.getFConst()));
 }
 
 // static
-TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate,
-                                                               TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::FoldAggregateConstructor(TIntermAggregate *aggregate)
 {
     ASSERT(aggregate->getSequence()->size() > 0u);
     size_t resultSize           = aggregate->getType().getObjectSize();
@@ -1996,7 +1843,8 @@
 }
 
 // static
-TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink)
+TConstantUnion *TIntermConstantUnion::FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+                                                           TDiagnostics *diagnostics)
 {
     TOperator op = aggregate->getOp();
     TIntermSequence *sequence = aggregate->getSequence();
@@ -2037,284 +1885,298 @@
         //
         switch (op)
         {
-          case EOpAtan:
+            case EOpAtan:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float y = unionArrays[0][i].getFConst();
-                        float x = unionArrays[1][i].getFConst();
-                        // Results are undefined if x and y are both 0.
-                        if (x == 0.0f && y == 0.0f)
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        else
-                            resultArray[i].setFConst(atan2f(y, x));
-                    }
+                    float y = unionArrays[0][i].getFConst();
+                    float x = unionArrays[1][i].getFConst();
+                    // Results are undefined if x and y are both 0.
+                    if (x == 0.0f && y == 0.0f)
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    else
+                        resultArray[i].setFConst(atan2f(y, x));
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpPow:
+            case EOpPow:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float x = unionArrays[0][i].getFConst();
-                        float y = unionArrays[1][i].getFConst();
-                        // Results are undefined if x < 0.
-                        // Results are undefined if x = 0 and y <= 0.
-                        if (x < 0.0f)
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        else if (x == 0.0f && y <= 0.0f)
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        else
-                            resultArray[i].setFConst(powf(x, y));
-                    }
+                    float x = unionArrays[0][i].getFConst();
+                    float y = unionArrays[1][i].getFConst();
+                    // Results are undefined if x < 0.
+                    // Results are undefined if x = 0 and y <= 0.
+                    if (x < 0.0f)
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    else if (x == 0.0f && y <= 0.0f)
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    else
+                        resultArray[i].setFConst(powf(x, y));
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpMod:
+            case EOpMod:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                    {
-                        float x = unionArrays[0][i].getFConst();
-                        float y = unionArrays[1][i].getFConst();
-                        resultArray[i].setFConst(x - y * floorf(x / y));
-                    }
+                    float x = unionArrays[0][i].getFConst();
+                    float y = unionArrays[1][i].getFConst();
+                    resultArray[i].setFConst(x - y * floorf(x / y));
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpMin:
+            case EOpMin:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
-                        break;
-                      case EbtInt:
-                        resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setFConst(std::min(unionArrays[0][i].getFConst(),
+                                                              unionArrays[1][i].getFConst()));
+                            break;
+                        case EbtInt:
+                            resultArray[i].setIConst(std::min(unionArrays[0][i].getIConst(),
+                                                              unionArrays[1][i].getIConst()));
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setUConst(std::min(unionArrays[0][i].getUConst(),
+                                                              unionArrays[1][i].getUConst()));
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpMax:
+            case EOpMax:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(), unionArrays[1][i].getFConst()));
-                        break;
-                      case EbtInt:
-                        resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(), unionArrays[1][i].getIConst()));
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(), unionArrays[1][i].getUConst()));
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setFConst(std::max(unionArrays[0][i].getFConst(),
+                                                              unionArrays[1][i].getFConst()));
+                            break;
+                        case EbtInt:
+                            resultArray[i].setIConst(std::max(unionArrays[0][i].getIConst(),
+                                                              unionArrays[1][i].getIConst()));
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setUConst(std::max(unionArrays[0][i].getUConst(),
+                                                              unionArrays[1][i].getUConst()));
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpStep:
+            case EOpStep:
             {
-                if (basicType == EbtFloat)
-                {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
-                        resultArray[i].setFConst(unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f : 1.0f);
-                }
-                else
-                    UNREACHABLE();
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
+                    resultArray[i].setFConst(
+                        unionArrays[1][i].getFConst() < unionArrays[0][i].getFConst() ? 0.0f
+                                                                                      : 1.0f);
+                break;
             }
-            break;
 
-          case EOpLessThan:
+            case EOpLessThan:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() < unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() < unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() < unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() <
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() <
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() <
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpLessThanEqual:
+            case EOpLessThanEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() <= unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() <= unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() <= unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() <=
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() <=
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() <=
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpGreaterThan:
+            case EOpGreaterThan:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() > unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() > unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() > unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() >
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() >
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() >
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
-
-          case EOpGreaterThanEqual:
+            case EOpGreaterThanEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() >= unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() >= unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() >= unionArrays[1][i].getUConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() >=
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() >=
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() >=
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
             }
             break;
 
-          case EOpVectorEqual:
+            case EOpVectorEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() == unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() == unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() == unionArrays[1][i].getUConst());
-                        break;
-                      case EbtBool:
-                        resultArray[i].setBConst(unionArrays[0][i].getBConst() == unionArrays[1][i].getBConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() ==
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() ==
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() ==
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        case EbtBool:
+                            resultArray[i].setBConst(unionArrays[0][i].getBConst() ==
+                                                     unionArrays[1][i].getBConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpVectorNotEqual:
+            case EOpVectorNotEqual:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
-                        resultArray[i].setBConst(unionArrays[0][i].getFConst() != unionArrays[1][i].getFConst());
-                        break;
-                      case EbtInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getIConst() != unionArrays[1][i].getIConst());
-                        break;
-                      case EbtUInt:
-                        resultArray[i].setBConst(unionArrays[0][i].getUConst() != unionArrays[1][i].getUConst());
-                        break;
-                      case EbtBool:
-                        resultArray[i].setBConst(unionArrays[0][i].getBConst() != unionArrays[1][i].getBConst());
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        case EbtFloat:
+                            resultArray[i].setBConst(unionArrays[0][i].getFConst() !=
+                                                     unionArrays[1][i].getFConst());
+                            break;
+                        case EbtInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getIConst() !=
+                                                     unionArrays[1][i].getIConst());
+                            break;
+                        case EbtUInt:
+                            resultArray[i].setBConst(unionArrays[0][i].getUConst() !=
+                                                     unionArrays[1][i].getUConst());
+                            break;
+                        case EbtBool:
+                            resultArray[i].setBConst(unionArrays[0][i].getBConst() !=
+                                                     unionArrays[1][i].getBConst());
+                            break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpDistance:
-            if (basicType == EbtFloat)
+            case EOpDistance:
             {
+                ASSERT(basicType == EbtFloat);
                 TConstantUnion *distanceArray = new TConstantUnion[maxObjectSize];
-                resultArray = new TConstantUnion();
+                resultArray                   = new TConstantUnion();
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     float x = unionArrays[0][i].getFConst();
@@ -2322,47 +2184,40 @@
                     distanceArray[i].setFConst(x - y);
                 }
                 resultArray->setFConst(VectorLength(distanceArray, maxObjectSize));
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpDot:
-
-            if (basicType == EbtFloat)
-            {
+            case EOpDot:
+                ASSERT(basicType == EbtFloat);
                 resultArray = new TConstantUnion();
-                resultArray->setFConst(VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
-            }
-            else
-                UNREACHABLE();
-            break;
+                resultArray->setFConst(
+                    VectorDotProduct(unionArrays[0], unionArrays[1], maxObjectSize));
+                break;
 
-          case EOpCross:
-            if (basicType == EbtFloat && maxObjectSize == 3)
+            case EOpCross:
             {
+                ASSERT(basicType == EbtFloat && maxObjectSize == 3);
                 resultArray = new TConstantUnion[maxObjectSize];
-                float x0 = unionArrays[0][0].getFConst();
-                float x1 = unionArrays[0][1].getFConst();
-                float x2 = unionArrays[0][2].getFConst();
-                float y0 = unionArrays[1][0].getFConst();
-                float y1 = unionArrays[1][1].getFConst();
-                float y2 = unionArrays[1][2].getFConst();
+                float x0    = unionArrays[0][0].getFConst();
+                float x1    = unionArrays[0][1].getFConst();
+                float x2    = unionArrays[0][2].getFConst();
+                float y0    = unionArrays[1][0].getFConst();
+                float y1    = unionArrays[1][1].getFConst();
+                float y2    = unionArrays[1][2].getFConst();
                 resultArray[0].setFConst(x1 * y2 - y1 * x2);
                 resultArray[1].setFConst(x2 * y0 - y2 * x0);
                 resultArray[2].setFConst(x0 * y1 - y0 * x1);
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpReflect:
-            if (basicType == EbtFloat)
+            case EOpReflect:
             {
+                ASSERT(basicType == EbtFloat);
                 // genType reflect (genType I, genType N) :
-                //     For the incident vector I and surface orientation N, returns the reflection direction:
+                //     For the incident vector I and surface orientation N, returns the reflection
+                //     direction:
                 //     I - 2 * dot(N, I) * N.
-                resultArray = new TConstantUnion[maxObjectSize];
+                resultArray      = new TConstantUnion[maxObjectSize];
                 float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
@@ -2370,45 +2225,40 @@
                                    2.0f * dotProduct * unionArrays[1][i].getFConst();
                     resultArray[i].setFConst(result);
                 }
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpMul:
-            if (basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
-                (*sequence)[1]->getAsTyped()->isMatrix())
+            case EOpMul:
             {
+                ASSERT(basicType == EbtFloat && (*sequence)[0]->getAsTyped()->isMatrix() &&
+                       (*sequence)[1]->getAsTyped()->isMatrix());
                 // Perform component-wise matrix multiplication.
                 resultArray = new TConstantUnion[maxObjectSize];
-                int size = (*sequence)[0]->getAsTyped()->getNominalSize();
+                int size    = (*sequence)[0]->getAsTyped()->getNominalSize();
                 angle::Matrix<float> result =
                     GetMatrix(unionArrays[0], size).compMult(GetMatrix(unionArrays[1], size));
                 SetUnionArrayFromMatrix(result, resultArray);
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpOuterProduct:
-            if (basicType == EbtFloat)
+            case EOpOuterProduct:
             {
+                ASSERT(basicType == EbtFloat);
                 size_t numRows = (*sequence)[0]->getAsTyped()->getType().getObjectSize();
                 size_t numCols = (*sequence)[1]->getAsTyped()->getType().getObjectSize();
-                resultArray = new TConstantUnion[numRows * numCols];
+                resultArray    = new TConstantUnion[numRows * numCols];
                 angle::Matrix<float> result =
                     GetMatrix(unionArrays[0], static_cast<int>(numRows), 1)
                         .outerProduct(GetMatrix(unionArrays[1], 1, static_cast<int>(numCols)));
                 SetUnionArrayFromMatrix(result, resultArray);
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          default:
-            UNREACHABLE();
-            // TODO: Add constant folding support for other built-in operations that take 2 parameters and not handled above.
-            return nullptr;
+            default:
+                UNREACHABLE();
+                // TODO: Add constant folding support for other built-in operations that take 2
+                // parameters and not handled above.
+                return nullptr;
         }
     }
     else if (paramsCount == 3)
@@ -2418,124 +2268,123 @@
         //
         switch (op)
         {
-          case EOpClamp:
+            case EOpClamp:
             {
                 resultArray = new TConstantUnion[maxObjectSize];
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     switch (basicType)
                     {
-                      case EbtFloat:
+                        case EbtFloat:
                         {
-                            float x = unionArrays[0][i].getFConst();
+                            float x   = unionArrays[0][i].getFConst();
                             float min = unionArrays[1][i].getFConst();
                             float max = unionArrays[2][i].getFConst();
                             // Results are undefined if min > max.
                             if (min > max)
-                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                              &resultArray[i]);
                             else
                                 resultArray[i].setFConst(gl::clamp(x, min, max));
+                            break;
                         }
-                        break;
-                      case EbtInt:
+
+                        case EbtInt:
                         {
-                            int x = unionArrays[0][i].getIConst();
+                            int x   = unionArrays[0][i].getIConst();
                             int min = unionArrays[1][i].getIConst();
                             int max = unionArrays[2][i].getIConst();
                             // Results are undefined if min > max.
                             if (min > max)
-                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                              &resultArray[i]);
                             else
                                 resultArray[i].setIConst(gl::clamp(x, min, max));
+                            break;
                         }
-                        break;
-                      case EbtUInt:
+                        case EbtUInt:
                         {
-                            unsigned int x = unionArrays[0][i].getUConst();
+                            unsigned int x   = unionArrays[0][i].getUConst();
                             unsigned int min = unionArrays[1][i].getUConst();
                             unsigned int max = unionArrays[2][i].getUConst();
                             // Results are undefined if min > max.
                             if (min > max)
-                                UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
+                                UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                              &resultArray[i]);
                             else
                                 resultArray[i].setUConst(gl::clamp(x, min, max));
+                            break;
                         }
-                        break;
-                      default:
-                        UNREACHABLE();
-                        break;
+                        default:
+                            UNREACHABLE();
+                            break;
                     }
                 }
+                break;
             }
-            break;
 
-          case EOpMix:
+            case EOpMix:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
+                    float x         = unionArrays[0][i].getFConst();
+                    float y         = unionArrays[1][i].getFConst();
+                    TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
+                    if (type == EbtFloat)
                     {
-                        float x = unionArrays[0][i].getFConst();
-                        float y = unionArrays[1][i].getFConst();
-                        TBasicType type = (*sequence)[2]->getAsTyped()->getType().getBasicType();
-                        if (type == EbtFloat)
-                        {
-                            // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
-                            float a = unionArrays[2][i].getFConst();
-                            resultArray[i].setFConst(x * (1.0f - a) + y * a);
-                        }
-                        else // 3rd parameter is EbtBool
-                        {
-                            ASSERT(type == EbtBool);
-                            // Selects which vector each returned component comes from.
-                            // For a component of a that is false, the corresponding component of x is returned.
-                            // For a component of a that is true, the corresponding component of y is returned.
-                            bool a = unionArrays[2][i].getBConst();
-                            resultArray[i].setFConst(a ? y : x);
-                        }
+                        // Returns the linear blend of x and y, i.e., x * (1 - a) + y * a.
+                        float a = unionArrays[2][i].getFConst();
+                        resultArray[i].setFConst(x * (1.0f - a) + y * a);
+                    }
+                    else  // 3rd parameter is EbtBool
+                    {
+                        ASSERT(type == EbtBool);
+                        // Selects which vector each returned component comes from.
+                        // For a component of a that is false, the corresponding component of x is
+                        // returned.
+                        // For a component of a that is true, the corresponding component of y is
+                        // returned.
+                        bool a = unionArrays[2][i].getBConst();
+                        resultArray[i].setFConst(a ? y : x);
                     }
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpSmoothStep:
+            case EOpSmoothStep:
             {
-                if (basicType == EbtFloat)
+                ASSERT(basicType == EbtFloat);
+                resultArray = new TConstantUnion[maxObjectSize];
+                for (size_t i = 0; i < maxObjectSize; i++)
                 {
-                    resultArray = new TConstantUnion[maxObjectSize];
-                    for (size_t i = 0; i < maxObjectSize; i++)
+                    float edge0 = unionArrays[0][i].getFConst();
+                    float edge1 = unionArrays[1][i].getFConst();
+                    float x     = unionArrays[2][i].getFConst();
+                    // Results are undefined if edge0 >= edge1.
+                    if (edge0 >= edge1)
                     {
-                        float edge0 = unionArrays[0][i].getFConst();
-                        float edge1 = unionArrays[1][i].getFConst();
-                        float x = unionArrays[2][i].getFConst();
-                        // Results are undefined if edge0 >= edge1.
-                        if (edge0 >= edge1)
-                        {
-                            UndefinedConstantFoldingError(loc, op, basicType, infoSink, &resultArray[i]);
-                        }
-                        else
-                        {
-                            // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
-                            // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
-                            float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
-                            resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
-                        }
+                        UndefinedConstantFoldingError(loc, op, basicType, diagnostics,
+                                                      &resultArray[i]);
+                    }
+                    else
+                    {
+                        // Returns 0.0 if x <= edge0 and 1.0 if x >= edge1 and performs smooth
+                        // Hermite interpolation between 0 and 1 when edge0 < x < edge1.
+                        float t = gl::clamp((x - edge0) / (edge1 - edge0), 0.0f, 1.0f);
+                        resultArray[i].setFConst(t * t * (3.0f - 2.0f * t));
                     }
                 }
-                else
-                    UNREACHABLE();
+                break;
             }
-            break;
 
-          case EOpFaceForward:
-            if (basicType == EbtFloat)
+            case EOpFaceForward:
             {
+                ASSERT(basicType == EbtFloat);
                 // genType faceforward(genType N, genType I, genType Nref) :
                 //     If dot(Nref, I) < 0 return N, otherwise return -N.
-                resultArray = new TConstantUnion[maxObjectSize];
+                resultArray      = new TConstantUnion[maxObjectSize];
                 float dotProduct = VectorDotProduct(unionArrays[2], unionArrays[1], maxObjectSize);
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
@@ -2544,43 +2393,42 @@
                     else
                         resultArray[i].setFConst(-unionArrays[0][i].getFConst());
                 }
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          case EOpRefract:
-            if (basicType == EbtFloat)
+            case EOpRefract:
             {
+                ASSERT(basicType == EbtFloat);
                 // genType refract(genType I, genType N, float eta) :
-                //     For the incident vector I and surface normal N, and the ratio of indices of refraction eta,
+                //     For the incident vector I and surface normal N, and the ratio of indices of
+                //     refraction eta,
                 //     return the refraction vector. The result is computed by
                 //         k = 1.0 - eta * eta * (1.0 - dot(N, I) * dot(N, I))
                 //         if (k < 0.0)
                 //             return genType(0.0)
                 //         else
                 //             return eta * I - (eta * dot(N, I) + sqrt(k)) * N
-                resultArray = new TConstantUnion[maxObjectSize];
+                resultArray      = new TConstantUnion[maxObjectSize];
                 float dotProduct = VectorDotProduct(unionArrays[1], unionArrays[0], maxObjectSize);
                 for (size_t i = 0; i < maxObjectSize; i++)
                 {
                     float eta = unionArrays[2][i].getFConst();
-                    float k = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
+                    float k   = 1.0f - eta * eta * (1.0f - dotProduct * dotProduct);
                     if (k < 0.0f)
                         resultArray[i].setFConst(0.0f);
                     else
                         resultArray[i].setFConst(eta * unionArrays[0][i].getFConst() -
-                                                    (eta * dotProduct + sqrtf(k)) * unionArrays[1][i].getFConst());
+                                                 (eta * dotProduct + sqrtf(k)) *
+                                                     unionArrays[1][i].getFConst());
                 }
+                break;
             }
-            else
-                UNREACHABLE();
-            break;
 
-          default:
-            UNREACHABLE();
-            // TODO: Add constant folding support for other built-in operations that take 3 parameters and not handled above.
-            return nullptr;
+            default:
+                UNREACHABLE();
+                // TODO: Add constant folding support for other built-in operations that take 3
+                // parameters and not handled above.
+                return nullptr;
         }
     }
     return resultArray;
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 5b4c5d9..f2cac93 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -319,6 +319,7 @@
     TIntermConstantUnion(const TConstantUnion *unionPointer, const TType &type)
         : TIntermTyped(type), mUnionArrayPointer(unionPointer)
     {
+        ASSERT(unionPointer);
     }
 
     TIntermTyped *deepCopy() const override { return new TIntermConstantUnion(*this); }
@@ -346,6 +347,7 @@
 
     void replaceConstantUnion(const TConstantUnion *safeConstantUnion)
     {
+        ASSERT(safeConstantUnion);
         // Previous union pointer freed on pool deallocation.
         mUnionArrayPointer = safeConstantUnion;
     }
@@ -357,12 +359,12 @@
     TConstantUnion *foldBinary(TOperator op,
                                TIntermConstantUnion *rightNode,
                                TDiagnostics *diagnostics);
-    TConstantUnion *foldUnaryWithDifferentReturnType(TOperator op, TInfoSink &infoSink);
-    TConstantUnion *foldUnaryWithSameReturnType(TOperator op, TInfoSink &infoSink);
+    TConstantUnion *foldUnaryNonComponentWise(TOperator op);
+    TConstantUnion *foldUnaryComponentWise(TOperator op, TDiagnostics *diagnostics);
 
-    static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate,
-                                                    TInfoSink &infoSink);
-    static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate, TInfoSink &infoSink);
+    static TConstantUnion *FoldAggregateConstructor(TIntermAggregate *aggregate);
+    static TConstantUnion *FoldAggregateBuiltIn(TIntermAggregate *aggregate,
+                                                TDiagnostics *diagnostics);
 
   protected:
     // Same data may be shared between multiple constant unions, so it can't be modified.
@@ -370,7 +372,9 @@
 
   private:
     typedef float(*FloatTypeUnaryFunc) (float);
-    bool foldFloatTypeUnary(const TConstantUnion &parameter, FloatTypeUnaryFunc builtinFunc, TInfoSink &infoSink, TConstantUnion *result) const;
+    void foldFloatTypeUnary(const TConstantUnion &parameter,
+                            FloatTypeUnaryFunc builtinFunc,
+                            TConstantUnion *result) const;
 
     TIntermConstantUnion(const TIntermConstantUnion &node);  // Note: not deleted, just private!
 };
@@ -468,6 +472,11 @@
           mOperand(NULL),
           mUseEmulatedFunction(false) {}
 
+    TIntermUnary(TOperator op, TIntermTyped *operand)
+        : TIntermOperator(op), mOperand(operand), mUseEmulatedFunction(false)
+    {
+    }
+
     TIntermTyped *deepCopy() const override { return new TIntermUnary(*this); }
 
     void traverse(TIntermTraverser *it) override;
@@ -479,7 +488,7 @@
     void setOperand(TIntermTyped *operand) { mOperand = operand; }
     TIntermTyped *getOperand() { return mOperand; }
     void promote(const TType *funcReturnType);
-    TIntermTyped *fold(TInfoSink &infoSink);
+    TIntermTyped *fold(TDiagnostics *diagnostics);
 
     void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
     bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
@@ -532,7 +541,7 @@
     bool insertChildNodes(TIntermSequence::size_type position, TIntermSequence insertions);
     // Conservatively assume function calls and other aggregate operators have side-effects
     bool hasSideEffects() const override { return true; }
-    TIntermTyped *fold(TInfoSink &infoSink);
+    TIntermTyped *fold(TDiagnostics *diagnostics);
 
     TIntermSequence *getSequence() { return &mSequence; }
     const TIntermSequence *getSequence() const { return &mSequence; }
diff --git a/src/compiler/translator/Intermediate.cpp b/src/compiler/translator/Intermediate.cpp
index b6fefa4..b747945 100644
--- a/src/compiler/translator/Intermediate.cpp
+++ b/src/compiler/translator/Intermediate.cpp
@@ -58,29 +58,6 @@
 }
 
 //
-// Add one node as the parent of another that it operates on.
-//
-// Returns the added node.
-//
-TIntermTyped *TIntermediate::addUnaryMath(
-    TOperator op, TIntermTyped *child, const TSourceLoc &line, const TType *funcReturnType)
-{
-    //
-    // Make a new node for the operator.
-    //
-    TIntermUnary *node = new TIntermUnary(op);
-    node->setLine(line);
-    node->setOperand(child);
-    node->promote(funcReturnType);
-
-    TIntermTyped *foldedNode = node->fold(mInfoSink);
-    if (foldedNode)
-        return foldedNode;
-
-    return node;
-}
-
-//
 // This is the safe way to change the operator on an aggregate, as it
 // does lots of error checking and fixing.  Especially for establishing
 // a function call's operation on it's set of parameters.  Sequences
@@ -388,7 +365,7 @@
 // This is to be executed once the final root is put on top by the parsing
 // process.
 //
-TIntermAggregate *TIntermediate::postProcess(TIntermNode *root)
+TIntermAggregate *TIntermediate::PostProcess(TIntermNode *root)
 {
     if (root == nullptr)
         return nullptr;
@@ -411,7 +388,8 @@
     return aggRoot;
 }
 
-TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate)
+TIntermTyped *TIntermediate::foldAggregateBuiltIn(TIntermAggregate *aggregate,
+                                                  TDiagnostics *diagnostics)
 {
     switch (aggregate->getOp())
     {
@@ -438,12 +416,12 @@
         case EOpFaceForward:
         case EOpReflect:
         case EOpRefract:
-            return aggregate->fold(mInfoSink);
+            return aggregate->fold(diagnostics);
         default:
             // TODO: Add support for folding array constructors
             if (aggregate->isConstructor() && !aggregate->isArray())
             {
-                return aggregate->fold(mInfoSink);
+                return aggregate->fold(diagnostics);
             }
             // Constant folding not supported for the built-in.
             return nullptr;
diff --git a/src/compiler/translator/Intermediate.h b/src/compiler/translator/Intermediate.h
index 339daa3..725f74d 100644
--- a/src/compiler/translator/Intermediate.h
+++ b/src/compiler/translator/Intermediate.h
@@ -16,15 +16,13 @@
 };
 
 //
-// Set of helper functions to help parse and build the tree.
+// Set of helper functions to help build the tree.
 //
-class TInfoSink;
 class TIntermediate
 {
   public:
     POOL_ALLOCATOR_NEW_DELETE();
-    TIntermediate(TInfoSink &i)
-        : mInfoSink(i) { }
+    TIntermediate() {}
 
     TIntermSymbol *addSymbol(
         int id, const TString &, const TType &, const TSourceLoc &);
@@ -56,16 +54,14 @@
     TIntermBranch *addBranch(TOperator, const TSourceLoc &);
     TIntermBranch *addBranch(TOperator, TIntermTyped *, const TSourceLoc &);
     TIntermTyped *addSwizzle(TVectorFields &, const TSourceLoc &);
-    TIntermAggregate *postProcess(TIntermNode *root);
+    static TIntermAggregate *PostProcess(TIntermNode *root);
 
     static void outputTree(TIntermNode *, TInfoSinkBase &);
 
-    TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate);
+    TIntermTyped *foldAggregateBuiltIn(TIntermAggregate *aggregate, TDiagnostics *diagnostics);
 
   private:
     void operator=(TIntermediate &); // prevent assignments
-
-    TInfoSink & mInfoSink;
 };
 
 #endif  // COMPILER_TRANSLATOR_INTERMEDIATE_H_
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index befec4a..ff40e96 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -2376,7 +2376,7 @@
 
     constructor->setType(type);
 
-    TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor);
+    TIntermTyped *constConstructor = intermediate.foldAggregateBuiltIn(constructor, &mDiagnostics);
     if (constConstructor)
     {
         return constConstructor;
@@ -3425,7 +3425,15 @@
             break;
     }
 
-    return intermediate.addUnaryMath(op, child, loc, funcReturnType);
+    TIntermUnary *node = new TIntermUnary(op, child);
+    node->setLine(loc);
+    node->promote(funcReturnType);
+
+    TIntermTyped *foldedNode = node->fold(&mDiagnostics);
+    if (foldedNode)
+        return foldedNode;
+
+    return node;
 }
 
 TIntermTyped *TParseContext::addUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc)
@@ -4054,7 +4062,8 @@
 
                     // See if we can constant fold a built-in. Note that this may be possible even
                     // if it is not const-qualified.
-                    TIntermTyped *foldedNode = intermediate.foldAggregateBuiltIn(aggregate);
+                    TIntermTyped *foldedNode =
+                        intermediate.foldAggregateBuiltIn(aggregate, &mDiagnostics);
                     if (foldedNode)
                     {
                         callNode = foldedNode;
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 9db6a66..02f6416 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -31,14 +31,13 @@
   public:
     TParseContext(TSymbolTable &symt,
                   TExtensionBehavior &ext,
-                  TIntermediate &interm,
                   sh::GLenum type,
                   ShShaderSpec spec,
                   int options,
                   bool checksPrecErrors,
                   TInfoSink &is,
                   const ShBuiltInResources &resources)
-        : intermediate(interm),
+        : intermediate(),
           symbolTable(symt),
           mDeferredSingleDeclarationErrorCheck(false),
           mShaderType(type),
@@ -367,7 +366,7 @@
         TIntermTyped *cond, TIntermTyped *trueBlock, TIntermTyped *falseBlock, const TSourceLoc &line);
 
     // TODO(jmadill): make these private
-    TIntermediate &intermediate; // to hold and build a parse tree
+    TIntermediate intermediate;  // to build a parse tree
     TSymbolTable &symbolTable;   // symbol table that goes with the language currently being parsed
 
   private: