Refactor debug output of types

Instead of passing around strings from TType::getCompleteString(),
add a stream operator to InfoSinkBase that takes a TType. This makes
the compiler executable a few kilobytes smaller and will help with
getting rid of TString altogether.

BUG=angleproject:2267
TEST=angle_unittests

Change-Id: I31a6693b40a28824b3959e19ad3c0a2ce0f0a35f
Reviewed-on: https://chromium-review.googlesource.com/1107712
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler/translator/InfoSink.cpp b/src/compiler/translator/InfoSink.cpp
index 79e07e3..fc19d7e 100644
--- a/src/compiler/translator/InfoSink.cpp
+++ b/src/compiler/translator/InfoSink.cpp
@@ -7,6 +7,7 @@
 #include "compiler/translator/InfoSink.h"
 
 #include "compiler/translator/ImmutableString.h"
+#include "compiler/translator/Types.h"
 
 namespace sh
 {
@@ -33,6 +34,39 @@
     return *this;
 }
 
+TInfoSinkBase &TInfoSinkBase::operator<<(const TType &type)
+{
+    if (type.isInvariant())
+        sink.append("invariant ");
+    if (type.getQualifier() != EvqTemporary && type.getQualifier() != EvqGlobal)
+    {
+        sink.append(type.getQualifierString());
+        sink.append(" ");
+    }
+    if (type.getPrecision() != EbpUndefined)
+    {
+        sink.append(type.getPrecisionString());
+        sink.append(" ");
+    }
+    if (type.isArray())
+    {
+        for (auto arraySizeIter = type.getArraySizes()->rbegin();
+             arraySizeIter != type.getArraySizes()->rend(); ++arraySizeIter)
+        {
+            *this << "array[" << (*arraySizeIter) << "] of ";
+        }
+    }
+    if (type.isMatrix())
+    {
+        *this << type.getCols() << "X" << type.getRows() << " matrix of ";
+    }
+    else if (type.isVector())
+        *this << type.getNominalSize() << "-component vector of ";
+
+    sink.append(type.getBasicString());
+    return *this;
+}
+
 void TInfoSinkBase::location(int file, int line)
 {
     TPersistStringStream stream;
diff --git a/src/compiler/translator/InfoSink.h b/src/compiler/translator/InfoSink.h
index ce326ce..44f0e83 100644
--- a/src/compiler/translator/InfoSink.h
+++ b/src/compiler/translator/InfoSink.h
@@ -16,6 +16,7 @@
 {
 
 class ImmutableString;
+class TType;
 
 // Returns the fractional part of the given floating-point number.
 inline float fractionalPart(float f)
@@ -69,6 +70,8 @@
     }
     TInfoSinkBase &operator<<(const ImmutableString &str);
 
+    TInfoSinkBase &operator<<(const TType &type);
+
     // Make sure floats are written with correct precision.
     TInfoSinkBase &operator<<(float f)
     {
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index d95494d..cac642b 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -160,7 +160,6 @@
     bool isScalar() const { return getType().isScalar(); }
     bool isScalarInt() const { return getType().isScalarInt(); }
     const char *getBasicString() const { return getType().getBasicString(); }
-    TString getCompleteString() const { return getType().getCompleteString(); }
 
     unsigned int getOutermostArraySize() const { return getType().getOutermostArraySize(); }
 
diff --git a/src/compiler/translator/OutputTree.cpp b/src/compiler/translator/OutputTree.cpp
index adcd242..316bdfc 100644
--- a/src/compiler/translator/OutputTree.cpp
+++ b/src/compiler/translator/OutputTree.cpp
@@ -96,7 +96,7 @@
         mOut << "'" << node->getName() << "' ";
     }
     mOut << "(symbol id " << node->uniqueId().get() << ") ";
-    mOut << "(" << node->getCompleteString() << ")";
+    mOut << "(" << node->getType() << ")";
     mOut << "\n";
 }
 
@@ -107,7 +107,7 @@
     node->writeOffsetsAsXYZW(&mOut);
     mOut << ")";
 
