Split TIntermDeclaration from TIntermAggregate

The new class TIntermDeclaration is now used for struct, interface
block and variable declarations. TIntermDeclaration nodes do not have
a type - rather the type is stored in each child node. The types may
differ in case the declaration is a series of array declarators with
mismatching sizes.

TIntermAggregate is still used for function calls, function
prototypes, function parameter lists and invariant declarations.

BUG=angleproject:1490
TEST=angle_unittests

Change-Id: I0457188f354481470855f61ac1c878fc2579b1d1
Reviewed-on: https://chromium-review.googlesource.com/400023
Commit-Queue: Olli Etuaho <oetuaho@nvidia.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
diff --git a/src/compiler/translator/DeferGlobalInitializers.cpp b/src/compiler/translator/DeferGlobalInitializers.cpp
index 067e6ce..5a12238 100644
--- a/src/compiler/translator/DeferGlobalInitializers.cpp
+++ b/src/compiler/translator/DeferGlobalInitializers.cpp
@@ -111,7 +111,7 @@
             if (symbolNode->getQualifier() == EvqConst)
             {
                 // All of the siblings in the same declaration need to have consistent qualifiers.
-                auto *siblings = getParentNode()->getAsAggregate()->getSequence();
+                auto *siblings = getParentNode()->getAsDeclarationNode()->getSequence();
                 for (TIntermNode *siblingNode : *siblings)
                 {
                     TIntermBinary *siblingBinary = siblingNode->getAsBinaryNode();
diff --git a/src/compiler/translator/EmulatePrecision.cpp b/src/compiler/translator/EmulatePrecision.cpp
index a8f6d64..f38d925 100644
--- a/src/compiler/translator/EmulatePrecision.cpp
+++ b/src/compiler/translator/EmulatePrecision.cpp
@@ -598,6 +598,24 @@
     return visitChildren;
 }
 
+bool EmulatePrecision::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+    // Variable or interface block declaration.
+    if (visit == PreVisit)
+    {
+        mDeclaringVariables = true;
+    }
+    else if (visit == InVisit)
+    {
+        mDeclaringVariables = true;
+    }
+    else
+    {
+        mDeclaringVariables = false;
+    }
+    return true;
+}
+
 bool EmulatePrecision::visitAggregate(Visit visit, TIntermAggregate *node)
 {
     bool visitChildren = true;
@@ -614,21 +632,6 @@
       case EOpInvariantDeclaration:
         visitChildren = false;
         break;
-      case EOpDeclaration:
-        // Variable declaration.
-        if (visit == PreVisit)
-        {
-            mDeclaringVariables = true;
-        }
-        else if (visit == InVisit)
-        {
-            mDeclaringVariables = true;
-        }
-        else
-        {
-            mDeclaringVariables = false;
-        }
-        break;
       case EOpFunctionCall:
       {
         // Function call.
diff --git a/src/compiler/translator/EmulatePrecision.h b/src/compiler/translator/EmulatePrecision.h
index f23e40b..22ae217 100644
--- a/src/compiler/translator/EmulatePrecision.h
+++ b/src/compiler/translator/EmulatePrecision.h
@@ -27,6 +27,7 @@
     bool visitBinary(Visit visit, TIntermBinary *node) override;
     bool visitUnary(Visit visit, TIntermUnary *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
+    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
 
     void writeEmulationHelpers(TInfoSinkBase &sink,
                                const int shaderVersion,
diff --git a/src/compiler/translator/ExpandIntegerPowExpressions.cpp b/src/compiler/translator/ExpandIntegerPowExpressions.cpp
index 9409c01..c5ff7b3 100644
--- a/src/compiler/translator/ExpandIntegerPowExpressions.cpp
+++ b/src/compiler/translator/ExpandIntegerPowExpressions.cpp
@@ -116,7 +116,7 @@
     TIntermTyped *lhs = sequence->at(0)->getAsTyped();
     ASSERT(lhs);
 
-    TIntermAggregate *init = createTempInitDeclaration(lhs);
+    TIntermDeclaration *init = createTempInitDeclaration(lhs);
     TIntermTyped *current  = createTempSymbol(lhs->getType());
 
     insertStatementInParentBlock(init);
diff --git a/src/compiler/translator/ForLoopUnroll.cpp b/src/compiler/translator/ForLoopUnroll.cpp
index 4cc1c26..9aa86b4 100644
--- a/src/compiler/translator/ForLoopUnroll.cpp
+++ b/src/compiler/translator/ForLoopUnroll.cpp
@@ -51,7 +51,7 @@
         // Check if loop index type is integer.
         // This is called after ValidateLimitations pass, so the loop has the limited form specified
         // in ESSL 1.00 appendix A.
-        TIntermSequence *declSeq = node->getInit()->getAsAggregate()->getSequence();
+        TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
         TIntermSymbol *symbol = (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
         if (symbol->getBasicType() == EbtInt)
             node->setUnrollFlag(true);
diff --git a/src/compiler/translator/IntermNode.cpp b/src/compiler/translator/IntermNode.cpp
index 0acfe40..add4946 100644
--- a/src/compiler/translator/IntermNode.cpp
+++ b/src/compiler/translator/IntermNode.cpp
@@ -222,6 +222,11 @@
     return replaceChildNodeInternal(original, replacement);
 }
 
+bool TIntermDeclaration::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
+{
+    return replaceChildNodeInternal(original, replacement);
+}
+
 bool TIntermAggregateBase::replaceChildNodeInternal(TIntermNode *original, TIntermNode *replacement)
 {
     for (size_t ii = 0; ii < getSequence()->size(); ++ii)
@@ -320,12 +325,26 @@
 
 void TIntermBlock::appendStatement(TIntermNode *statement)
 {
-    if (statement != nullptr)
+    // Declaration nodes with no children can appear if all the declarators just added constants to
+    // the symbol table instead of generating code. They're no-ops so they aren't added to blocks.
+    if (statement != nullptr && (statement->getAsDeclarationNode() == nullptr ||
+                                 !statement->getAsDeclarationNode()->getSequence()->empty()))
     {
         mStatements.push_back(statement);
     }
 }
 
+void TIntermDeclaration::appendDeclarator(TIntermTyped *declarator)
+{
+    ASSERT(declarator != nullptr);
+    ASSERT(declarator->getAsSymbolNode() != nullptr ||
+           (declarator->getAsBinaryNode() != nullptr &&
+            declarator->getAsBinaryNode()->getOp() == EOpInitialize));
+    ASSERT(mDeclarators.empty() ||
+           declarator->getType().sameElementType(mDeclarators.back()->getAsTyped()->getType()));
+    mDeclarators.push_back(declarator);
+}
+
 bool TIntermTernary::replaceChildNode(TIntermNode *original, TIntermNode *replacement)
 {
     REPLACE_IF_IS(mCondition, TIntermTyped, original, replacement);
diff --git a/src/compiler/translator/IntermNode.h b/src/compiler/translator/IntermNode.h
index f465a5d..fcf9999 100644
--- a/src/compiler/translator/IntermNode.h
+++ b/src/compiler/translator/IntermNode.h
@@ -32,6 +32,7 @@
 class TIntermTraverser;
 class TIntermAggregate;
 class TIntermBlock;
+class TIntermDeclaration;
 class TIntermFunctionDefinition;
 class TIntermSwizzle;
 class TIntermBinary;
@@ -98,6 +99,7 @@
     virtual TIntermFunctionDefinition *getAsFunctionDefinition() { return nullptr; }
     virtual TIntermAggregate *getAsAggregate() { return 0; }
     virtual TIntermBlock *getAsBlock() { return nullptr; }
+    virtual TIntermDeclaration *getAsDeclarationNode() { return nullptr; }
     virtual TIntermSwizzle *getAsSwizzleNode() { return nullptr; }
     virtual TIntermBinary *getAsBinaryNode() { return 0; }
     virtual TIntermUnary *getAsUnaryNode() { return 0; }
@@ -710,6 +712,28 @@
     TIntermSequence mStatements;
 };
 
+// Struct, interface block or variable declaration. Can contain multiple variable declarators.
+class TIntermDeclaration : public TIntermNode, public TIntermAggregateBase
+{
+  public:
+    TIntermDeclaration() : TIntermNode() {}
+    ~TIntermDeclaration() {}
+
+    TIntermDeclaration *getAsDeclarationNode() override { return this; }
+    void traverse(TIntermTraverser *it) override;
+    bool replaceChildNode(TIntermNode *original, TIntermNode *replacement) override;
+
+    // Only intended for initially building the declaration.
+    // The declarator node should be either TIntermSymbol or TIntermBinary with op set to
+    // EOpInitialize.
+    void appendDeclarator(TIntermTyped *declarator);
+
+    TIntermSequence *getSequence() override { return &mDeclarators; }
+    const TIntermSequence *getSequence() const override { return &mDeclarators; }
+  protected:
+    TIntermSequence mDeclarators;
+};
+
 // For ternary operators like a ? b : c.
 class TIntermTernary : public TIntermTyped
 {
@@ -858,6 +882,7 @@
     }
     virtual bool visitAggregate(Visit visit, TIntermAggregate *node) { return true; }
     virtual bool visitBlock(Visit visit, TIntermBlock *node) { return true; }
+    virtual bool visitDeclaration(Visit visit, TIntermDeclaration *node) { return true; }
     virtual bool visitLoop(Visit visit, TIntermLoop *node) { return true; }
     virtual bool visitBranch(Visit visit, TIntermBranch *node) { return true; }
 
@@ -877,6 +902,7 @@
     virtual void traverseFunctionDefinition(TIntermFunctionDefinition *node);
     virtual void traverseAggregate(TIntermAggregate *node);
     virtual void traverseBlock(TIntermBlock *node);
+    virtual void traverseDeclaration(TIntermDeclaration *node);
     virtual void traverseLoop(TIntermLoop *node);
     virtual void traverseBranch(TIntermBranch *node);
 
@@ -987,11 +1013,11 @@
     // Helper to create a temporary symbol node.
     TIntermSymbol *createTempSymbol(const TType &type);
     // Create a node that declares but doesn't initialize a temporary symbol.
-    TIntermAggregate *createTempDeclaration(const TType &type);
+    TIntermDeclaration *createTempDeclaration(const TType &type);
     // Create a node that initializes the current temporary symbol with initializer having the given qualifier.
-    TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier);
+    TIntermDeclaration *createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier);
     // Create a node that initializes the current temporary symbol with initializer.
-    TIntermAggregate *createTempInitDeclaration(TIntermTyped *initializer);
+    TIntermDeclaration *createTempInitDeclaration(TIntermTyped *initializer);
     // Create a node that assigns rightNode to the current temporary symbol.
     TIntermBinary *createTempAssignment(TIntermTyped *rightNode);
     // Increment temporary symbol index.
diff --git a/src/compiler/translator/IntermTraverse.cpp b/src/compiler/translator/IntermTraverse.cpp
index 29f81f4..1e623d1 100644
--- a/src/compiler/translator/IntermTraverse.cpp
+++ b/src/compiler/translator/IntermTraverse.cpp
@@ -68,6 +68,11 @@
     it->traverseBlock(this);
 }
 
+void TIntermDeclaration::traverse(TIntermTraverser *it)
+{
+    it->traverseDeclaration(this);
+}
+
 void TIntermAggregate::traverse(TIntermTraverser *it)
 {
     it->traverseAggregate(this);
@@ -155,24 +160,25 @@
     return createTempSymbol(type, EvqTemporary);
 }
 
-TIntermAggregate *TIntermTraverser::createTempDeclaration(const TType &type)
+TIntermDeclaration *TIntermTraverser::createTempDeclaration(const TType &type)
 {
-    TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
-    tempDeclaration->getSequence()->push_back(createTempSymbol(type));
+    TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
+    tempDeclaration->appendDeclarator(createTempSymbol(type));
     return tempDeclaration;
 }
 
-TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer, TQualifier qualifier)
+TIntermDeclaration *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer,
+                                                                TQualifier qualifier)
 {
     ASSERT(initializer != nullptr);
     TIntermSymbol *tempSymbol = createTempSymbol(initializer->getType(), qualifier);
-    TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
+    TIntermDeclaration *tempDeclaration = new TIntermDeclaration();
     TIntermBinary *tempInit           = new TIntermBinary(EOpInitialize, tempSymbol, initializer);
-    tempDeclaration->getSequence()->push_back(tempInit);
+    tempDeclaration->appendDeclarator(tempInit);
     return tempDeclaration;
 }
 
