Make TVariable type immutable

This enables using constexpr types for built-in variables and some of
the variables created in AST transformations.

BUG=angleproject:2267
TEST=angle_unittests

Change-Id: Ie85b3c9872a071a7c023ced013b14ad91cff7cee
Reviewed-on: https://chromium-review.googlesource.com/868134
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Jamie Madill <jmadill@chromium.org>
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index 4264f90..139fe56 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -1109,29 +1109,29 @@
 //
 bool TParseContext::declareVariable(const TSourceLoc &line,
                                     const TString &identifier,
-                                    const TType &type,
+                                    const TType *type,
                                     TVariable **variable)
 {
     ASSERT((*variable) == nullptr);
 
     (*variable) = new TVariable(&symbolTable, &identifier, type, SymbolType::UserDefined);
 
-    checkBindingIsValid(line, type);
+    checkBindingIsValid(line, *type);
 
     bool needsReservedCheck = true;
 
     // gl_LastFragData may be redeclared with a new precision qualifier
-    if (type.isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0)
+    if (type->isArray() && identifier.compare(0, 15, "gl_LastFragData") == 0)
     {
         const TVariable *maxDrawBuffers = static_cast<const TVariable *>(
             symbolTable.findBuiltIn("gl_MaxDrawBuffers", mShaderVersion));
-        if (type.isArrayOfArrays())
+        if (type->isArrayOfArrays())
         {
             error(line, "redeclaration of gl_LastFragData as an array of arrays",
                   identifier.c_str());
             return false;
         }
-        else if (static_cast<int>(type.getOutermostArraySize()) ==
+        else if (static_cast<int>(type->getOutermostArraySize()) ==
                  maxDrawBuffers->getConstPointer()->getIConst())
         {
             if (TSymbol *builtInSymbol = symbolTable.findBuiltIn(identifier, mShaderVersion))
@@ -1156,7 +1156,7 @@
         return false;
     }
 
-    if (!checkIsNonVoid(line, identifier, type.getBasicType()))
+    if (!checkIsNonVoid(line, identifier, type->getBasicType()))
         return false;
 
     return true;
@@ -1914,21 +1914,39 @@
 // Returns true on success.
 bool TParseContext::executeInitializer(const TSourceLoc &line,
                                        const TString &identifier,
-                                       TType type,
+                                       TType *type,
                                        TIntermTyped *initializer,
                                        TIntermBinary **initNode)
 {
     ASSERT(initNode != nullptr);
     ASSERT(*initNode == nullptr);
 
-    if (type.isUnsizedArray())
+    if (type->isUnsizedArray())
     {
         // In case initializer is not an array or type has more dimensions than initializer, this
         // will default to setting array sizes to 1. We have not checked yet whether the initializer
         // actually is an array or not. Having a non-array initializer for an unsized array will
         // result in an error later, so we don't generate an error message here.
         auto *arraySizes = initializer->getType().getArraySizes();
-        type.sizeUnsizedArrays(arraySizes);
+        type->sizeUnsizedArrays(arraySizes);
+    }
+
+    const TQualifier qualifier = type->getQualifier();
+
+    bool constError = false;
+    if (qualifier == EvqConst)
+    {
+        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(), "=");
+
+            // We're still going to declare the variable to avoid extra error messages.
+            type->setQualifier(EvqTemporary);
+            constError = true;
+        }
     }
 
     TVariable *variable = nullptr;
@@ -1937,6 +1955,11 @@
         return false;
     }
 
+    if (constError)
+    {
+        return false;
+    }
+
     bool globalInitWarning = false;
     if (symbolTable.atGlobalLevel() &&
         !ValidateGlobalInitializer(initializer, mShaderVersion, &globalInitWarning))
@@ -1955,40 +1978,26 @@
             "=");
     }
 
-    //
     // identifier must be of type constant, a global, or a temporary
-    //
-    TQualifier qualifier = variable->getType().getQualifier();
     if ((qualifier != EvqTemporary) && (qualifier != EvqGlobal) && (qualifier != EvqConst))
     {
         error(line, " cannot initialize this type of qualifier ",
               variable->getType().getQualifierString());
         return false;
     }
-    //
-    // test for and propagate constant
-    //
+
+    TIntermSymbol *intermSymbol = new TIntermSymbol(variable);
+    intermSymbol->setLine(line);
+
+    if (!binaryOpCommonCheck(EOpInitialize, intermSymbol, initializer, line))
+    {
+        assignError(line, "=", variable->getType().getCompleteString(),
+                    initializer->getCompleteString());
+        return false;
+    }
 
     if (qualifier == EvqConst)
     {
-        if (qualifier != initializer->getType().getQualifier())
-        {
-            std::stringstream reasonStream;
-            reasonStream << "assigning non-constant to '" << variable->getType().getCompleteString()
-                         << "'";
-            std::string reason = reasonStream.str();
-            error(line, reason.c_str(), "=");
-            variable->getType().setQualifier(EvqTemporary);
-            return false;
-        }
-        if (type != initializer->getType())
-        {
-            error(line, " non-matching types for const initializer ",
-                  variable->getType().getQualifierString());
-            variable->getType().setQualifier(EvqTemporary);
-            return false;
-        }
-
         // Save the constant folded value to the variable if possible.
         const TConstantUnion *constArray = initializer->getConstantValue();
         if (constArray)
@@ -2002,15 +2011,8 @@
         }
     }
 