-    mOut << " (" << node->getCompleteString() << ")";
+    mOut << " (" << node->getType() << ")";
     mOut << "\n";
     return true;
 }
@@ -262,7 +262,7 @@
             mOut << "<unknown op>";
     }
 
-    mOut << " (" << node->getCompleteString() << ")";
+    mOut << " (" << node->getType() << ")";
 
     mOut << "\n";
 
@@ -345,7 +345,7 @@
             break;
     }
 
-    mOut << " (" << node->getCompleteString() << ")";
+    mOut << " (" << node->getType() << ")";
 
     mOut << "\n";
 
@@ -370,15 +370,14 @@
 {
     OutputTreeText(mOut, node, getCurrentIndentDepth());
     OutputFunction(mOut, "Function Prototype", node->getFunction());
-    mOut << " (" << node->getCompleteString() << ")";
+    mOut << " (" << node->getType() << ")";
     mOut << "\n";
     size_t paramCount = node->getFunction()->getParamCount();
     for (size_t i = 0; i < paramCount; ++i)
     {
         const TVariable *param = node->getFunction()->getParam(i);
         OutputTreeText(mOut, node, getCurrentIndentDepth() + 1);
-        mOut << "parameter: " << param->name() << " (" << param->getType().getCompleteString()
-             << ")";
+        mOut << "parameter: " << param->name() << " (" << param->getType() << ")";
     }
 }
 
@@ -447,7 +446,7 @@
             break;
     }
 
-    mOut << " (" << node->getCompleteString() << ")";
+    mOut << " (" << node->getType() << ")";
 
     mOut << "\n";
 
@@ -475,7 +474,7 @@
     OutputTreeText(mOut, node, getCurrentIndentDepth());
 
     mOut << "Ternary selection";
-    mOut << " (" << node->getCompleteString() << ")\n";
+    mOut << " (" << node->getType() << ")\n";
 
     ++mIndentDepth;
 
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index f8df136..81eb6cd 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -367,25 +367,26 @@
 //
 // Same error message for all places assignments don't work.
 //
-void TParseContext::assignError(const TSourceLoc &line, const char *op, TString left, TString right)
+void TParseContext::assignError(const TSourceLoc &line,
+                                const char *op,
+                                const TType &left,
+                                const TType &right)
 {
-    std::stringstream reasonStream;
+    TInfoSinkBase reasonStream;
     reasonStream << "cannot convert from '" << right << "' to '" << left << "'";
-    std::string reason = reasonStream.str();
-    error(line, reason.c_str(), op);
+    error(line, reasonStream.c_str(), op);
 }
 
 //
 // Same error message for all places unary operations don't work.
 //
-void TParseContext::unaryOpError(const TSourceLoc &line, const char *op, TString operand)
+void TParseContext::unaryOpError(const TSourceLoc &line, const char *op, const TType &operand)
 {
-    std::stringstream reasonStream;
+    TInfoSinkBase reasonStream;
     reasonStream << "wrong operand type - no operation '" << op
                  << "' exists that takes an operand of type " << operand
                  << " (or there is no acceptable conversion)";
-    std::string reason = reasonStream.str();
-    error(line, reason.c_str(), op);
+    error(line, reasonStream.c_str(), op);
 }
 
 //