-TIntermAggregate *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer)
+TIntermDeclaration *TIntermTraverser::createTempInitDeclaration(TIntermTyped *initializer)
 {
     return createTempInitDeclaration(initializer, EvqTemporary);
 }
@@ -490,6 +496,37 @@
         visitBlock(PostVisit, node);
 }
 
+// Traverse a declaration node.
+void TIntermTraverser::traverseDeclaration(TIntermDeclaration *node)
+{
+    bool visit = true;
+
+    TIntermSequence *sequence = node->getSequence();
+
+    if (preVisit)
+        visit = visitDeclaration(PreVisit, node);
+
+    if (visit)
+    {
+        incrementDepth(node);
+
+        for (auto *child : *sequence)
+        {
+            child->traverse(this);
+            if (visit && inVisit)
+            {
+                if (child != sequence->back())
+                    visit = visitDeclaration(InVisit, node);
+            }
+        }
+
+        decrementDepth();
+    }
+
+    if (visit && postVisit)
+        visitDeclaration(PostVisit, node);
+}
+
 // Traverse an aggregate node.  Same comments in binary node apply here.
 void TIntermTraverser::traverseAggregate(TIntermAggregate *node)
 {
diff --git a/src/compiler/translator/LoopInfo.cpp b/src/compiler/translator/LoopInfo.cpp
index d931a18..e53bd4d 100644
--- a/src/compiler/translator/LoopInfo.cpp
+++ b/src/compiler/translator/LoopInfo.cpp
@@ -93,8 +93,7 @@
 
     // Here we assume all the operations are valid, because the loop node is
     // already validated in ValidateLimitations.
-    TIntermSequence *declSeq =
-        node->getInit()->getAsAggregate()->getSequence();
+    TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
     TIntermBinary *declInit = (*declSeq)[0]->getAsBinaryNode();
     TIntermSymbol *symbol = declInit->getLeft()->getAsSymbolNode();
 
diff --git a/src/compiler/translator/Operator.h b/src/compiler/translator/Operator.h
index 42c5c63..f7706f8 100644
--- a/src/compiler/translator/Operator.h
+++ b/src/compiler/translator/Operator.h
@@ -16,7 +16,6 @@
     EOpFunctionCall,
     EOpParameters,      // an aggregate listing the parameters to a function
 
-    EOpDeclaration,
     EOpInvariantDeclaration, // Specialized declarations for attributing invariance
     EOpPrototype,
 
diff --git a/src/compiler/translator/OutputGLSLBase.cpp b/src/compiler/translator/OutputGLSLBase.cpp
index 2be8e7e..8abafb8 100644
--- a/src/compiler/translator/OutputGLSLBase.cpp
+++ b/src/compiler/translator/OutputGLSLBase.cpp
@@ -916,27 +916,6 @@
         out << ")";
         visitChildren = false;
         break;
-      case EOpDeclaration:
-        // Variable declaration.
-        if (visit == PreVisit)
-        {
-            const TIntermSequence &sequence = *(node->getSequence());
-            const TIntermTyped *variable = sequence.front()->getAsTyped();
-            writeLayoutQualifier(variable->getType());
-            writeVariableType(variable->getType());
-            out << " ";
-            mDeclaringVariables = true;
-        }
-        else if (visit == InVisit)
-        {
-            out << ", ";
-            mDeclaringVariables = true;
-        }
-        else
-        {
-            mDeclaringVariables = false;
-        }
-        break;
       case EOpInvariantDeclaration:
         // Invariant declaration.
         ASSERT(visit == PreVisit);
@@ -1059,6 +1038,32 @@
     return visitChildren;
 }
 
+bool TOutputGLSLBase::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+    TInfoSinkBase &out = objSink();
+
+    // Variable declaration.
+    if (visit == PreVisit)
+    {
+        const TIntermSequence &sequence = *(node->getSequence());
+        const TIntermTyped *variable    = sequence.front()->getAsTyped();
+        writeLayoutQualifier(variable->getType());
+        writeVariableType(variable->getType());
+        out << " ";
+        mDeclaringVariables = true;
+    }
+    else if (visit == InVisit)
+    {
+        out << ", ";
+        mDeclaringVariables = true;
+    }
+    else
+    {
+        mDeclaringVariables = false;
+    }
+    return true;
+}
+
 bool TOutputGLSLBase::visitLoop(Visit visit, TIntermLoop *node)
 {
     TInfoSinkBase &out = objSink();
@@ -1092,8 +1097,7 @@
         else
         {
             // Need to put a one-iteration loop here to handle break.
-            TIntermSequence *declSeq =
-                node->getInit()->getAsAggregate()->getSequence();
+            TIntermSequence *declSeq = node->getInit()->getAsDeclarationNode()->getSequence();
             TIntermSymbol *indexSymbol =
                 (*declSeq)[0]->getAsBinaryNode()->getLeft()->getAsSymbolNode();
             TString name = hashVariableName(indexSymbol->getSymbol());
diff --git a/src/compiler/translator/OutputGLSLBase.h b/src/compiler/translator/OutputGLSLBase.h
index aa861c7..01dc2e8 100644
--- a/src/compiler/translator/OutputGLSLBase.h
+++ b/src/compiler/translator/OutputGLSLBase.h
@@ -52,6 +52,7 @@
     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *node) override;
     bool visitBlock(Visit visit, TIntermBlock *node) override;
+    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
     bool visitLoop(Visit visit, TIntermLoop *node) override;
     bool visitBranch(Visit visit, TIntermBranch *node) override;
 
diff --git a/src/compiler/translator/OutputHLSL.cpp b/src/compiler/translator/OutputHLSL.cpp
index feae684..3af0e17 100644
--- a/src/compiler/translator/OutputHLSL.cpp
+++ b/src/compiler/translator/OutputHLSL.cpp
@@ -1519,84 +1519,80 @@
     return false;
 }
 
+bool OutputHLSL::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+    TInfoSinkBase &out = getInfoSink();
+    if (visit == PreVisit)
+    {
+        TIntermSequence *sequence = node->getSequence();
+        TIntermTyped *variable    = (*sequence)[0]->getAsTyped();
+        ASSERT(sequence->size() == 1);
+
+        if (variable &&
+            (variable->getQualifier() == EvqTemporary || variable->getQualifier() == EvqGlobal ||
+             variable->getQualifier() == EvqConst))
+        {
+            ensureStructDefined(variable->getType());
+
+            if (!variable->getAsSymbolNode() ||
+                variable->getAsSymbolNode()->getSymbol() != "")  // Variable declaration
+            {
+                if (!mInsideFunction)
+                {
+                    out << "static ";
+                }
+
+                out << TypeString(variable->getType()) + " ";
+
+                TIntermSymbol *symbol = variable->getAsSymbolNode();
+
+                if (symbol)
+                {
+                    symbol->traverse(this);
+                    out << ArrayString(symbol->getType());
+                    out << " = " + initializer(symbol->getType());
+                }
+                else
+                {
+                    variable->traverse(this);
+                }
+            }
+            else if (variable->getAsSymbolNode() &&
+                     variable->getAsSymbolNode()->getSymbol() == "")  // Type (struct) declaration
+            {
+                // Already added to constructor map
+            }
+            else
+                UNREACHABLE();
+        }
+        else if (variable && IsVaryingOut(variable->getQualifier()))
+        {
+            for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end(); sit++)
+            {
+                TIntermSymbol *symbol = (*sit)->getAsSymbolNode();
+
+                if (symbol)
+                {
+                    // Vertex (output) varyings which are declared but not written to should
+                    // still be declared to allow successful linking
+                    mReferencedVaryings[symbol->getSymbol()] = symbol;
+                }
+                else
+                {
+                    (*sit)->traverse(this);
+                }
+            }
+        }
+    }
+    return false;
+}
+
 bool OutputHLSL::visitAggregate(Visit visit, TIntermAggregate *node)
 {
     TInfoSinkBase &out = getInfoSink();
 
     switch (node->getOp())
     {
-        case EOpDeclaration:
-            if (visit == PreVisit)
-            {
-                TIntermSequence *sequence = node->getSequence();
-                TIntermTyped *variable    = (*sequence)[0]->getAsTyped();
-                ASSERT(sequence->size() == 1);
-
-                if (variable &&
-                    (variable->getQualifier() == EvqTemporary ||
-                     variable->getQualifier() == EvqGlobal || variable->getQualifier() == EvqConst))
-                {
-                    ensureStructDefined(variable->getType());
-
-                    if (!variable->getAsSymbolNode() ||
-                        variable->getAsSymbolNode()->getSymbol() != "")  // Variable declaration
-                    {
-                        if (!mInsideFunction)
-                        {
-                            out << "static ";
-                        }
-
-                        out << TypeString(variable->getType()) + " ";
-
-                        TIntermSymbol *symbol = variable->getAsSymbolNode();
-
-                        if (symbol)
-                        {
-                            symbol->traverse(this);
-                            out << ArrayString(symbol->getType());
-                            out << " = " + initializer(symbol->getType());
-                        }
-                        else
-                        {
-                            variable->traverse(this);
-                        }
-                    }
-                    else if (variable->getAsSymbolNode() &&
-                             variable->getAsSymbolNode()->getSymbol() ==
-                                 "")  // Type (struct) declaration
-                    {
-                        // Already added to constructor map
-                    }
-                    else
-                        UNREACHABLE();
-                }
-                else if (variable && IsVaryingOut(variable->getQualifier()))
-                {
-                    for (TIntermSequence::iterator sit = sequence->begin(); sit != sequence->end();
-                         sit++)
-                    {
-                        TIntermSymbol *symbol = (*sit)->getAsSymbolNode();
-
-                        if (symbol)
-                        {
-                            // Vertex (output) varyings which are declared but not written to should
-                            // still be declared to allow successful linking
-                            mReferencedVaryings[symbol->getSymbol()] = symbol;
-                        }
-                        else
-                        {
-                            (*sit)->traverse(this);
-                        }
-                    }
-                }
-
-                return false;
-            }
-            else if (visit == InVisit)
-            {
-                out << ", ";
-            }
-            break;
         case EOpInvariantDeclaration:
             // Do not do any translation
             return false;
@@ -2177,39 +2173,6 @@
     return true;
 }
 
-bool OutputHLSL::isSingleStatement(TIntermNode *node)
-{
-    if (node->getAsBlock())
-    {
-        return false;
-    }
-
-    TIntermAggregate *aggregate = node->getAsAggregate();
-    if (aggregate)
-    {
-        if (aggregate->getOp() == EOpDeclaration)
-        {
-            // Declaring multiple comma-separated variables must be considered multiple statements
-            // because each individual declaration has side effects which are visible in the next.
-            return false;
-        }
-        else
-        {
-            for (TIntermSequence::iterator sit = aggregate->getSequence()->begin(); sit != aggregate->getSequence()->end(); sit++)
-            {
-                if (!isSingleStatement(*sit))
-                {
-                    return false;
-                }
-            }
-
-            return true;
-        }
-    }
-
-    return true;
-}
-
 // Handle loops with more than 254 iterations (unsupported by D3D9) by splitting them
 // (The D3D documentation says 255 iterations, but the compiler complains at anything more than 254).
 bool OutputHLSL::handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node)
