Add a class for function lookups

Using a purpose-built class for function lookups instead of using
a combination of TFunction and a struct container for the this node
and arguments makes the code clearer.

BUG=angleproject:2267
TEST=angle_unittests

Change-Id: I3f345d836abeaa7f84cc46b4b840fd06c7e2e1a7
Reviewed-on: https://chromium-review.googlesource.com/897363
Reviewed-by: Jamie Madill <jmadill@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
diff --git a/src/compiler.gypi b/src/compiler.gypi
index be25a12..90a07c5 100644
--- a/src/compiler.gypi
+++ b/src/compiler.gypi
@@ -69,6 +69,8 @@
             'compiler/translator/FlagStd140Structs.h',
             'compiler/translator/FoldExpressions.cpp',
             'compiler/translator/FoldExpressions.h',
+            'compiler/translator/FunctionLookup.cpp',
+            'compiler/translator/FunctionLookup.h',
             'compiler/translator/HashNames.cpp',
             'compiler/translator/HashNames.h',
             'compiler/translator/ImmutableString.cpp',
diff --git a/src/compiler/translator/EmulatePrecision.cpp b/src/compiler/translator/EmulatePrecision.cpp
index 1470ace..da3ff5a 100644
--- a/src/compiler/translator/EmulatePrecision.cpp
+++ b/src/compiler/translator/EmulatePrecision.cpp
@@ -6,6 +6,8 @@
 
 #include "compiler/translator/EmulatePrecision.h"
 
+#include "compiler/translator/FunctionLookup.h"
+
 #include <memory>
 
 namespace sh
