Don't allocate name strings for empty symbols

This removes unnecessary memory allocations.

BUG=angleproject:2267
TEST=angle_unittests

Change-Id: Ide575ea19ab2f8e9fc93092490f1352efa6024a3
Reviewed-on: https://chromium-review.googlesource.com/817415
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/CollectVariables.cpp b/src/compiler/translator/CollectVariables.cpp
index b98c9a9..b7136f2 100644
--- a/src/compiler/translator/CollectVariables.cpp
+++ b/src/compiler/translator/CollectVariables.cpp
@@ -91,7 +91,8 @@
                                              std::vector<InterfaceBlock> *infoList)
 {
     ASSERT(interfaceBlock);
-    InterfaceBlock *namedBlock = FindVariable(interfaceBlock->name(), infoList);
+    ASSERT(interfaceBlock->name());
+    InterfaceBlock *namedBlock = FindVariable(*interfaceBlock->name(), infoList);
     ASSERT(namedBlock);
 
     // Set static use on the parent interface block here
@@ -569,7 +570,10 @@
     {
         // Structures use a NONE type that isn't exposed outside ANGLE.
         variableOut->type       = GL_NONE;
-        variableOut->structName = structure->name().c_str();
+        if (structure->symbolType() != SymbolType::Empty)
+        {
+            variableOut->structName = structure->name()->c_str();
+        }
 
         const TFieldList &fields = structure->fields();
 
@@ -578,7 +582,7 @@
             // Regardless of the variable type (uniform, in/out etc.) its fields are always plain
             // ShaderVariable objects.
             ShaderVariable fieldVariable;
-            setCommonVariableProperties(*field->type(), TName(field->name()), &fieldVariable);
+            setCommonVariableProperties(*field->type(), TName(&field->name()), &fieldVariable);
             variableOut->fields.push_back(fieldVariable);
         }
     }
@@ -657,7 +661,7 @@
     const TInterfaceBlock *blockType = interfaceBlockType.getInterfaceBlock();
     ASSERT(blockType);
 
-    interfaceBlock->name       = blockType->name().c_str();
+    interfaceBlock->name         = blockType->name()->c_str();
     interfaceBlock->mappedName = getMappedName(TName(blockType->name()));
     interfaceBlock->instanceName = instanceName.c_str();
     ASSERT(!interfaceBlockType.isArrayOfArrays());  // Disallowed by GLSL ES 3.10 section 4.3.9
@@ -679,7 +683,7 @@
         const TType &fieldType = *field->type();
 
         InterfaceBlockField fieldVariable;
-        setCommonVariableProperties(fieldType, TName(field->name()), &fieldVariable);
+        setCommonVariableProperties(fieldType, TName(&field->name()), &fieldVariable);
         fieldVariable.isRowMajorLayout =
             (fieldType.getLayoutQualifier().matrixPacking == EmpRowMajor);
         interfaceBlock->fields.push_back(fieldVariable);
@@ -825,7 +829,7 @@
         const TInterfaceBlock *interfaceBlock = blockNode->getType().getInterfaceBlock();
         if (!namedBlock)
         {
-            namedBlock = findNamedInterfaceBlock(interfaceBlock->name());
+            namedBlock = findNamedInterfaceBlock(*interfaceBlock->name());
         }
         ASSERT(namedBlock);
         namedBlock->staticUse   = true;
diff --git a/src/compiler/translator/EmulatePrecision.cpp b/src/compiler/translator/EmulatePrecision.cpp
index ba09fd7..85844d6 100644
--- a/src/compiler/translator/EmulatePrecision.cpp
+++ b/src/compiler/translator/EmulatePrecision.cpp
@@ -431,7 +431,7 @@
                                                  TString name,
                                                  TIntermSequence *arguments)
 {
-    TName nameObj(name);
+    TName nameObj(&name);
     nameObj.setInternal(true);
     TIntermAggregate *callNode =
         TIntermAggregate::Create(type, EOpCallInternalRawFunction, arguments);
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 088eb43..b68724c 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -144,6 +144,10 @@
 
 }  // namespace anonymous
 