@@ -2227,7 +2190,7 @@
     // Parse index name and intial value
     if (node->getInit())
     {
-        TIntermAggregate *init = node->getInit()->getAsAggregate();
+        TIntermDeclaration *init = node->getInit()->getAsDeclarationNode();
 
         if (init)
         {
diff --git a/src/compiler/translator/OutputHLSL.h b/src/compiler/translator/OutputHLSL.h
index db78859..833f473 100644
--- a/src/compiler/translator/OutputHLSL.h
+++ b/src/compiler/translator/OutputHLSL.h
@@ -69,10 +69,10 @@
     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate*);
     bool visitBlock(Visit visit, TIntermBlock *node);
+    bool visitDeclaration(Visit visit, TIntermDeclaration *node);
     bool visitLoop(Visit visit, TIntermLoop*);
     bool visitBranch(Visit visit, TIntermBranch*);
 
-    bool isSingleStatement(TIntermNode *node);
     bool handleExcessiveLoop(TInfoSinkBase &out, TIntermLoop *node);
 
     // Emit one of three strings depending on traverse phase. Called with literal strings so using const char* instead of TString.
diff --git a/src/compiler/translator/ParseContext.cpp b/src/compiler/translator/ParseContext.cpp
index fc5ea60..2e2c27e 100644
--- a/src/compiler/translator/ParseContext.cpp
+++ b/src/compiler/translator/ParseContext.cpp
@@ -1475,9 +1475,10 @@
                                        const TString &identifier,
                                        const TPublicType &pType,
                                        TIntermTyped *initializer,
-                                       TIntermNode **intermNode)
+                                       TIntermBinary **initNode)
 {
-    ASSERT(intermNode != nullptr);
+    ASSERT(initNode != nullptr);
+    ASSERT(*initNode == nullptr);
     TType type = TType(pType);
 
     TVariable *variable = nullptr;
@@ -1549,7 +1550,7 @@
         if (initializer->getAsConstantUnion())
         {
             variable->shareConstPointer(initializer->getAsConstantUnion()->getUnionArrayPointer());
-            *intermNode = nullptr;
+            *initNode = nullptr;
             return false;
         }
         else if (initializer->getAsSymbolNode())
@@ -1562,7 +1563,7 @@
             if (constArray)
             {
                 variable->shareConstPointer(constArray);
-                *intermNode = nullptr;
+                *initNode = nullptr;
                 return false;
             }
         }
@@ -1570,8 +1571,8 @@
 
     TIntermSymbol *intermSymbol = intermediate.addSymbol(
         variable->getUniqueId(), variable->getName(), variable->getType(), line);
-    *intermNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
-    if (*intermNode == nullptr)
+    *initNode = createAssign(EOpInitialize, intermSymbol, initializer, line);
+    if (*initNode == nullptr)
     {
         assignError(line, "=", intermSymbol->getCompleteString(), initializer->getCompleteString());
         return true;
@@ -1753,9 +1754,10 @@
     return true;
 }
 
-TIntermAggregate *TParseContext::parseSingleDeclaration(TPublicType &publicType,
-                                                        const TSourceLoc &identifierOrTypeLocation,
-                                                        const TString &identifier)
+TIntermDeclaration *TParseContext::parseSingleDeclaration(
+    TPublicType &publicType,
+    const TSourceLoc &identifierOrTypeLocation,
+    const TString &identifier)
 {
     TType type(publicType);
     if ((mCompileOptions & SH_FLATTEN_PRAGMA_STDGL_INVARIANT_ALL) &&
@@ -1791,6 +1793,9 @@
 
     mDeferredSingleDeclarationErrorCheck = emptyDeclaration;
 
+    TIntermDeclaration *declaration = new TIntermDeclaration();
+    declaration->setLine(identifierOrTypeLocation);
+
     if (emptyDeclaration)
     {
         if (publicType.isUnsizedArray())
@@ -1811,17 +1816,23 @@
         declareVariable(identifierOrTypeLocation, identifier, type, &variable);
 
         if (variable && symbol)
+        {
             symbol->setId(variable->getUniqueId());
+        }
     }
 
-    return TIntermediate::MakeAggregate(symbol, identifierOrTypeLocation);
+    // We append the symbol even if the declaration is empty, mainly because of struct declarations
+    // that may just declare a type.
+    declaration->appendDeclarator(symbol);
+
+    return declaration;
 }
 
-TIntermAggregate *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType,
-                                                             const TSourceLoc &identifierLocation,
-                                                             const TString &identifier,
-                                                             const TSourceLoc &indexLocation,
-                                                             TIntermTyped *indexExpression)
+TIntermDeclaration *TParseContext::parseSingleArrayDeclaration(TPublicType &publicType,
+                                                               const TSourceLoc &identifierLocation,
+                                                               const TString &identifier,
+                                                               const TSourceLoc &indexLocation,
+                                                               TIntermTyped *indexExpression)
 {
     mDeferredSingleDeclarationErrorCheck = false;
 
@@ -1841,38 +1852,44 @@
     TVariable *variable = nullptr;
     declareVariable(identifierLocation, identifier, arrayType, &variable);
 
+    TIntermDeclaration *declaration = new TIntermDeclaration();
+    declaration->setLine(identifierLocation);
+
     TIntermSymbol *symbol = intermediate.addSymbol(0, identifier, arrayType, identifierLocation);
     if (variable && symbol)
+    {
         symbol->setId(variable->getUniqueId());
+        declaration->appendDeclarator(symbol);
+    }
 
-    return TIntermediate::MakeAggregate(symbol, identifierLocation);
+    return declaration;
 }
 
-TIntermAggregate *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
-                                                            const TSourceLoc &identifierLocation,
-                                                            const TString &identifier,
-                                                            const TSourceLoc &initLocation,
-                                                            TIntermTyped *initializer)
+TIntermDeclaration *TParseContext::parseSingleInitDeclaration(const TPublicType &publicType,
+                                                              const TSourceLoc &identifierLocation,
+                                                              const TString &identifier,
+                                                              const TSourceLoc &initLocation,
+                                                              TIntermTyped *initializer)
 {
     mDeferredSingleDeclarationErrorCheck = false;
 
     singleDeclarationErrorCheck(publicType, identifierLocation);
 
-    TIntermNode *intermNode = nullptr;
-    if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode))
+    TIntermDeclaration *declaration = new TIntermDeclaration();
+    declaration->setLine(identifierLocation);
+
+    TIntermBinary *initNode = nullptr;
+    if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &initNode))
     {
-        //
-        // Build intermediate representation
-        //
-        return intermNode ? TIntermediate::MakeAggregate(intermNode, initLocation) : nullptr;
+        if (initNode)
+        {
+            declaration->appendDeclarator(initNode);
+        }
     }
-    else
-    {
-        return nullptr;
-    }
+    return declaration;
 }
 
-TIntermAggregate *TParseContext::parseSingleArrayInitDeclaration(
+TIntermDeclaration *TParseContext::parseSingleArrayInitDeclaration(
     TPublicType &publicType,
     const TSourceLoc &identifierLocation,
     const TString &identifier,
@@ -1900,16 +1917,20 @@
     // This ensures useless error messages regarding the variable's non-arrayness won't follow.
     arrayType.setArraySize(size);
 
+    TIntermDeclaration *declaration = new TIntermDeclaration();
+    declaration->setLine(identifierLocation);
+
     // initNode will correspond to the whole of "type b[n] = initializer".
-    TIntermNode *initNode = nullptr;
+    TIntermBinary *initNode = nullptr;
     if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
     {
-        return initNode ? TIntermediate::MakeAggregate(initNode, initLocation) : nullptr;
+        if (initNode)
+        {
+            declaration->appendDeclarator(initNode);
+        }
     }
-    else
-    {
-        return nullptr;
-    }
+
+    return declaration;
 }
 
 TIntermAggregate *TParseContext::parseInvariantDeclaration(
@@ -1967,10 +1988,10 @@
     return aggregate;
 }
 
-TIntermAggregate *TParseContext::parseDeclarator(TPublicType &publicType,
-                                                 TIntermAggregate *aggregateDeclaration,
-                                                 const TSourceLoc &identifierLocation,
-                                                 const TString &identifier)
+void TParseContext::parseDeclarator(TPublicType &publicType,
+                                    const TSourceLoc &identifierLocation,
+                                    const TString &identifier,
+                                    TIntermDeclaration *declarationOut)
 {
     // If the declaration starting this declarator list was empty (example: int,), some checks were
     // not performed.
@@ -1990,17 +2011,18 @@
     TIntermSymbol *symbol =
         intermediate.addSymbol(0, identifier, TType(publicType), identifierLocation);
     if (variable && symbol)
+    {
         symbol->setId(variable->getUniqueId());
-
-    return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation);
+        declarationOut->appendDeclarator(symbol);
+    }
 }
 