@@ -713,7 +715,7 @@
                                                        const TVector<TConstParameter> &parameters,
                                                        bool knownToNotHaveSideEffects)
 {
-    TString mangledName = TFunction::GetMangledNameFromCall(*functionName, *arguments);
+    TString mangledName = TFunctionLookup::GetMangledName(*functionName, *arguments);
     if (mInternalFunctions.find(mangledName) == mInternalFunctions.end())
     {
         TFunction *func = new TFunction(mSymbolTable, functionName, new TType(returnType),
diff --git a/src/compiler/translator/FunctionLookup.cpp b/src/compiler/translator/FunctionLookup.cpp
new file mode 100644
index 0000000..ee575a8
--- /dev/null
+++ b/src/compiler/translator/FunctionLookup.cpp
@@ -0,0 +1,94 @@
+//
+// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// FunctionLookup.cpp: Used for storing function calls that have not yet been resolved during
+// parsing.
+//
+
+#include "compiler/translator/FunctionLookup.h"
+
+namespace sh
+{
+
+namespace
+{
+
+const char kFunctionMangledNameSeparator = '(';
+
+}  // anonymous namespace
+
+TFunctionLookup::TFunctionLookup(const TString *name, const TType *constructorType)
+    : mName(name), mConstructorType(constructorType), mThisNode(nullptr)
+{
+}
+
+// static
+TFunctionLookup *TFunctionLookup::CreateConstructor(const TType *type)
+{
+    ASSERT(type != nullptr);
+    return new TFunctionLookup(nullptr, type);
+}
+
+// static
+TFunctionLookup *TFunctionLookup::CreateFunctionCall(const TString *name)
+{
+    ASSERT(name != nullptr);
+    return new TFunctionLookup(name, nullptr);
+}
+
+const TString &TFunctionLookup::name() const
+{
+    return *mName;
+}
+
+const TString &TFunctionLookup::getMangledName() const
+{
+    return GetMangledName(*mName, mArguments);
+}
+
+const TString &TFunctionLookup::GetMangledName(const TString &functionName,
+                                               const TIntermSequence &arguments)
+{
+    std::string newName = functionName.c_str();
+    newName += kFunctionMangledNameSeparator;
+
+    for (TIntermNode *argument : arguments)
+    {
+        newName += argument->getAsTyped()->getType().getMangledName();
+    }
+    return *NewPoolTString(newName.c_str());
+}
+
+bool TFunctionLookup::isConstructor() const
+{
+    return mConstructorType != nullptr;
+}
+
+const TType &TFunctionLookup::constructorType() const
+{
+    return *mConstructorType;
+}
+
+void TFunctionLookup::setThisNode(TIntermTyped *thisNode)
+{
+    mThisNode = thisNode;
+}
+
+TIntermTyped *TFunctionLookup::thisNode() const
+{
+    return mThisNode;
+}
+
+void TFunctionLookup::addArgument(TIntermTyped *argument)
+{
+    mArguments.push_back(argument);
+}
+
+TIntermSequence &TFunctionLookup::arguments()
+{
+    return mArguments;
+}
+
+}  // namespace sh
diff --git a/src/compiler/translator/FunctionLookup.h b/src/compiler/translator/FunctionLookup.h
new file mode 100644
index 0000000..1a62357
--- /dev/null
+++ b/src/compiler/translator/FunctionLookup.h
@@ -0,0 +1,51 @@
+//
+// Copyright (c) 2018 The ANGLE Project Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// FunctionLookup.h: Used for storing function calls that have not yet been resolved during parsing.
+//
+
+#ifndef COMPILER_TRANSLATOR_FUNCTIONLOOKUP_H_
+#define COMPILER_TRANSLATOR_FUNCTIONLOOKUP_H_
+
+#include "compiler/translator/IntermNode.h"
+
+namespace sh
+{
+
+// A function look-up.
+class TFunctionLookup : angle::NonCopyable
+{
+  public:
+    POOL_ALLOCATOR_NEW_DELETE();
+
+    static TFunctionLookup *CreateConstructor(const TType *type);
+    static TFunctionLookup *CreateFunctionCall(const TString *name);
+
+    const TString &name() const;
+    const TString &getMangledName() const;
+    static const TString &GetMangledName(const TString &functionName,
+                                         const TIntermSequence &arguments);
+
+    bool isConstructor() const;
+    const TType &constructorType() const;
+
+    void setThisNode(TIntermTyped *thisNode);
+    TIntermTyped *thisNode() const;
+
+    void addArgument(TIntermTyped *argument);
+    TIntermSequence &arguments();
+
+  private:
+    TFunctionLookup(const TString *name, const TType *constructorType);
+
+    const TString *mName;
+    const TType *const mConstructorType;
+    TIntermTyped *mThisNode;
+    TIntermSequence mArguments;
+};
+
+}  // namespace sh
+
+#endif  // COMPILER_TRANSLATOR_FUNCTIONLOOKUP_H_
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 1582634..965327c 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -509,21 +509,6 @@
         mType.setPrecision(precision);
 }
 
-TString TIntermAggregate::getSymbolTableMangledName() const
-{
-    ASSERT(!isConstructor());
-    switch (mOp)
-    {
-        case EOpCallInternalRawFunction:
-        case EOpCallBuiltInFunction:
-        case EOpCallFunctionInAST:
-            return TFunction::GetMangledNameFromCall(mFunction->name(), mArguments);
-        default:
-            TString opString = GetOperatorString(mOp);
-            return TFunction::GetMangledNameFromCall(opString, mArguments);
-    }
-}
-
 const char *TIntermAggregate::functionName() const
 {
     ASSERT(!isConstructor());
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 3db2464..764c11e 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -533,15 +533,6 @@
 typedef TVector<TIntermNode *> TIntermSequence;
 typedef TVector<int> TQualifierList;
 
-//
-// This is just to help yacc.
-//
-struct TIntermFunctionCallOrMethod
-{
-    TIntermSequence *arguments;
-    TIntermNode *thisNode;
-};
-
 // Interface for node classes that have an arbitrarily sized set of children.
 class TIntermAggregateBase
 {
@@ -599,8 +590,6 @@
     TIntermSequence *getSequence() override { return &mArguments; }
     const TIntermSequence *getSequence() const override { return &mArguments; }
 
-    TString getSymbolTableMangledName() const;
-
     void setUseEmulatedFunction() { mUseEmulatedFunction = true; }
     bool getUseEmulatedFunction() { return mUseEmulatedFunction; }
 
diff --git a/src/compiler/translator/IntermNode_util.cpp b/src/compiler/translator/IntermNode_util.cpp
index 5a20c82..1cbbcd4 100644
--- a/src/compiler/translator/IntermNode_util.cpp
+++ b/src/compiler/translator/IntermNode_util.cpp
@@ -8,6 +8,7 @@
 
 #include "compiler/translator/IntermNode_util.h"
 
+#include "compiler/translator/FunctionLookup.h"
 #include "compiler/translator/SymbolTable.h"
 
 namespace sh
@@ -21,7 +22,7 @@
                                        const TSymbolTable &symbolTable,
                                        int shaderVersion)
 {
-    TString mangledName = TFunction::GetMangledNameFromCall(name, *arguments);
+    const TString &mangledName = TFunctionLookup::GetMangledName(name, *arguments);
     const TSymbol *symbol = symbolTable.findBuiltIn(mangledName, shaderVersion);
     if (symbol)
     {
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index c7c4c77..dc7ba1c 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -695,16 +695,16 @@
 
 // Make sure the argument types are correct for constructing a specific type.
 bool TParseContext::checkConstructorArguments(const TSourceLoc &line,
-                                              const TIntermSequence *arguments,
+                                              const TIntermSequence &arguments,
                                               const TType &type)
 {
-    if (arguments->empty())
+    if (arguments.empty())
     {
         error(line, "constructor does not have any arguments", "constructor");
         return false;
     }
 
-    for (TIntermNode *arg : *arguments)
+    for (TIntermNode *arg : arguments)
     {
         const TIntermTyped *argTyped = arg->getAsTyped();
         ASSERT(argTyped != nullptr);
@@ -731,14 +731,14 @@
     {
         // The size of an unsized constructor should already have been determined.
         ASSERT(!type.isUnsizedArray());
-        if (static_cast<size_t>(type.getOutermostArraySize()) != arguments->size())
+        if (static_cast<size_t>(type.getOutermostArraySize()) != arguments.size())
         {
             error(line, "array constructor needs one argument per array element", "constructor");
             return false;
         }
         // GLSL ES 3.00 section 5.4.4: Each argument must be the same type as the element type of
         // the array.
-        for (TIntermNode *const &argNode : *arguments)
+        for (TIntermNode *const &argNode : arguments)
         {
             const TType &argType = argNode->getAsTyped()->getType();
             if (mShaderVersion < 310 && argType.isArray())
@@ -756,7 +756,7 @@
     else if (type.getBasicType() == EbtStruct)
     {
         const TFieldList &fields = type.getStruct()->fields();
-        if (fields.size() != arguments->size())
+        if (fields.size() != arguments.size())
         {
             error(line,
                   "Number of constructor parameters does not match the number of structure fields",
@@ -766,8 +766,8 @@
 
         for (size_t i = 0; i < fields.size(); i++)
         {
-            if (i >= arguments->size() ||
-                (*arguments)[i]->getAsTyped()->getType() != *fields[i]->type())
+            if (i >= arguments.size() ||
+                arguments[i]->getAsTyped()->getType() != *fields[i]->type())
             {
                 error(line, "Structure constructor arguments do not match structure fields",
                       "constructor");
@@ -787,7 +787,7 @@
         bool full      = false;
         bool overFull  = false;
         bool matrixArg = false;
-        for (TIntermNode *arg : *arguments)
+        for (TIntermNode *arg : arguments)
         {
             const TIntermTyped *argTyped = arg->getAsTyped();
             ASSERT(argTyped != nullptr);
@@ -821,7 +821,7 @@
 
         if (type.isMatrix() && matrixArg)
         {
-            if (arguments->size() != 1)
+            if (arguments.size() != 1)
             {
                 error(line, "constructing matrix from matrix can only take one argument",
                       "constructor");
@@ -3418,19 +3418,12 @@
     return new TFunction(&symbolTable, name, new TType(type), SymbolType::UserDefined, false);
 }
 
-TFunction *TParseContext::addNonConstructorFunc(const TString *name, const TSourceLoc &loc)
+TFunctionLookup *TParseContext::addNonConstructorFunc(const TString *name)
 {
-    const TType *returnType = StaticType::GetQualified<EbtVoid, EvqTemporary>();
-    // TODO(oetuaho): Some more appropriate data structure than TFunction could be used here. We're
-    // really only interested in the mangled name of the function to look up the actual function
-    // from the symbol table. If we just had the name string and the types of the parameters that
-    // would be enough, but TFunction carries a lot of extra information in addition to that.
-    // Besides function calls we do have to store constructor calls in the same data structure, for
-    // them we need to store a TType.
-    return new TFunction(&symbolTable, name, returnType, SymbolType::NotResolved, false);
+    return TFunctionLookup::CreateFunctionCall(name);
 }
 
-TFunction *TParseContext::addConstructorFunc(const TPublicType &publicType)
+TFunctionLookup *TParseContext::addConstructorFunc(const TPublicType &publicType)
 {
     if (mShaderVersion < 300 && publicType.isArray())
     {
@@ -3450,8 +3443,7 @@
               getBasicString(publicType.getBasicType()));
         type->setBasicType(EbtFloat);
     }
-
-    return new TFunction(&symbolTable, nullptr, type, SymbolType::NotResolved, true, EOpConstruct);
+    return TFunctionLookup::CreateConstructor(type);
 }
 
 void TParseContext::checkIsNotUnsizedArray(const TSourceLoc &line,
@@ -3502,18 +3494,19 @@
     return parseParameterDeclarator(arrayType, name, nameLoc);
 }
 
-bool TParseContext::checkUnsizedArrayConstructorArgumentDimensionality(TIntermSequence *arguments,
-                                                                       TType type,
-                                                                       const TSourceLoc &line)
+bool TParseContext::checkUnsizedArrayConstructorArgumentDimensionality(
+    const TIntermSequence &arguments,
+    TType type,
+    const TSourceLoc &line)
 {
-    if (arguments->empty())
+    if (arguments.empty())
     {
         error(line, "implicitly sized array constructor must have at least one argument", "[]");
         return false;
     }
-    for (TIntermNode *arg : *arguments)
+    for (TIntermNode *arg : arguments)
     {
-        TIntermTyped *element = arg->getAsTyped();
+        const TIntermTyped *element = arg->getAsTyped();
         ASSERT(element);
         size_t dimensionalityFromElement = element->getType().getNumArraySizes() + 1u;
         if (dimensionalityFromElement > type.getNumArraySizes())
@@ -3546,10 +3539,10 @@
 //
 // Returns a node to add to the tree regardless of if an error was generated or not.
 //
-TIntermTyped *TParseContext::addConstructor(TIntermSequence *arguments,
-                                            TType type,
-                                            const TSourceLoc &line)
+TIntermTyped *TParseContext::addConstructor(TFunctionLookup *fnCall, const TSourceLoc &line)
 {
+    TType type                 = fnCall->constructorType();
+    TIntermSequence &arguments = fnCall->arguments();
     if (type.isUnsizedArray())
     {
         if (!checkUnsizedArrayConstructorArgumentDimensionality(arguments, type, line))
@@ -3557,11 +3550,11 @@
             type.sizeUnsizedArrays(nullptr);
             return CreateZeroNode(type);
         }
-        TIntermTyped *firstElement = arguments->at(0)->getAsTyped();
+        TIntermTyped *firstElement = arguments.at(0)->getAsTyped();
         ASSERT(firstElement);
         if (type.getOutermostArraySize() == 0u)
         {
-            type.sizeOutermostUnsizedArray(static_cast<unsigned int>(arguments->size()));
+            type.sizeOutermostUnsizedArray(static_cast<unsigned int>(arguments.size()));
         }
         for (size_t i = 0; i < firstElement->getType().getNumArraySizes(); ++i)
         {
@@ -3578,7 +3571,7 @@
         return CreateZeroNode(type);
     }
 
-    TIntermAggregate *constructorNode = TIntermAggregate::CreateConstructor(type, arguments);
+    TIntermAggregate *constructorNode = TIntermAggregate::CreateConstructor(type, &arguments);
     constructorNode->setLine(line);
 
     return constructorNode->fold(mDiagnostics);
@@ -5744,56 +5737,39 @@
     }
 }
 
-TIntermSequence *TParseContext::createEmptyArgumentsList()
+TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunctionLookup *fnCall, const TSourceLoc &loc)
 {
-    return new TIntermSequence();
+    if (fnCall->thisNode() != nullptr)
+    {
+        return addMethod(fnCall, loc);
+    }
+    if (fnCall->isConstructor())
+    {
+        return addConstructor(fnCall, loc);
+    }
+    return addNonConstructorFunctionCall(fnCall, loc);
 }
 
-TIntermTyped *TParseContext::addFunctionCallOrMethod(TFunction *fnCall,
-                                                     TIntermSequence *arguments,
-                                                     TIntermNode *thisNode,
-                                                     const TSourceLoc &loc)
+TIntermTyped *TParseContext::addMethod(TFunctionLookup *fnCall, const TSourceLoc &loc)
 {
-    if (thisNode != nullptr)
-    {
-        return addMethod(fnCall->name(), arguments, thisNode, loc);
-    }
-
-    TOperator op = fnCall->getBuiltInOp();
-    if (op == EOpConstruct)
-    {
-        return addConstructor(arguments, fnCall->getReturnType(), loc);
-    }
-    else
-    {
-        ASSERT(op == EOpNull);
-        return addNonConstructorFunctionCall(fnCall->name(), arguments, loc);
-    }
-}
-
-TIntermTyped *TParseContext::addMethod(const TString &name,
-                                       TIntermSequence *arguments,
-                                       TIntermNode *thisNode,
-                                       const TSourceLoc &loc)
-{
-    TIntermTyped *typedThis    = thisNode->getAsTyped();
+    TIntermTyped *thisNode = fnCall->thisNode();
     // It's possible for the name pointer in the TFunction to be null in case it gets parsed as
     // a constructor. But such a TFunction can't reach here, since the lexer goes into FIELDS
     // mode after a dot, which makes type identifiers to be parsed as FIELD_SELECTION instead.
     // So accessing fnCall->name() below is safe.
-    if (name != "length")
+    if (fnCall->name() != "length")
     {
-        error(loc, "invalid method", name.c_str());
+        error(loc, "invalid method", fnCall->name().c_str());
     }
-    else if (!arguments->empty())
+    else if (!fnCall->arguments().empty())
     {
         error(loc, "method takes no parameters", "length");
     }
-    else if (typedThis == nullptr || !typedThis->isArray())
+    else if (!thisNode->isArray())
     {
         error(loc, "length can only be called on arrays", "length");
     }
-    else if (typedThis->getQualifier() == EvqPerVertexIn &&
+    else if (thisNode->getQualifier() == EvqPerVertexIn &&
              mGeometryShaderInputPrimitiveType == EptUndefined)
     {
         ASSERT(mShaderType == GL_GEOMETRY_SHADER_EXT);
@@ -5801,32 +5777,30 @@
     }
     else
     {
-        TIntermUnary *node = new TIntermUnary(EOpArrayLength, typedThis);
+        TIntermUnary *node = new TIntermUnary(EOpArrayLength, thisNode);
         node->setLine(loc);
         return node->fold(mDiagnostics);
     }
     return CreateZeroNode(TType(EbtInt, EbpUndefined, EvqConst));
 }
 
-TIntermTyped *TParseContext::addNonConstructorFunctionCall(const TString &name,
-                                                           TIntermSequence *arguments,
+TIntermTyped *TParseContext::addNonConstructorFunctionCall(TFunctionLookup *fnCall,
                                                            const TSourceLoc &loc)
 {
     // First find by unmangled name to check whether the function name has been
     // hidden by a variable name or struct typename.
     // If a function is found, check for one with a matching argument list.
-    const TSymbol *symbol = symbolTable.find(name, mShaderVersion);
+    const TSymbol *symbol = symbolTable.find(fnCall->name(), mShaderVersion);
     if (symbol != nullptr && !symbol->isFunction())
     {
-        error(loc, "function name expected", name.c_str());
+        error(loc, "function name expected", fnCall->name().c_str());
     }
     else
     {
-        symbol =
-            symbolTable.find(TFunction::GetMangledNameFromCall(name, *arguments), mShaderVersion);
+        symbol = symbolTable.find(fnCall->getMangledName(), mShaderVersion);
         if (symbol == nullptr)
         {
-            error(loc, "no matching overloaded function found", name.c_str());
+            error(loc, "no matching overloaded function found", fnCall->name().c_str());
         }
         else
         {
@@ -5839,13 +5813,13 @@
                 checkCanUseExtension(loc, fnCandidate->extension());
             }
             TOperator op = fnCandidate->getBuiltInOp();
-            if (fnCandidate->symbolType() == SymbolType::BuiltIn && op != EOpNull)
+            if (op != EOpNull)
             {
                 // A function call mapped to a built-in operation.
                 if (fnCandidate->getParamCount() == 1)
                 {
                     // Treat it like a built-in unary operator.
-                    TIntermNode *unaryParamNode = arguments->front();
+                    TIntermNode *unaryParamNode = fnCall->arguments().front();
                     TIntermTyped *callNode = createUnaryMath(op, unaryParamNode->getAsTyped(), loc);
                     ASSERT(callNode != nullptr);
                     return callNode;
@@ -5853,7 +5827,7 @@
                 else
                 {
                     TIntermAggregate *callNode =
-                        TIntermAggregate::Create(*fnCandidate, op, arguments);
+                        TIntermAggregate::Create(*fnCandidate, op, &fnCall->arguments());
                     callNode->setLine(loc);
 
                     // Some built-in functions have out parameters too.
@@ -5874,7 +5848,8 @@
                 // function with no op associated with it.
                 if (fnCandidate->symbolType() == SymbolType::BuiltIn)
                 {
-                    callNode = TIntermAggregate::CreateBuiltInFunctionCall(*fnCandidate, arguments);
+                    callNode = TIntermAggregate::CreateBuiltInFunctionCall(*fnCandidate,
+                                                                           &fnCall->arguments());
                     checkTextureOffsetConst(callNode);
                     checkTextureGather(callNode);
                     checkImageMemoryAccessForBuiltinFunctions(callNode);
@@ -5882,7 +5857,8 @@
                 }
                 else
                 {
-                    callNode = TIntermAggregate::CreateFunctionCall(*fnCandidate, arguments);
+                    callNode =
+                        TIntermAggregate::CreateFunctionCall(*fnCandidate, &fnCall->arguments());
                     checkImageMemoryAccessForUserDefinedFunctions(fnCandidate, callNode);
                 }
 
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index 8527a20..9706236 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -11,6 +11,7 @@
 #include "compiler/translator/Declarator.h"
 #include "compiler/translator/Diagnostics.h"
 #include "compiler/translator/DirectiveHandler.h"
+#include "compiler/translator/FunctionLookup.h"
 #include "compiler/translator/QualifierTypes.h"
 #include "compiler/translator/SymbolTable.h"
 
@@ -120,7 +121,7 @@
     void checkIsScalarInteger(TIntermTyped *node, const char *token);
     bool checkIsAtGlobalLevel(const TSourceLoc &line, const char *token);
     bool checkConstructorArguments(const TSourceLoc &line,
-                                   const TIntermSequence *arguments,
+                                   const TIntermSequence &arguments,
                                    const TType &type);
 
     // Returns a sanitized array size to use (the size is at least 1).
@@ -291,8 +292,8 @@
     TFunction *parseFunctionHeader(const TPublicType &type,
                                    const TString *name,
                                    const TSourceLoc &location);
-    TFunction *addNonConstructorFunc(const TString *name, const TSourceLoc &loc);
-    TFunction *addConstructorFunc(const TPublicType &publicType);
+    TFunctionLookup *addNonConstructorFunc(const TString *name);
+    TFunctionLookup *addConstructorFunc(const TPublicType &publicType);
     TParameter parseParameterDeclarator(const TPublicType &publicType,
                                         const TString *name,
                                         const TSourceLoc &nameLoc);
@@ -419,14 +420,10 @@
     void checkImageMemoryAccessForUserDefinedFunctions(const TFunction *functionDefinition,
                                                        const TIntermAggregate *functionCall);
     void checkAtomicMemoryBuiltinFunctions(TIntermAggregate *functionCall);
-    TIntermSequence *createEmptyArgumentsList();
 
     // fnCall is only storing the built-in op, and function name or constructor type. arguments
     // has the arguments.
-    TIntermTyped *addFunctionCallOrMethod(TFunction *fnCall,
-                                          TIntermSequence *arguments,
-                                          TIntermNode *thisNode,
-                                          const TSourceLoc &loc);
+    TIntermTyped *addFunctionCallOrMethod(TFunctionLookup *fnCall, const TSourceLoc &loc);
 
     TIntermTyped *addTernarySelection(TIntermTyped *cond,
                                       TIntermTyped *trueExpression,
@@ -523,7 +520,7 @@
 
     void checkYuvIsNotSpecified(const TSourceLoc &location, bool yuv);
 
-    bool checkUnsizedArrayConstructorArgumentDimensionality(TIntermSequence *arguments,
+    bool checkUnsizedArrayConstructorArgumentDimensionality(const TIntermSequence &arguments,
                                                             TType type,
                                                             const TSourceLoc &line);
 
@@ -549,16 +546,9 @@
                                 const TSourceLoc &loc);
     TIntermTyped *createUnaryMath(TOperator op, TIntermTyped *child, const TSourceLoc &loc);
 
-    TIntermTyped *addMethod(const TString &name,
-                            TIntermSequence *arguments,
-                            TIntermNode *thisNode,
-                            const TSourceLoc &loc);
-    TIntermTyped *addConstructor(TIntermSequence *arguments,
-                                 TType type,
-                                 const TSourceLoc &line);
-    TIntermTyped *addNonConstructorFunctionCall(const TString &name,
-                                                TIntermSequence *arguments,
-                                                const TSourceLoc &loc);
+    TIntermTyped *addMethod(TFunctionLookup *fnCall, const TSourceLoc &loc);
+    TIntermTyped *addConstructor(TFunctionLookup *fnCall, const TSourceLoc &line);
+    TIntermTyped *addNonConstructorFunctionCall(TFunctionLookup *fnCall, const TSourceLoc &loc);
 
     // Return either the original expression or the folded version of the expression in case the
     // folded node will validate the same way during subsequent parsing.
diff --git a/src/compiler/translator/Symbol.cpp b/src/compiler/translator/Symbol.cpp
index 664efcd..6ec6449 100644
--- a/src/compiler/translator/Symbol.cpp
+++ b/src/compiler/translator/Symbol.cpp
@@ -35,7 +35,7 @@
 {
     ASSERT(mSymbolType == SymbolType::BuiltIn || mExtension == TExtension::UNDEFINED);
     ASSERT(mName != nullptr || mSymbolType == SymbolType::AngleInternal ||
-           mSymbolType == SymbolType::NotResolved || mSymbolType == SymbolType::Empty);
+           mSymbolType == SymbolType::Empty);
     ASSERT(mName == nullptr || *mName != "");
 }
 
@@ -167,19 +167,6 @@
     return NewPoolTString(newName.c_str());
 }
 
-const TString &TFunction::GetMangledNameFromCall(const TString &functionName,
-                                                 const TIntermSequence &arguments)
-{
-    std::string newName = functionName.c_str();
-    newName += kFunctionMangledNameSeparator;
-
-    for (TIntermNode *argument : arguments)
-    {
-        newName += argument->getAsTyped()->getType().getMangledName();
-    }
-    return *NewPoolTString(newName.c_str());
-}
-
 bool TFunction::isMain() const
 {
     return symbolType() == SymbolType::UserDefined && name() == "main";
diff --git a/src/compiler/translator/Symbol.h b/src/compiler/translator/Symbol.h
index f6750c6..cff5d7a 100644
--- a/src/compiler/translator/Symbol.h
+++ b/src/compiler/translator/Symbol.h
@@ -24,8 +24,7 @@
     BuiltIn,
     UserDefined,
     AngleInternal,
-    Empty,  // Meaning symbol without a name.
-    NotResolved
+    Empty  // Meaning symbol without a name.
 };
 
 // Symbol base class. (Can build functions or variables out of these...)
@@ -209,9 +208,6 @@
         return *mangledName;
     }
 
-    static const TString &GetMangledNameFromCall(const TString &functionName,
-                                                 const TIntermSequence &arguments);
-
     const TType &getReturnType() const { return *returnType; }
 
     TOperator getBuiltInOp() const { return op; }
@@ -236,11 +232,9 @@
 
     typedef TVector<TConstParameter> TParamList;
     TParamList parameters;
-    const TType *returnType;
+    const TType *const returnType;
     mutable const TString *mangledName;
-    // TODO(oetuaho): Remove op from TFunction once TFunction is not used for looking up builtins or
-    // constructors.
-    TOperator op;
+    const TOperator op;  // Only set for built-ins
     bool defined;
     bool mHasPrototypeDeclaration;
     bool mKnownToNotHaveSideEffects;
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index aeddf29..897e1e9 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -76,7 +76,6 @@
         union {
             TIntermNode *intermNode;
             TIntermNodePair nodePair;
-            TIntermFunctionCallOrMethod callOrMethodPair;
             TIntermTyped *intermTypedNode;
             TIntermAggregate *intermAggregate;
             TIntermBlock *intermBlock;
@@ -93,6 +92,7 @@
             TLayoutQualifier layoutQualifier;
             TQualifier qualifier;
             TFunction *function;
+            TFunctionLookup *functionLookup;
             TParameter param;
             TDeclarator *declarator;
             TDeclaratorList *declaratorList;
@@ -236,10 +236,12 @@
 %type <interm.declarator> struct_declarator
 %type <interm.declaratorList> struct_declarator_list
 %type <interm.fieldList> struct_declaration struct_declaration_list
-%type <interm.function> function_header function_declarator function_identifier
-%type <interm.function> function_header_with_parameters function_call_header
-%type <interm> function_call_header_with_parameters function_call_header_no_parameters function_call_generic function_prototype
-%type <interm> function_call_or_method
+%type <interm.function> function_header function_declarator
+%type <interm.function> function_header_with_parameters
+%type <interm.functionLookup> function_identifier function_call_header
+%type <interm.functionLookup> function_call_header_with_parameters function_call_header_no_parameters
+%type <interm.functionLookup> function_call_generic function_call_or_method
+%type <interm> function_prototype
 
 %type <lex> enter_struct
 
@@ -328,19 +330,18 @@
 
 function_call
     : function_call_or_method {
-        $$ = context->addFunctionCallOrMethod($1.function, $1.callOrMethodPair.arguments, $1.callOrMethodPair.thisNode, @1);
+        $$ = context->addFunctionCallOrMethod($1, @1);
     }
     ;
 
 function_call_or_method
     : function_call_generic {
         $$ = $1;
-        $$.callOrMethodPair.thisNode = nullptr;
     }
     | postfix_expression DOT function_call_generic {
         ES3_OR_NEWER("", @3, "methods");
         $$ = $3;
-        $$.callOrMethodPair.thisNode = $1;
+        $$->setThisNode($1);
     }
     ;
 
@@ -355,24 +356,21 @@
 
 function_call_header_no_parameters
     : function_call_header VOID_TYPE {
-        $$.function = $1;
-        $$.callOrMethodPair.arguments = context->createEmptyArgumentsList();
+        $$ = $1;
     }
     | function_call_header {
-        $$.function = $1;
-        $$.callOrMethodPair.arguments = context->createEmptyArgumentsList();
+        $$ = $1;
     }
     ;
 
 function_call_header_with_parameters
     : function_call_header assignment_expression {
-        $$.callOrMethodPair.arguments = context->createEmptyArgumentsList();
-        $$.function = $1;
-        $$.callOrMethodPair.arguments->push_back($2);
+        $$ = $1;
+        $$->addArgument($2);
     }
     | function_call_header_with_parameters COMMA assignment_expression {
-        $$.function = $1.function;
-        $$.callOrMethodPair.arguments->push_back($3);
+        $$ = $1;
+        $$->addArgument($3);
     }
     ;
 
@@ -389,10 +387,10 @@
         $$ = context->addConstructorFunc($1);
     }
     | IDENTIFIER {
-        $$ = context->addNonConstructorFunc($1.string, @1);
+        $$ = context->addNonConstructorFunc($1.string);
     }
     | FIELD_SELECTION {
-        $$ = context->addNonConstructorFunc($1.string, @1);
+        $$ = context->addNonConstructorFunc($1.string);
     }
     ;
 
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 00809dd..89bd638 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -317,7 +317,6 @@
         union {
             TIntermNode *intermNode;
             TIntermNodePair nodePair;
-            TIntermFunctionCallOrMethod callOrMethodPair;
             TIntermTyped *intermTypedNode;
             TIntermAggregate *intermAggregate;
             TIntermBlock *intermBlock;
@@ -334,6 +333,7 @@
             TLayoutQualifier layoutQualifier;
             TQualifier qualifier;
             TFunction *function;
+            TFunctionLookup *functionLookup;
             TParameter param;
             TDeclarator *declarator;
             TDeclaratorList *declaratorList;
@@ -743,36 +743,36 @@
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint16 yyrline[] =
 {
-       0,   250,   250,   251,   254,   264,   267,   272,   277,   282,
-     287,   296,   302,   305,   308,   311,   314,   317,   323,   330,
-     336,   340,   348,   351,   357,   361,   368,   373,   380,   388,
-     391,   394,   400,   403,   406,   409,   416,   417,   418,   419,
-     427,   428,   431,   434,   441,   442,   445,   451,   452,   456,
-     463,   464,   467,   470,   473,   479,   480,   483,   489,   490,
-     497,   498,   505,   506,   513,   514,   520,   521,   527,   528,
-     534,   535,   541,   542,   548,   549,   550,   551,   555,   556,
-     557,   561,   565,   569,   573,   580,   583,   589,   596,   603,
-     606,   609,   613,   617,   621,   625,   629,   636,   643,   646,
-     653,   661,   678,   688,   691,   697,   701,   705,   709,   716,
-     723,   726,   730,   734,   739,   746,   750,   754,   758,   763,
-     770,   774,   780,   783,   789,   793,   800,   806,   810,   814,
-     817,   820,   829,   834,   838,   841,   844,   847,   850,   854,
-     857,   861,   864,   867,   870,   873,   876,   883,   890,   893,
-     896,   902,   909,   912,   918,   921,   924,   927,   933,   936,
-     943,   948,   955,   960,   971,   974,   977,   980,   983,   986,
-     990,   994,   998,  1002,  1006,  1010,  1014,  1018,  1022,  1026,
-    1030,  1034,  1038,  1042,  1046,  1050,  1054,  1058,  1062,  1066,
-    1070,  1077,  1080,  1083,  1086,  1089,  1092,  1095,  1098,  1101,
-    1104,  1107,  1110,  1113,  1116,  1119,  1122,  1125,  1128,  1131,
-    1141,  1148,  1155,  1158,  1161,  1164,  1167,  1170,  1173,  1176,
-    1179,  1182,  1185,  1188,  1191,  1194,  1197,  1205,  1205,  1208,
-    1208,  1214,  1217,  1223,  1226,  1233,  1237,  1243,  1246,  1252,
-    1256,  1260,  1261,  1267,  1268,  1269,  1270,  1271,  1272,  1273,
-    1277,  1281,  1281,  1281,  1288,  1289,  1293,  1293,  1294,  1294,
-    1299,  1303,  1310,  1314,  1321,  1322,  1326,  1332,  1336,  1345,
-    1345,  1352,  1355,  1361,  1365,  1371,  1371,  1376,  1376,  1380,
-    1380,  1388,  1391,  1397,  1400,  1406,  1410,  1417,  1420,  1423,
-    1426,  1429,  1437,  1443,  1449,  1452,  1458,  1458
+       0,   252,   252,   253,   256,   266,   269,   274,   279,   284,
+     289,   298,   304,   307,   310,   313,   316,   319,   325,   332,
+     338,   341,   349,   352,   358,   361,   367,   371,   378,   386,
+     389,   392,   398,   401,   404,   407,   414,   415,   416,   417,
+     425,   426,   429,   432,   439,   440,   443,   449,   450,   454,
+     461,   462,   465,   468,   471,   477,   478,   481,   487,   488,
+     495,   496,   503,   504,   511,   512,   518,   519,   525,   526,
+     532,   533,   539,   540,   546,   547,   548,   549,   553,   554,
+     555,   559,   563,   567,   571,   578,   581,   587,   594,   601,
+     604,   607,   611,   615,   619,   623,   627,   634,   641,   644,
+     651,   659,   676,   686,   689,   695,   699,   703,   707,   714,
+     721,   724,   728,   732,   737,   744,   748,   752,   756,   761,
+     768,   772,   778,   781,   787,   791,   798,   804,   808,   812,
+     815,   818,   827,   832,   836,   839,   842,   845,   848,   852,
+     855,   859,   862,   865,   868,   871,   874,   881,   888,   891,
+     894,   900,   907,   910,   916,   919,   922,   925,   931,   934,
+     941,   946,   953,   958,   969,   972,   975,   978,   981,   984,
+     988,   992,   996,  1000,  1004,  1008,  1012,  1016,  1020,  1024,
+    1028,  1032,  1036,  1040,  1044,  1048,  1052,  1056,  1060,  1064,
+    1068,  1075,  1078,  1081,  1084,  1087,  1090,  1093,  1096,  1099,
+    1102,  1105,  1108,  1111,  1114,  1117,  1120,  1123,  1126,  1129,
+    1139,  1146,  1153,  1156,  1159,  1162,  1165,  1168,  1171,  1174,
+    1177,  1180,  1183,  1186,  1189,  1192,  1195,  1203,  1203,  1206,
+    1206,  1212,  1215,  1221,  1224,  1231,  1235,  1241,  1244,  1250,
+    1254,  1258,  1259,  1265,  1266,  1267,  1268,  1269,  1270,  1271,
+    1275,  1279,  1279,  1279,  1286,  1287,  1291,  1291,  1292,  1292,
+    1297,  1301,  1308,  1312,  1319,  1320,  1324,  1330,  1334,  1343,
+    1343,  1350,  1353,  1359,  1363,  1369,  1369,  1374,  1374,  1378,
+    1378,  1386,  1389,  1395,  1398,  1404,  1408,  1415,  1418,  1421,
+    1424,  1427,  1435,  1441,  1447,  1450,  1456,  1456
 };
 #endif
 
@@ -2636,7 +2636,7 @@
   case 19:
 
     {
-        (yyval.interm.intermTypedNode) = context->addFunctionCallOrMethod((yyvsp[0].interm).function, (yyvsp[0].interm).callOrMethodPair.arguments, (yyvsp[0].interm).callOrMethodPair.thisNode, (yylsp[0]));
+        (yyval.interm.intermTypedNode) = context->addFunctionCallOrMethod((yyvsp[0].interm.functionLookup), (yylsp[0]));
     }
 
     break;
@@ -2644,8 +2644,7 @@
   case 20:
 
     {
-        (yyval.interm) = (yyvsp[0].interm);
-        (yyval.interm).callOrMethodPair.thisNode = nullptr;
+        (yyval.interm.functionLookup) = (yyvsp[0].interm.functionLookup);
     }
 
     break;
@@ -2654,8 +2653,8 @@
 
     {
         ES3_OR_NEWER("", (yylsp[0]), "methods");
-        (yyval.interm) = (yyvsp[0].interm);
-        (yyval.interm).callOrMethodPair.thisNode = (yyvsp[-2].interm.intermTypedNode);
+        (yyval.interm.functionLookup) = (yyvsp[0].interm.functionLookup);
+        (yyval.interm.functionLookup)->setThisNode((yyvsp[-2].interm.intermTypedNode));
     }
 
     break;
@@ -2663,7 +2662,7 @@
   case 22:
 
     {
-        (yyval.interm) = (yyvsp[-1].interm);
+        (yyval.interm.functionLookup) = (yyvsp[-1].interm.functionLookup);
     }
 
     break;
@@ -2671,7 +2670,7 @@
   case 23:
 
     {
-        (yyval.interm) = (yyvsp[-1].interm);
+        (yyval.interm.functionLookup) = (yyvsp[-1].interm.functionLookup);
     }
 
     break;
@@ -2679,8 +2678,7 @@
   case 24:
 
     {
-        (yyval.interm).function = (yyvsp[-1].interm.function);
-        (yyval.interm).callOrMethodPair.arguments = context->createEmptyArgumentsList();
+        (yyval.interm.functionLookup) = (yyvsp[-1].interm.functionLookup);
     }
 
     break;
@@ -2688,8 +2686,7 @@
   case 25:
 
     {
-        (yyval.interm).function = (yyvsp[0].interm.function);
-        (yyval.interm).callOrMethodPair.arguments = context->createEmptyArgumentsList();
+        (yyval.interm.functionLookup) = (yyvsp[0].interm.functionLookup);
     }
 
     break;
@@ -2697,9 +2694,8 @@
   case 26:
 
     {
-        (yyval.interm).callOrMethodPair.arguments = context->createEmptyArgumentsList();
-        (yyval.interm).function = (yyvsp[-1].interm.function);
-        (yyval.interm).callOrMethodPair.arguments->push_back((yyvsp[0].interm.intermTypedNode));
+        (yyval.interm.functionLookup) = (yyvsp[-1].interm.functionLookup);
+        (yyval.interm.functionLookup)->addArgument((yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -2707,8 +2703,8 @@
   case 27:
 
     {
-        (yyval.interm).function = (yyvsp[-2].interm).function;
-        (yyval.interm).callOrMethodPair.arguments->push_back((yyvsp[0].interm.intermTypedNode));
+        (yyval.interm.functionLookup) = (yyvsp[-2].interm.functionLookup);
+        (yyval.interm.functionLookup)->addArgument((yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -2716,7 +2712,7 @@
   case 28:
 
     {
-        (yyval.interm.function) = (yyvsp[-1].interm.function);
+        (yyval.interm.functionLookup) = (yyvsp[-1].interm.functionLookup);
     }
 
     break;
@@ -2724,7 +2720,7 @@
   case 29:
 
     {
-        (yyval.interm.function) = context->addConstructorFunc((yyvsp[0].interm.type));
+        (yyval.interm.functionLookup) = context->addConstructorFunc((yyvsp[0].interm.type));
     }
 
     break;
@@ -2732,7 +2728,7 @@
   case 30:
 
     {
-        (yyval.interm.function) = context->addNonConstructorFunc((yyvsp[0].lex).string, (yylsp[0]));
+        (yyval.interm.functionLookup) = context->addNonConstructorFunc((yyvsp[0].lex).string);
     }
 
     break;
@@ -2740,7 +2736,7 @@
   case 31:
 
     {
-        (yyval.interm.function) = context->addNonConstructorFunc((yyvsp[0].lex).string, (yylsp[0]));
+        (yyval.interm.functionLookup) = context->addNonConstructorFunc((yyvsp[0].lex).string);
     }
 
     break;
diff --git a/src/compiler/translator/glslang_tab.h b/src/compiler/translator/glslang_tab.h
index 048831b..1f1d9b7 100644
--- a/src/compiler/translator/glslang_tab.h
+++ b/src/compiler/translator/glslang_tab.h
@@ -228,7 +228,6 @@
         union {
             TIntermNode *intermNode;
             TIntermNodePair nodePair;
-            TIntermFunctionCallOrMethod callOrMethodPair;
             TIntermTyped *intermTypedNode;
             TIntermAggregate *intermAggregate;
             TIntermBlock *intermBlock;
@@ -245,6 +244,7 @@
             TLayoutQualifier layoutQualifier;
             TQualifier qualifier;
             TFunction *function;
+            TFunctionLookup *functionLookup;
             TParameter param;
             TDeclarator *declarator;
             TDeclaratorList *declaratorList;
diff --git a/src/tests/test_utils/compiler_test.cpp b/src/tests/test_utils/compiler_test.cpp
index 24d24b6..cb2c9d1 100644
--- a/src/tests/test_utils/compiler_test.cpp
+++ b/src/tests/test_utils/compiler_test.cpp
@@ -10,6 +10,7 @@
 
 #include "angle_gl.h"
 #include "compiler/translator/Compiler.h"
+#include "compiler/translator/FunctionLookup.h"
 #include "compiler/translator/IntermTraverse.h"
 
 namespace sh
@@ -18,6 +19,22 @@
 namespace
 {
 
+const TString &GetSymbolTableMangledName(TIntermAggregate *node)
+{
+    ASSERT(!node->isConstructor());
+    switch (node->getOp())
+    {
+        case EOpCallInternalRawFunction:
+        case EOpCallBuiltInFunction:
+        case EOpCallFunctionInAST:
+            return TFunctionLookup::GetMangledName(node->getFunction()->name(),
+                                                   *node->getSequence());
+        default:
+            TString opString = GetOperatorString(node->getOp());
+            return TFunctionLookup::GetMangledName(opString, *node->getSequence());
+    }
+}
+
 class FunctionCallFinder : public TIntermTraverser
 {
   public:
@@ -30,7 +47,7 @@
 
     bool visitAggregate(Visit visit, TIntermAggregate *node) override
     {
-        if (node->isFunctionCall() && node->getSymbolTableMangledName() == mFunctionMangledName)
+        if (node->isFunctionCall() && GetSymbolTableMangledName(node) == mFunctionMangledName)
         {
             mNodeFound = node;
             return false;