+TName::TName(const TString *name) : mName(name ? (*name) : ""), mIsInternal(false)
+{
+}
+
 ////////////////////////////////////////////////////////////////
 //
 // Member functions of the nodes used for building the tree.
@@ -576,7 +580,7 @@
 
 void TFunctionSymbolInfo::setFromFunction(const TFunction &function)
 {
-    setName(function.name());
+    setName(*function.name());
     setId(TSymbolUniqueId(function));
 }
 
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index 7172d66..0b2436d 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -66,7 +66,7 @@
 {
   public:
     POOL_ALLOCATOR_NEW_DELETE();
-    explicit TName(const TString &name) : mName(name), mIsInternal(false) {}
+    explicit TName(const TString *name);
     TName() : mName(), mIsInternal(false) {}
     TName(const TName &) = default;
     TName &operator=(const TName &) = default;
diff --git a/src/compiler/translator/IntermNode_util.cpp b/src/compiler/translator/IntermNode_util.cpp
index 98389da..367dc71 100644
--- a/src/compiler/translator/IntermNode_util.cpp
+++ b/src/compiler/translator/IntermNode_util.cpp
@@ -19,7 +19,7 @@
 TName GetInternalFunctionName(const char *name)
 {
     TString nameStr(name);
-    TName nameObj(nameStr);
+    TName nameObj(&nameStr);
     nameObj.setInternal(true);
     return nameObj;
 }
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 488921d..9d02919 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -585,7 +585,7 @@
                 TString fieldName = field->name();
                 if (structure->symbolType() == SymbolType::UserDefined ||
                     structure->symbolType() == SymbolType::Empty)
-                    fieldName = hashName(TName(fieldName));
+                    fieldName = hashName(TName(&fieldName));
 
                 out << fieldName;
                 visitChildren = false;
@@ -604,11 +604,11 @@
                 ASSERT(interfaceBlock->symbolType() != SymbolType::Empty);
                 if (interfaceBlock->symbolType() == SymbolType::UserDefined)
                 {
-                    fieldName = hashName(TName(fieldName));
+                    fieldName = hashName(TName(&fieldName));
                 }
                 else
                 {
-                    ASSERT(interfaceBlock->name() == "gl_PerVertex");
+                    ASSERT(*interfaceBlock->name() == "gl_PerVertex");
                 }
 
                 out << fieldName;
@@ -1177,7 +1177,7 @@
         const TField *field = fields[i];
         if (writeVariablePrecision(field->type()->getPrecision()))
             out << " ";
-        out << getTypeName(*field->type()) << " " << hashName(TName(field->name()));
+        out << getTypeName(*field->type()) << " " << hashName(TName(&field->name()));
         if (field->type()->isArray())
             out << ArrayString(*field->type());
         out << ";\n";
@@ -1257,7 +1257,7 @@
 
         if (writeVariablePrecision(field->type()->getPrecision()))
             out << " ";
-        out << getTypeName(*field->type()) << " " << hashName(TName(field->name()));
+        out << getTypeName(*field->type()) << " " << hashName(TName(&field->name()));
         if (field->type()->isArray())
             out << ArrayString(*field->type());
         out << ";\n";
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index 70bd12f..748c788 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -341,7 +341,7 @@
     {
         TInterfaceBlock *interfaceBlock =
             mappedStruct.blockDeclarator->getType().getInterfaceBlock();
-        const TString &interfaceBlockName = interfaceBlock->name();
+        const TString &interfaceBlockName = *interfaceBlock->name();
         const TName &instanceName         = mappedStruct.blockDeclarator->getName();
         if (mReferencedUniformBlocks.count(interfaceBlockName) == 0)
         {
@@ -380,7 +380,7 @@
 
             TType *structType = mappedStruct.field->type();
             mappedStructs +=
-                "static " + Decorate(structType->getStruct()->name()) + " " + mappedName;
+                "static " + Decorate(*structType->getStruct()->name()) + " " + mappedName;
 
             if (structType->isArray())
             {
@@ -890,7 +890,7 @@
 
             if (interfaceBlock)
             {
-                mReferencedUniformBlocks[interfaceBlock->name()] = node;
+                mReferencedUniformBlocks[*interfaceBlock->name()] = node;
             }
             else
             {
@@ -1238,7 +1238,7 @@
                 {
                     TInterfaceBlock *interfaceBlock = leftType.getInterfaceBlock();
                     TIntermSymbol *instanceArraySymbol = node->getLeft()->getAsSymbolNode();
-                    mReferencedUniformBlocks[interfaceBlock->name()] = instanceArraySymbol;
+                    mReferencedUniformBlocks[*interfaceBlock->name()] = instanceArraySymbol;
                     const int arrayIndex = node->getRight()->getAsConstantUnion()->getIConst(0);
                     out << mUniformHLSL->UniformBlockInstanceString(
                         instanceArraySymbol->getSymbol(), arrayIndex);
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index ed51367..a2b486d 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -2445,7 +2445,7 @@
         if (type.getBasicType() == EbtStruct)
         {
             TVariable *emptyVariable =
-                new TVariable(&symbolTable, NewPoolTString(""), type, SymbolType::Empty);
+                new TVariable(&symbolTable, nullptr, type, SymbolType::Empty);
             symbol = new TIntermSymbol(emptyVariable);
         }
         else if (IsAtomicCounter(publicType.getBasicType()))
@@ -3153,7 +3153,8 @@
     const TSourceLoc &location,
     bool insertParametersToSymbolTable)
 {
-    checkIsNotReserved(location, function.name());
+    ASSERT(function.name());
+    checkIsNotReserved(location, *function.name());
 
     TIntermFunctionPrototype *prototype =
         new TIntermFunctionPrototype(function.getReturnType(), TSymbolUniqueId(function));
@@ -3200,7 +3201,7 @@
             // The parameter had no name or declaring the symbol failed - either way, add a nameless
             // symbol.
             TVariable *emptyVariable =
-                new TVariable(&symbolTable, NewPoolTString(""), *param.type, SymbolType::Empty);
+                new TVariable(&symbolTable, nullptr, *param.type, SymbolType::Empty);
             symbol = new TIntermSymbol(emptyVariable);
         }
         symbol->setLine(location);
@@ -3271,12 +3272,13 @@
 {
     ASSERT(function);
     ASSERT(*function);
+    ASSERT((*function)->name());
     const TSymbol *builtIn =
         symbolTable.findBuiltIn((*function)->getMangledName(), getShaderVersion());
 
     if (builtIn)
     {
-        error(location, "built-in functions cannot be redefined", (*function)->name().c_str());
+        error(location, "built-in functions cannot be redefined", (*function)->name()->c_str());
     }
     else
     {
@@ -3298,7 +3300,7 @@
 
         if ((*function)->isDefined())
         {
-            error(location, "function already has a body", (*function)->name().c_str());
+            error(location, "function already has a body", (*function)->name()->c_str());
         }
 
         (*function)->setDefined();
@@ -3325,6 +3327,8 @@
     TFunction *prevDec =
         static_cast<TFunction *>(symbolTable.find(function->getMangledName(), getShaderVersion()));
 
+    ASSERT(function->name() != nullptr);
+
     for (size_t i = 0u; i < function->getParamCount(); ++i)
     {
         auto &param = function->getParam(i);
@@ -3332,17 +3336,17 @@
         {
             // ESSL 3.00.6 section 12.10.
             error(location, "Function parameter type cannot be a structure definition",
-                  function->name().c_str());
+                  function->name()->c_str());
         }
     }
 
     if (getShaderVersion() >= 300 && symbolTable.hasUnmangledBuiltInForShaderVersion(
-                                         function->name().c_str(), getShaderVersion()))
+                                         function->name()->c_str(), getShaderVersion()))
     {
         // With ESSL 3.00 and above, names of built-in functions cannot be redeclared as functions.
         // Therefore overloading or redefining builtin functions is an error.
         error(location, "Name of a built-in function cannot be redeclared as function",
-              function->name().c_str());
+              function->name()->c_str());
     }
     else if (prevDec)
     {
@@ -3366,12 +3370,12 @@
     //
     // Check for previously declared variables using the same name.
     //
-    TSymbol *prevSym = symbolTable.find(function->name(), getShaderVersion());
+    TSymbol *prevSym = symbolTable.find(*function->name(), getShaderVersion());
     if (prevSym)
     {
         if (!prevSym->isFunction())
         {
-            error(location, "redefinition of a function", function->name().c_str());
+            error(location, "redefinition of a function", function->name()->c_str());
         }
     }
     else
@@ -3385,7 +3389,7 @@
     symbolTable.getOuterLevel()->insert(function);
 
     // Raise error message if main function takes any parameters or return anything other than void
-    if (function->name() == "main")
+    if (*function->name() == "main")
     {
         if (function->getParamCount() > 0)
         {
@@ -3814,16 +3818,13 @@
     }
 
     // The instance variable gets created to refer to the interface block type from the AST
-    // regardless of if there's an instance name. It just has an empty name if there's no instance
-    // name.
-    if (!instanceName)
-    {
-        instanceName = NewPoolTString("");
-    }
+    // regardless of if there's an instance name. It's created as an empty symbol if there is no
+    // instance name.
     TVariable *instanceVariable =
-        new TVariable(&symbolTable, instanceName, interfaceBlockType, SymbolType::UserDefined);
+        new TVariable(&symbolTable, instanceName, interfaceBlockType,
+                      instanceName ? SymbolType::UserDefined : SymbolType::Empty);
 
-    if (instanceName->empty())
+    if (instanceVariable->symbolType() == SymbolType::Empty)
     {
         // define symbols for the members of the interface block
         for (size_t memberIndex = 0; memberIndex < fieldList->size(); ++memberIndex)
@@ -3902,8 +3903,9 @@
     // one to the field's struct nesting.
     if (1 + field.type()->getDeepestStructNesting() > kWebGLMaxStructNesting)
     {
+        ASSERT(field.type()->getStruct()->name() != nullptr);
         std::stringstream reasonStream;
-        reasonStream << "Reference of struct type " << field.type()->getStruct()->name().c_str()
+        reasonStream << "Reference of struct type " << field.type()->getStruct()->name()->c_str()
                      << " exceeds maximum allowed nesting level of " << kWebGLMaxStructNesting;
         std::string reason = reasonStream.str();
         error(line, reason.c_str(), field.name().c_str());
@@ -4770,7 +4772,6 @@
     SymbolType structSymbolType = SymbolType::UserDefined;
     if (structName == nullptr)
     {
-        structName       = NewPoolTString("");
         structSymbolType = SymbolType::Empty;
     }
     TStructure *structure = new TStructure(&symbolTable, structName, fieldList, structSymbolType);
@@ -5780,10 +5781,10 @@
     // 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->getName() below is safe.
-    if (fnCall->name() != "length")
+    // So accessing fnCall->name() below is safe.
+    if (*fnCall->name() != "length")
     {
-        error(loc, "invalid method", fnCall->name().c_str());
+        error(loc, "invalid method", fnCall->name()->c_str());
     }
     else if (!arguments->empty())
     {
@@ -5816,18 +5817,18 @@
     // hidden by a variable name or struct typename.
     // If a function is found, check for one with a matching argument list.
     bool builtIn;
-    const TSymbol *symbol = symbolTable.find(fnCall->name(), mShaderVersion, &builtIn);
+    const TSymbol *symbol = symbolTable.find(*fnCall->name(), mShaderVersion, &builtIn);
     if (symbol != nullptr && !symbol->isFunction())
     {
-        error(loc, "function name expected", fnCall->name().c_str());
+        error(loc, "function name expected", fnCall->name()->c_str());
     }
     else
     {
-        symbol = symbolTable.find(TFunction::GetMangledNameFromCall(fnCall->name(), *arguments),
+        symbol = symbolTable.find(TFunction::GetMangledNameFromCall(*fnCall->name(), *arguments),
                                   mShaderVersion, &builtIn);
         if (symbol == nullptr)
         {
-            error(loc, "no matching overloaded function found", fnCall->name().c_str());
+            error(loc, "no matching overloaded function found", fnCall->name()->c_str());
         }
         else
         {
diff --git a/src/compiler/translator/RegenerateStructNames.cpp b/src/compiler/translator/RegenerateStructNames.cpp
index a17e5d8..32940d8 100644
--- a/src/compiler/translator/RegenerateStructNames.cpp
+++ b/src/compiler/translator/RegenerateStructNames.cpp
@@ -19,12 +19,15 @@
     if (!userType)
         return;
 
-    if (userType->symbolType() == SymbolType::BuiltIn)
+    if (userType->symbolType() == SymbolType::BuiltIn ||
+        userType->symbolType() == SymbolType::Empty)
     {
-        // Built-in struct, do not touch it.
+        // Built-in struct or nameless struct, do not touch it.
         return;
     }
 
+    ASSERT(userType->name() != nullptr);
+
     int uniqueId = userType->uniqueId().get();
 
     ASSERT(mScopeDepth > 0);
@@ -50,14 +53,14 @@
         return;
     // Map {name} to _webgl_struct_{uniqueId}_{name}.
     const char kPrefix[] = "_webgl_struct_";
-    if (userType->name().find(kPrefix) == 0)
+    if (userType->name()->find(kPrefix) == 0)
     {
         // The name has already been regenerated.
         return;
     }
     std::string id = Str(uniqueId);
     TString tmp    = kPrefix + TString(id.c_str());
-    tmp += "_" + userType->name();
+    tmp += "_" + *userType->name();
     userType->setName(tmp);
 }
 
diff --git a/src/compiler/translator/RemoveUnreferencedVariables.cpp b/src/compiler/translator/RemoveUnreferencedVariables.cpp
index 36ed3f2..0f845cd 100644
--- a/src/compiler/translator/RemoveUnreferencedVariables.cpp
+++ b/src/compiler/translator/RemoveUnreferencedVariables.cpp
@@ -192,8 +192,8 @@
                 // Already an empty declaration - nothing to do.
                 return;
             }
-            TVariable *emptyVariable = new TVariable(mSymbolTable, NewPoolTString(""),
-                                                     declarator->getType(), SymbolType::Empty);
+            TVariable *emptyVariable =
+                new TVariable(mSymbolTable, nullptr, declarator->getType(), SymbolType::Empty);
             queueReplacementWithParent(node, declarator, new TIntermSymbol(emptyVariable),
                                        OriginalNode::IS_DROPPED);
             return;
diff --git a/src/compiler/translator/SymbolTable.cpp b/src/compiler/translator/SymbolTable.cpp
index 04956d3..18f2f80 100644
--- a/src/compiler/translator/SymbolTable.cpp
+++ b/src/compiler/translator/SymbolTable.cpp
@@ -40,19 +40,25 @@
 {
     ASSERT(mSymbolType == SymbolType::BuiltIn || mExtension == TExtension::UNDEFINED);
     ASSERT(mName != nullptr || mSymbolType == SymbolType::AngleInternal ||
-           mSymbolType == SymbolType::NotResolved);
+           mSymbolType == SymbolType::NotResolved || mSymbolType == SymbolType::Empty);
 }
 
-const TString &TSymbol::name() const
+const TString *TSymbol::name() const
 {
-    if (mName != nullptr)
+    if (mName != nullptr || mSymbolType == SymbolType::Empty)
     {
-        return *mName;
+        return mName;
     }
     ASSERT(mSymbolType == SymbolType::AngleInternal);
     TInfoSinkBase symbolNameOut;
     symbolNameOut << "s" << mUniqueId.get();
-    return *NewPoolTString(symbolNameOut.c_str());
+    return NewPoolTString(symbolNameOut.c_str());
+}
+
+const TString &TSymbol::getMangledName() const
+{
+    ASSERT(mSymbolType != SymbolType::Empty);
+    return *name();
 }
 
 TVariable::TVariable(TSymbolTable *symbolTable,
@@ -109,6 +115,7 @@
       mBlockStorage(layoutQualifier.blockStorage),
       mBinding(layoutQualifier.binding)
 {
+    ASSERT(name != nullptr);
 }
 
 //
@@ -138,7 +145,7 @@
 
 const TString *TFunction::buildMangledName() const
 {
-    std::string newName = name().c_str();
+    std::string newName = name()->c_str();
     newName += kFunctionMangledNameSeparator;
 
     for (const auto &p : parameters)
@@ -181,7 +188,8 @@
 bool TSymbolTableLevel::insertUnmangled(TFunction *function)
 {
     // returning true means symbol was added to the table
-    tInsertResult result = level.insert(tLevelPair(function->name(), function));
+    ASSERT(function->name() != nullptr);
+    tInsertResult result = level.insert(tLevelPair(*function->name(), function));
 
     return result.second;
 }
diff --git a/src/compiler/translator/SymbolTable.h b/src/compiler/translator/SymbolTable.h
index 9827ee9..41bbc40 100644
--- a/src/compiler/translator/SymbolTable.h
+++ b/src/compiler/translator/SymbolTable.h
@@ -67,8 +67,8 @@
         // don't delete name, it's from the pool
     }
 
-    const TString &name() const;
-    virtual const TString &getMangledName() const { return name(); }
+    const TString *name() const;
+    virtual const TString &getMangledName() const;
     virtual bool isFunction() const { return false; }
     virtual bool isVariable() const { return false; }
     virtual bool isStruct() const { return false; }
diff --git a/src/compiler/translator/Types.cpp b/src/compiler/translator/Types.cpp
index 1488df7..3ca8806 100644
--- a/src/compiler/translator/Types.cpp
+++ b/src/compiler/translator/Types.cpp
@@ -475,12 +475,16 @@
         {
             case EbtStruct:
                 mangledName += "struct-";
-                mangledName += mStructure->name();
+                if (mStructure->name() != nullptr)
+                {
+                    mangledName += *mStructure->name();
+                }
                 mangledName += mStructure->mangledFieldList();
                 break;
             case EbtInterfaceBlock:
                 mangledName += "iblock-";
-                mangledName += mInterfaceBlock->name();
+                ASSERT(mInterfaceBlock->name() != nullptr);
+                mangledName += *mInterfaceBlock->name();
                 mangledName += mInterfaceBlock->mangledFieldList();
                 break;
             default:
diff --git a/src/compiler/translator/UniformHLSL.cpp b/src/compiler/translator/UniformHLSL.cpp
index ebea83d..079c550 100644
--- a/src/compiler/translator/UniformHLSL.cpp
+++ b/src/compiler/translator/UniformHLSL.cpp
@@ -61,7 +61,8 @@
 
 static TString InterfaceBlockStructName(const TInterfaceBlock &interfaceBlock)
 {
-    return DecoratePrivate(interfaceBlock.name()) + "_type";
+    ASSERT(interfaceBlock.name() != nullptr);
+    return DecoratePrivate(*interfaceBlock.name()) + "_type";
 }
 
 void OutputSamplerIndexArrayInitializer(TInfoSinkBase &out,
@@ -479,7 +480,8 @@
         }
 
         unsigned int activeRegister                             = mUniformBlockRegister;
-        mUniformBlockRegisterMap[interfaceBlock.name().c_str()] = activeRegister;
+        ASSERT(interfaceBlock.name() != nullptr);
+        mUniformBlockRegisterMap[interfaceBlock.name()->c_str()] = activeRegister;
 
         if (instanceName != "" && nodeType.isArray())
         {
@@ -510,7 +512,8 @@
 {
     const TString &arrayIndexString =
         (arrayIndex != GL_INVALID_INDEX ? Decorate(str(arrayIndex)) : "");
-    const TString &blockName = interfaceBlock.name() + arrayIndexString;
+    ASSERT(interfaceBlock.name() != nullptr);
+    const TString &blockName = *interfaceBlock.name() + arrayIndexString;
     TString hlsl;
 
     hlsl += "cbuffer " + blockName + " : register(b" + str(registerIndex) +
diff --git a/src/compiler/translator/UtilsHLSL.cpp b/src/compiler/translator/UtilsHLSL.cpp
index f6c7497..1569225 100644
--- a/src/compiler/translator/UtilsHLSL.cpp
+++ b/src/compiler/translator/UtilsHLSL.cpp
@@ -855,10 +855,10 @@
     // translation so that we can link between shader stages.
     if (structure.atGlobalScope())
     {
-        return Decorate(structure.name());
+        return Decorate(*structure.name());
     }
 
-    return "ss" + str(structure.uniqueId().get()) + "_" + structure.name();
+    return "ss" + str(structure.uniqueId().get()) + "_" + *structure.name();
 }
 
 TString QualifiedStructNameString(const TStructure &structure,
diff --git a/src/tests/angle_unittests.gypi b/src/tests/angle_unittests.gypi
index 239155f..038a82b 100644
--- a/src/tests/angle_unittests.gypi
+++ b/src/tests/angle_unittests.gypi
@@ -78,6 +78,7 @@
             '<(angle_path)/src/tests/compiler_tests/QualificationOrderESSL31_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/QualificationOrder_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/RecordConstantPrecision_test.cpp',
+            '<(angle_path)/src/tests/compiler_tests/RegenerateStructNames_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/RemovePow_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/RemoveUnreferencedVariables_test.cpp',
             '<(angle_path)/src/tests/compiler_tests/RewriteDoWhile_test.cpp',
diff --git a/src/tests/compiler_tests/CollectVariables_test.cpp b/src/tests/compiler_tests/CollectVariables_test.cpp
index fb1c31c..25f2d18 100644
--- a/src/tests/compiler_tests/CollectVariables_test.cpp
+++ b/src/tests/compiler_tests/CollectVariables_test.cpp
@@ -809,17 +809,21 @@
     EXPECT_TRUE(field.fields.empty());
 }
 
+// Test a struct uniform where the struct does have a name.
 TEST_F(CollectHashedVertexVariablesTest, StructUniform)
 {
     const std::string &shaderString =
-        "#version 300 es\n"
-        "struct sType {\n"
-        "  float field;\n"
-        "};"
-        "uniform sType u;"
-        "void main() {\n"
-        "   gl_Position = vec4(u.field, 0.0, 0.0, 1.0);\n"
-        "}\n";
+        R"(#version 300 es
+        struct sType
+        {
+            float field;
+        };
+        uniform sType u;
+
+        void main()
+        {
+            gl_Position = vec4(u.field, 0.0, 0.0, 1.0);
+        })";
 
     compile(shaderString);
 
@@ -831,6 +835,47 @@
     EXPECT_FALSE(uniform.isArray());
     EXPECT_EQ("u", uniform.name);
     EXPECT_EQ("webgl_1", uniform.mappedName);
+    EXPECT_EQ("sType", uniform.structName);
+    EXPECT_TRUE(uniform.staticUse);
+
+    ASSERT_EQ(1u, uniform.fields.size());
+
+    const ShaderVariable &field = uniform.fields[0];
+
+    EXPECT_GLENUM_EQ(GL_HIGH_FLOAT, field.precision);
+    // EXPECT_TRUE(field.staticUse); // we don't yet support struct static use
+    EXPECT_GLENUM_EQ(GL_FLOAT, field.type);
+    EXPECT_EQ("field", field.name);
+    EXPECT_EQ("webgl_5", field.mappedName);
+    EXPECT_TRUE(field.fields.empty());
+}
+
+// Test a struct uniform where the struct doesn't have a name.
+TEST_F(CollectHashedVertexVariablesTest, NamelessStructUniform)
+{
+    const std::string &shaderString =
+        R"(#version 300 es
+        uniform struct
+        {
+            float field;
+        } u;
+
+        void main()
+        {
+            gl_Position = vec4(u.field, 0.0, 0.0, 1.0);
+        })";
+
+    compile(shaderString);
+
+    const auto &uniforms = mTranslator->getUniforms();
+    ASSERT_EQ(1u, uniforms.size());
+
+    const Uniform &uniform = uniforms[0];
+
+    EXPECT_FALSE(uniform.isArray());
+    EXPECT_EQ("u", uniform.name);
+    EXPECT_EQ("webgl_1", uniform.mappedName);
+    EXPECT_EQ("", uniform.structName);
     EXPECT_TRUE(uniform.staticUse);
 
     ASSERT_EQ(1u, uniform.fields.size());
diff --git a/src/tests/compiler_tests/InitOutputVariables_test.cpp b/src/tests/compiler_tests/InitOutputVariables_test.cpp
index c61f01c..c59d1a5 100644
--- a/src/tests/compiler_tests/InitOutputVariables_test.cpp
+++ b/src/tests/compiler_tests/InitOutputVariables_test.cpp
@@ -167,7 +167,8 @@
 
         TStructure *structure = symbol->getTypePointer()->getStruct();
 
-        if (structure != nullptr && structure->name() == mStructName)
+        if (structure != nullptr && structure->name() != nullptr &&
+            *structure->name() == mStructName)
         {
             mStructure = structure;
         }
diff --git a/src/tests/compiler_tests/RegenerateStructNames_test.cpp b/src/tests/compiler_tests/RegenerateStructNames_test.cpp
new file mode 100644
index 0000000..c4d88df
--- /dev/null
+++ b/src/tests/compiler_tests/RegenerateStructNames_test.cpp
@@ -0,0 +1,74 @@
+//
+// Copyright (c) 2017 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.
+//
+// RegenerateStructNames_test.cpp:
+//   Tests for regenerating struct names.
+//
+
+#include "GLSLANG/ShaderLang.h"
+#include "angle_gl.h"
+#include "gtest/gtest.h"
+#include "tests/test_utils/compiler_test.h"
+
+using namespace sh;
+
+class RegenerateStructNamesTest : public MatchOutputCodeTest
+{
+  public:
+    RegenerateStructNamesTest()
+        : MatchOutputCodeTest(GL_FRAGMENT_SHADER, SH_REGENERATE_STRUCT_NAMES, SH_ESSL_OUTPUT)
+    {
+    }
+};
+
+// Test that a struct defined in a function scope is renamed. The global struct that's used as a
+// type of a uniform cannot be renamed.
+TEST_F(RegenerateStructNamesTest, GlobalStructAndLocalStructWithTheSameName)
+{
+    const std::string &shaderString =
+        R"(precision mediump float;
+
+        struct myStruct
+        {
+            float foo;
+        };
+
+        uniform myStruct us;
+
+        void main()
+        {
+            struct myStruct
+            {
+                vec2 bar;
+            };
+            myStruct scoped;
+            scoped.bar = vec2(1.0, 2.0) * us.foo;
+            gl_FragColor = vec4(scoped.bar, 0.0, 1.0);
+        })";
+    compile(shaderString);
+    EXPECT_TRUE(foundInCode("struct _umyStruct"));
+    EXPECT_TRUE(foundInCode("struct _u_webgl_struct_"));
+}
+
+// Test that a nameless struct is handled gracefully.
+TEST_F(RegenerateStructNamesTest, NamelessStruct)
+{
+    const std::string &shaderString =
+        R"(precision mediump float;
+
+        uniform float u;
+
+        void main()
+        {
+            struct
+            {
+                vec2 bar;
+            } scoped;
+            scoped.bar = vec2(1.0, 2.0) * u;
+            gl_FragColor = vec4(scoped.bar, 0.0, 1.0);
+        })";
+    compile(shaderString);
+    EXPECT_TRUE(foundInCode("struct"));
+}