moved SkSL VarDeclarations data into IRNode

Change-Id: I03bdef43c79bc3c997f9a9a6aa8fbb1a7194943a
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/326437
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 33fcb43..68910d6 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -154,9 +154,10 @@
     for (const auto& elem : program->elements()) {
         // Variables (uniform, varying, etc.)
         if (elem->is<SkSL::GlobalVarDeclaration>()) {
-            const auto& varDecl = elem->as<SkSL::GlobalVarDeclaration>().fDecl;
+            const SkSL::GlobalVarDeclaration& global = elem->as<SkSL::GlobalVarDeclaration>();
+            const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>();
 
-            const SkSL::Variable& var = *varDecl->fVar;
+            const SkSL::Variable& var = varDecl.var();
             const SkSL::Type& varType = var.type();
 
             // Varyings (only used in conjunction with drawVertices)
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index ba59e66..a462fb3 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -479,12 +479,12 @@
         }
         case Statement::Kind::kVarDeclaration: {
             auto& v = s.template as<VarDeclaration>();
-            for (auto& sizeExpr : v.fSizes) {
-                if (sizeExpr && this->visitExpression(*sizeExpr)) {
+            for (int i = 0; i < v.sizeCount(); ++i) {
+                if (v.size(i) && this->visitExpression(*v.size(i))) {
                     return true;
                 }
             }
-            return v.fValue && this->visitExpression(*v.fValue);
+            return v.value() && this->visitExpression(*v.value());
         }
         case Statement::Kind::kWhile: {
             auto& w = s.template as<WhileStatement>();
@@ -517,7 +517,7 @@
             return false;
 
         case ProgramElement::Kind::kGlobalVar:
-            if (this->visitStatement(*pe.template as<GlobalVarDeclaration>().fDecl)) {
+            if (this->visitStatement(*pe.template as<GlobalVarDeclaration>().declaration())) {
                 return true;
             }
             return false;
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 4b25fa3..a3927e5 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -161,17 +161,17 @@
             }
             case ProgramElement::Kind::kGlobalVar: {
                 const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
-                const Variable* declVar = decl.fDecl->fVar;
-                if (declVar->type() == *fContext.fFragmentProcessor_Type) {
+                const Variable& declVar = decl.declaration()->as<VarDeclaration>().var();
+                if (declVar.type() == *fContext.fFragmentProcessor_Type) {
                     fOutput->fChildFPCount++;
                 }
-                if (declVar->modifiers().fLayout.fBuiltin >= 0 || is_in(*declVar)) {
+                if (declVar.modifiers().fLayout.fBuiltin >= 0 || is_in(declVar)) {
                     continue;
                 }
-                if (is_uniform(*declVar)) {
-                    this->gatherUniforms(declVar->type(), declVar->name());
+                if (is_uniform(declVar)) {
+                    this->gatherUniforms(declVar.type(), declVar.name());
                 } else {
-                    fOutput->fGlobalSlotCount += SlotCount(declVar->type());
+                    fOutput->fGlobalSlotCount += SlotCount(declVar.type());
                 }
                 break;
             }
@@ -457,11 +457,11 @@
                 for (const auto& e : fProgram.elements()) {
                     if (e->is<GlobalVarDeclaration>()) {
                         const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
-                        const Variable* declVar = decl.fDecl->fVar;
-                        if (declVar->type() != *fContext.fFragmentProcessor_Type) {
+                        const Variable& declVar = decl.declaration()->as<VarDeclaration>().var();
+                        if (declVar.type() != *fContext.fFragmentProcessor_Type) {
                             continue;
                         }
-                        if (declVar == &var) {
+                        if (&declVar == &var) {
                             SkASSERT(offset <= 255);
                             return { offset, Storage::kChildFP };
                         }
@@ -485,18 +485,18 @@
             for (const auto& e : fProgram.elements()) {
                 if (e->is<GlobalVarDeclaration>()) {
                     const GlobalVarDeclaration& decl = e->as<GlobalVarDeclaration>();
-                    const Variable* declVar = decl.fDecl->fVar;
-                    if (declVar->modifiers().fLayout.fBuiltin >= 0 || is_in(*declVar)) {
+                    const Variable& declVar = decl.declaration()->as<VarDeclaration>().var();
+                    if (declVar.modifiers().fLayout.fBuiltin >= 0 || is_in(declVar)) {
                         continue;
                     }
-                    if (isUniform != is_uniform(*declVar)) {
+                    if (isUniform != is_uniform(declVar)) {
                         continue;
                     }
-                    if (declVar == &var) {
+                    if (&declVar == &var) {
                         SkASSERT(offset <= 255);
                         return  { offset, isUniform ? Storage::kUniform : Storage::kGlobal };
                     }
-                    offset += SlotCount(declVar->type());
+                    offset += SlotCount(declVar.type());
                 }
             }
             SkASSERT(false);
@@ -1762,10 +1762,10 @@
 
 void ByteCodeGenerator::writeVarDeclaration(const VarDeclaration& decl) {
     // we need to grab the location even if we don't use it, to ensure it has been allocated
-    Location location = this->getLocation(*decl.fVar);
-    if (decl.fValue) {
-        this->writeExpression(*decl.fValue);
-        int count = SlotCount(decl.fValue->type());
+    Location location = this->getLocation(decl.var());
+    if (decl.value()) {
+        this->writeExpression(*decl.value());
+        int count = SlotCount(decl.value()->type());
         this->write(ByteCodeInstruction::kStore, count);
         this->write8(location.fSlot);
     }
diff --git a/src/sksl/SkSLCFGGenerator.cpp b/src/sksl/SkSLCFGGenerator.cpp
index 3166687..79c128a 100644
--- a/src/sksl/SkSLCFGGenerator.cpp
+++ b/src/sksl/SkSLCFGGenerator.cpp
@@ -513,8 +513,8 @@
         }
         case Statement::Kind::kVarDeclaration: {
             VarDeclaration& vd = (*s)->as<VarDeclaration>();
-            if (vd.fValue) {
-                this->addExpression(cfg, &vd.fValue, /*constantPropagate=*/true);
+            if (vd.value()) {
+                this->addExpression(cfg, &vd.value(), /*constantPropagate=*/true);
             }
             cfg.currentBlock().fNodes.push_back(BasicBlock::MakeStatement(s));
             break;
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index 7001518..4537608 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -415,10 +415,11 @@
     int index = 0;
     for (const auto& p : fProgram.elements()) {
         if (p->is<GlobalVarDeclaration>()) {
-            const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-            if (decl.fVar == &var) {
+            const VarDeclaration& decl =
+                                  p->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>();
+            if (&decl.var() == &var) {
                 return index;
-            } else if (decl.fVar->type().nonnullable() == *fContext.fFragmentProcessor_Type) {
+            } else if (decl.var().type().nonnullable() == *fContext.fFragmentProcessor_Type) {
                 ++index;
             }
         }
@@ -660,7 +661,7 @@
             return;
         case ProgramElement::Kind::kGlobalVar: {
             const GlobalVarDeclaration& decl = p.as<GlobalVarDeclaration>();
-            const Variable& var = *decl.fDecl->fVar;
+            const Variable& var = decl.declaration()->as<VarDeclaration>().var();
             if (var.modifiers().fFlags & (Modifiers::kIn_Flag | Modifiers::kUniform_Flag) ||
                 -1 != var.modifiers().fLayout.fBuiltin) {
                 return;
@@ -706,27 +707,27 @@
 void CPPCodeGenerator::writePrivateVars() {
     for (const auto& p : fProgram.elements()) {
         if (p->is<GlobalVarDeclaration>()) {
-            const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-            if (is_private(*decl.fVar)) {
-                if (decl.fVar->type() == *fContext.fFragmentProcessor_Type) {
-                    fErrors.error(decl.fOffset,
+            const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+            const Variable& var = global.declaration()->as<VarDeclaration>().var();
+            if (is_private(var)) {
+                if (var.type() == *fContext.fFragmentProcessor_Type) {
+                    fErrors.error(global.fOffset,
                                   "fragmentProcessor variables must be declared 'in'");
                     return;
                 }
                 this->writef("%s %s = %s;\n",
-                             HCodeGenerator::FieldType(fContext, decl.fVar->type(),
-                                                       decl.fVar->modifiers().fLayout)
-                                     .c_str(),
-                             String(decl.fVar->name()).c_str(),
-                             default_value(*decl.fVar).c_str());
-            } else if (decl.fVar->modifiers().fLayout.fFlags & Layout::kTracked_Flag) {
+                             HCodeGenerator::FieldType(fContext, var.type(),
+                                                       var.modifiers().fLayout).c_str(),
+                             String(var.name()).c_str(),
+                             default_value(var).c_str());
+            } else if (var.modifiers().fLayout.fFlags & Layout::kTracked_Flag) {
                 // An auto-tracked uniform in variable, so add a field to hold onto the prior
                 // state. Note that tracked variables must be uniform in's and that is validated
                 // before writePrivateVars() is called.
-                const UniformCTypeMapper* mapper = UniformCTypeMapper::Get(fContext, *decl.fVar);
+                const UniformCTypeMapper* mapper = UniformCTypeMapper::Get(fContext, var);
                 SkASSERT(mapper && mapper->supportsTracking());
 
-                String name = HCodeGenerator::FieldName(String(decl.fVar->name()).c_str());
+                String name = HCodeGenerator::FieldName(String(var.name()).c_str());
                 // The member statement is different if the mapper reports a default value
                 if (mapper->defaultValue().size() > 0) {
                     this->writef("%s %sPrev = %s;\n",
@@ -744,11 +745,12 @@
 void CPPCodeGenerator::writePrivateVarValues() {
     for (const auto& p : fProgram.elements()) {
         if (p->is<GlobalVarDeclaration>()) {
-            const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-            if (is_private(*decl.fVar) && decl.fValue) {
-                this->writef("%s = ", String(decl.fVar->name()).c_str());
+            const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+            const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+            if (is_private(decl.var()) && decl.value()) {
+                this->writef("%s = ", String(decl.var().name()).c_str());
                 fCPPMode = true;
-                this->writeExpression(*decl.fValue, kAssignment_Precedence);
+                this->writeExpression(*decl.value(), kAssignment_Precedence);
                 fCPPMode = false;
                 this->write(";\n");
             }
@@ -958,11 +960,12 @@
                  fFullName.c_str(), fFullName.c_str());
     for (const auto& p : fProgram.elements()) {
         if (p->is<GlobalVarDeclaration>()) {
-            const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-            String nameString(decl.fVar->name());
+            const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+            const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+            String nameString(decl.var().name());
             const char* name = nameString.c_str();
-            if (SectionAndParameterHelper::IsParameter(*decl.fVar) &&
-                is_accessible(*decl.fVar)) {
+            if (SectionAndParameterHelper::IsParameter(decl.var()) &&
+                is_accessible(decl.var())) {
                 this->writef("        auto %s = _outer.%s;\n"
                              "        (void) %s;\n",
                              name, name, name);
@@ -1071,8 +1074,9 @@
         int samplerIndex = 0;
         for (const auto& p : fProgram.elements()) {
             if (p->is<GlobalVarDeclaration>()) {
-                const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-                const Variable& variable = *decl.fVar;
+                const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+                const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+                const Variable& variable = decl.var();
                 String nameString(variable.name());
                 const char* name = nameString.c_str();
                 if (variable.type().typeKind() == Type::TypeKind::kSampler) {
@@ -1244,8 +1248,9 @@
                  fFullName.c_str());
     for (const auto& p : fProgram.elements()) {
         if (p->is<GlobalVarDeclaration>()) {
-            const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-            const Variable& var = *decl.fVar;
+            const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+            const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+            const Variable& var = decl.var();
             const Type& varType = var.type();
             String nameString(var.name());
             const char* name = nameString.c_str();
@@ -1260,9 +1265,9 @@
                                         HCodeGenerator::FieldType(fContext, varType,
                                                                   var.modifiers().fLayout).c_str(),
                                         String(var.name()).c_str());
-                        if (decl.fValue) {
+                        if (decl.value()) {
                             fCPPMode = true;
-                            this->writeExpression(*decl.fValue, kAssignment_Precedence);
+                            this->writeExpression(*decl.value(), kAssignment_Precedence);
                             fCPPMode = false;
                         } else {
                             this->writef("%s", default_value(var).c_str());
@@ -1318,25 +1323,26 @@
     std::vector<const Variable*> uniforms;
     for (const auto& p : fProgram.elements()) {
         if (p->is<GlobalVarDeclaration>()) {
-            const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-            if ((decl.fVar->modifiers().fFlags & Modifiers::kUniform_Flag) &&
-                        decl.fVar->type().typeKind() != Type::TypeKind::kSampler) {
-                uniforms.push_back(decl.fVar);
+            const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+            const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+            if ((decl.var().modifiers().fFlags & Modifiers::kUniform_Flag) &&
+                        decl.var().type().typeKind() != Type::TypeKind::kSampler) {
+                uniforms.push_back(&decl.var());
             }
 
-            if (is_uniform_in(*decl.fVar)) {
+            if (is_uniform_in(decl.var())) {
                 // Validate the "uniform in" declarations to make sure they are fully supported,
                 // instead of generating surprising C++
                 const UniformCTypeMapper* mapper =
-                        UniformCTypeMapper::Get(fContext, *decl.fVar);
+                        UniformCTypeMapper::Get(fContext, decl.var());
                 if (mapper == nullptr) {
-                    fErrors.error(decl.fOffset, String(decl.fVar->name())
+                    fErrors.error(decl.fOffset, String(decl.var().name())
                             + "'s type is not supported for use as a 'uniform in'");
                     return false;
                 }
-                if (decl.fVar->modifiers().fLayout.fFlags & Layout::kTracked_Flag) {
+                if (decl.var().modifiers().fLayout.fFlags & Layout::kTracked_Flag) {
                     if (!mapper->supportsTracking()) {
-                        fErrors.error(decl.fOffset, String(decl.fVar->name())
+                        fErrors.error(decl.fOffset, String(decl.var().name())
                                 + "'s type does not support state tracking");
                         return false;
                     }
@@ -1344,7 +1350,7 @@
 
             } else {
                 // If it's not a uniform_in, it's an error to be tracked
-                if (decl.fVar->modifiers().fLayout.fFlags & Layout::kTracked_Flag) {
+                if (decl.var().modifiers().fLayout.fFlags & Layout::kTracked_Flag) {
                     fErrors.error(decl.fOffset, "Non-'in uniforms' cannot be tracked");
                     return false;
                 }
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 1bde5dc..ee29937 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -371,9 +371,10 @@
                 break;
             }
             case ProgramElement::Kind::kGlobalVar: {
-                const Variable* var = element->as<GlobalVarDeclaration>().fDecl->fVar;
-                SkASSERT(var->isBuiltin());
-                intrinsics->insertOrDie(var->name(), std::move(element));
+                const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
+                const Variable& var = global.declaration()->as<VarDeclaration>().var();
+                SkASSERT(var.isBuiltin());
+                intrinsics->insertOrDie(var.name(), std::move(element));
                 break;
             }
             case ProgramElement::Kind::kInterfaceBlock: {
@@ -515,8 +516,8 @@
         Statement* stmt = node.statement()->get();
         if (stmt->is<VarDeclaration>()) {
             VarDeclaration& vd = stmt->as<VarDeclaration>();
-            if (vd.fValue) {
-                definitions->set(vd.fVar, &vd.fValue);
+            if (vd.value()) {
+                definitions->set(&vd.var(), &vd.value());
             }
         }
     }
@@ -570,7 +571,7 @@
             if (node.isStatement()) {
                 const Statement* s = node.statement()->get();
                 if (s->is<VarDeclaration>()) {
-                    result[s->as<VarDeclaration>().fVar] = nullptr;
+                    result[&s->as<VarDeclaration>().var()] = nullptr;
                 }
             }
         }
@@ -1284,12 +1285,12 @@
     switch (stmt->kind()) {
         case Statement::Kind::kVarDeclaration: {
             const auto& varDecl = stmt->as<VarDeclaration>();
-            if (varDecl.fVar->dead() &&
-                (!varDecl.fValue ||
-                 !varDecl.fValue->hasSideEffects())) {
-                if (varDecl.fValue) {
+            if (varDecl.var().dead() &&
+                (!varDecl.value() ||
+                 !varDecl.value()->hasSideEffects())) {
+                if (varDecl.value()) {
                     SkASSERT((*iter)->statement()->get() == stmt);
-                    if (!b.tryRemoveExpressionBefore(iter, varDecl.fValue.get())) {
+                    if (!b.tryRemoveExpressionBefore(iter, varDecl.value().get())) {
                         *outNeedsRescan = true;
                     }
                 }
@@ -1616,8 +1617,10 @@
                                        if (!element->is<GlobalVarDeclaration>()) {
                                            return false;
                                        }
-                                       const auto& varDecl = element->as<GlobalVarDeclaration>();
-                                       bool dead = varDecl.fDecl->fVar->dead();
+                                       const auto& global = element->as<GlobalVarDeclaration>();
+                                       const auto& varDecl =
+                                                         global.declaration()->as<VarDeclaration>();
+                                       bool dead = varDecl.var().dead();
                                        madeChanges |= dead;
                                        return dead;
                                    }),
diff --git a/src/sksl/SkSLDehydrator.cpp b/src/sksl/SkSLDehydrator.cpp
index 1492412..6bbcfd4 100644
--- a/src/sksl/SkSLDehydrator.cpp
+++ b/src/sksl/SkSLDehydrator.cpp
@@ -485,13 +485,13 @@
             case Statement::Kind::kVarDeclaration: {
                 const VarDeclaration& v = s->as<VarDeclaration>();
                 this->writeCommand(Rehydrator::kVarDeclaration_Command);
-                this->writeU16(this->symbolId(v.fVar));
-                this->write(v.fBaseType);
-                this->writeU8(v.fSizes.size());
-                for (const std::unique_ptr<Expression>& sizeExpr : v.fSizes) {
-                    this->write(sizeExpr.get());
+                this->writeU16(this->symbolId(&v.var()));
+                this->write(v.baseType());
+                this->writeU8(v.sizeCount());
+                for (int i = 0; i < v.sizeCount(); ++i) {
+                    this->write(v.size(i).get());
                 }
-                this->write(v.fValue.get());
+                this->write(v.value().get());
                 break;
             }
             case Statement::Kind::kWhile: {
@@ -562,7 +562,7 @@
         case ProgramElement::Kind::kGlobalVar: {
             const GlobalVarDeclaration& v = e.as<GlobalVarDeclaration>();
             this->writeCommand(Rehydrator::kVarDeclarations_Command);
-            this->write(v.fDecl.get());
+            this->write(v.declaration().get());
             break;
         }
     }
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 94fd0b8..1d53ad4a 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -1246,23 +1246,23 @@
 }
 
 void GLSLCodeGenerator::writeVarDeclaration(const VarDeclaration& var, bool global) {
-    this->writeModifiers(var.fVar->modifiers(), global);
-    this->writeTypePrecision(var.fBaseType);
-    this->writeType(var.fBaseType);
+    this->writeModifiers(var.var().modifiers(), global);
+    this->writeTypePrecision(var.baseType());
+    this->writeType(var.baseType());
     this->write(" ");
-    this->write(var.fVar->name());
-    for (const auto& size : var.fSizes) {
+    this->write(var.var().name());
+    for (int i = 0; i < var.sizeCount(); ++i) {
         this->write("[");
-        if (size) {
-            this->writeExpression(*size, kTopLevel_Precedence);
+        if (var.size(i)) {
+            this->writeExpression(*var.size(i), kTopLevel_Precedence);
         }
         this->write("]");
     }
-    if (var.fValue) {
+    if (var.value()) {
         this->write(" = ");
-        this->writeVarInitializer(*var.fVar, *var.fValue);
+        this->writeVarInitializer(var.var(), *var.value());
     }
-    if (!fFoundExternalSamplerDecl && var.fVar->type() == *fContext.fSamplerExternalOES_Type) {
+    if (!fFoundExternalSamplerDecl && var.var().type() == *fContext.fSamplerExternalOES_Type) {
         if (fProgram.fSettings.fCaps->externalTextureExtensionString()) {
             this->writeExtension(fProgram.fSettings.fCaps->externalTextureExtensionString());
         }
@@ -1271,7 +1271,7 @@
         }
         fFoundExternalSamplerDecl = true;
     }
-    if (!fFoundRectSamplerDecl && var.fVar->type() == *fContext.fSampler2DRect_Type) {
+    if (!fFoundRectSamplerDecl && var.var().type() == *fContext.fSampler2DRect_Type) {
         fFoundRectSamplerDecl = true;
     }
     this->write(";");
@@ -1487,15 +1487,16 @@
             this->writeExtension(e.as<Extension>().name());
             break;
         case ProgramElement::Kind::kGlobalVar: {
-            const VarDeclaration& decl = *e.as<GlobalVarDeclaration>().fDecl;
-            int builtin = decl.fVar->modifiers().fLayout.fBuiltin;
+            const VarDeclaration& decl =
+                                   e.as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>();
+            int builtin = decl.var().modifiers().fLayout.fBuiltin;
             if (builtin == -1) {
                 // normal var
                 this->writeVarDeclaration(decl, true);
                 this->writeLine();
             } else if (builtin == SK_FRAGCOLOR_BUILTIN &&
                        fProgram.fSettings.fCaps->mustDeclareFragmentShaderOutput() &&
-                       decl.fVar->writeCount()) {
+                       decl.var().writeCount()) {
                 if (fProgram.fSettings.fFragColorIsInOut) {
                     this->write("inout ");
                 } else {
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index d4d0937..a1b784f 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -1099,19 +1099,19 @@
                             "only the last entry in an interface block may be a runtime-sized "
                             "array");
             }
-            if (vd.fVar == fRTAdjust) {
+            if (&vd.var() == fRTAdjust) {
                 foundRTAdjust = true;
-                SkASSERT(vd.fVar->type() == *fContext.fFloat4_Type);
+                SkASSERT(vd.var().type() == *fContext.fFloat4_Type);
                 fRTAdjustFieldIndex = fields.size();
             }
-            fields.push_back(Type::Field(vd.fVar->modifiers(), vd.fVar->name(),
-                                        &vd.fVar->type()));
-            if (vd.fValue) {
+            fields.push_back(Type::Field(vd.var().modifiers(), vd.var().name(),
+                                        &vd.var().type()));
+            if (vd.value()) {
                 fErrors.error(decl->fOffset,
                             "initializers are not permitted on interface block fields");
             }
-            if (vd.fVar->type().typeKind() == Type::TypeKind::kArray &&
-                vd.fVar->type().columns() == Type::kUnsizedArray) {
+            if (vd.var().type().typeKind() == Type::TypeKind::kArray &&
+                vd.var().type().columns() == Type::kUnsizedArray) {
                 haveRuntimeArray = true;
             }
         }
@@ -2829,8 +2829,10 @@
                 const Expression* initialValue = nullptr;
 
                 if (clonedDecl->is<GlobalVarDeclaration>()) {
-                    sharedVar = clonedDecl->as<GlobalVarDeclaration>().fDecl->fVar;
-                    initialValue = clonedDecl->as<GlobalVarDeclaration>().fDecl->fValue.get();
+                    GlobalVarDeclaration& global = clonedDecl->as<GlobalVarDeclaration>();
+                    VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+                    sharedVar = &decl.var();
+                    initialValue = decl.value().get();
                 } else {
                     SkASSERT(clonedDecl->is<InterfaceBlock>());
                     sharedVar = clonedDecl->as<InterfaceBlock>().fVariable;
@@ -2847,7 +2849,8 @@
 
                 // Go back and update the declaring element to point at the cloned Variable.
                 if (clonedDecl->is<GlobalVarDeclaration>()) {
-                    clonedDecl->as<GlobalVarDeclaration>().fDecl->fVar = clonedVar;
+                    GlobalVarDeclaration& global = clonedDecl->as<GlobalVarDeclaration>();
+                    global.declaration()->as<VarDeclaration>().setVar(clonedVar);
                 } else {
                     clonedDecl->as<InterfaceBlock>().fVariable = clonedVar;
                 }
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index 2f757d2..c80374d 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -543,29 +543,29 @@
         case Statement::Kind::kVarDeclaration: {
             const VarDeclaration& decl = statement.as<VarDeclaration>();
             ExpressionArray sizes;
-            sizes.reserve(decl.fSizes.size());
-            for (const auto& size : decl.fSizes) {
-                sizes.push_back(expr(size));
+            sizes.reserve(decl.sizeCount());
+            for (int i = 0; i < decl.sizeCount(); ++i) {
+                sizes.push_back(expr(decl.size(i)));
             }
-            std::unique_ptr<Expression> initialValue = expr(decl.fValue);
-            const Variable* old = decl.fVar;
+            std::unique_ptr<Expression> initialValue = expr(decl.value());
+            const Variable& old = decl.var();
             // We assign unique names to inlined variables--scopes hide most of the problems in this
             // regard, but see `InlinerAvoidsVariableNameOverlap` for a counterexample where unique
             // names are important.
             auto name = std::make_unique<String>(
-                    this->uniqueNameForInlineVar(String(old->name()), symbolTableForStatement));
+                    this->uniqueNameForInlineVar(String(old.name()), symbolTableForStatement));
             const String* namePtr = symbolTableForStatement->takeOwnershipOfString(std::move(name));
-            const Type* baseTypePtr = copy_if_needed(&decl.fBaseType, *symbolTableForStatement);
-            const Type* typePtr = copy_if_needed(&old->type(), *symbolTableForStatement);
+            const Type* baseTypePtr = copy_if_needed(&decl.baseType(), *symbolTableForStatement);
+            const Type* typePtr = copy_if_needed(&old.type(), *symbolTableForStatement);
             const Variable* clone = symbolTableForStatement->takeOwnershipOfSymbol(
                     std::make_unique<Variable>(offset,
-                                               old->modifiersHandle(),
+                                               old.modifiersHandle(),
                                                namePtr->c_str(),
                                                typePtr,
                                                isBuiltinCode,
-                                               old->storage(),
+                                               old.storage(),
                                                initialValue.get()));
-            (*varMap)[old] = std::make_unique<VariableReference>(offset, clone);
+            (*varMap)[&old] = std::make_unique<VariableReference>(offset, clone);
             return std::make_unique<VarDeclaration>(clone, baseTypePtr, std::move(sizes),
                                                     std::move(initialValue));
         }
@@ -941,7 +941,7 @@
             case Statement::Kind::kVarDeclaration: {
                 VarDeclaration& varDeclStmt = (*stmt)->as<VarDeclaration>();
                 // Don't need to scan the declaration's sizes; those are always IntLiterals.
-                this->visitExpression(&varDeclStmt.fValue);
+                this->visitExpression(&varDeclStmt.value());
                 break;
             }
             case Statement::Kind::kWhile: {
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index e1df944..710bf60 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -956,28 +956,28 @@
         for (const auto& e : fProgram.elements()) {
             if (e->is<GlobalVarDeclaration>()) {
                 const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
-                const VarDeclaration& var = *decls.fDecl;
-                if (var.fVar->type().typeKind() == Type::TypeKind::kSampler) {
-                    if (var.fVar->modifiers().fLayout.fBinding < 0) {
+                const VarDeclaration& var = decls.declaration()->as<VarDeclaration>();
+                if (var.var().type().typeKind() == Type::TypeKind::kSampler) {
+                    if (var.var().modifiers().fLayout.fBinding < 0) {
                         fErrors.error(decls.fOffset,
                                         "Metal samplers must have 'layout(binding=...)'");
                         return;
                     }
-                    if (var.fVar->type().dimensions() != SpvDim2D) {
+                    if (var.var().type().dimensions() != SpvDim2D) {
                         // TODO: Support other texture types (skbug.com/10797)
                         fErrors.error(decls.fOffset, "Unsupported texture dimensions");
                         return;
                     }
                     this->write(", texture2d<float> ");
-                    this->writeName(var.fVar->name());
+                    this->writeName(var.var().name());
                     this->write("[[texture(");
-                    this->write(to_string(var.fVar->modifiers().fLayout.fBinding));
+                    this->write(to_string(var.var().modifiers().fLayout.fBinding));
                     this->write(")]]");
                     this->write(", sampler ");
-                    this->writeName(var.fVar->name());
+                    this->writeName(var.var().name());
                     this->write(SAMPLER_SUFFIX);
                     this->write("[[sampler(");
-                    this->write(to_string(var.fVar->modifiers().fLayout.fBinding));
+                    this->write(to_string(var.var().modifiers().fLayout.fBinding));
                     this->write(")]]");
                 }
             } else if (e->is<InterfaceBlock>()) {
@@ -1218,23 +1218,23 @@
 }
 
 void MetalCodeGenerator::writeVarDeclaration(const VarDeclaration& var, bool global) {
-    if (global && !(var.fVar->modifiers().fFlags & Modifiers::kConst_Flag)) {
+    if (global && !(var.var().modifiers().fFlags & Modifiers::kConst_Flag)) {
         return;
     }
-    this->writeModifiers(var.fVar->modifiers(), global);
-    this->writeType(var.fBaseType);
+    this->writeModifiers(var.var().modifiers(), global);
+    this->writeType(var.baseType());
     this->write(" ");
-    this->writeName(var.fVar->name());
-    for (const auto& size : var.fSizes) {
+    this->writeName(var.var().name());
+    for (int i = 0; i < var.sizeCount(); ++i) {
         this->write("[");
-        if (size) {
-            this->writeExpression(*size, kTopLevel_Precedence);
+        if (var.size(i)) {
+            this->writeExpression(*var.size(i), kTopLevel_Precedence);
         }
         this->write("]");
     }
-    if (var.fValue) {
+    if (var.value()) {
         this->write(" = ");
-        this->writeVarInitializer(*var.fVar, *var.fValue);
+        this->writeVarInitializer(var.var(), *var.value());
     }
     this->write(";");
 }
@@ -1395,7 +1395,7 @@
     for (const auto& e : fProgram.elements()) {
         if (e->is<GlobalVarDeclaration>()) {
             const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
-            const Variable& var = *decls.fDecl->fVar;
+            const Variable& var = decls.declaration()->as<VarDeclaration>().var();
             if (var.modifiers().fFlags & Modifiers::kUniform_Flag &&
                 var.type().typeKind() != Type::TypeKind::kSampler) {
                 if (-1 == fUniformBuffer) {
@@ -1428,7 +1428,7 @@
     for (const auto& e : fProgram.elements()) {
         if (e->is<GlobalVarDeclaration>()) {
             const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
-            const Variable& var = *decls.fDecl->fVar;
+            const Variable& var = decls.declaration()->as<VarDeclaration>().var();
             if (var.modifiers().fFlags & Modifiers::kIn_Flag &&
                 -1 == var.modifiers().fLayout.fBuiltin) {
                 this->write("    ");
@@ -1461,7 +1461,7 @@
     for (const auto& e : fProgram.elements()) {
         if (e->is<GlobalVarDeclaration>()) {
             const GlobalVarDeclaration& decls = e->as<GlobalVarDeclaration>();
-            const Variable& var = *decls.fDecl->fVar;
+            const Variable& var = decls.declaration()->as<VarDeclaration>().var();
             if (var.modifiers().fFlags & Modifiers::kOut_Flag &&
                 -1 == var.modifiers().fLayout.fBuiltin) {
                 this->write("    ");
@@ -1514,9 +1514,9 @@
         if (!element->is<GlobalVarDeclaration>()) {
             continue;
         }
-        const GlobalVarDeclaration& decls = element->as<GlobalVarDeclaration>();
-        const VarDeclaration& decl = *decls.fDecl;
-        const Variable& var = *decl.fVar;
+        const GlobalVarDeclaration& global = element->as<GlobalVarDeclaration>();
+        const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+        const Variable& var = decl.var();
         if ((!var.modifiers().fFlags && -1 == var.modifiers().fLayout.fBuiltin) ||
             var.type().typeKind() == Type::TypeKind::kSampler) {
             if (var.type().typeKind() == Type::TypeKind::kSampler) {
@@ -1525,7 +1525,7 @@
                 visitor->VisitSampler(var.type(), String(var.name()) + SAMPLER_SUFFIX);
             } else {
                 // Visit a regular variable.
-                visitor->VisitVariable(var, decl.fValue.get());
+                visitor->VisitVariable(var, decl.value().get());
             }
         }
     }
@@ -1640,8 +1640,9 @@
         case ProgramElement::Kind::kExtension:
             break;
         case ProgramElement::Kind::kGlobalVar: {
-            const VarDeclaration& decl = *e.as<GlobalVarDeclaration>().fDecl;
-            int builtin = decl.fVar->modifiers().fLayout.fBuiltin;
+            const GlobalVarDeclaration& global = e.as<GlobalVarDeclaration>();
+            const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+            int builtin = decl.var().modifiers().fLayout.fBuiltin;
             if (-1 == builtin) {
                 // normal var
                 this->writeVarDeclaration(decl, true);
@@ -1756,7 +1757,7 @@
         }
         case Statement::Kind::kVarDeclaration: {
             const VarDeclaration& var = s->as<VarDeclaration>();
-            return this->requirements(var.fValue.get());
+            return this->requirements(var.value().get());
         }
         case Statement::Kind::kExpression:
             return this->requirements(s->as<ExpressionStatement>().expression().get());
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
index 2cb2b4f..be2c69a 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
@@ -46,10 +46,11 @@
         bool found = false;
         for (const auto& p : fProgram.elements()) {
             if (p->is<GlobalVarDeclaration>()) {
-                const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-                if (decl.fVar == arguments[0]->as<VariableReference>().variable()) {
+                const GlobalVarDeclaration& global = p->as<GlobalVarDeclaration>();
+                const VarDeclaration& decl = global.declaration()->as<VarDeclaration>();
+                if (&decl.var() == arguments[0]->as<VariableReference>().variable()) {
                     found = true;
-                } else if (decl.fVar->type() == *fContext.fFragmentProcessor_Type) {
+                } else if (decl.var().type() == *fContext.fFragmentProcessor_Type) {
                     ++index;
                 }
             }
@@ -125,7 +126,8 @@
                         break;
                     }
                     if (e->is<GlobalVarDeclaration>()) {
-                        const Variable& var = *e->as<GlobalVarDeclaration>().fDecl->fVar;
+                        const GlobalVarDeclaration& global = e->as<GlobalVarDeclaration>();
+                        const Variable& var = global.declaration()->as<VarDeclaration>().var();
                         if (&var == ref.variable()) {
                             found = true;
                             break;
@@ -213,7 +215,8 @@
         return;
     }
     if (p.is<GlobalVarDeclaration>()) {
-        const Variable& var = *p.as<GlobalVarDeclaration>().fDecl->fVar;
+        const GlobalVarDeclaration& global = p.as<GlobalVarDeclaration>();
+        const Variable& var = global.declaration()->as<VarDeclaration>().var();
         if (var.modifiers().fFlags &
                     (Modifiers::kIn_Flag | Modifiers::kUniform_Flag | Modifiers::kVarying_Flag) ||
             var.modifiers().fLayout.fBuiltin == -1) {
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index 08a1665..f3f0580 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -2780,32 +2780,32 @@
 #define BUILTIN_IGNORE 9999
 void SPIRVCodeGenerator::writeGlobalVar(Program::Kind kind, const VarDeclaration& varDecl,
                                         OutputStream& out) {
-    const Variable* var = varDecl.fVar;
+    const Variable& var = varDecl.var();
     // These haven't been implemented in our SPIR-V generator yet and we only currently use them
     // in the OpenGL backend.
-    SkASSERT(!(var->modifiers().fFlags & (Modifiers::kReadOnly_Flag |
+    SkASSERT(!(var.modifiers().fFlags & (Modifiers::kReadOnly_Flag |
                                           Modifiers::kWriteOnly_Flag |
                                           Modifiers::kCoherent_Flag |
                                           Modifiers::kVolatile_Flag |
                                           Modifiers::kRestrict_Flag)));
-    if (var->modifiers().fLayout.fBuiltin == BUILTIN_IGNORE) {
+    if (var.modifiers().fLayout.fBuiltin == BUILTIN_IGNORE) {
         return;
     }
-    if (var->modifiers().fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN &&
+    if (var.modifiers().fLayout.fBuiltin == SK_FRAGCOLOR_BUILTIN &&
         kind != Program::kFragment_Kind) {
         SkASSERT(!fProgram.fSettings.fFragColorIsInOut);
         return;
     }
-    if (is_dead(*var)) {
+    if (is_dead(var)) {
         return;
     }
-    const Type& type = var->type();
+    const Type& type = var.type();
     SpvStorageClass_ storageClass;
-    if (var->modifiers().fFlags & Modifiers::kIn_Flag) {
+    if (var.modifiers().fFlags & Modifiers::kIn_Flag) {
         storageClass = SpvStorageClassInput;
-    } else if (var->modifiers().fFlags & Modifiers::kOut_Flag) {
+    } else if (var.modifiers().fFlags & Modifiers::kOut_Flag) {
         storageClass = SpvStorageClassOutput;
-    } else if (var->modifiers().fFlags & Modifiers::kUniform_Flag) {
+    } else if (var.modifiers().fFlags & Modifiers::kUniform_Flag) {
         if (type.typeKind() == Type::TypeKind::kSampler ||
             type.typeKind() == Type::TypeKind::kSeparateSampler ||
             type.typeKind() == Type::TypeKind::kTexture) {
@@ -2817,9 +2817,9 @@
         storageClass = SpvStorageClassPrivate;
     }
     SpvId id = this->nextId();
-    fVariableMap[var] = id;
+    fVariableMap[&var] = id;
     SpvId typeId;
-    if (var->modifiers().fLayout.fBuiltin == SK_IN_BUILTIN) {
+    if (var.modifiers().fLayout.fBuiltin == SK_IN_BUILTIN) {
         typeId = this->getPointerType(
                 Type("sk_in", Type::TypeKind::kArray, type.componentType(), fSkInCount),
                 storageClass);
@@ -2827,41 +2827,41 @@
         typeId = this->getPointerType(type, storageClass);
     }
     this->writeInstruction(SpvOpVariable, typeId, id, storageClass, fConstantBuffer);
-    this->writeInstruction(SpvOpName, id, var->name(), fNameBuffer);
+    this->writeInstruction(SpvOpName, id, var.name(), fNameBuffer);
     this->writePrecisionModifier(type, id);
-    if (varDecl.fValue) {
+    if (varDecl.value()) {
         SkASSERT(!fCurrentBlock);
         fCurrentBlock = -1;
-        SpvId value = this->writeExpression(*varDecl.fValue, fGlobalInitializersBuffer);
+        SpvId value = this->writeExpression(*varDecl.value(), fGlobalInitializersBuffer);
         this->writeInstruction(SpvOpStore, id, value, fGlobalInitializersBuffer);
         fCurrentBlock = 0;
     }
-    this->writeLayout(var->modifiers().fLayout, id);
-    if (var->modifiers().fFlags & Modifiers::kFlat_Flag) {
+    this->writeLayout(var.modifiers().fLayout, id);
+    if (var.modifiers().fFlags & Modifiers::kFlat_Flag) {
         this->writeInstruction(SpvOpDecorate, id, SpvDecorationFlat, fDecorationBuffer);
     }
-    if (var->modifiers().fFlags & Modifiers::kNoPerspective_Flag) {
+    if (var.modifiers().fFlags & Modifiers::kNoPerspective_Flag) {
         this->writeInstruction(SpvOpDecorate, id, SpvDecorationNoPerspective,
                                 fDecorationBuffer);
     }
 }
 
 void SPIRVCodeGenerator::writeVarDeclaration(const VarDeclaration& varDecl, OutputStream& out) {
-    const Variable* var = varDecl.fVar;
+    const Variable& var = varDecl.var();
     // These haven't been implemented in our SPIR-V generator yet and we only currently use them
     // in the OpenGL backend.
-    SkASSERT(!(var->modifiers().fFlags & (Modifiers::kReadOnly_Flag |
-                                          Modifiers::kWriteOnly_Flag |
-                                          Modifiers::kCoherent_Flag |
-                                          Modifiers::kVolatile_Flag |
-                                          Modifiers::kRestrict_Flag)));
+    SkASSERT(!(var.modifiers().fFlags & (Modifiers::kReadOnly_Flag |
+                                         Modifiers::kWriteOnly_Flag |
+                                         Modifiers::kCoherent_Flag |
+                                         Modifiers::kVolatile_Flag |
+                                         Modifiers::kRestrict_Flag)));
     SpvId id = this->nextId();
-    fVariableMap[var] = id;
-    SpvId type = this->getPointerType(var->type(), SpvStorageClassFunction);
+    fVariableMap[&var] = id;
+    SpvId type = this->getPointerType(var.type(), SpvStorageClassFunction);
     this->writeInstruction(SpvOpVariable, type, id, SpvStorageClassFunction, fVariableBuffer);
-    this->writeInstruction(SpvOpName, id, var->name(), fNameBuffer);
-    if (varDecl.fValue) {
-        SpvId value = this->writeExpression(*varDecl.fValue, out);
+    this->writeInstruction(SpvOpName, id, var.name(), fNameBuffer);
+    if (varDecl.value()) {
+        SpvId value = this->writeExpression(*varDecl.value(), out);
         this->writeInstruction(SpvOpStore, id, value, out);
     }
 }
@@ -3214,7 +3214,9 @@
     }
     for (const auto& e : program.elements()) {
         if (e->is<GlobalVarDeclaration>()) {
-            this->writeGlobalVar(program.fKind, *e->as<GlobalVarDeclaration>().fDecl, body);
+            this->writeGlobalVar(program.fKind,
+                                 e->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>(),
+                                 body);
         }
     }
     for (const auto& e : program.elements()) {
diff --git a/src/sksl/SkSLSectionAndParameterHelper.cpp b/src/sksl/SkSLSectionAndParameterHelper.cpp
index 3ecf4f5..d700587 100644
--- a/src/sksl/SkSLSectionAndParameterHelper.cpp
+++ b/src/sksl/SkSLSectionAndParameterHelper.cpp
@@ -30,9 +30,10 @@
     for (const auto& p : fProgram.elements()) {
         switch (p->kind()) {
             case ProgramElement::Kind::kGlobalVar: {
-                const VarDeclaration& decl = *p->as<GlobalVarDeclaration>().fDecl;
-                if (IsParameter(*decl.fVar)) {
-                    fParameters.push_back(decl.fVar);
+                const VarDeclaration& decl =
+                                  p->as<GlobalVarDeclaration>().declaration()->as<VarDeclaration>();
+                if (IsParameter(decl.var())) {
+                    fParameters.push_back(&decl.var());
                 }
                 break;
             }
diff --git a/src/sksl/ir/SkSLIRNode.cpp b/src/sksl/ir/SkSLIRNode.cpp
index 44a500c..8cfcda3 100644
--- a/src/sksl/ir/SkSLIRNode.cpp
+++ b/src/sksl/ir/SkSLIRNode.cpp
@@ -137,6 +137,11 @@
 , fKind(kind)
 , fData(data) {}
 
+IRNode::IRNode(int offset, int kind, const VarDeclarationData& data)
+: fOffset(offset)
+, fKind(kind)
+, fData(data) {}
+
 IRNode::IRNode(int offset, int kind, const VariableData& data)
 : fOffset(offset)
 , fKind(kind)
diff --git a/src/sksl/ir/SkSLIRNode.h b/src/sksl/ir/SkSLIRNode.h
index 10ee8f0..de68ceb 100644
--- a/src/sksl/ir/SkSLIRNode.h
+++ b/src/sksl/ir/SkSLIRNode.h
@@ -202,6 +202,11 @@
         std::vector<const FunctionDeclaration*> fFunctions;
     };
 
+    struct VarDeclarationData {
+        const Type* fBaseType;
+        const Variable* fVar;
+    };
+
     struct VariableData {
         StringFragment fName;
         const Type* fType;
@@ -249,6 +254,7 @@
             kTypeReference,
             kTypeToken,
             kUnresolvedFunction,
+            kVarDeclaration,
             kVariable,
             kVariableReference,
         } fKind = Kind::kType;
@@ -280,6 +286,7 @@
             TypeReferenceData fTypeReference;
             TypeTokenData fTypeToken;
             UnresolvedFunctionData fUnresolvedFunction;
+            VarDeclarationData fVarDeclaration;
             VariableData fVariable;
             VariableReferenceData fVariableReference;
 
@@ -413,6 +420,11 @@
             *(new(&fContents) UnresolvedFunctionData) = data;
         }
 
+        NodeData(const VarDeclarationData& data)
+            : fKind(Kind::kVarDeclaration) {
+            *(new(&fContents) VarDeclarationData) = data;
+        }
+
         NodeData(const VariableData& data)
             : fKind(Kind::kVariable) {
             *(new(&fContents) VariableData) = data;
@@ -508,6 +520,9 @@
                 case Kind::kUnresolvedFunction:
                     *(new(&fContents) UnresolvedFunctionData) = other.fContents.fUnresolvedFunction;
                     break;
+                case Kind::kVarDeclaration:
+                    *(new(&fContents) VarDeclarationData) = other.fContents.fVarDeclaration;
+                    break;
                 case Kind::kVariable:
                     *(new(&fContents) VariableData) = other.fContents.fVariable;
                     break;
@@ -599,6 +614,9 @@
                 case Kind::kUnresolvedFunction:
                     fContents.fUnresolvedFunction.~UnresolvedFunctionData();
                     break;
+                case Kind::kVarDeclaration:
+                    fContents.fVarDeclaration.~VarDeclarationData();
+                    break;
                 case Kind::kVariable:
                     fContents.fVariable.~VariableData();
                     break;
@@ -659,6 +677,8 @@
 
     IRNode(int offset, int kind, const UnresolvedFunctionData& data);
 
+    IRNode(int offset, int kind, const VarDeclarationData& data);
+
     IRNode(int offset, int kind, const VariableData& data);
 
     IRNode(int offset, int kind, const VariableReferenceData& data);
@@ -847,6 +867,16 @@
         return fData.fContents.fUnresolvedFunction;
     }
 
+    VarDeclarationData& varDeclarationData() {
+        SkASSERT(fData.fKind == NodeData::Kind::kVarDeclaration);
+        return fData.fContents.fVarDeclaration;
+    }
+
+    const VarDeclarationData& varDeclarationData() const {
+        SkASSERT(fData.fKind == NodeData::Kind::kVarDeclaration);
+        return fData.fContents.fVarDeclaration;
+    }
+
     VariableData& variableData() {
         SkASSERT(fData.fKind == NodeData::Kind::kVariable);
         return fData.fContents.fVariable;
diff --git a/src/sksl/ir/SkSLModifiersDeclaration.h b/src/sksl/ir/SkSLModifiersDeclaration.h
index 13c87e9..3d76226 100644
--- a/src/sksl/ir/SkSLModifiersDeclaration.h
+++ b/src/sksl/ir/SkSLModifiersDeclaration.h
@@ -18,7 +18,8 @@
  *
  * layout(blend_support_all_equations) out;
  */
-struct ModifiersDeclaration : public ProgramElement {
+class ModifiersDeclaration : public ProgramElement {
+public:
     static constexpr Kind kProgramElementKind = Kind::kModifiers;
 
     ModifiersDeclaration(ModifiersPool::Handle modifiers)
@@ -40,6 +41,7 @@
         return this->modifiers().description() + ";";
     }
 
+private:
     using INHERITED = ProgramElement;
 };
 
diff --git a/src/sksl/ir/SkSLStatement.h b/src/sksl/ir/SkSLStatement.h
index a37ec0c..8ceabc2 100644
--- a/src/sksl/ir/SkSLStatement.h
+++ b/src/sksl/ir/SkSLStatement.h
@@ -58,6 +58,9 @@
     Statement(int offset, const InlineMarkerData& data)
     : INHERITED(offset, (int) Kind::kInlineMarker, data) {}
 
+    Statement(int offset, const VarDeclarationData& data)
+    : INHERITED(offset, (int) Kind::kVarDeclaration, data) {}
+
     Kind kind() const {
         return (Kind) fKind;
     }
diff --git a/src/sksl/ir/SkSLVarDeclarations.h b/src/sksl/ir/SkSLVarDeclarations.h
index 7514e46..67f53f9 100644
--- a/src/sksl/ir/SkSLVarDeclarations.h
+++ b/src/sksl/ir/SkSLVarDeclarations.h
@@ -20,55 +20,82 @@
  * separate (sequential) statements. For instance, the SkSL 'int x = 2, y[3];' produces two
  * VarDeclaration instances (wrapped in an unscoped Block).
  */
-struct VarDeclaration : public Statement {
+class VarDeclaration : public Statement {
+public:
     static constexpr Kind kStatementKind = Kind::kVarDeclaration;
 
     VarDeclaration(const Variable* var,
                    const Type* baseType,
                    ExpressionArray sizes,
                    std::unique_ptr<Expression> value)
-            : INHERITED(var->fOffset, kStatementKind)
-            , fVar(var)
-            , fBaseType(*baseType)
-            , fSizes(std::move(sizes))
-            , fValue(std::move(value)) {}
+            : INHERITED(var->fOffset, VarDeclarationData{baseType, var}) {
+        fExpressionChildren.reserve(sizes.size() + 1);
+        fExpressionChildren.move_back_n(sizes.size(), sizes.data());
+        fExpressionChildren.push_back(std::move(value));
+    }
+
+    const Type& baseType() const {
+        return *this->varDeclarationData().fBaseType;
+    }
+
+    const Variable& var() const {
+        return *this->varDeclarationData().fVar;
+    }
+
+    void setVar(const Variable* var) {
+        this->varDeclarationData().fVar = var;
+    }
+
+    int sizeCount() const {
+        return fExpressionChildren.size() - 1;
+    }
+
+    const std::unique_ptr<Expression>& size(int index) const {
+        SkASSERT(index >= 0 && index < this->sizeCount());
+        return fExpressionChildren[index];
+    }
+
+    std::unique_ptr<Expression>& value() {
+        return fExpressionChildren.back();
+    }
+
+    const std::unique_ptr<Expression>& value() const {
+        return fExpressionChildren.back();
+    }
 
     std::unique_ptr<Statement> clone() const override {
         ExpressionArray sizesClone;
-        sizesClone.reserve(fSizes.size());
-        for (const auto& s : fSizes) {
-            if (s) {
-                sizesClone.push_back(s->clone());
+        sizesClone.reserve(this->sizeCount());
+        for (int i = 0; i < this->sizeCount(); ++i) {
+            if (this->size(i)) {
+                sizesClone.push_back(this->size(i)->clone());
             } else {
                 sizesClone.push_back(nullptr);
             }
         }
-        return std::make_unique<VarDeclaration>(fVar, &fBaseType, std::move(sizesClone),
-                                                fValue ? fValue->clone() : nullptr);
+        return std::make_unique<VarDeclaration>(&this->var(),
+                                                &this->baseType(),
+                                                std::move(sizesClone),
+                                                this->value() ? this->value()->clone() : nullptr);
     }
 
     String description() const override {
-        String result = fVar->modifiers().description() + fBaseType.description() + " " +
-                        fVar->name();
-        for (const auto& size : fSizes) {
-            if (size) {
-                result += "[" + size->description() + "]";
+        String result = this->var().modifiers().description() + this->baseType().description() +
+                        " " + this->var().name();
+        for (int i = 0; i < this->sizeCount(); ++i) {
+            if (this->size(i)) {
+                result += "[" + this->size(i)->description() + "]";
             } else {
                 result += "[]";
             }
         }
-        if (fValue) {
-            result += " = " + fValue->description();
+        if (this->value()) {
+            result += " = " + this->value()->description();
         }
         result += ";";
         return result;
     }
 
-    const Variable* fVar;
-    const Type& fBaseType;
-    ExpressionArray fSizes;
-    std::unique_ptr<Expression> fValue;
-
     using INHERITED = Statement;
 };
 
@@ -76,26 +103,33 @@
  * A variable declaration appearing at global scope. A global declaration like 'int x, y;' produces
  * two GlobalVarDeclaration elements, each containing the declaration of one variable.
  */
-struct GlobalVarDeclaration : public ProgramElement {
+class GlobalVarDeclaration : public ProgramElement {
+public:
     static constexpr Kind kProgramElementKind = Kind::kGlobalVar;
 
-    // decl must be a unique_ptr<VarDeclaration>, but to simplify construction, we take a Statement
     GlobalVarDeclaration(int offset, std::unique_ptr<Statement> decl)
             : INHERITED(offset, kProgramElementKind) {
         SkASSERT(decl->is<VarDeclaration>());
-        fDecl.reset(static_cast<VarDeclaration*>(decl.release()));
+        fStatementChildren.push_back(std::move(decl));
+    }
+
+    std::unique_ptr<Statement>& declaration() {
+        return fStatementChildren[0];
+    }
+
+    const std::unique_ptr<Statement>& declaration() const {
+        return fStatementChildren[0];
     }
 
     std::unique_ptr<ProgramElement> clone() const override {
-        return std::make_unique<GlobalVarDeclaration>(fOffset, fDecl->clone());
+        return std::make_unique<GlobalVarDeclaration>(fOffset, this->declaration()->clone());
     }
 
     String description() const override {
-        return fDecl->description();
+        return this->declaration()->description();
     }
 
-    std::unique_ptr<VarDeclaration> fDecl;
-
+private:
     using INHERITED = ProgramElement;
 };