-TIntermAggregate *TParseContext::parseArrayDeclarator(TPublicType &publicType,
-                                                      TIntermAggregate *aggregateDeclaration,
-                                                      const TSourceLoc &identifierLocation,
-                                                      const TString &identifier,
-                                                      const TSourceLoc &arrayLocation,
-                                                      TIntermTyped *indexExpression)
+void TParseContext::parseArrayDeclarator(TPublicType &publicType,
+                                         const TSourceLoc &identifierLocation,
+                                         const TString &identifier,
+                                         const TSourceLoc &arrayLocation,
+                                         TIntermTyped *indexExpression,
+                                         TIntermDeclaration *declarationOut)
 {
     // If the declaration starting this declarator list was empty (example: int,), some checks were
     // not performed.
@@ -2028,18 +2050,16 @@
         if (variable && symbol)
             symbol->setId(variable->getUniqueId());
 
-        return intermediate.growAggregate(aggregateDeclaration, symbol, identifierLocation);
+        declarationOut->appendDeclarator(symbol);
     }
-
-    return nullptr;
 }
 
-TIntermAggregate *TParseContext::parseInitDeclarator(const TPublicType &publicType,
-                                                     TIntermAggregate *aggregateDeclaration,
-                                                     const TSourceLoc &identifierLocation,
-                                                     const TString &identifier,
-                                                     const TSourceLoc &initLocation,
-                                                     TIntermTyped *initializer)
+void TParseContext::parseInitDeclarator(const TPublicType &publicType,
+                                        const TSourceLoc &identifierLocation,
+                                        const TString &identifier,
+                                        const TSourceLoc &initLocation,
+                                        TIntermTyped *initializer,
+                                        TIntermDeclaration *declarationOut)
 {
     // If the declaration starting this declarator list was empty (example: int,), some checks were
     // not performed.
@@ -2051,35 +2071,27 @@
 
     checkDeclaratorLocationIsNotSpecified(identifierLocation, publicType);
 
-    TIntermNode *intermNode = nullptr;
-    if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &intermNode))
+    TIntermBinary *initNode = nullptr;
+    if (!executeInitializer(identifierLocation, identifier, publicType, initializer, &initNode))
     {
         //
         // build the intermediate representation
         //
-        if (intermNode)
+        if (initNode)
         {
-            return intermediate.growAggregate(aggregateDeclaration, intermNode, initLocation);
+            declarationOut->appendDeclarator(initNode);
         }
-        else
-        {
-            return aggregateDeclaration;
-        }
-    }
-    else
-    {
-        return nullptr;
     }
 }
 
-TIntermAggregate *TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
-                                                          TIntermAggregate *aggregateDeclaration,
-                                                          const TSourceLoc &identifierLocation,
-                                                          const TString &identifier,
-                                                          const TSourceLoc &indexLocation,
-                                                          TIntermTyped *indexExpression,
-                                                          const TSourceLoc &initLocation,
-                                                          TIntermTyped *initializer)
+void TParseContext::parseArrayInitDeclarator(const TPublicType &publicType,
+                                             const TSourceLoc &identifierLocation,
+                                             const TString &identifier,
+                                             const TSourceLoc &indexLocation,
+                                             TIntermTyped *indexExpression,
+                                             const TSourceLoc &initLocation,
+                                             TIntermTyped *initializer,
+                                             TIntermDeclaration *declarationOut)
 {
     // If the declaration starting this declarator list was empty (example: int,), some checks were
     // not performed.
@@ -2107,21 +2119,13 @@
     arrayType.setArraySize(size);
 
     // initNode will correspond to the whole of "b[n] = initializer".
-    TIntermNode *initNode = nullptr;
+    TIntermBinary *initNode = nullptr;
     if (!executeInitializer(identifierLocation, identifier, arrayType, initializer, &initNode))
     {
         if (initNode)
         {
-            return intermediate.growAggregate(aggregateDeclaration, initNode, initLocation);
+            declarationOut->appendDeclarator(initNode);
         }
-        else
-        {
-            return aggregateDeclaration;
-        }
-    }
-    else
-    {
-        return nullptr;
     }
 }
 
@@ -2620,7 +2624,7 @@
 //
 // Interface/uniform blocks
 //
-TIntermAggregate *TParseContext::addInterfaceBlock(
+TIntermDeclaration *TParseContext::addInterfaceBlock(
     const TTypeQualifierBuilder &typeQualifierBuilder,
     const TSourceLoc &nameLine,
     const TString &blockName,
@@ -2782,13 +2786,14 @@
         symbolName = instanceTypeDef->getName();
     }
 
-    TIntermAggregate *aggregate = TIntermediate::MakeAggregate(
-        intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line),
-        nameLine);
-    aggregate->setOp(EOpDeclaration);
+    TIntermSymbol *blockSymbol =
+        intermediate.addSymbol(symbolId, symbolName, interfaceBlockType, typeQualifier.line);
+    TIntermDeclaration *declaration = new TIntermDeclaration();
+    declaration->appendDeclarator(blockSymbol);
+    declaration->setLine(nameLine);
 
     exitStructDeclaration();
-    return aggregate;
+    return declaration;
 }
 
 void TParseContext::enterStructDeclaration(const TSourceLoc &line, const TString &identifier)
@@ -3903,10 +3908,10 @@
     return node;
 }
 
-TIntermTyped *TParseContext::createAssign(TOperator op,
-                                          TIntermTyped *left,
-                                          TIntermTyped *right,
-                                          const TSourceLoc &loc)
+TIntermBinary *TParseContext::createAssign(TOperator op,
+                                           TIntermTyped *left,
+                                           TIntermTyped *right,
+                                           const TSourceLoc &loc)
 {
     if (binaryOpCommonCheck(op, left, right, loc))
     {
diff --git a/src/compiler/translator/ParseContext.h b/src/compiler/translator/ParseContext.h
index df34dd3..4b1c6ae 100644
--- a/src/compiler/translator/ParseContext.h
+++ b/src/compiler/translator/ParseContext.h
@@ -202,66 +202,66 @@
                             const TString &identifier,
                             const TPublicType &pType,
                             TIntermTyped *initializer,
-                            TIntermNode **intermNode);
+                            TIntermBinary **initNode);
 
     TPublicType addFullySpecifiedType(const TTypeQualifierBuilder &typeQualifierBuilder,
                                       const TPublicType &typeSpecifier);
 
-    TIntermAggregate *parseSingleDeclaration(TPublicType &publicType,
-                                             const TSourceLoc &identifierOrTypeLocation,
-                                             const TString &identifier);
-    TIntermAggregate *parseSingleArrayDeclaration(TPublicType &publicType,
-                                                  const TSourceLoc &identifierLocation,
-                                                  const TString &identifier,
-                                                  const TSourceLoc &indexLocation,
-                                                  TIntermTyped *indexExpression);
-    TIntermAggregate *parseSingleInitDeclaration(const TPublicType &publicType,
-                                                 const TSourceLoc &identifierLocation,
-                                                 const TString &identifier,
-                                                 const TSourceLoc &initLocation,
-                                                 TIntermTyped *initializer);
+    TIntermDeclaration *parseSingleDeclaration(TPublicType &publicType,
+                                               const TSourceLoc &identifierOrTypeLocation,
+                                               const TString &identifier);
+    TIntermDeclaration *parseSingleArrayDeclaration(TPublicType &publicType,
+                                                    const TSourceLoc &identifierLocation,
+                                                    const TString &identifier,
+                                                    const TSourceLoc &indexLocation,
+                                                    TIntermTyped *indexExpression);
+    TIntermDeclaration *parseSingleInitDeclaration(const TPublicType &publicType,
+                                                   const TSourceLoc &identifierLocation,
+                                                   const TString &identifier,
+                                                   const TSourceLoc &initLocation,
+                                                   TIntermTyped *initializer);
 
     // Parse a declaration like "type a[n] = initializer"
     // Note that this does not apply to declarations like "type[n] a = initializer"
-    TIntermAggregate *parseSingleArrayInitDeclaration(TPublicType &publicType,
-                                                      const TSourceLoc &identifierLocation,
-                                                      const TString &identifier,
-                                                      const TSourceLoc &indexLocation,
-                                                      TIntermTyped *indexExpression,
-                                                      const TSourceLoc &initLocation,
-                                                      TIntermTyped *initializer);
+    TIntermDeclaration *parseSingleArrayInitDeclaration(TPublicType &publicType,
+                                                        const TSourceLoc &identifierLocation,
+                                                        const TString &identifier,
+                                                        const TSourceLoc &indexLocation,
+                                                        TIntermTyped *indexExpression,
+                                                        const TSourceLoc &initLocation,
+                                                        TIntermTyped *initializer);
 
     TIntermAggregate *parseInvariantDeclaration(const TTypeQualifierBuilder &typeQualifierBuilder,
                                                 const TSourceLoc &identifierLoc,
                                                 const TString *identifier,
                                                 const TSymbol *symbol);
 
-    TIntermAggregate *parseDeclarator(TPublicType &publicType,
-                                      TIntermAggregate *aggregateDeclaration,
-                                      const TSourceLoc &identifierLocation,
-                                      const TString &identifier);
-    TIntermAggregate *parseArrayDeclarator(TPublicType &publicType,
-                                           TIntermAggregate *aggregateDeclaration,
-                                           const TSourceLoc &identifierLocation,
-                                           const TString &identifier,
-                                           const TSourceLoc &arrayLocation,
-                                           TIntermTyped *indexExpression);
-    TIntermAggregate *parseInitDeclarator(const TPublicType &publicType,
-                                          TIntermAggregate *aggregateDeclaration,
-                                          const TSourceLoc &identifierLocation,
-                                          const TString &identifier,
-                                          const TSourceLoc &initLocation,
-                                          TIntermTyped *initializer);
+    void parseDeclarator(TPublicType &publicType,
+                         const TSourceLoc &identifierLocation,
+                         const TString &identifier,
+                         TIntermDeclaration *declarationOut);
+    void parseArrayDeclarator(TPublicType &publicType,
+                              const TSourceLoc &identifierLocation,
+                              const TString &identifier,
+                              const TSourceLoc &arrayLocation,
+                              TIntermTyped *indexExpression,
+                              TIntermDeclaration *declarationOut);
+    void parseInitDeclarator(const TPublicType &publicType,
+                             const TSourceLoc &identifierLocation,
+                             const TString &identifier,
+                             const TSourceLoc &initLocation,
+                             TIntermTyped *initializer,
+                             TIntermDeclaration *declarationOut);
 
     // Parse a declarator like "a[n] = initializer"