@@ -393,16 +394,15 @@
 //
 void TParseContext::binaryOpError(const TSourceLoc &line,
                                   const char *op,
-                                  TString left,
-                                  TString right)
+                                  const TType &left,
+                                  const TType &right)
 {
-    std::stringstream reasonStream;
+    TInfoSinkBase reasonStream;
     reasonStream << "wrong operand types - no operation '" << op
                  << "' exists that takes a left-hand operand of type '" << left
                  << "' and a right operand of type '" << right
                  << "' (or there is no acceptable conversion)";
-    std::string reason = reasonStream.str();
-    error(line, reason.c_str(), op);
+    error(line, reasonStream.c_str(), op);
 }
 
 void TParseContext::checkPrecisionSpecified(const TSourceLoc &line,
@@ -1045,8 +1045,9 @@
 {
     if (mShaderVersion < 310 && elementType.isArray())
     {
-        error(line, "cannot declare arrays of arrays",
-              TType(elementType).getCompleteString().c_str());
+        TInfoSinkBase typeString;
+        typeString << TType(elementType);
+        error(line, "cannot declare arrays of arrays", typeString.c_str());
         return false;
     }
     return true;
@@ -1076,8 +1077,10 @@
         sh::IsVarying(elementType.qualifier) &&
         !IsGeometryShaderInput(mShaderType, elementType.qualifier))
     {
+        TInfoSinkBase typeString;
+        typeString << TType(elementType);
         error(indexLocation, "cannot declare arrays of structs of this qualifier",
-              TType(elementType).getCompleteString().c_str());
+              typeString.c_str());
         return false;
     }
     return checkIsValidQualifierForArray(indexLocation, elementType);
@@ -1922,10 +1925,9 @@
     {
         if (EvqConst != initializer->getType().getQualifier())
         {
-            std::stringstream reasonStream;
-            reasonStream << "assigning non-constant to '" << type->getCompleteString() << "'";
-            std::string reason = reasonStream.str();
-            error(line, reason.c_str(), "=");
+            TInfoSinkBase reasonStream;
+            reasonStream << "assigning non-constant to '" << *type << "'";
+            error(line, reasonStream.c_str(), "=");
 
             // We're still going to declare the variable to avoid extra error messages.
             type->setQualifier(EvqTemporary);
@@ -1975,8 +1977,7 @@
 
     if (!binaryOpCommonCheck(EOpInitialize, intermSymbol, initializer, line))
     {
-        assignError(line, "=", variable->getType().getCompleteString(),
-                    initializer->getCompleteString());
+        assignError(line, "=", variable->getType(), initializer->getType());
         return false;
     }
 
@@ -3396,8 +3397,10 @@
         if (type.isStructureContainingArrays())
         {
             // ESSL 1.00.17 section 6.1 Function Definitions
+            TInfoSinkBase typeString;
+            typeString << TType(type);
             error(location, "structures containing arrays can't be function return values",
-                  TType(type).getCompleteString().c_str());
+                  typeString.c_str());
         }
     }
 
@@ -4871,7 +4874,7 @@
             if (child->getBasicType() != EbtBool || child->isMatrix() || child->isArray() ||
                 child->isVector())
             {
-                unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
+                unaryOpError(loc, GetOperatorString(op), child->getType());
                 return nullptr;
             }
             break;
@@ -4879,7 +4882,7 @@
             if ((child->getBasicType() != EbtInt && child->getBasicType() != EbtUInt) ||
                 child->isMatrix() || child->isArray())
             {
-                unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
+                unaryOpError(loc, GetOperatorString(op), child->getType());
                 return nullptr;
             }
             break;
@@ -4893,7 +4896,7 @@
                 child->getBasicType() == EbtBool || child->isArray() ||
                 IsOpaqueType(child->getBasicType()))
             {
-                unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
+                unaryOpError(loc, GetOperatorString(op), child->getType());
                 return nullptr;
             }
             break;
@@ -4904,7 +4907,7 @@
 
     if (child->getMemoryQualifier().writeonly)
     {
-        unaryOpError(loc, GetOperatorString(op), child->getCompleteString());
+        unaryOpError(loc, GetOperatorString(op), child->getType());
         return nullptr;
     }
 
@@ -5327,8 +5330,7 @@
     TIntermTyped *node = addBinaryMathInternal(op, left, right, loc);
     if (node == 0)
     {
-        binaryOpError(loc, GetOperatorString(op), left->getCompleteString(),
-                      right->getCompleteString());
+        binaryOpError(loc, GetOperatorString(op), left->getType(), right->getType());
         return left;
     }
     return node;
