Add symbol table function to get TFunction for a built-in op

Built-in function parameter qualifiers are stored in the symbol table.
Some AST traversers need the qualifier information for ops to
determine whether a node is being written to. Add an utility function
that maps a TIntermAggregate node to a symbol table entry, so that the
traversers can get to this information in a convenient way.

This will be necessary for adding more built-ins that have out
parameters from ESSL 3.10.

BUG=angleproject:1730
TEST=angle_unittests

Change-Id: I4bc622d70b2326a04cc858ff1258c22320c590dc
Reviewed-on: https://chromium-review.googlesource.com/431109
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/ValidateMultiviewWebGL.cpp b/src/compiler/translator/ValidateMultiviewWebGL.cpp
index 11740c3..08ae816 100644
--- a/src/compiler/translator/ValidateMultiviewWebGL.cpp
+++ b/src/compiler/translator/ValidateMultiviewWebGL.cpp
@@ -31,6 +31,8 @@
     // errors.
     static bool validate(TIntermBlock *root,
                          GLenum shaderType,
+                         const TSymbolTable &symbolTable,
+                         int shaderVersion,
                          bool multiview2,
                          TDiagnostics *diagnostics);
 
@@ -44,7 +46,11 @@
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
 
   private:
-    ValidateMultiviewTraverser(GLenum shaderType, bool multiview2, TDiagnostics *diagnostics);
+    ValidateMultiviewTraverser(GLenum shaderType,
+                               const TSymbolTable &symbolTable,
+                               int shaderVersion,
+                               bool multiview2,
+                               TDiagnostics *diagnostics);
 
     static bool IsGLPosition(TIntermNode *node);
     static bool IsGLViewIDOVR(TIntermNode *node);
@@ -56,6 +62,8 @@
     bool mValid;
     bool mMultiview2;
     GLenum mShaderType;
+    const TSymbolTable &mSymbolTable;
+    const int mShaderVersion;
 
     bool mInsideViewIDConditional;  // Only set if mMultiview2 is false.
     bool mInsideRestrictedAssignment;
@@ -67,22 +75,29 @@
 
 bool ValidateMultiviewTraverser::validate(TIntermBlock *root,
                                           GLenum shaderType,
+                                          const TSymbolTable &symbolTable,
+                                          int shaderVersion,
                                           bool multiview2,
                                           TDiagnostics *diagnostics)
 {
-    ValidateMultiviewTraverser validate(shaderType, multiview2, diagnostics);
+    ValidateMultiviewTraverser validate(shaderType, symbolTable, shaderVersion, multiview2,
+                                        diagnostics);
     ASSERT(root);
     root->traverse(&validate);
     return validate.isValid();
 }
 
 ValidateMultiviewTraverser::ValidateMultiviewTraverser(GLenum shaderType,
+                                                       const TSymbolTable &symbolTable,
+                                                       int shaderVersion,
                                                        bool multiview2,
                                                        TDiagnostics *diagnostics)
     : TIntermTraverser(true, true, true),
       mValid(true),
       mMultiview2(multiview2),
       mShaderType(shaderType),
+      mSymbolTable(symbolTable),
+      mShaderVersion(shaderVersion),
       mInsideViewIDConditional(false),
       mInsideRestrictedAssignment(false),
       mGLPositionAllowed(multiview2),
@@ -361,17 +376,21 @@
                 mValid = false;
             }
         }
-        else if (node->getOp() == EOpModf)
+        else if (!node->isConstructor())
         {
-            // TODO(oetuaho@nvidia.com): It's quite hacky to hard-code modf - should maybe refactor
-            // out parameter detecting functionality in LValueTrackingTraverser so that it could be
-            // used here as well?
-            // LValueTrackingTraverser itself seems like a bad fit with the needs of this traverser.
-            mDiagnostics->error(node->getLine(),
-                                "Disallowed use of a function with an out parameter inside "
-                                "assignment to gl_Position.x when using OVR_multiview",
-                                GetOperatorString(node->getOp()));
-            mValid = false;
+            TFunction *builtInFunc = mSymbolTable.findBuiltInOp(node, mShaderVersion);
+            for (size_t paramIndex = 0u; paramIndex < builtInFunc->getParamCount(); ++paramIndex)
+            {
+                TQualifier qualifier = builtInFunc->getParam(paramIndex).type->getQualifier();
+                if (qualifier == EvqOut || qualifier == EvqInOut)
+                {
+                    mDiagnostics->error(node->getLine(),
+                                        "Disallowed use of a function with an out parameter inside "
+                                        "assignment to gl_Position.x when using OVR_multiview",
+                                        GetOperatorString(node->getOp()));
+                    mValid = false;
+                }
+            }
         }
     }
     return true;
@@ -381,6 +400,8 @@
 
 bool ValidateMultiviewWebGL(TIntermBlock *root,
                             GLenum shaderType,
+                            const TSymbolTable &symbolTable,
+                            int shaderVersion,
                             bool multiview2,
                             TDiagnostics *diagnostics)
 {
@@ -388,7 +409,8 @@
     {
         return true;
     }
-    return ValidateMultiviewTraverser::validate(root, shaderType, multiview2, diagnostics);
+    return ValidateMultiviewTraverser::validate(root, shaderType, symbolTable, shaderVersion,
+                                                multiview2, diagnostics);
 }
 
 }  // namespace sh