-    TIntermAggregate *parseArrayInitDeclarator(const TPublicType &publicType,
-                                               TIntermAggregate *aggregateDeclaration,
-                                               const TSourceLoc &identifierLocation,
-                                               const TString &identifier,
-                                               const TSourceLoc &indexLocation,
-                                               TIntermTyped *indexExpression,
-                                               const TSourceLoc &initLocation,
-                                               TIntermTyped *initializer);
+    void parseArrayInitDeclarator(const TPublicType &publicType,
+                                  const TSourceLoc &identifierLocation,
+                                  const TString &identifier,
+                                  const TSourceLoc &indexLocation,
+                                  TIntermTyped *indexExpression,
+                                  const TSourceLoc &initLocation,
+                                  TIntermTyped *initializer,
+                                  TIntermDeclaration *declarationOut);
 
     void parseGlobalLayoutQualifier(const TTypeQualifierBuilder &typeQualifierBuilder);
     TIntermAggregate *addFunctionPrototypeDeclaration(const TFunction &parsedFunction,
@@ -302,14 +302,14 @@
                                         const TString *structName,
                                         TFieldList *fieldList);
 
-    TIntermAggregate *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
-                                        const TSourceLoc &nameLine,
-                                        const TString &blockName,
-                                        TFieldList *fieldList,
-                                        const TString *instanceName,
-                                        const TSourceLoc &instanceLine,
-                                        TIntermTyped *arrayIndex,
-                                        const TSourceLoc &arrayIndexLine);
+    TIntermDeclaration *addInterfaceBlock(const TTypeQualifierBuilder &typeQualifierBuilder,
+                                          const TSourceLoc &nameLine,
+                                          const TString &blockName,
+                                          TFieldList *fieldList,
+                                          const TString *instanceName,
+                                          const TSourceLoc &instanceLine,
+                                          TIntermTyped *arrayIndex,
+                                          const TSourceLoc &arrayIndexLine);
 
     void parseLocalSize(const TString &qualifierType,
                         const TSourceLoc &qualifierTypeLine,
@@ -409,8 +409,10 @@
 
     TIntermTyped *addBinaryMathInternal(
         TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
-    TIntermTyped *createAssign(
-        TOperator op, TIntermTyped *left, TIntermTyped *right, const TSourceLoc &loc);
+    TIntermBinary *createAssign(TOperator op,
+                                TIntermTyped *left,
+                                TIntermTyped *right,
+                                const TSourceLoc &loc);
     // The funcReturnType parameter is expected to be non-null when the operation is a built-in function.
     // It is expected to be null for other unary operators.
     TIntermTyped *createUnaryMath(
diff --git a/src/compiler/translator/PruneEmptyDeclarations.cpp b/src/compiler/translator/PruneEmptyDeclarations.cpp
index 4763dd6..133130d 100644
--- a/src/compiler/translator/PruneEmptyDeclarations.cpp
+++ b/src/compiler/translator/PruneEmptyDeclarations.cpp
@@ -18,7 +18,7 @@
     static void apply(TIntermNode *root);
   private:
     PruneEmptyDeclarationsTraverser();
-    bool visitAggregate(Visit, TIntermAggregate *node) override;
+    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
 };
 
 void PruneEmptyDeclarationsTraverser::apply(TIntermNode *root)
@@ -33,74 +33,71 @@
 {
 }
 
-bool PruneEmptyDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node)
+bool PruneEmptyDeclarationsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
 {
-    if (node->getOp() == EOpDeclaration)
+    TIntermSequence *sequence = node->getSequence();
+    if (sequence->size() >= 1)
     {
-        TIntermSequence *sequence = node->getSequence();
-        if (sequence->size() >= 1)
+        TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
+        // Prune declarations without a variable name, unless it's an interface block declaration.
+        if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock())
         {
-            TIntermSymbol *sym = sequence->front()->getAsSymbolNode();
-            // Prune declarations without a variable name, unless it's an interface block declaration.
-            if (sym != nullptr && sym->getSymbol() == "" && !sym->isInterfaceBlock())
+            if (sequence->size() > 1)
             {
-                if (sequence->size() > 1)
+                // Generate a replacement that will remove the empty declarator in the beginning of
+                // a declarator list. Example of a declaration that will be changed:
+                // float, a;
+                // will be changed to
+                // float a;
+                // This applies also to struct declarations.
+                TIntermSequence emptyReplacement;
+                mMultiReplacements.push_back(
+                    NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
+            }
+            else if (sym->getBasicType() != EbtStruct)
+            {
+                // Single struct declarations may just declare the struct type and no variables, so
+                // they should not be pruned. All other single empty declarations can be pruned
+                // entirely. Example of an empty declaration that will be pruned:
+                // float;
+                TIntermSequence emptyReplacement;
+                TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
+                // The declaration may be inside a block or in a loop init expression.
+                ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
+                if (parentAsBlock)
                 {
-                    // Generate a replacement that will remove the empty declarator in the beginning of a declarator
-                    // list. Example of a declaration that will be changed:
-                    // float, a;
-                    // will be changed to
-                    // float a;
-                    // This applies also to struct declarations.
-                    TIntermSequence emptyReplacement;
-                    mMultiReplacements.push_back(NodeReplaceWithMultipleEntry(node, sym, emptyReplacement));
+                    mMultiReplacements.push_back(
+                        NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
                 }
-                else if (sym->getBasicType() != EbtStruct)
+                else
                 {
-                    // Single struct declarations may just declare the struct type and no variables, so they should
-                    // not be pruned. All other single empty declarations can be pruned entirely. Example of an empty
-                    // declaration that will be pruned:
-                    // float;
-                    TIntermSequence emptyReplacement;
-                    TIntermBlock *parentAsBlock = getParentNode()->getAsBlock();
-                    // The declaration may be inside a block or in a loop init expression.
-                    ASSERT(parentAsBlock != nullptr || getParentNode()->getAsLoopNode() != nullptr);
-                    if (parentAsBlock)
-                    {
-                        mMultiReplacements.push_back(
-                            NodeReplaceWithMultipleEntry(parentAsBlock, node, emptyReplacement));
-                    }
-                    else
-                    {
-                        queueReplacement(node, nullptr, OriginalNode::IS_DROPPED);
-                    }
+                    queueReplacement(node, nullptr, OriginalNode::IS_DROPPED);
                 }
-                else if (sym->getType().getQualifier() != EvqGlobal &&
-                         sym->getType().getQualifier() != EvqTemporary)
-                {
-                    // We've hit an empty struct declaration with a qualifier, for example like
-                    // this:
-                    // const struct a { int i; };
-                    // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so
-                    // we convert the declaration to a regular struct declaration. This is okay,
-                    // since ESSL 1.00 spec section 4.1.8 says about structs that "The optional
-                    // qualifiers only apply to any declarators, and are not part of the type being
-                    // defined for name."
+            }
+            else if (sym->getType().getQualifier() != EvqGlobal &&
+                     sym->getType().getQualifier() != EvqTemporary)
+            {
+                // We've hit an empty struct declaration with a qualifier, for example like
+                // this:
+                // const struct a { int i; };
+                // NVIDIA GL driver version 367.27 doesn't accept this kind of declarations, so
+                // we convert the declaration to a regular struct declaration. This is okay,
+                // since ESSL 1.00 spec section 4.1.8 says about structs that "The optional
+                // qualifiers only apply to any declarators, and are not part of the type being
+                // defined for name."
 
-                    if (mInGlobalScope)
-                    {
-                        sym->getTypePointer()->setQualifier(EvqGlobal);
-                    }
-                    else
-                    {
-                        sym->getTypePointer()->setQualifier(EvqTemporary);
-                    }
+                if (mInGlobalScope)
+                {
+                    sym->getTypePointer()->setQualifier(EvqGlobal);
+                }
+                else
+                {
+                    sym->getTypePointer()->setQualifier(EvqTemporary);
                 }
             }
         }
-        return false;
     }
-    return true;
+    return false;
 }
 
 } // namespace
diff --git a/src/compiler/translator/RemoveDynamicIndexing.cpp b/src/compiler/translator/RemoveDynamicIndexing.cpp
index 994ae31..ad2d08a 100644
--- a/src/compiler/translator/RemoveDynamicIndexing.cpp
+++ b/src/compiler/translator/RemoveDynamicIndexing.cpp
@@ -390,7 +390,7 @@
             // Now v_expr[s0] can be safely executed several times without unintended side effects.
 
             // Init the temp variable holding the index
-            TIntermAggregate *initIndex = createTempInitDeclaration(node->getRight());
+            TIntermDeclaration *initIndex = createTempInitDeclaration(node->getRight());
             insertStatementInParentBlock(initIndex);
             mUsedTreeInsertion = true;
 
@@ -441,7 +441,7 @@
 
                 // Store the index in a temporary signed int variable.
                 TIntermTyped *indexInitializer = EnsureSignedInt(node->getRight());
-                TIntermAggregate *initIndex = createTempInitDeclaration(indexInitializer);
+                TIntermDeclaration *initIndex  = createTempInitDeclaration(indexInitializer);
                 initIndex->setLine(node->getLine());
                 insertionsBefore.push_back(initIndex);
 
diff --git a/src/compiler/translator/RewriteDoWhile.cpp b/src/compiler/translator/RewriteDoWhile.cpp
index 9cc551b..7336e5c 100644
--- a/src/compiler/translator/RewriteDoWhile.cpp
+++ b/src/compiler/translator/RewriteDoWhile.cpp
@@ -67,7 +67,7 @@
             TType boolType = TType(EbtBool);
 
             // bool temp = false;
-            TIntermAggregate *tempDeclaration = nullptr;
+            TIntermDeclaration *tempDeclaration = nullptr;
             {
                 TConstantUnion *falseConstant = new TConstantUnion();
                 falseConstant->setBConst(false);
diff --git a/src/compiler/translator/RewriteElseBlocks.cpp b/src/compiler/translator/RewriteElseBlocks.cpp
index aafb86f..937de11 100644
--- a/src/compiler/translator/RewriteElseBlocks.cpp
+++ b/src/compiler/translator/RewriteElseBlocks.cpp
@@ -70,8 +70,7 @@
 
     nextTemporaryIndex();
 
-    TIntermTyped *typedCondition     = ifElse->getCondition()->getAsTyped();
-    TIntermAggregate *storeCondition = createTempInitDeclaration(typedCondition);
+    TIntermDeclaration *storeCondition = createTempInitDeclaration(ifElse->getCondition());
 
     TIntermBlock *falseBlock = nullptr;
 
diff --git a/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp b/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
index 96789bd..f12afa4 100644
--- a/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
+++ b/src/compiler/translator/ScalarizeVecAndMatConstructorArgs.cpp
@@ -265,8 +265,8 @@
     TIntermSymbol *symbolNode = new TIntermSymbol(-1, tempVarName, type);
     TIntermBinary *init       = new TIntermBinary(EOpInitialize, symbolNode, original);
 
-    TIntermAggregate *decl = new TIntermAggregate(EOpDeclaration);
-    decl->getSequence()->push_back(init);
+    TIntermDeclaration *decl = new TIntermDeclaration();
+    decl->appendDeclarator(init);
 
     ASSERT(mBlockStack.size() > 0);
     TIntermSequence &sequence = mBlockStack.back();
diff --git a/src/compiler/translator/SeparateArrayInitialization.cpp b/src/compiler/translator/SeparateArrayInitialization.cpp
index 8cd1fa1..2c853e3 100644
--- a/src/compiler/translator/SeparateArrayInitialization.cpp
+++ b/src/compiler/translator/SeparateArrayInitialization.cpp
@@ -29,7 +29,7 @@
     static void apply(TIntermNode *root);
   private:
     SeparateArrayInitTraverser();
-    bool visitAggregate(Visit, TIntermAggregate *node) override;
+    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
 };
 
 void SeparateArrayInitTraverser::apply(TIntermNode *root)
@@ -44,43 +44,38 @@
 {
 }
 
-bool SeparateArrayInitTraverser::visitAggregate(Visit, TIntermAggregate *node)
+bool SeparateArrayInitTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
 {
-    if (node->getOp() == EOpDeclaration)
+    TIntermSequence *sequence = node->getSequence();
+    TIntermBinary *initNode   = sequence->back()->getAsBinaryNode();
+    if (initNode != nullptr && initNode->getOp() == EOpInitialize)
     {
-        TIntermSequence *sequence = node->getSequence();
-        TIntermBinary *initNode = sequence->back()->getAsBinaryNode();
-        if (initNode != nullptr && initNode->getOp() == EOpInitialize)
+        TIntermTyped *initializer = initNode->getRight();
+        if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer))
         {
-            TIntermTyped *initializer = initNode->getRight();
-            if (initializer->isArray() && !sh::OutputHLSL::canWriteAsHLSLLiteral(initializer))
-            {
-                // We rely on that array declarations have been isolated to single declarations.
-                ASSERT(sequence->size() == 1);
-                TIntermTyped *symbol = initNode->getLeft();
-                TIntermBlock *parentBlock = getParentNode()->getAsBlock();
-                ASSERT(parentBlock != nullptr);
+            // We rely on that array declarations have been isolated to single declarations.
+            ASSERT(sequence->size() == 1);
+            TIntermTyped *symbol      = initNode->getLeft();
+            TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+            ASSERT(parentBlock != nullptr);
 
-                TIntermSequence replacements;
+            TIntermSequence replacements;
 
-                TIntermAggregate *replacementDeclaration = new TIntermAggregate;
-                replacementDeclaration->setOp(EOpDeclaration);
-                replacementDeclaration->getSequence()->push_back(symbol);
-                replacementDeclaration->setLine(symbol->getLine());
-                replacements.push_back(replacementDeclaration);
+            TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
+            replacementDeclaration->appendDeclarator(symbol);
+            replacementDeclaration->setLine(symbol->getLine());
+            replacements.push_back(replacementDeclaration);
 
-                TIntermBinary *replacementAssignment =
-                    new TIntermBinary(EOpAssign, symbol, initializer);
-                replacementAssignment->setLine(symbol->getLine());
-                replacements.push_back(replacementAssignment);
+            TIntermBinary *replacementAssignment =
+                new TIntermBinary(EOpAssign, symbol, initializer);
+            replacementAssignment->setLine(symbol->getLine());
+            replacements.push_back(replacementAssignment);
 
-                mMultiReplacements.push_back(
-                    NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
-            }
+            mMultiReplacements.push_back(
+                NodeReplaceWithMultipleEntry(parentBlock, node, replacements));
         }
-        return false;
     }
-    return true;
+    return false;
 }
 
 } // namespace