@@ -5342,8 +5344,7 @@
     TIntermTyped *node = addBinaryMathInternal(op, left, right, loc);
     if (node == nullptr)
     {
-        binaryOpError(loc, GetOperatorString(op), left->getCompleteString(),
-                      right->getCompleteString());
+        binaryOpError(loc, GetOperatorString(op), left->getType(), right->getType());
         node = CreateBoolNode(false);
         node->setLine(loc);
     }
@@ -5374,7 +5375,7 @@
     }
     if (node == nullptr)
     {
-        assignError(loc, "assign", left->getCompleteString(), right->getCompleteString());
+        assignError(loc, "assign", left->getType(), right->getType());
         return left;
     }
     if (op != EOpAssign)
@@ -5885,12 +5886,10 @@
 
     if (trueExpression->getType() != falseExpression->getType())
     {
-        std::stringstream reasonStream;
-        reasonStream << "mismatching ternary operator operand types '"
-                     << trueExpression->getCompleteString() << " and '"
-                     << falseExpression->getCompleteString() << "'";
-        std::string reason = reasonStream.str();
-        error(loc, reason.c_str(), "?:");
+        TInfoSinkBase reasonStream;
+        reasonStream << "mismatching ternary operator operand types '" << trueExpression->getType()
+                     << " and '" << falseExpression->getType() << "'";
+        error(loc, reasonStream.c_str(), "?:");
         return falseExpression;
     }
     if (IsOpaqueType(trueExpression->getBasicType()))
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 568e432..36a934a 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -109,9 +109,12 @@
                            int vecSize,
                            TVector<int> *fieldOffsets);
 
-    void assignError(const TSourceLoc &line, const char *op, TString left, TString right);
-    void unaryOpError(const TSourceLoc &line, const char *op, TString operand);
-    void binaryOpError(const TSourceLoc &line, const char *op, TString left, TString right);
+    void assignError(const TSourceLoc &line, const char *op, const TType &left, const TType &right);
+    void unaryOpError(const TSourceLoc &line, const char *op, const TType &operand);
+    void binaryOpError(const TSourceLoc &line,
+                       const char *op,
+                       const TType &left,
+                       const TType &right);
 
     // Check functions - the ones that return bool return false if an error was generated.
 
diff --git a/src/compiler/translator/Types.cpp b/src/compiler/translator/Types.cpp
index e217a4c..35723af 100644
--- a/src/compiler/translator/Types.cpp
+++ b/src/compiler/translator/Types.cpp
@@ -396,33 +396,6 @@
     return getBasicString();
 }
 
-TString TType::getCompleteString() const
-{
-    TStringStream stream;
-
-    if (invariant)
-        stream << "invariant ";
-    if (qualifier != EvqTemporary && qualifier != EvqGlobal)
-        stream << getQualifierString() << " ";
-    if (precision != EbpUndefined)
-        stream << getPrecisionString() << " ";
-    if (mArraySizes)
-    {
-        for (auto arraySizeIter = mArraySizes->rbegin(); arraySizeIter != mArraySizes->rend();
-             ++arraySizeIter)
-        {
-            stream << "array[" << (*arraySizeIter) << "] of ";
-        }
-    }
-    if (isMatrix())
-        stream << getCols() << "X" << getRows() << " matrix of ";
-    else if (isVector())
-        stream << getNominalSize() << "-component vector of ";
-
-    stream << getBasicString();
-    return stream.str();
-}
-
 int TType::getDeepestStructNesting() const
 {
     return mStructure ? mStructure->deepestNesting() : 0;
diff --git a/src/compiler/translator/Types.h b/src/compiler/translator/Types.h
index 3a7be65..5a6504a 100644
--- a/src/compiler/translator/Types.h
+++ b/src/compiler/translator/Types.h
@@ -291,8 +291,6 @@
 
     const char *getBuiltInTypeNameString() const;
 
-    TString getCompleteString() const;
-
     // If this type is a struct, returns the deepest struct nesting of
     // any field in the struct. For example:
     //   struct nesting1 {