-    TIntermSymbol *intermSymbol = new TIntermSymbol(variable);
-    intermSymbol->setLine(line);
-    *initNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
-    if (*initNode == nullptr)
-    {
-        assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
-        return false;
-    }
-
+    *initNode = new TIntermBinary(EOpInitialize, intermSymbol, initializer);
+    (*initNode)->setLine(line);
     return true;
 }
 
@@ -2021,7 +2023,7 @@
 {
     checkIsScalarBool(loc, pType);
     TIntermBinary *initNode = nullptr;
-    TType type(pType);
+    TType *type             = new TType(pType);
     if (executeInitializer(loc, identifier, type, initializer, &initNode))
     {
         // The initializer is valid. The init condition needs to have a node - either the
@@ -2393,11 +2395,11 @@
     const TSourceLoc &identifierOrTypeLocation,
     const TString &identifier)
 {
-    TType type(publicType);
+    TType *type = new TType(publicType);
     if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) &&
         mDirectiveHandler.pragma().stdgl.invariantAll)
     {
-        TQualifier qualifier = type.getQualifier();
+        TQualifier qualifier = type->getQualifier();
 
         // The directive handler has already taken care of rejecting invalid uses of this pragma
         // (for example, in ESSL 3.00 fragment shaders), so at this point, flatten it into all
@@ -2417,11 +2419,11 @@
         // behavior of the #pragma when specified in ESSL 1.00 fragment shaders.
         if (qualifier == EvqVaryingOut || qualifier == EvqVertexOut || qualifier == EvqVaryingIn)
         {
-            type.setInvariant(true);
+            type->setInvariant(true);
         }
     }
 
-    checkGeometryShaderInputAndSetArraySize(identifierOrTypeLocation, identifier.c_str(), &type);
+    checkGeometryShaderInputAndSetArraySize(identifierOrTypeLocation, identifier.c_str(), type);
 
     declarationQualifierErrorCheck(publicType.qualifier, publicType.layoutQualifier,
                                    identifierOrTypeLocation);
@@ -2432,10 +2434,10 @@
     TIntermSymbol *symbol = nullptr;
     if (emptyDeclaration)
     {
-        emptyDeclarationErrorCheck(type, identifierOrTypeLocation);
+        emptyDeclarationErrorCheck(*type, identifierOrTypeLocation);
         // In most cases we don't need to create a symbol node for an empty declaration.
         // But if the empty declaration is declaring a struct type, the symbol node will store that.
-        if (type.getBasicType() == EbtStruct)
+        if (type->getBasicType() == EbtStruct)
         {
             TVariable *emptyVariable =
                 new TVariable(&symbolTable, nullptr, type, SymbolType::Empty);
@@ -2450,9 +2452,9 @@
     {
         nonEmptyDeclarationErrorCheck(publicType, identifierOrTypeLocation);
 
-        checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, &type);
+        checkCanBeDeclaredWithoutInitializer(identifierOrTypeLocation, identifier, type);
 
-        checkAtomicCounterOffsetDoesNotOverlap(false, identifierOrTypeLocation, &type);
+        checkAtomicCounterOffsetDoesNotOverlap(false, identifierOrTypeLocation, type);
 
         TVariable *variable = nullptr;
         if (declareVariable(identifierOrTypeLocation, identifier, type, &variable))
@@ -2487,14 +2489,14 @@
 
     checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
 
-    TType arrayType(elementType);
-    arrayType.makeArrays(arraySizes);
+    TType *arrayType = new TType(elementType);
+    arrayType->makeArrays(arraySizes);
 
-    checkGeometryShaderInputAndSetArraySize(indexLocation, identifier.c_str(), &arrayType);
+    checkGeometryShaderInputAndSetArraySize(indexLocation, identifier.c_str(), arrayType);
 
-    checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &arrayType);
+    checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, arrayType);
 
-    checkAtomicCounterOffsetDoesNotOverlap(false, identifierLocation, &arrayType);
+    checkAtomicCounterOffsetDoesNotOverlap(false, identifierLocation, arrayType);
 
     TIntermDeclaration *declaration = new TIntermDeclaration();
     declaration->setLine(identifierLocation);
@@ -2527,7 +2529,7 @@
     declaration->setLine(identifierLocation);
 
     TIntermBinary *initNode = nullptr;
-    TType type(publicType);
+    TType *type             = new TType(publicType);
     if (executeInitializer(identifierLocation, identifier, type, initializer, &initNode))
     {
         if (initNode)
@@ -2556,8 +2558,8 @@
 
     checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
 
-    TType arrayType(elementType);
-    arrayType.makeArrays(arraySizes);
+    TType *arrayType = new TType(elementType);
+    arrayType->makeArrays(arraySizes);
 
     TIntermDeclaration *declaration = new TIntermDeclaration();
     declaration->setLine(identifierLocation);
@@ -2646,13 +2648,13 @@
 
     checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
 
-    TType type(publicType);
+    TType *type = new TType(publicType);
 
-    checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), &type);
+    checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), type);
 