diff --git a/src/compiler/translator/SeparateDeclarations.cpp b/src/compiler/translator/SeparateDeclarations.cpp
index cc339ea..a6aab22 100644
--- a/src/compiler/translator/SeparateDeclarations.cpp
+++ b/src/compiler/translator/SeparateDeclarations.cpp
@@ -26,7 +26,7 @@
     static void apply(TIntermNode *root);
   private:
     SeparateDeclarationsTraverser();
-    bool visitAggregate(Visit, TIntermAggregate *node) override;
+    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
 };
 
 void SeparateDeclarationsTraverser::apply(TIntermNode *root)
@@ -41,33 +41,28 @@
 {
 }
 
-bool SeparateDeclarationsTraverser::visitAggregate(Visit, TIntermAggregate *node)
+bool SeparateDeclarationsTraverser::visitDeclaration(Visit, TIntermDeclaration *node)
 {
-    if (node->getOp() == EOpDeclaration)
+    TIntermSequence *sequence = node->getSequence();
+    if (sequence->size() > 1)
     {
-        TIntermSequence *sequence = node->getSequence();
-        if (sequence->size() > 1)
+        TIntermBlock *parentBlock = getParentNode()->getAsBlock();
+        ASSERT(parentBlock != nullptr);
+
+        TIntermSequence replacementDeclarations;
+        for (size_t ii = 0; ii < sequence->size(); ++ii)
         {
-            TIntermBlock *parentBlock = getParentNode()->getAsBlock();
-            ASSERT(parentBlock != nullptr);
+            TIntermDeclaration *replacementDeclaration = new TIntermDeclaration();
 
-            TIntermSequence replacementDeclarations;
-            for (size_t ii = 0; ii < sequence->size(); ++ii)
-            {
-                TIntermAggregate *replacementDeclaration = new TIntermAggregate;
-
-                replacementDeclaration->setOp(EOpDeclaration);
-                replacementDeclaration->getSequence()->push_back(sequence->at(ii));
-                replacementDeclaration->setLine(sequence->at(ii)->getLine());
-                replacementDeclarations.push_back(replacementDeclaration);
-            }
-
-            mMultiReplacements.push_back(
-                NodeReplaceWithMultipleEntry(parentBlock, node, replacementDeclarations));
+            replacementDeclaration->appendDeclarator(sequence->at(ii)->getAsTyped());
+            replacementDeclaration->setLine(sequence->at(ii)->getLine());
+            replacementDeclarations.push_back(replacementDeclaration);
         }
-        return false;
+
+        mMultiReplacements.push_back(
+            NodeReplaceWithMultipleEntry(parentBlock, node, replacementDeclarations));
     }
-    return true;
+    return false;
 }
 
 } // namespace
diff --git a/src/compiler/translator/UnfoldShortCircuitToIf.cpp b/src/compiler/translator/UnfoldShortCircuitToIf.cpp
index 712f59a..a221bdf 100644
--- a/src/compiler/translator/UnfoldShortCircuitToIf.cpp
+++ b/src/compiler/translator/UnfoldShortCircuitToIf.cpp
@@ -134,9 +134,7 @@
     // Unfold "b ? x : y" into "type s; if(b) s = x; else s = y;"
     TIntermSequence insertions;
 
-    TIntermSymbol *tempSymbol         = createTempSymbol(node->getType());
-    TIntermAggregate *tempDeclaration = new TIntermAggregate(EOpDeclaration);
-    tempDeclaration->getSequence()->push_back(tempSymbol);
+    TIntermDeclaration *tempDeclaration = createTempDeclaration(node->getType());
     insertions.push_back(tempDeclaration);
 
     TIntermBlock *trueBlock       = new TIntermBlock();
diff --git a/src/compiler/translator/ValidateLimitations.cpp b/src/compiler/translator/ValidateLimitations.cpp
index 5dcfe69..660436b 100644
--- a/src/compiler/translator/ValidateLimitations.cpp
+++ b/src/compiler/translator/ValidateLimitations.cpp
@@ -213,8 +213,8 @@
     // init-declaration has the form:
     //     type-specifier identifier = constant-expression
     //
-    TIntermAggregate *decl = init->getAsAggregate();
-    if ((decl == NULL) || (decl->getOp() != EOpDeclaration))
+    TIntermDeclaration *decl = init->getAsDeclarationNode();
+    if (decl == nullptr)
     {
         error(init->getLine(), "Invalid init declaration", "for");
         return -1;
diff --git a/src/compiler/translator/VariableInfo.cpp b/src/compiler/translator/VariableInfo.cpp
index 7e1e5cd..07fe12e 100644
--- a/src/compiler/translator/VariableInfo.cpp
+++ b/src/compiler/translator/VariableInfo.cpp
@@ -559,54 +559,43 @@
     }
 }
 
-bool CollectVariables::visitAggregate(Visit, TIntermAggregate *node)
+bool CollectVariables::visitDeclaration(Visit, TIntermDeclaration *node)
 {
-    bool visitChildren = true;
+    const TIntermSequence &sequence = *(node->getSequence());
+    ASSERT(!sequence.empty());
 
-    switch (node->getOp())
+    const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
+    TQualifier qualifier          = typedNode.getQualifier();
+
+    if (typedNode.getBasicType() == EbtInterfaceBlock)
     {
-      case EOpDeclaration:
+        visitInfoList(sequence, mInterfaceBlocks);
+        return false;
+    }
+    else if (qualifier == EvqAttribute || qualifier == EvqVertexIn || qualifier == EvqFragmentOut ||
+             qualifier == EvqUniform || IsVarying(qualifier))
+    {
+        switch (qualifier)
         {
-            const TIntermSequence &sequence = *(node->getSequence());
-            ASSERT(!sequence.empty());
-
-            const TIntermTyped &typedNode = *(sequence.front()->getAsTyped());
-            TQualifier qualifier = typedNode.getQualifier();
-
-            if (typedNode.getBasicType() == EbtInterfaceBlock)
-            {
-                visitInfoList(sequence, mInterfaceBlocks);
-                visitChildren = false;
-            }
-            else if (qualifier == EvqAttribute || qualifier == EvqVertexIn ||
-                     qualifier == EvqFragmentOut || qualifier == EvqUniform ||
-                     IsVarying(qualifier))
-            {
-                switch (qualifier)
-                {
-                  case EvqAttribute:
-                  case EvqVertexIn:
-                    visitInfoList(sequence, mAttribs);
-                    break;
-                  case EvqFragmentOut:
-                    visitInfoList(sequence, mOutputVariables);
-                    break;
-                  case EvqUniform:
-                    visitInfoList(sequence, mUniforms);
-                    break;
-                  default:
-                    visitInfoList(sequence, mVaryings);
-                    break;
-                }
-
-                visitChildren = false;
-            }
-            break;
+            case EvqAttribute:
+            case EvqVertexIn:
+                visitInfoList(sequence, mAttribs);
+                break;
+            case EvqFragmentOut:
+                visitInfoList(sequence, mOutputVariables);
+                break;
+            case EvqUniform:
+                visitInfoList(sequence, mUniforms);
+                break;
+            default:
+                visitInfoList(sequence, mVaryings);
+                break;
         }
-      default: break;
+
+        return false;
     }
 
-    return visitChildren;
+    return true;
 }
 
 bool CollectVariables::visitBinary(Visit, TIntermBinary *binaryNode)
