Only include one variable per declaration statement

This removes VarDeclarationsStatement entirely. VarDeclaration instances
appear directly as statements in Programs. SkSL that declares multiple
variables in a single declaration is transformed to represent that as a
series of VarDeclaration statements.

Similarly, global variable declarations are represented by
GlobalVarDeclaration program elements, one per variable.

Bug: skia:10806
Change-Id: Idd8a2d971a8217733ed57f0dd2249d62f2f0e9c5
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/323102
Commit-Queue: Brian Osman <brianosman@google.com>
Reviewed-by: John Stiles <johnstiles@google.com>
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index d510af3..2ef5451 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -951,36 +951,31 @@
                         to_string(fUniformBuffer) + ")]]");
         }
         for (const auto& e : fProgram) {
-            if (e.kind() == ProgramElement::Kind::kVar) {
-                const VarDeclarations& decls = e.as<VarDeclarations>();
-                if (!decls.fVars.size()) {
-                    continue;
-                }
-                for (const auto& stmt: decls.fVars) {
-                    VarDeclaration& var = stmt->as<VarDeclaration>();
-                    if (var.fVar->type().typeKind() == Type::TypeKind::kSampler) {
-                        if (var.fVar->fModifiers.fLayout.fBinding < 0) {
-                            fErrors.error(decls.fOffset,
-                                          "Metal samplers must have 'layout(binding=...)'");
-                            return;
-                        }
-                        if (var.fVar->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->write("[[texture(");
-                        this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
-                        this->write(")]]");
-                        this->write(", sampler ");
-                        this->writeName(var.fVar->name());
-                        this->write(SAMPLER_SUFFIX);
-                        this->write("[[sampler(");
-                        this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
-                        this->write(")]]");
+            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->fModifiers.fLayout.fBinding < 0) {
+                        fErrors.error(decls.fOffset,
+                                        "Metal samplers must have 'layout(binding=...)'");
+                        return;
                     }
+                    if (var.fVar->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->write("[[texture(");
+                    this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
+                    this->write(")]]");
+                    this->write(", sampler ");
+                    this->writeName(var.fVar->name());
+                    this->write(SAMPLER_SUFFIX);
+                    this->write("[[sampler(");
+                    this->write(to_string(var.fVar->fModifiers.fLayout.fBinding));
+                    this->write(")]]");
                 }
             } else if (e.kind() == ProgramElement::Kind::kInterfaceBlock) {
                 InterfaceBlock& intf = (InterfaceBlock&) e;
@@ -1219,38 +1214,26 @@
     this->write(name);
 }
 
-void MetalCodeGenerator::writeVarDeclarations(const VarDeclarations& decl, bool global) {
-    SkASSERT(decl.fVars.size() > 0);
-    bool wroteType = false;
-    for (const auto& stmt : decl.fVars) {
-        VarDeclaration& var = (VarDeclaration&) *stmt;
-        if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
-            continue;
-        }
-        if (wroteType) {
-            this->write(", ");
-        } else {
-            this->writeModifiers(var.fVar->fModifiers, global);
-            this->writeType(decl.fBaseType);
-            this->write(" ");
-            wroteType = true;
-        }
-        this->writeName(var.fVar->name());
-        for (const auto& size : var.fSizes) {
-            this->write("[");
-            if (size) {
-                this->writeExpression(*size, kTopLevel_Precedence);
-            }
-            this->write("]");
-        }
-        if (var.fValue) {
-            this->write(" = ");
-            this->writeVarInitializer(*var.fVar, *var.fValue);
-        }
+void MetalCodeGenerator::writeVarDeclaration(const VarDeclaration& var, bool global) {
+    if (global && !(var.fVar->fModifiers.fFlags & Modifiers::kConst_Flag)) {
+        return;
     }
-    if (wroteType) {
-        this->write(";");
+    this->writeModifiers(var.fVar->fModifiers, global);
+    this->writeType(var.fBaseType);
+    this->write(" ");
+    this->writeName(var.fVar->name());
+    for (const auto& size : var.fSizes) {
+        this->write("[");
+        if (size) {
+            this->writeExpression(*size, kTopLevel_Precedence);
+        }
+        this->write("]");
     }
+    if (var.fValue) {
+        this->write(" = ");
+        this->writeVarInitializer(*var.fVar, *var.fValue);
+    }
+    this->write(";");
 }
 
 void MetalCodeGenerator::writeStatement(const Statement& s) {
@@ -1265,8 +1248,8 @@
         case Statement::Kind::kReturn:
             this->writeReturnStatement(s.as<ReturnStatement>());
             break;
-        case Statement::Kind::kVarDeclarations:
-            this->writeVarDeclarations(*s.as<VarDeclarationsStatement>().fDeclaration, false);
+        case Statement::Kind::kVarDeclaration:
+            this->writeVarDeclaration(s.as<VarDeclaration>(), false);
             break;
         case Statement::Kind::kIf:
             this->writeIfStatement(s.as<IfStatement>());
@@ -1407,33 +1390,27 @@
 
 void MetalCodeGenerator::writeUniformStruct() {
     for (const auto& e : fProgram) {
-        if (e.kind() == ProgramElement::Kind::kVar) {
-            const VarDeclarations& decls = e.as<VarDeclarations>();
-            if (!decls.fVars.size()) {
-                continue;
-            }
-            const Variable& first = *decls.fVars[0]->as<VarDeclaration>().fVar;
-            if (first.fModifiers.fFlags & Modifiers::kUniform_Flag &&
-                first.type().typeKind() != Type::TypeKind::kSampler) {
+        if (e.is<GlobalVarDeclaration>()) {
+            const GlobalVarDeclaration& decls = e.as<GlobalVarDeclaration>();
+            const Variable& var = *decls.fDecl->fVar;
+            if (var.fModifiers.fFlags & Modifiers::kUniform_Flag &&
+                var.type().typeKind() != Type::TypeKind::kSampler) {
                 if (-1 == fUniformBuffer) {
                     this->write("struct Uniforms {\n");
-                    fUniformBuffer = first.fModifiers.fLayout.fSet;
+                    fUniformBuffer = var.fModifiers.fLayout.fSet;
                     if (-1 == fUniformBuffer) {
                         fErrors.error(decls.fOffset, "Metal uniforms must have 'layout(set=...)'");
                     }
-                } else if (first.fModifiers.fLayout.fSet != fUniformBuffer) {
+                } else if (var.fModifiers.fLayout.fSet != fUniformBuffer) {
                     if (-1 == fUniformBuffer) {
                         fErrors.error(decls.fOffset, "Metal backend requires all uniforms to have "
                                     "the same 'layout(set=...)'");
                     }
                 }
                 this->write("    ");
-                this->writeType(first.type());
+                this->writeType(var.type());
                 this->write(" ");
-                for (const auto& stmt : decls.fVars) {
-                    const VarDeclaration& var = stmt->as<VarDeclaration>();
-                    this->writeName(var.fVar->name());
-                }
+                this->writeName(var.name());
                 this->write(";\n");
             }
         }
@@ -1446,28 +1423,22 @@
 void MetalCodeGenerator::writeInputStruct() {
     this->write("struct Inputs {\n");
     for (const auto& e : fProgram) {
-        if (e.kind() == ProgramElement::Kind::kVar) {
-            const VarDeclarations& decls = e.as<VarDeclarations>();
-            if (!decls.fVars.size()) {
-                continue;
-            }
-            const Variable& first = *decls.fVars[0]->as<VarDeclaration>().fVar;
-            if (first.fModifiers.fFlags & Modifiers::kIn_Flag &&
-                -1 == first.fModifiers.fLayout.fBuiltin) {
+        if (e.is<GlobalVarDeclaration>()) {
+            const GlobalVarDeclaration& decls = e.as<GlobalVarDeclaration>();
+            const Variable& var = *decls.fDecl->fVar;
+            if (var.fModifiers.fFlags & Modifiers::kIn_Flag &&
+                -1 == var.fModifiers.fLayout.fBuiltin) {
                 this->write("    ");
-                this->writeType(first.type());
+                this->writeType(var.type());
                 this->write(" ");
-                for (const auto& stmt : decls.fVars) {
-                    const VarDeclaration& var = stmt->as<VarDeclaration>();
-                    this->writeName(var.fVar->name());
-                    if (-1 != var.fVar->fModifiers.fLayout.fLocation) {
-                        if (fProgram.fKind == Program::kVertex_Kind) {
-                            this->write("  [[attribute(" +
-                                        to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
-                        } else if (fProgram.fKind == Program::kFragment_Kind) {
-                            this->write("  [[user(locn" +
-                                        to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
-                        }
+                this->writeName(var.name());
+                if (-1 != var.fModifiers.fLayout.fLocation) {
+                    if (fProgram.fKind == Program::kVertex_Kind) {
+                        this->write("  [[attribute(" +
+                                    to_string(var.fModifiers.fLayout.fLocation) + ")]]");
+                    } else if (fProgram.fKind == Program::kFragment_Kind) {
+                        this->write("  [[user(locn" +
+                                    to_string(var.fModifiers.fLayout.fLocation) + ")]]");
                     }
                 }
                 this->write(";\n");
@@ -1485,32 +1456,26 @@
         this->write("    float4 sk_FragColor [[color(0)]];\n");
     }
     for (const auto& e : fProgram) {
-        if (e.kind() == ProgramElement::Kind::kVar) {
-            const VarDeclarations& decls = e.as<VarDeclarations>();
-            if (!decls.fVars.size()) {
-                continue;
-            }
-            const Variable& first = *decls.fVars[0]->as<VarDeclaration>().fVar;
-            if (first.fModifiers.fFlags & Modifiers::kOut_Flag &&
-                -1 == first.fModifiers.fLayout.fBuiltin) {
+        if (e.is<GlobalVarDeclaration>()) {
+            const GlobalVarDeclaration& decls = e.as<GlobalVarDeclaration>();
+            const Variable& var = *decls.fDecl->fVar;
+            if (var.fModifiers.fFlags & Modifiers::kOut_Flag &&
+                -1 == var.fModifiers.fLayout.fBuiltin) {
                 this->write("    ");
-                this->writeType(first.type());
+                this->writeType(var.type());
                 this->write(" ");
-                for (const auto& stmt : decls.fVars) {
-                    const VarDeclaration& var = stmt->as<VarDeclaration>();
-                    this->writeName(var.fVar->name());
-                    if (fProgram.fKind == Program::kVertex_Kind) {
-                        this->write("  [[user(locn" +
-                                    to_string(var.fVar->fModifiers.fLayout.fLocation) + ")]]");
-                    } else if (fProgram.fKind == Program::kFragment_Kind) {
-                        this->write(" [[color(" +
-                                    to_string(var.fVar->fModifiers.fLayout.fLocation) +")");
-                        int colorIndex = var.fVar->fModifiers.fLayout.fIndex;
-                        if (colorIndex) {
-                            this->write(", index(" + to_string(colorIndex) + ")");
-                        }
-                        this->write("]]");
+                this->writeName(var.name());
+                if (fProgram.fKind == Program::kVertex_Kind) {
+                    this->write("  [[user(locn" +
+                                to_string(var.fModifiers.fLayout.fLocation) + ")]]");
+                } else if (fProgram.fKind == Program::kFragment_Kind) {
+                    this->write(" [[color(" +
+                                to_string(var.fModifiers.fLayout.fLocation) +")");
+                    int colorIndex = var.fModifiers.fLayout.fIndex;
+                    if (colorIndex) {
+                        this->write(", index(" + to_string(colorIndex) + ")");
                     }
+                    this->write("]]");
                 }
                 this->write(";\n");
             }
@@ -1543,27 +1508,21 @@
         visitor->VisitInterfaceBlock(*interfaceType, interfaceName);
     }
     for (const ProgramElement& element : fProgram) {
-        if (element.kind() != ProgramElement::Kind::kVar) {
+        if (!element.is<GlobalVarDeclaration>()) {
             continue;
         }
-        const VarDeclarations& decls = static_cast<const VarDeclarations&>(element);
-        if (decls.fVars.empty()) {
-            continue;
-        }
-        const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
-        if ((!first.fModifiers.fFlags && -1 == first.fModifiers.fLayout.fBuiltin) ||
-            first.type().typeKind() == Type::TypeKind::kSampler) {
-            for (const auto& stmt : decls.fVars) {
-                VarDeclaration& var = static_cast<VarDeclaration&>(*stmt);
-
-                if (var.fVar->type().typeKind() == Type::TypeKind::kSampler) {
-                    // Samplers are represented as a "texture/sampler" duo in the global struct.
-                    visitor->VisitTexture(first.type(), var.fVar->name());
-                    visitor->VisitSampler(first.type(), String(var.fVar->name()) + SAMPLER_SUFFIX);
-                } else {
-                    // Visit a regular variable.
-                    visitor->VisitVariable(*var.fVar, var.fValue.get());
-                }
+        const GlobalVarDeclaration& decls = element.as<GlobalVarDeclaration>();
+        const VarDeclaration& decl = *decls.fDecl;
+        const Variable& var = *decl.fVar;
+        if ((!var.fModifiers.fFlags && -1 == var.fModifiers.fLayout.fBuiltin) ||
+            var.type().typeKind() == Type::TypeKind::kSampler) {
+            if (var.type().typeKind() == Type::TypeKind::kSampler) {
+                // Samplers are represented as a "texture/sampler" duo in the global struct.
+                visitor->VisitTexture(var.type(), var.name());
+                visitor->VisitSampler(var.type(), String(var.name()) + SAMPLER_SUFFIX);
+            } else {
+                // Visit a regular variable.
+                visitor->VisitVariable(var, decl.fValue.get());
             }
         }
     }
@@ -1677,17 +1636,15 @@
     switch (e.kind()) {
         case ProgramElement::Kind::kExtension:
             break;
-        case ProgramElement::Kind::kVar: {
-            const VarDeclarations& decl = e.as<VarDeclarations>();
-            if (decl.fVars.size() > 0) {
-                int builtin = decl.fVars[0]->as<VarDeclaration>().fVar->fModifiers.fLayout.fBuiltin;
-                if (-1 == builtin) {
-                    // normal var
-                    this->writeVarDeclarations(decl, true);
-                    this->writeLine();
-                } else if (SK_FRAGCOLOR_BUILTIN == builtin) {
-                    // ignore
-                }
+        case ProgramElement::Kind::kGlobalVar: {
+            const VarDeclaration& decl = *e.as<GlobalVarDeclaration>().fDecl;
+            int builtin = decl.fVar->fModifiers.fLayout.fBuiltin;
+            if (-1 == builtin) {
+                // normal var
+                this->writeVarDeclaration(decl, true);
+                this->writeLine();
+            } else if (SK_FRAGCOLOR_BUILTIN == builtin) {
+                // ignore
             }
             break;
         }
@@ -1797,14 +1754,6 @@
             const VarDeclaration& var = s->as<VarDeclaration>();
             return this->requirements(var.fValue.get());
         }
-        case Statement::Kind::kVarDeclarations: {
-            Requirements result = kNo_Requirements;
-            const VarDeclarations& decls = *s->as<VarDeclarationsStatement>().fDeclaration;
-            for (const auto& stmt : decls.fVars) {
-                result |= this->requirements(stmt.get());
-            }
-            return result;
-        }
         case Statement::Kind::kExpression:
             return this->requirements(s->as<ExpressionStatement>().expression().get());
         case Statement::Kind::kReturn: {