-    checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &type);
+    checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, type);
 
-    checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, &type);
+    checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, type);
 
     TVariable *variable = nullptr;
     if (declareVariable(identifierLocation, identifier, type, &variable))
@@ -2682,14 +2684,14 @@
 
     if (checkIsValidTypeAndQualifierForArray(arrayLocation, elementType))
     {
-        TType arrayType(elementType);
-        arrayType.makeArrays(arraySizes);
+        TType *arrayType = new TType(elementType);
+        arrayType->makeArrays(arraySizes);
 
-        checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), &arrayType);
+        checkGeometryShaderInputAndSetArraySize(identifierLocation, identifier.c_str(), arrayType);
 
-        checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, &arrayType);
+        checkCanBeDeclaredWithoutInitializer(identifierLocation, identifier, arrayType);
 
-        checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, &arrayType);
+        checkAtomicCounterOffsetDoesNotOverlap(true, identifierLocation, arrayType);
 
         TVariable *variable = nullptr;
         if (declareVariable(identifierLocation, identifier, arrayType, &variable))
@@ -2719,7 +2721,7 @@
     checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
 
     TIntermBinary *initNode = nullptr;
-    TType type(publicType);
+    TType *type             = new TType(publicType);
     if (executeInitializer(identifierLocation, identifier, type, initializer, &initNode))
     {
         //
@@ -2753,8 +2755,8 @@
 
     checkIsValidTypeAndQualifierForArray(indexLocation, elementType);
 
-    TType arrayType(elementType);
-    arrayType.makeArrays(arraySizes);
+    TType *arrayType = new TType(elementType);
+    arrayType->makeArrays(arraySizes);
 
     // initNode will correspond to the whole of "b[n] = initializer".
     TIntermBinary *initNode = nullptr;
@@ -2838,8 +2840,8 @@
     {
         TSymbol *glPerVertex              = symbolTable.findBuiltIn("gl_PerVertex", 310);
         TInterfaceBlock *glPerVertexBlock = static_cast<TInterfaceBlock *>(glPerVertex);
-        TType glInType(glPerVertexBlock, EvqPerVertexIn, TLayoutQualifier::Create());
-        glInType.makeArray(inputArraySize);
+        TType *glInType = new TType(glPerVertexBlock, EvqPerVertexIn, TLayoutQualifier::Create());
+        glInType->makeArray(inputArraySize);
         mGlInVariableWithArraySize =
             new TVariable(&symbolTable, NewPoolTString("gl_in"), glInType, SymbolType::BuiltIn,
                           TExtension::EXT_geometry_shader);
@@ -3168,7 +3170,7 @@
         if (param.name != nullptr)
         {
             TVariable *variable =
-                new TVariable(&symbolTable, param.name, *param.type, SymbolType::UserDefined);
+                new TVariable(&symbolTable, param.name, param.type, SymbolType::UserDefined);
             symbol = new TIntermSymbol(variable);
             // Insert the parameter in the symbol table.
             if (insertParametersToSymbolTable)
@@ -3195,7 +3197,7 @@
             // The parameter had no name or declaring the symbol failed - either way, add a nameless
             // symbol.
             TVariable *emptyVariable =
-                new TVariable(&symbolTable, nullptr, *param.type, SymbolType::Empty);
+                new TVariable(&symbolTable, nullptr, param.type, SymbolType::Empty);
             symbol = new TIntermSymbol(emptyVariable);
         }
         symbol->setLine(location);
@@ -3797,10 +3799,11 @@
         error(nameLine, "redefinition of an interface block name", blockName.c_str());
     }
 
-    TType interfaceBlockType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier);
+    TType *interfaceBlockType =
+        new TType(interfaceBlock, typeQualifier.qualifier, blockLayoutQualifier);
     if (arrayIndex != nullptr)
     {
-        interfaceBlockType.makeArray(arraySize);
+        interfaceBlockType->makeArray(arraySize);
     }
 
     // The instance variable gets created to refer to the interface block type from the AST
@@ -3816,18 +3819,16 @@
         for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex)
         {
             TField *field    = (*fieldList)[memberIndex];
-            TType *fieldType = field->type();
+            TType *fieldType = new TType(*field->type());
 
             // set parent pointer of the field variable
             fieldType->setInterfaceBlock(interfaceBlock);
 
+            fieldType->setQualifier(typeQualifier.qualifier);
+
             TVariable *fieldVariable =
-                new TVariable(&symbolTable, &field->name(), *fieldType, SymbolType::UserDefined);
-            if (symbolTable.declareVariable(fieldVariable))
-            {
-                fieldVariable->setQualifier(typeQualifier.qualifier);
-            }
-            else
+                new TVariable(&symbolTable, &field->name(), fieldType, SymbolType::UserDefined);
+            if (!symbolTable.declareVariable(fieldVariable))
             {
                 error(field->line(), "redefinition of an interface block member name",
                       field->name().c_str());