diff --git a/src/compiler/translator/VariableInfo.h b/src/compiler/translator/VariableInfo.h
index f79035d..6b21156 100644
--- a/src/compiler/translator/VariableInfo.h
+++ b/src/compiler/translator/VariableInfo.h
@@ -31,7 +31,7 @@
                      const TExtensionBehavior &extensionBehavior);
 
     void visitSymbol(TIntermSymbol *symbol) override;
-    bool visitAggregate(Visit, TIntermAggregate *node) override;
+    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
     bool visitBinary(Visit visit, TIntermBinary *binaryNode) override;
 
   private:
diff --git a/src/compiler/translator/VersionGLSL.cpp b/src/compiler/translator/VersionGLSL.cpp
index 5c0c519..b967065 100644
--- a/src/compiler/translator/VersionGLSL.cpp
+++ b/src/compiler/translator/VersionGLSL.cpp
@@ -62,21 +62,22 @@
     }
 }
 
+bool TVersionGLSL::visitDeclaration(Visit, TIntermDeclaration *node)
+{
+    const TIntermSequence &sequence = *(node->getSequence());
+    if (sequence.front()->getAsTyped()->getType().isInvariant())
+    {
+        ensureVersionIsAtLeast(GLSL_VERSION_120);
+    }
+    return true;
+}
+
 bool TVersionGLSL::visitAggregate(Visit, TIntermAggregate *node)
 {
     bool visitChildren = true;
 
     switch (node->getOp())
     {
-      case EOpDeclaration:
-        {
-            const TIntermSequence &sequence = *(node->getSequence());
-            if (sequence.front()->getAsTyped()->getType().isInvariant())
-            {
-                ensureVersionIsAtLeast(GLSL_VERSION_120);
-            }
-            break;
-        }
       case EOpInvariantDeclaration:
         ensureVersionIsAtLeast(GLSL_VERSION_120);
         break;
diff --git a/src/compiler/translator/VersionGLSL.h b/src/compiler/translator/VersionGLSL.h
index c41069d..f03a48b 100644
--- a/src/compiler/translator/VersionGLSL.h
+++ b/src/compiler/translator/VersionGLSL.h
@@ -58,6 +58,7 @@
 
     void visitSymbol(TIntermSymbol *) override;
     bool visitAggregate(Visit, TIntermAggregate *) override;
+    bool visitDeclaration(Visit, TIntermDeclaration *node) override;
 
   private:
     void ensureVersionIsAtLeast(int version);
diff --git a/src/compiler/translator/glslang.y b/src/compiler/translator/glslang.y
index d1132c6..0aa15ec 100644
--- a/src/compiler/translator/glslang.y
+++ b/src/compiler/translator/glslang.y
@@ -77,6 +77,7 @@
             TIntermTyped* intermTypedNode;
             TIntermAggregate* intermAggregate;
             TIntermBlock* intermBlock;
+            TIntermDeclaration* intermDeclaration;
             TIntermSwitch* intermSwitch;
             TIntermCase* intermCase;
         };
@@ -606,10 +607,7 @@
         $$ = context->addFunctionPrototypeDeclaration(*($1.function), @1);
     }
     | init_declarator_list SEMICOLON {
-        TIntermAggregate *aggNode = $1.intermAggregate;
-        if (aggNode && aggNode->getOp() == EOpNull)
-            aggNode->setOp(EOpDeclaration);
-        $$ = aggNode;
+        $$ = $1.intermDeclaration;
     }
     | PRECISION precision_qualifier type_specifier_no_prec SEMICOLON {
         if (($2 == EbpHigh) && (context->getShaderType() == GL_FRAGMENT_SHADER) && !context->getFragmentPrecisionHigh()) {
@@ -762,54 +760,54 @@
     }
     | init_declarator_list COMMA identifier {
         $$ = $1;
-        $$.intermAggregate = context->parseDeclarator($$.type, $1.intermAggregate, @3, *$3.string);
+        context->parseDeclarator($$.type, @3, *$3.string, $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
         $$ = $1;
-        $$.intermAggregate = context->parseArrayDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5);
+        context->parseArrayDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
         ES3_OR_NEWER("[]", @3, "implicitly sized array");
         $$ = $1;
-        $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, nullptr, @6, $7);
+        context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, nullptr, @6, $7, $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
         ES3_OR_NEWER("=", @7, "first-class arrays (array initializer)");
         $$ = $1;
-        $$.intermAggregate = context->parseArrayInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5, @7, $8);
+        context->parseArrayInitDeclarator($$.type, @3, *$3.string, @4, $5, @7, $8, $$.intermDeclaration);
     }
     | init_declarator_list COMMA identifier EQUAL initializer {
         $$ = $1;
-        $$.intermAggregate = context->parseInitDeclarator($$.type, $1.intermAggregate, @3, *$3.string, @4, $5);
+        context->parseInitDeclarator($$.type, @3, *$3.string, @4, $5, $$.intermDeclaration);
     }
     ;
 
 single_declaration
     : fully_specified_type {
         $$.type = $1;
-        $$.intermAggregate = context->parseSingleDeclaration($$.type, @1, "");
+        $$.intermDeclaration = context->parseSingleDeclaration($$.type, @1, "");
     }
     | fully_specified_type identifier {
         $$.type = $1;
-        $$.intermAggregate = context->parseSingleDeclaration($$.type, @2, *$2.string);
+        $$.intermDeclaration = context->parseSingleDeclaration($$.type, @2, *$2.string);
     }
     | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET {
         $$.type = $1;
-        $$.intermAggregate = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4);
+        $$.intermDeclaration = context->parseSingleArrayDeclaration($$.type, @2, *$2.string, @3, $4);
     }
     | fully_specified_type identifier LEFT_BRACKET RIGHT_BRACKET EQUAL initializer {
         ES3_OR_NEWER("[]", @3, "implicitly sized array");
         $$.type = $1;
-        $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6);
+        $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, nullptr, @5, $6);
     }
     | fully_specified_type identifier LEFT_BRACKET constant_expression RIGHT_BRACKET EQUAL initializer {
         ES3_OR_NEWER("=", @6, "first-class arrays (array initializer)");
         $$.type = $1;
-        $$.intermAggregate = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7);
+        $$.intermDeclaration = context->parseSingleArrayInitDeclaration($$.type, @2, *$2.string, @3, $4, @6, $7);
     }
     | fully_specified_type identifier EQUAL initializer {
         $$.type = $1;
-        $$.intermAggregate = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
+        $$.intermDeclaration = context->parseSingleInitDeclaration($$.type, @2, *$2.string, @3, $4);
     }
     ;
 
@@ -1416,10 +1414,10 @@
         context->checkIsScalarBool($1->getLine(), $1);
     }
     | fully_specified_type identifier EQUAL initializer {
-        TIntermNode *intermNode;
+        TIntermBinary *initNode = nullptr;
         context->checkIsScalarBool(@2, $1);
 
-        if (!context->executeInitializer(@2, *$2.string, $1, $4, &intermNode))
+        if (!context->executeInitializer(@2, *$2.string, $1, $4, &initNode))
             $$ = $4;
         else {
             $$ = 0;
diff --git a/src/compiler/translator/glslang_tab.cpp b/src/compiler/translator/glslang_tab.cpp
index 4f7007d..aa62e48 100644
--- a/src/compiler/translator/glslang_tab.cpp
+++ b/src/compiler/translator/glslang_tab.cpp
@@ -306,6 +306,7 @@
             TIntermTyped* intermTypedNode;
             TIntermAggregate* intermAggregate;
             TIntermBlock* intermBlock;
+            TIntermDeclaration* intermDeclaration;
             TIntermSwitch* intermSwitch;
             TIntermCase* intermCase;
         };
@@ -729,35 +730,35 @@
   /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
 static const yytype_uint16 yyrline[] =
 {
-       0,   240,   240,   241,   244,   254,   257,   262,   267,   272,
-     277,   283,   286,   289,   292,   295,   298,   304,   311,   322,
-     326,   334,   337,   343,   347,   354,   360,   369,   377,   383,
-     389,   398,   401,   404,   407,   417,   418,   419,   420,   428,
-     429,   432,   435,   442,   443,   446,   452,   453,   457,   464,
-     465,   468,   471,   474,   480,   481,   484,   490,   491,   498,
-     499,   506,   507,   514,   515,   521,   522,   528,   529,   535,
-     536,   542,   543,   550,   551,   552,   553,   557,   558,   559,
-     563,   567,   571,   575,   582,   585,   591,   598,   605,   608,
-     614,   623,   627,   631,   635,   639,   646,   653,   656,   663,
-     671,   691,   701,   709,   734,   738,   742,   746,   753,   760,
-     763,   767,   771,   776,   781,   788,   792,   796,   800,   805,
-     810,   817,   827,   833,   836,   842,   846,   853,   859,   863,
-     867,   870,   873,   882,   888,   896,   899,   919,   938,   945,
-     949,   953,   956,   962,   972,   975,   978,   984,   991,   994,
-    1000,  1003,  1006,  1012,  1015,  1020,  1031,  1034,  1037,  1040,
-    1043,  1046,  1050,  1054,  1058,  1062,  1066,  1070,  1074,  1078,
-    1082,  1086,  1090,  1094,  1098,  1102,  1106,  1110,  1114,  1118,
-    1122,  1126,  1130,  1133,  1136,  1139,  1142,  1145,  1148,  1151,
-    1154,  1157,  1160,  1163,  1166,  1169,  1172,  1175,  1182,  1188,
-    1191,  1194,  1197,  1200,  1203,  1206,  1209,  1212,  1215,  1218,
-    1221,  1224,  1227,  1239,  1239,  1242,  1242,  1248,  1251,  1266,
-    1269,  1276,  1280,  1286,  1292,  1304,  1308,  1312,  1313,  1319,
-    1320,  1321,  1322,  1323,  1324,  1325,  1329,  1330,  1330,  1330,
-    1339,  1340,  1344,  1344,  1345,  1345,  1350,  1353,  1362,  1367,
-    1374,  1375,  1379,  1386,  1390,  1397,  1397,  1404,  1407,  1414,
-    1418,  1431,  1431,  1436,  1436,  1442,  1442,  1450,  1453,  1459,
-    1462,  1468,  1472,  1479,  1482,  1485,  1488,  1491,  1500,  1506,
-    1512,  1515,  1521,  1521
+       0,   241,   241,   242,   245,   255,   258,   263,   268,   273,
+     278,   284,   287,   290,   293,   296,   299,   305,   312,   323,
+     327,   335,   338,   344,   348,   355,   361,   370,   378,   384,
+     390,   399,   402,   405,   408,   418,   419,   420,   421,   429,
+     430,   433,   436,   443,   444,   447,   453,   454,   458,   465,
+     466,   469,   472,   475,   481,   482,   485,   491,   492,   499,
+     500,   507,   508,   515,   516,   522,   523,   529,   530,   536,
+     537,   543,   544,   551,   552,   553,   554,   558,   559,   560,
+     564,   568,   572,   576,   583,   586,   592,   599,   606,   609,
+     612,   621,   625,   629,   633,   637,   644,   651,   654,   661,
+     669,   689,   699,   707,   732,   736,   740,   744,   751,   758,
+     761,   765,   769,   774,   779,   786,   790,   794,   798,   803,
+     808,   815,   825,   831,   834,   840,   844,   851,   857,   861,
+     865,   868,   871,   880,   886,   894,   897,   917,   936,   943,
+     947,   951,   954,   960,   970,   973,   976,   982,   989,   992,
+     998,  1001,  1004,  1010,  1013,  1018,  1029,  1032,  1035,  1038,
+    1041,  1044,  1048,  1052,  1056,  1060,  1064,  1068,  1072,  1076,
+    1080,  1084,  1088,  1092,  1096,  1100,  1104,  1108,  1112,  1116,
+    1120,  1124,  1128,  1131,  1134,  1137,  1140,  1143,  1146,  1149,
+    1152,  1155,  1158,  1161,  1164,  1167,  1170,  1173,  1180,  1186,
+    1189,  1192,  1195,  1198,  1201,  1204,  1207,  1210,  1213,  1216,
+    1219,  1222,  1225,  1237,  1237,  1240,  1240,  1246,  1249,  1264,
+    1267,  1274,  1278,  1284,  1290,  1302,  1306,  1310,  1311,  1317,
+    1318,  1319,  1320,  1321,  1322,  1323,  1327,  1328,  1328,  1328,
+    1337,  1338,  1342,  1342,  1343,  1343,  1348,  1351,  1360,  1365,
+    1372,  1373,  1377,  1384,  1388,  1395,  1395,  1402,  1405,  1412,
+    1416,  1429,  1429,  1434,  1434,  1440,  1440,  1448,  1451,  1457,
+    1460,  1466,  1470,  1477,  1480,  1483,  1486,  1489,  1498,  1504,
+    1510,  1513,  1519,  1519
 };
 #endif
 
@@ -3143,10 +3144,7 @@
   case 89:
 
     {
-        TIntermAggregate *aggNode = (yyvsp[-1].interm).intermAggregate;
-        if (aggNode && aggNode->getOp() == EOpNull)
-            aggNode->setOp(EOpDeclaration);
-        (yyval.interm.intermNode) = aggNode;
+        (yyval.interm.intermNode) = (yyvsp[-1].interm).intermDeclaration;
     }
 
     break;
@@ -3369,7 +3367,7 @@
 
     {
         (yyval.interm) = (yyvsp[-2].interm);
-        (yyval.interm).intermAggregate = context->parseDeclarator((yyval.interm).type, (yyvsp[-2].interm).intermAggregate, (yylsp[0]), *(yyvsp[0].lex).string);
+        context->parseDeclarator((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string, (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3378,7 +3376,7 @@
 
     {
         (yyval.interm) = (yyvsp[-5].interm);
-        (yyval.interm).intermAggregate = context->parseArrayDeclarator((yyval.interm).type, (yyvsp[-5].interm).intermAggregate, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
+        context->parseArrayDeclarator((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3388,7 +3386,7 @@
     {
         ES3_OR_NEWER("[]", (yylsp[-4]), "implicitly sized array");
         (yyval.interm) = (yyvsp[-6].interm);
-        (yyval.interm).intermAggregate = context->parseArrayInitDeclarator((yyval.interm).type, (yyvsp[-6].interm).intermAggregate, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        context->parseArrayInitDeclarator((yyval.interm).type, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3398,7 +3396,7 @@
     {
         ES3_OR_NEWER("=", (yylsp[-1]), "first-class arrays (array initializer)");
         (yyval.interm) = (yyvsp[-7].interm);
-        (yyval.interm).intermAggregate = context->parseArrayInitDeclarator((yyval.interm).type, (yyvsp[-7].interm).intermAggregate, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        context->parseArrayInitDeclarator((yyval.interm).type, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3407,7 +3405,7 @@
 
     {
         (yyval.interm) = (yyvsp[-4].interm);
-        (yyval.interm).intermAggregate = context->parseInitDeclarator((yyval.interm).type, (yyvsp[-4].interm).intermAggregate, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        context->parseInitDeclarator((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode), (yyval.interm).intermDeclaration);
     }
 
     break;
@@ -3416,7 +3414,7 @@
 
     {
         (yyval.interm).type = (yyvsp[0].interm.type);
-        (yyval.interm).intermAggregate = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), "");
+        (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), "");
     }
 
     break;
@@ -3425,7 +3423,7 @@
 
     {
         (yyval.interm).type = (yyvsp[-1].interm.type);
-        (yyval.interm).intermAggregate = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string);
+        (yyval.interm).intermDeclaration = context->parseSingleDeclaration((yyval.interm).type, (yylsp[0]), *(yyvsp[0].lex).string);
     }
 
     break;
@@ -3434,7 +3432,7 @@
 
     {
         (yyval.interm).type = (yyvsp[-4].interm.type);
-        (yyval.interm).intermAggregate = context->parseSingleArrayDeclaration((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
+        (yyval.interm).intermDeclaration = context->parseSingleArrayDeclaration((yyval.interm).type, (yylsp[-3]), *(yyvsp[-3].lex).string, (yylsp[-2]), (yyvsp[-1].interm.intermTypedNode));
     }
 
     break;
@@ -3444,7 +3442,7 @@
     {
         ES3_OR_NEWER("[]", (yylsp[-3]), "implicitly sized array");
         (yyval.interm).type = (yyvsp[-5].interm.type);
-        (yyval.interm).intermAggregate = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        (yyval.interm).intermDeclaration = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-4]), *(yyvsp[-4].lex).string, (yylsp[-3]), nullptr, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -3454,7 +3452,7 @@
     {
         ES3_OR_NEWER("=", (yylsp[-1]), "first-class arrays (array initializer)");
         (yyval.interm).type = (yyvsp[-6].interm.type);
-        (yyval.interm).intermAggregate = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        (yyval.interm).intermDeclaration = context->parseSingleArrayInitDeclaration((yyval.interm).type, (yylsp[-5]), *(yyvsp[-5].lex).string, (yylsp[-4]), (yyvsp[-3].interm.intermTypedNode), (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -3463,7 +3461,7 @@
 
     {
         (yyval.interm).type = (yyvsp[-3].interm.type);
-        (yyval.interm).intermAggregate = context->parseSingleInitDeclaration((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
+        (yyval.interm).intermDeclaration = context->parseSingleInitDeclaration((yyval.interm).type, (yylsp[-2]), *(yyvsp[-2].lex).string, (yylsp[-1]), (yyvsp[0].interm.intermTypedNode));
     }
 
     break;
@@ -4672,10 +4670,10 @@
   case 260:
 
     {
-        TIntermNode *intermNode;
+        TIntermBinary *initNode = nullptr;
         context->checkIsScalarBool((yylsp[-2]), (yyvsp[-3].interm.type));
 
-        if (!context->executeInitializer((yylsp[-2]), *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), (yyvsp[0].interm.intermTypedNode), &intermNode))
+        if (!context->executeInitializer((yylsp[-2]), *(yyvsp[-2].lex).string, (yyvsp[-3].interm.type), (yyvsp[0].interm.intermTypedNode), &initNode))
             (yyval.interm.intermTypedNode) = (yyvsp[0].interm.intermTypedNode);
         else {
             (yyval.interm.intermTypedNode) = 0;
diff --git a/src/compiler/translator/glslang_tab.h b/src/compiler/translator/glslang_tab.h
index 09025b0..707d946 100644
--- a/src/compiler/translator/glslang_tab.h
+++ b/src/compiler/translator/glslang_tab.h
@@ -219,6 +219,7 @@
             TIntermTyped* intermTypedNode;
             TIntermAggregate* intermAggregate;
             TIntermBlock *intermBlock;
+            TIntermDeclaration *intermDeclaration;
             TIntermSwitch* intermSwitch;
             TIntermCase* intermCase;
         };
diff --git a/src/compiler/translator/intermOut.cpp b/src/compiler/translator/intermOut.cpp
index 352fde3..9767c9f 100644
--- a/src/compiler/translator/intermOut.cpp
+++ b/src/compiler/translator/intermOut.cpp
@@ -53,6 +53,7 @@
     bool visitFunctionDefinition(Visit visit, TIntermFunctionDefinition *node) override;
     bool visitAggregate(Visit visit, TIntermAggregate *) override;
     bool visitBlock(Visit visit, TIntermBlock *) override;
+    bool visitDeclaration(Visit visit, TIntermDeclaration *node) override;
     bool visitLoop(Visit visit, TIntermLoop *) override;
     bool visitBranch(Visit visit, TIntermBranch *) override;
 };
@@ -467,7 +468,6 @@
 
       case EOpOuterProduct:  out << "outer product";   break;
 
-      case EOpDeclaration:   out << "Declaration: ";   break;
       case EOpInvariantDeclaration: out << "Invariant Declaration: "; break;
 
       default:
@@ -493,6 +493,16 @@
     return true;
 }
 
+bool TOutputTraverser::visitDeclaration(Visit visit, TIntermDeclaration *node)
+{
+    TInfoSinkBase &out = sink;
+
+    OutputTreeText(out, node, mDepth);
+    out << "Declaration\n";
+
+    return true;
+}
+
 bool TOutputTraverser::visitTernary(Visit visit, TIntermTernary *node)
 {
     TInfoSinkBase &out = sink;