Represent vector/matrix composition with the ConstructorComposite type.

ConstructorComposite is a slight rework of ConstructorVector;
mechanically, both vector and matrix composition behave the same and
can share the same logic.

The generated code in SPIR-V and Metal still has some tweaks due to
different handling for matrices in these languages, but the SkSL
internal model mimics GLSL's view that vectors and matrices can be
created by lumping together any mix of scalars and vectors.
The backends will continue to adapt this model to their reality.

Change-Id: Ia2781c8a9dd3b4ba55ef93e33ac252eaeec844ac
Bug: skia:11032
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/393178
Commit-Queue: John Stiles <johnstiles@google.com>
Auto-Submit: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
diff --git a/gn/sksl.gni b/gn/sksl.gni
index 1a39a90..03486e6 100644
--- a/gn/sksl.gni
+++ b/gn/sksl.gni
@@ -96,6 +96,8 @@
   "$_src/sksl/ir/SkSLConstructor.h",
   "$_src/sksl/ir/SkSLConstructorArray.cpp",
   "$_src/sksl/ir/SkSLConstructorArray.h",
+  "$_src/sksl/ir/SkSLConstructorComposite.cpp",
+  "$_src/sksl/ir/SkSLConstructorComposite.h",
   "$_src/sksl/ir/SkSLConstructorDiagonalMatrix.cpp",
   "$_src/sksl/ir/SkSLConstructorDiagonalMatrix.h",
   "$_src/sksl/ir/SkSLConstructorMatrixResize.cpp",
@@ -104,8 +106,6 @@
   "$_src/sksl/ir/SkSLConstructorScalarCast.h",
   "$_src/sksl/ir/SkSLConstructorSplat.cpp",
   "$_src/sksl/ir/SkSLConstructorSplat.h",
-  "$_src/sksl/ir/SkSLConstructorVector.cpp",
-  "$_src/sksl/ir/SkSLConstructorVector.h",
   "$_src/sksl/ir/SkSLConstructorVectorCast.cpp",
   "$_src/sksl/ir/SkSLConstructorVectorCast.h",
   "$_src/sksl/ir/SkSLContinueStatement.h",
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index fbeef94..64371e2 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -748,11 +748,11 @@
 
         case Expression::Kind::kConstructor:
         case Expression::Kind::kConstructorArray:
+        case Expression::Kind::kConstructorComposite:
         case Expression::Kind::kConstructorDiagonalMatrix:
         case Expression::Kind::kConstructorMatrixResize:
         case Expression::Kind::kConstructorScalarCast:
         case Expression::Kind::kConstructorSplat:
-        case Expression::Kind::kConstructorVector:
         case Expression::Kind::kConstructorVectorCast: {
             if (left.kind() != right.kind()) {
                 return false;
@@ -1028,11 +1028,11 @@
             case Expression::Kind::kBinary:
             case Expression::Kind::kConstructor:
             case Expression::Kind::kConstructorArray:
+            case Expression::Kind::kConstructorComposite:
             case Expression::Kind::kConstructorDiagonalMatrix:
             case Expression::Kind::kConstructorMatrixResize:
             case Expression::Kind::kConstructorScalarCast:
             case Expression::Kind::kConstructorSplat:
-            case Expression::Kind::kConstructorVector:
             case Expression::Kind::kConstructorVectorCast:
             case Expression::Kind::kFieldAccess:
             case Expression::Kind::kIndex:
@@ -1157,11 +1157,11 @@
         }
         case Expression::Kind::kConstructor:
         case Expression::Kind::kConstructorArray:
+        case Expression::Kind::kConstructorComposite:
         case Expression::Kind::kConstructorDiagonalMatrix:
         case Expression::Kind::kConstructorMatrixResize:
         case Expression::Kind::kConstructorScalarCast:
         case Expression::Kind::kConstructorSplat:
-        case Expression::Kind::kConstructorVector:
         case Expression::Kind::kConstructorVectorCast: {
             auto& c = e.asAnyConstructor();
             for (auto& arg : c.argumentSpan()) {
diff --git a/src/sksl/SkSLDehydrator.cpp b/src/sksl/SkSLDehydrator.cpp
index abf0314..7099fed 100644
--- a/src/sksl/SkSLDehydrator.cpp
+++ b/src/sksl/SkSLDehydrator.cpp
@@ -17,11 +17,11 @@
 #include "src/sksl/ir/SkSLBreakStatement.h"
 #include "src/sksl/ir/SkSLConstructor.h"
 #include "src/sksl/ir/SkSLConstructorArray.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
-#include "src/sksl/ir/SkSLConstructorVector.h"
 #include "src/sksl/ir/SkSLConstructorVectorCast.h"
 #include "src/sksl/ir/SkSLContinueStatement.h"
 #include "src/sksl/ir/SkSLDiscardStatement.h"
@@ -300,6 +300,12 @@
                 this->writeExpressionSpan(e->as<ConstructorArray>().argumentSpan());
                 break;
 
+            case Expression::Kind::kConstructorComposite:
+                this->writeCommand(Rehydrator::kConstructorComposite_Command);
+                this->write(e->type());
+                this->writeExpressionSpan(e->as<ConstructorComposite>().argumentSpan());
+                break;
+
             case Expression::Kind::kConstructorDiagonalMatrix:
                 this->writeCommand(Rehydrator::kConstructorDiagonalMatrix_Command);
                 this->write(e->type());
@@ -324,12 +330,6 @@
                 this->writeExpressionSpan(e->as<ConstructorSplat>().argumentSpan());
                 break;
 
-            case Expression::Kind::kConstructorVector:
-                this->writeCommand(Rehydrator::kConstructorVector_Command);
-                this->write(e->type());
-                this->writeExpressionSpan(e->as<ConstructorVector>().argumentSpan());
-                break;
-
             case Expression::Kind::kConstructorVectorCast:
                 this->writeCommand(Rehydrator::kConstructorVectorCast_Command);
                 this->write(e->type());
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index b41d9d5..2bcfdc9 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -198,10 +198,10 @@
             break;
         case Expression::Kind::kConstructor:
         case Expression::Kind::kConstructorArray:
+        case Expression::Kind::kConstructorComposite:
         case Expression::Kind::kConstructorDiagonalMatrix:
         case Expression::Kind::kConstructorMatrixResize:
         case Expression::Kind::kConstructorSplat:
-        case Expression::Kind::kConstructorVector:
             this->writeAnyConstructor(expr.asAnyConstructor(), parentPrecedence);
             break;
         case Expression::Kind::kConstructorScalarCast:
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index cbd973b..611c7c5 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -18,11 +18,11 @@
 #include "src/sksl/ir/SkSLBreakStatement.h"
 #include "src/sksl/ir/SkSLConstructor.h"
 #include "src/sksl/ir/SkSLConstructorArray.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
-#include "src/sksl/ir/SkSLConstructorVector.h"
 #include "src/sksl/ir/SkSLConstructorVectorCast.h"
 #include "src/sksl/ir/SkSLContinueStatement.h"
 #include "src/sksl/ir/SkSLDiscardStatement.h"
@@ -325,6 +325,12 @@
                                           *ctor.type().clone(symbolTableForExpression),
                                           argList(ctor.arguments()));
         }
+        case Expression::Kind::kConstructorComposite: {
+            const ConstructorComposite& ctor = expression.as<ConstructorComposite>();
+            return ConstructorComposite::Make(*fContext, offset,
+                                              *ctor.type().clone(symbolTableForExpression),
+                                              argList(ctor.arguments()));
+        }
         case Expression::Kind::kConstructorDiagonalMatrix: {
             const ConstructorDiagonalMatrix& ctor = expression.as<ConstructorDiagonalMatrix>();
             return ConstructorDiagonalMatrix::Make(*fContext, offset,
@@ -349,12 +355,6 @@
                                           *ctor.type().clone(symbolTableForExpression),
                                           expr(ctor.argument()));
         }
-        case Expression::Kind::kConstructorVector: {
-            const ConstructorVector& ctor = expression.as<ConstructorVector>();
-            return ConstructorVector::Make(*fContext, offset,
-                                           *ctor.type().clone(symbolTableForExpression),
-                                           argList(ctor.arguments()));
-        }
         case Expression::Kind::kConstructorVectorCast: {
             const ConstructorVectorCast& ctor = expression.as<ConstructorVectorCast>();
             return ConstructorVectorCast::Make(*fContext, offset,
@@ -957,11 +957,11 @@
             }
             case Expression::Kind::kConstructor:
             case Expression::Kind::kConstructorArray:
+            case Expression::Kind::kConstructorComposite:
             case Expression::Kind::kConstructorDiagonalMatrix:
             case Expression::Kind::kConstructorMatrixResize:
             case Expression::Kind::kConstructorScalarCast:
             case Expression::Kind::kConstructorSplat:
-            case Expression::Kind::kConstructorVector:
             case Expression::Kind::kConstructorVectorCast: {
                 AnyConstructor& constructorExpr = (*expr)->asAnyConstructor();
                 for (std::unique_ptr<Expression>& arg : constructorExpr.argumentSpan()) {
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index dc6e632..e33a002 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -173,21 +173,21 @@
         case Expression::Kind::kBoolLiteral:
             this->writeBoolLiteral(expr.as<BoolLiteral>());
             break;
-        case Expression::Kind::kConstructor:
-            this->writeConstructor(expr.as<Constructor>(), parentPrecedence);
+        case Expression::Kind::kConstructorArray:
+            this->writeAnyConstructor(expr.asAnyConstructor(), "{", "}", parentPrecedence);
+            break;
+        case Expression::Kind::kConstructorComposite:
+            this->writeConstructorComposite(expr.as<ConstructorComposite>(), parentPrecedence);
+            break;
+        case Expression::Kind::kConstructorDiagonalMatrix:
+        case Expression::Kind::kConstructorSplat:
+            this->writeAnyConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
             break;
         case Expression::Kind::kConstructorMatrixResize:
             this->writeConstructorMatrixResize(expr.as<ConstructorMatrixResize>(),
                                                parentPrecedence);
             break;
-        case Expression::Kind::kConstructorArray:
-            this->writeAnyConstructor(expr.asAnyConstructor(), "{", "}", parentPrecedence);
-            break;
-        case Expression::Kind::kConstructorDiagonalMatrix:
-        case Expression::Kind::kConstructorSplat:
-        case Expression::Kind::kConstructorVector:
-            this->writeAnyConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
-            break;
+        case Expression::Kind::kConstructor:
         case Expression::Kind::kConstructorScalarCast:
         case Expression::Kind::kConstructorVectorCast:
             this->writeCastConstructor(expr.asAnyConstructor(), "(", ")", parentPrecedence);
@@ -1010,11 +1010,8 @@
     return t1.isFloat() && t2.isFloat();
 }
 
-bool MetalCodeGenerator::matrixConstructHelperIsNeeded(const Constructor& c) {
-    // A matrix construct helper is only necessary if we are, in fact, constructing a matrix.
-    if (!c.type().isMatrix()) {
-        return false;
-    }
+bool MetalCodeGenerator::matrixConstructHelperIsNeeded(const ConstructorComposite& c) {
+    SkASSERT(c.type().isMatrix());
 
     // GLSL is fairly free-form about inputs to its matrix constructors, but Metal is not; it
     // expects exactly R vectors of C components apiece. (Metal 2.0 also allows a list of R*C
@@ -1067,10 +1064,17 @@
     this->write(")");
 }
 
-void MetalCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
-    const Type& constructorType = c.type();
-    SkASSERT(!constructorType.isArray());
+void MetalCodeGenerator::writeConstructorComposite(const ConstructorComposite& c,
+                                                   Precedence parentPrecedence) {
+    if (c.type().isMatrix()) {
+        this->writeConstructorCompositeMatrix(c, parentPrecedence);
+    } else {
+        this->writeAnyConstructor(c, "(", ")", parentPrecedence);
+    }
+}
 
+void MetalCodeGenerator::writeConstructorCompositeMatrix(const ConstructorComposite& c,
+                                                         Precedence parentPrecedence) {
     // Emit and invoke a matrix-constructor helper method if one is necessary.
     if (this->matrixConstructHelperIsNeeded(c)) {
         this->write(this->getMatrixConstructHelper(c));
@@ -1085,27 +1089,32 @@
         return;
     }
 
-    // Explicitly invoke the constructor, passing in the necessary arguments.
-    this->writeType(constructorType);
+    // Metal doesn't allow creating matrices by passing in scalars and vectors in a jumble; it
+    // requires your scalars to be grouped up into columns. Because `matrixConstructHelperIsNeeded`
+    // returned false, we know that none of our scalars/vectors "wrap" across across a column, so we
+    // can group our inputs up and synthesize a constructor for each column.
+    const Type& matrixType = c.type();
+    const Type& columnType = matrixType.componentType().toCompound(
+            fContext, /*columns=*/matrixType.rows(), /*rows=*/1);
+
+    this->writeType(matrixType);
     this->write("(");
     const char* separator = "";
     int scalarCount = 0;
     for (const std::unique_ptr<Expression>& arg : c.arguments()) {
-        const Type& argType = arg->type();
         this->write(separator);
         separator = ", ";
-        if (constructorType.isMatrix() &&
-            argType.columns() < constructorType.rows()) {
-            // Merge scalars and smaller vectors together.
+        if (arg->type().columns() < matrixType.rows()) {
+            // Write a `floatN(` constructor to group scalars and smaller vectors together.
             if (!scalarCount) {
-                this->writeType(constructorType.componentType());
-                this->write(to_string(constructorType.rows()));
+                this->writeType(columnType);
                 this->write("(");
             }
-            scalarCount += argType.columns();
+            scalarCount += arg->type().columns();
         }
         this->writeExpression(*arg, Precedence::kSequence);
-        if (scalarCount && scalarCount == constructorType.rows()) {
+        if (scalarCount && scalarCount == matrixType.rows()) {
+            // Close our `floatN(...` constructor block from above.
             this->write(")");
             scalarCount = 0;
         }
@@ -2267,11 +2276,11 @@
             return result;
         }
         case Expression::Kind::kConstructor:
+        case Expression::Kind::kConstructorComposite:
         case Expression::Kind::kConstructorArray:
         case Expression::Kind::kConstructorDiagonalMatrix:
         case Expression::Kind::kConstructorScalarCast:
         case Expression::Kind::kConstructorSplat:
-        case Expression::Kind::kConstructorVector:
         case Expression::Kind::kConstructorVectorCast: {
             const AnyConstructor& c = e->asAnyConstructor();
             Requirements result = kNo_Requirements;
diff --git a/src/sksl/SkSLMetalCodeGenerator.h b/src/sksl/SkSLMetalCodeGenerator.h
index a8db99a..4d0831c 100644
--- a/src/sksl/SkSLMetalCodeGenerator.h
+++ b/src/sksl/SkSLMetalCodeGenerator.h
@@ -22,6 +22,7 @@
 #include "src/sksl/ir/SkSLBinaryExpression.h"
 #include "src/sksl/ir/SkSLBoolLiteral.h"
 #include "src/sksl/ir/SkSLConstructor.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
 #include "src/sksl/ir/SkSLDoStatement.h"
 #include "src/sksl/ir/SkSLExtension.h"
@@ -207,7 +208,7 @@
 
     void writeFunctionCall(const FunctionCall& c);
 
-    bool matrixConstructHelperIsNeeded(const Constructor& c);
+    bool matrixConstructHelperIsNeeded(const ConstructorComposite& c);
     String getMatrixConstructHelper(const AnyConstructor& c);
     void assembleMatrixFromMatrix(const Type& sourceMatrix, int rows, int columns);
     void assembleMatrixFromExpressions(const AnyConstructor& ctor, int rows, int columns);
@@ -230,6 +231,11 @@
 
     void writeConstructor(const Constructor& c, Precedence parentPrecedence);
 
+    void writeConstructorComposite(const ConstructorComposite& c, Precedence parentPrecedence);
+
+    void writeConstructorCompositeMatrix(const ConstructorComposite& c,
+                                         Precedence parentPrecedence);
+
     void writeConstructorMatrixResize(const ConstructorMatrixResize& c,
                                       Precedence parentPrecedence);
 
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
index 57b4075..a71f1de 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
@@ -410,11 +410,11 @@
             break;
         case Expression::Kind::kConstructor:
         case Expression::Kind::kConstructorArray:
+        case Expression::Kind::kConstructorComposite:
         case Expression::Kind::kConstructorDiagonalMatrix:
         case Expression::Kind::kConstructorMatrixResize:
         case Expression::Kind::kConstructorScalarCast:
         case Expression::Kind::kConstructorSplat:
-        case Expression::Kind::kConstructorVector:
         case Expression::Kind::kConstructorVectorCast:
             this->writeAnyConstructor(expr.asAnyConstructor(), parentPrecedence);
             break;
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index aef928c..4f2d953 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -17,11 +17,11 @@
 #include "src/sksl/ir/SkSLBreakStatement.h"
 #include "src/sksl/ir/SkSLConstructor.h"
 #include "src/sksl/ir/SkSLConstructorArray.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
-#include "src/sksl/ir/SkSLConstructorVector.h"
 #include "src/sksl/ir/SkSLConstructorVectorCast.h"
 #include "src/sksl/ir/SkSLContinueStatement.h"
 #include "src/sksl/ir/SkSLDiscardStatement.h"
@@ -468,6 +468,11 @@
             const Type* type = this->type();
             return ConstructorArray::Make(fContext, /*offset=*/-1, *type, this->expressionArray());
         }
+        case Rehydrator::kConstructorComposite_Command: {
+            const Type* type = this->type();
+            return ConstructorComposite::Make(fContext, /*offset=*/-1, *type,
+                                              this->expressionArray());
+        }
         case Rehydrator::kConstructorDiagonalMatrix_Command: {
             const Type* type = this->type();
             ExpressionArray args = this->expressionArray();
@@ -494,10 +499,6 @@
             SkASSERT(args.size() == 1);
             return ConstructorSplat::Make(fContext, /*offset=*/-1, *type, std::move(args[0]));
         }
-        case Rehydrator::kConstructorVector_Command: {
-            const Type* type = this->type();
-            return ConstructorVector::Make(fContext, /*offset=*/-1, *type, this->expressionArray());
-        }
         case Rehydrator::kConstructorVectorCast_Command: {
             const Type* type = this->type();
             ExpressionArray args = this->expressionArray();
diff --git a/src/sksl/SkSLRehydrator.h b/src/sksl/SkSLRehydrator.h
index 86ab8a3..7fc448b 100644
--- a/src/sksl/SkSLRehydrator.h
+++ b/src/sksl/SkSLRehydrator.h
@@ -52,11 +52,11 @@
         // (All constructors) Type type, uint8 argCount, Expression[] arguments
         kConstructor_Command,
         kConstructorArray_Command,
+        kConstructorComposite_Command,
         kConstructorDiagonalMatrix_Command,
         kConstructorMatrixResize_Command,
         kConstructorScalarCast_Command,
         kConstructorSplat_Command,
-        kConstructorVector_Command,
         kConstructorVectorCast_Command,
         kConstructorReserved4_Command,
         kConstructorReserved5_Command,
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index dcabd01..9eed162 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -721,8 +721,8 @@
             return this->writeConstructorScalarCast(expr.as<ConstructorScalarCast>(), out);
         case Expression::Kind::kConstructorSplat:
             return this->writeConstructorSplat(expr.as<ConstructorSplat>(), out);
-        case Expression::Kind::kConstructorVector:
-            return this->writeVectorConstructor(expr.as<ConstructorVector>(), out);
+        case Expression::Kind::kConstructorComposite:
+            return this->writeConstructorComposite(expr.as<ConstructorComposite>(), out);
         case Expression::Kind::kConstructorVectorCast:
             return this->writeConstructorVectorCast(expr.as<ConstructorVectorCast>(), out);
         case Expression::Kind::kIntLiteral:
@@ -929,7 +929,7 @@
             args.reserve_back(2);
             args.push_back(IntLiteral::Make(fContext, /*offset=*/-1, /*value=*/0));
             args.push_back(IntLiteral::Make(fContext, /*offset=*/-1, /*value=*/0));
-            Constructor ctor(/*offset=*/-1, *fContext.fTypes.fInt2, std::move(args));
+            ConstructorComposite ctor(/*offset=*/-1, *fContext.fTypes.fInt2, std::move(args));
             SpvId coords = this->writeConstantVector(ctor);
             if (arguments.size() == 1) {
                 this->writeInstruction(SpvOpImageRead,
@@ -1510,26 +1510,24 @@
     }
 }
 
-SpvId SPIRVCodeGenerator::writeMatrixConstructor(const Constructor& c, OutputStream& out) {
+SpvId SPIRVCodeGenerator::writeMatrixConstructor(const ConstructorComposite& c, OutputStream& out) {
     const Type& type = c.type();
     SkASSERT(type.isMatrix());
-    SkASSERT(c.arguments().size() > 0);
+    SkASSERT(!c.arguments().empty());
     const Type& arg0Type = c.arguments()[0]->type();
     // go ahead and write the arguments so we don't try to write new instructions in the middle of
     // an instruction
     std::vector<SpvId> arguments;
-    for (size_t i = 0; i < c.arguments().size(); i++) {
-        arguments.push_back(this->writeExpression(*c.arguments()[i], out));
+    arguments.reserve(c.arguments().size());
+    for (const std::unique_ptr<Expression>& arg : c.arguments()) {
+        arguments.push_back(this->writeExpression(*arg, out));
     }
     SpvId result = this->nextId(&type);
     int rows = type.rows();
     int columns = type.columns();
-    if (arguments.size() == 1 && arg0Type.isScalar()) {
-        this->writeUniformScaleMatrix(result, arguments[0], type, out);
-    } else if (arguments.size() == 1 && arg0Type.isMatrix()) {
+    if (arguments.size() == 1 && arg0Type.isMatrix()) {
         this->writeMatrixCopy(result, arguments[0], arg0Type, type, out);
-    } else if (arguments.size() == 1 &&
-               arg0Type.isVector()) {
+    } else if (arguments.size() == 1 && arg0Type.isVector()) {
         SkASSERT(type.rows() == 2 && type.columns() == 2);
         SkASSERT(arg0Type.columns() == 4);
         SpvId componentType = this->getType(type.componentType());
@@ -1587,7 +1585,13 @@
     return result;
 }
 
-SpvId SPIRVCodeGenerator::writeVectorConstructor(const ConstructorVector& c, OutputStream& out) {
+SpvId SPIRVCodeGenerator::writeConstructorComposite(const ConstructorComposite& c,
+                                                    OutputStream& out) {
+    return c.type().isMatrix() ? this->writeMatrixConstructor(c, out)
+                               : this->writeVectorConstructor(c, out);
+}
+
+SpvId SPIRVCodeGenerator::writeVectorConstructor(const ConstructorComposite& c, OutputStream& out) {
     const Type& type = c.type();
     const Type& componentType = type.componentType();
     SkASSERT(type.isVector());
@@ -1667,9 +1671,6 @@
         this->getActualType(type) == this->getActualType(c.arguments()[0]->type())) {
         return this->writeExpression(*c.arguments()[0], out);
     }
-    if (type.isMatrix()) {
-        return this->writeMatrixConstructor(c, out);
-    }
     fErrors.error(c.fOffset, "unsupported constructor: " + c.description());
     return -1;
 }
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.h b/src/sksl/SkSLSPIRVCodeGenerator.h
index 5e2d341..187105a 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.h
+++ b/src/sksl/SkSLSPIRVCodeGenerator.h
@@ -23,11 +23,11 @@
 #include "src/sksl/ir/SkSLBoolLiteral.h"
 #include "src/sksl/ir/SkSLConstructor.h"
 #include "src/sksl/ir/SkSLConstructorArray.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
-#include "src/sksl/ir/SkSLConstructorVector.h"
 #include "src/sksl/ir/SkSLConstructorVectorCast.h"
 #include "src/sksl/ir/SkSLDoStatement.h"
 #include "src/sksl/ir/SkSLFieldAccess.h"
@@ -289,9 +289,11 @@
                         std::vector<SpvId>* columnIds, int* currentCount, int rows, SpvId entry,
                         OutputStream& out);
 
-    SpvId writeMatrixConstructor(const Constructor& c, OutputStream& out);
+    SpvId writeConstructorComposite(const ConstructorComposite& c, OutputStream& out);
 
-    SpvId writeVectorConstructor(const ConstructorVector& c, OutputStream& out);
+    SpvId writeMatrixConstructor(const ConstructorComposite& c, OutputStream& out);
+
+    SpvId writeVectorConstructor(const ConstructorComposite& c, OutputStream& out);
 
     SpvId writeArrayConstructor(const ConstructorArray& c, OutputStream& out);
 
diff --git a/src/sksl/SkSLVMGenerator.cpp b/src/sksl/SkSLVMGenerator.cpp
index 8d1da7f..1b785d0 100644
--- a/src/sksl/SkSLVMGenerator.cpp
+++ b/src/sksl/SkSLVMGenerator.cpp
@@ -1465,7 +1465,7 @@
         case Expression::Kind::kConstructor:
             return this->writeConstructor(e.as<Constructor>());
         case Expression::Kind::kConstructorArray:
-        case Expression::Kind::kConstructorVector:
+        case Expression::Kind::kConstructorComposite:
             return this->writeAggregationConstructor(e.asAnyConstructor());
         case Expression::Kind::kConstructorDiagonalMatrix:
             return this->writeConstructorDiagonalMatrix(e.as<ConstructorDiagonalMatrix>());
diff --git a/src/sksl/generated/sksl_gpu.dehydrated.sksl b/src/sksl/generated/sksl_gpu.dehydrated.sksl
index e34a310..7e5a440 100644
--- a/src/sksl/generated/sksl_gpu.dehydrated.sksl
+++ b/src/sksl/generated/sksl_gpu.dehydrated.sksl
@@ -3899,7 +3899,7 @@
 2,
 51,0,0,0,0,1,
 43,
-11,
+12,
 49,15,2,1,
 28,
 49,176,0,0,0,0,0,1,0,
@@ -3948,11 +3948,11 @@
 53,
 1,
 58,167,3,0,66,
-11,
+12,
 49,15,2,1,
 28,
 49,176,0,0,0,0,0,
-11,
+12,
 49,15,2,1,
 28,
 49,176,0,0,0,0,0,
@@ -4125,7 +4125,7 @@
 0,0,3,
 56,35,4,
 49,15,2,0,
-12,
+8,
 49,15,2,4,
 30,
 49,176,0,199,3,2,
@@ -4385,7 +4385,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,15,2,4,
 30,
 49,176,0,218,3,2,
@@ -4522,7 +4522,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,15,2,4,
 30,
 49,176,0,224,3,2,
@@ -4788,7 +4788,7 @@
 28,
 49,176,0,0,0,0,0,
 58,234,3,0,
-12,
+8,
 49,15,2,4,
 30,
 49,176,0,233,3,2,
@@ -4823,7 +4823,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -4861,7 +4861,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -4892,7 +4892,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -4934,7 +4934,7 @@
 43,
 30,
 49,176,0,166,1,2,
-12,
+8,
 49,172,1,3,
 28,
 49,176,0,154,153,153,62,
@@ -5093,7 +5093,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,172,1,3,
 28,
 49,176,0,0,0,0,0,
@@ -5115,7 +5115,7 @@
 2,
 51,0,0,0,0,1,
 43,
-11,
+12,
 49,172,1,1,
 28,
 49,176,0,0,0,0,0,1,1,1,211,3,
@@ -5252,7 +5252,7 @@
 48,
 58,4,4,0,1,3,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -5316,7 +5316,7 @@
 48,
 58,7,4,0,1,3,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -5380,7 +5380,7 @@
 48,
 58,10,4,0,1,3,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -5441,7 +5441,7 @@
 48,
 58,13,4,0,1,3,
 43,
-12,
+8,
 49,15,2,2,
 1,
 1,
@@ -5823,7 +5823,7 @@
 58,18,4,0,
 59,
 43,
-11,
+12,
 49,15,2,1,
 28,
 49,176,0,0,0,0,0,1,29,154,3,157,3,160,3,163,3,166,3,169,3,172,3,175,3,178,3,181,3,184,3,187,3,190,3,193,3,196,3,202,3,205,3,208,3,221,3,227,3,230,3,236,3,239,3,242,3,245,3,6,4,9,4,12,4,15,4,
@@ -5831,7 +5831,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,15,2,2,
 1,
 48,
@@ -5848,7 +5848,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,132,1,2,
 1,
 48,
@@ -5878,7 +5878,7 @@
 44,240,9,
 30,
 49,168,0,105,2,1,
-6,
+8,
 49,218,1,2,
 58,27,4,0,
 58,28,4,0,
@@ -5901,7 +5901,7 @@
 44,240,9,
 30,
 49,176,0,109,2,1,
-6,
+8,
 49,8,2,2,
 58,31,4,0,
 58,32,4,0,
diff --git a/src/sksl/generated/sksl_public.dehydrated.sksl b/src/sksl/generated/sksl_public.dehydrated.sksl
index 1456cc8..de9ea6f 100644
--- a/src/sksl/generated/sksl_public.dehydrated.sksl
+++ b/src/sksl/generated/sksl_public.dehydrated.sksl
@@ -1323,7 +1323,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,139,1,2,
 1,
 48,
@@ -1340,7 +1340,7 @@
 2,
 51,0,0,0,0,1,
 43,
-12,
+8,
 49,142,1,2,
 1,
 48,
diff --git a/src/sksl/ir/SkSLConstructor.cpp b/src/sksl/ir/SkSLConstructor.cpp
index ab03858..c1b2d09 100644
--- a/src/sksl/ir/SkSLConstructor.cpp
+++ b/src/sksl/ir/SkSLConstructor.cpp
@@ -9,11 +9,11 @@
 
 #include "src/sksl/ir/SkSLBoolLiteral.h"
 #include "src/sksl/ir/SkSLConstructorArray.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
 #include "src/sksl/ir/SkSLConstructorMatrixResize.h"
 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
-#include "src/sksl/ir/SkSLConstructorVector.h"
 #include "src/sksl/ir/SkSLConstructorVectorCast.h"
 #include "src/sksl/ir/SkSLFloatLiteral.h"
 #include "src/sksl/ir/SkSLIntLiteral.h"
@@ -132,52 +132,7 @@
         return nullptr;
     }
 
-    if (context.fConfig->fSettings.fOptimize) {
-        // Find constructors embedded inside constructors and flatten them out where possible.
-        //   -  float4(float2(1, 2), 3, 4)                -->  float4(1, 2, 3, 4)
-        //   -  float4(w, float3(sin(x), cos(y), tan(z))) -->  float4(w, sin(x), cos(y), tan(z))
-
-        // Inspect each constructor argument to see if it's a candidate for flattening.
-        // Remember matched arguments in a bitfield, "argsToOptimize".
-        int argsToOptimize = 0;
-        int currBit = 1;
-        for (const std::unique_ptr<Expression>& arg : args) {
-            if (arg->isAnyConstructor()) {
-                AnyConstructor& inner = arg->asAnyConstructor();
-                if (inner.argumentSpan().size() > 1 &&
-                    inner.type().componentType() == type.componentType()) {
-                    argsToOptimize |= currBit;
-                }
-            }
-            currBit <<= 1;
-        }
-
-        if (argsToOptimize) {
-            // We found at least one argument that could be flattened out. Re-walk the constructor
-            // args and flatten the candidates we found during our initial pass.
-            ExpressionArray flattened;
-            flattened.reserve_back(type.columns());
-            currBit = 1;
-            for (std::unique_ptr<Expression>& arg : args) {
-                if (argsToOptimize & currBit) {
-                    AnyConstructor& inner = arg->asAnyConstructor();
-                    for (std::unique_ptr<Expression>& innerArg : inner.argumentSpan()) {
-                        flattened.push_back(std::move(innerArg));
-                    }
-                } else {
-                    flattened.push_back(std::move(arg));
-                }
-                currBit <<= 1;
-            }
-            args = std::move(flattened);
-        }
-    }
-
-    if (type.isVector()) {
-        return ConstructorVector::Make(context, offset, type, std::move(args));
-    }
-
-    return std::make_unique<Constructor>(offset, type, std::move(args));
+    return ConstructorComposite::Make(context, offset, type, std::move(args));
 }
 
 const Expression* AnyConstructor::getConstantSubexpression(int n) const {
diff --git a/src/sksl/ir/SkSLConstructor.h b/src/sksl/ir/SkSLConstructor.h
index 37e61db..1554b8e 100644
--- a/src/sksl/ir/SkSLConstructor.h
+++ b/src/sksl/ir/SkSLConstructor.h
@@ -188,11 +188,6 @@
     }
 
 private:
-    static std::unique_ptr<Expression> MakeScalarConstructor(const Context& context,
-                                                             int offset,
-                                                             const Type& type,
-                                                             ExpressionArray args);
-
     static std::unique_ptr<Expression> MakeCompoundConstructor(const Context& context,
                                                                int offset,
                                                                const Type& type,
diff --git a/src/sksl/ir/SkSLConstructorComposite.cpp b/src/sksl/ir/SkSLConstructorComposite.cpp
new file mode 100644
index 0000000..c757cce
--- /dev/null
+++ b/src/sksl/ir/SkSLConstructorComposite.cpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "src/sksl/ir/SkSLConstructorComposite.h"
+
+#include <algorithm>
+#include <numeric>
+
+namespace SkSL {
+
+std::unique_ptr<Expression> ConstructorComposite::Make(const Context& context,
+                                                       int offset,
+                                                       const Type& type,
+                                                       ExpressionArray args) {
+    // A scalar "composite" type with a single scalar argument is a no-op and can be eliminated.
+    // (Pedantically, this isn't a composite at all, but it's harmless to allow and simplifies
+    // call sites which need to narrow a vector and may sometimes end up with a scalar.)
+    if (type.isScalar() && args.size() == 1 && args.front()->type() == type) {
+        return std::move(args.front());
+    }
+
+    // The type must be a vector or matrix, and all the arguments must have matching component type.
+    SkASSERT(type.isVector() || type.isMatrix());
+    SkASSERT(std::all_of(args.begin(), args.end(), [&](const std::unique_ptr<Expression>& arg) {
+        const Type& argType = arg->type();
+        return (argType.isScalar() || argType.isVector() || argType.isMatrix()) &&
+               (argType.componentType() == type.componentType());
+    }));
+
+    // The slot count of the combined argument list must match the composite type's slot count.
+    SkASSERT(type.slotCount() ==
+             std::accumulate(args.begin(), args.end(), /*initial value*/ (size_t)0,
+                             [](size_t n, const std::unique_ptr<Expression>& arg) {
+                                 return n + arg->type().slotCount();
+                             }));
+
+    if (context.fConfig->fSettings.fOptimize) {
+        // Find ConstructorComposites embedded inside other ConstructorComposites and flatten them.
+        //   -  float4(float2(1, 2), 3, 4)                -->  float4(1, 2, 3, 4)
+        //   -  float4(w, float3(sin(x), cos(y), tan(z))) -->  float4(w, sin(x), cos(y), tan(z))
+        //   -  mat2(float2(a, b), float2(c, d))          -->  mat2(a, b, c, d)
+
+        // See how many fields we would have if composite constructors were flattened out.
+        size_t fields = 0;
+        for (const std::unique_ptr<Expression>& arg : args) {
+            fields += arg->is<ConstructorComposite>()
+                              ? arg->as<ConstructorComposite>().arguments().size()
+                              : 1;
+        }
+
+        // If we added up more fields than we're starting with, we found at least one input that can
+        // be flattened out.
+        if (fields > args.size()) {
+            ExpressionArray flattened;
+            flattened.reserve_back(fields);
+            for (std::unique_ptr<Expression>& arg : args) {
+                // For non-ConstructorComposite fields, move them over as-is.
+                if (!arg->is<ConstructorComposite>()) {
+                    flattened.push_back(std::move(arg));
+                    continue;
+                }
+                // For ConstructorComposite fields, move over their inner arguments individually.
+                ConstructorComposite& compositeCtor = arg->as<ConstructorComposite>();
+                for (std::unique_ptr<Expression>& innerArg : compositeCtor.arguments()) {
+                    flattened.push_back(std::move(innerArg));
+                }
+            }
+            args = std::move(flattened);
+        }
+    }
+
+    return std::make_unique<ConstructorComposite>(offset, type, std::move(args));
+}
+
+}  // namespace SkSL
diff --git a/src/sksl/ir/SkSLConstructorComposite.h b/src/sksl/ir/SkSLConstructorComposite.h
new file mode 100644
index 0000000..b2410e7
--- /dev/null
+++ b/src/sksl/ir/SkSLConstructorComposite.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2021 Google LLC
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef SKSL_CONSTRUCTOR_VECTOR
+#define SKSL_CONSTRUCTOR_VECTOR
+
+#include "src/sksl/SkSLContext.h"
+#include "src/sksl/ir/SkSLConstructor.h"
+#include "src/sksl/ir/SkSLExpression.h"
+
+#include <memory>
+
+namespace SkSL {
+
+/**
+ * Represents a vector or matrix that is composed from other expressions, such as
+ * `half3(pos.xy, 1)` or `mat3(a.xyz, b.xyz, 0, 0, 1)`
+ *
+ * These can contain a mix of scalars and aggregates. The total number of scalar values inside the
+ * constructor must always match the type's slot count. (e.g. `pos.xy` consumes two slots.)
+ * The inner values must have the same component type as the vector/matrix.
+ */
+class ConstructorComposite final : public MultiArgumentConstructor {
+public:
+    static constexpr Kind kExpressionKind = Kind::kConstructorComposite;
+
+    ConstructorComposite(int offset, const Type& type, ExpressionArray args)
+            : INHERITED(offset, kExpressionKind, &type, std::move(args)) {}
+
+    static std::unique_ptr<Expression> Make(const Context& context,
+                                            int offset,
+                                            const Type& type,
+                                            ExpressionArray args);
+
+    std::unique_ptr<Expression> clone() const override {
+        return std::make_unique<ConstructorComposite>(fOffset, this->type(),
+                                                      this->cloneArguments());
+    }
+
+private:
+    using INHERITED = MultiArgumentConstructor;
+};
+
+}  // namespace SkSL
+
+#endif
diff --git a/src/sksl/ir/SkSLConstructorVector.cpp b/src/sksl/ir/SkSLConstructorVector.cpp
deleted file mode 100644
index 8f958fc..0000000
--- a/src/sksl/ir/SkSLConstructorVector.cpp
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#include "src/sksl/ir/SkSLConstructorVector.h"
-
-#include <algorithm>
-#include <numeric>
-
-namespace SkSL {
-
-std::unique_ptr<Expression> ConstructorVector::Make(const Context& context,
-                                                    int offset,
-                                                    const Type& type,
-                                                    ExpressionArray args) {
-    // A scalar "vector" type with a single scalar argument is a no-op and can be eliminated.
-    // (Pedantically, this isn't a vector, but it's harmless to allow and simplifies things for
-    // callers which need to narrow a vector.)
-    if (type.isScalar() && args.size() == 1 && args.front()->type() == type) {
-        return std::move(args.front());
-    }
-
-    // The type must be a vector, and all the arguments must have matching component type.
-    SkASSERT(type.isVector());
-    SkASSERT(std::all_of(args.begin(), args.end(), [&](const std::unique_ptr<Expression>& arg) {
-        const Type& argType = arg->type();
-        return (argType.isScalar() || argType.isVector()) &&
-               (argType.componentType() == type.componentType());
-    }));
-
-    // The slot count of the combined argument list must match the composite type's slot count.
-    SkASSERT(type.slotCount() ==
-             std::accumulate(args.begin(), args.end(), /*initial value*/ (size_t)0,
-                             [](size_t n, const std::unique_ptr<Expression>& arg) {
-                                 return n + arg->type().slotCount();
-                             }));
-
-    return std::make_unique<ConstructorVector>(offset, type, std::move(args));
-}
-
-}  // namespace SkSL
diff --git a/src/sksl/ir/SkSLConstructorVector.h b/src/sksl/ir/SkSLConstructorVector.h
deleted file mode 100644
index f3173fe..0000000
--- a/src/sksl/ir/SkSLConstructorVector.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * Copyright 2021 Google LLC
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef SKSL_CONSTRUCTOR_VECTOR
-#define SKSL_CONSTRUCTOR_VECTOR
-
-#include "src/sksl/SkSLContext.h"
-#include "src/sksl/ir/SkSLConstructor.h"
-#include "src/sksl/ir/SkSLExpression.h"
-
-#include <memory>
-
-namespace SkSL {
-
-/**
- * Represents the construction of a vector, such as `half3(pos.xy, 1)`.
- *
- * These can contain a mix of scalars and vectors; the total number of scalar values inside the
- * constructor must always match the type width. (e.g. `pos.xy` counts for two scalars)
- * The inner values must have the same component type as the vector.
- */
-class ConstructorVector final : public MultiArgumentConstructor {
-public:
-    static constexpr Kind kExpressionKind = Kind::kConstructorVector;
-
-    ConstructorVector(int offset, const Type& type, ExpressionArray args)
-            : INHERITED(offset, kExpressionKind, &type, std::move(args)) {}
-
-    static std::unique_ptr<Expression> Make(const Context& context,
-                                            int offset,
-                                            const Type& type,
-                                            ExpressionArray args);
-
-    std::unique_ptr<Expression> clone() const override {
-        return std::make_unique<ConstructorVector>(fOffset, this->type(), this->cloneArguments());
-    }
-
-private:
-    using INHERITED = MultiArgumentConstructor;
-};
-
-}  // namespace SkSL
-
-#endif
diff --git a/src/sksl/ir/SkSLConstructorVectorCast.cpp b/src/sksl/ir/SkSLConstructorVectorCast.cpp
index ad77602..c836e27 100644
--- a/src/sksl/ir/SkSLConstructorVectorCast.cpp
+++ b/src/sksl/ir/SkSLConstructorVectorCast.cpp
@@ -8,6 +8,7 @@
 #include "src/sksl/ir/SkSLConstructorVectorCast.h"
 
 #include "src/sksl/ir/SkSLConstructor.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorScalarCast.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
 
@@ -27,7 +28,7 @@
                                             std::move(splat.argument())));
     }
 
-    // Create a vector Constructor(literal, ...) which typecasts each argument inside.
+    // Create a composite Constructor(literal, ...) which typecasts each argument inside.
     auto inputArgs = constCtor->asAnyConstructor().argumentSpan();
     ExpressionArray typecastArgs;
     typecastArgs.reserve_back(inputArgs.size());
@@ -47,11 +48,8 @@
         }
     }
 
-    // TODO(skia:11032): once we have ConstructorVector::Make, use it.
-    auto typecastVec = Constructor::Convert(context, constCtor->fOffset, destType,
-                                            std::move(typecastArgs));
-    SkASSERT(typecastVec);
-    return typecastVec;
+    return ConstructorComposite::Make(context, constCtor->fOffset, destType,
+                                      std::move(typecastArgs));
 }
 
 std::unique_ptr<Expression> ConstructorVectorCast::Make(const Context& context,
diff --git a/src/sksl/ir/SkSLConstructorVectorCast.h b/src/sksl/ir/SkSLConstructorVectorCast.h
index 7cb2a59..9deecd9 100644
--- a/src/sksl/ir/SkSLConstructorVectorCast.h
+++ b/src/sksl/ir/SkSLConstructorVectorCast.h
@@ -34,7 +34,7 @@
                                             std::unique_ptr<Expression> arg);
 
     bool isCompileTimeConstant() const override {
-        // If this were a compile-time constant, we would have created a ConstructorVector instead.
+        // If this were a compile-time constant, we would have made a ConstructorComposite instead.
         return false;
     }
 
diff --git a/src/sksl/ir/SkSLExpression.h b/src/sksl/ir/SkSLExpression.h
index 38f9df1..32cdf7b 100644
--- a/src/sksl/ir/SkSLExpression.h
+++ b/src/sksl/ir/SkSLExpression.h
@@ -32,11 +32,11 @@
         kCodeString,
         kConstructor,
         kConstructorArray,
+        kConstructorComposite,
         kConstructorDiagonalMatrix,
         kConstructorMatrixResize,
         kConstructorScalarCast,
         kConstructorSplat,
-        kConstructorVector,
         kConstructorVectorCast,
         kDefined,
         kExternalFunctionCall,
diff --git a/src/sksl/ir/SkSLPrefixExpression.cpp b/src/sksl/ir/SkSLPrefixExpression.cpp
index 6683c85..72c8ecc 100644
--- a/src/sksl/ir/SkSLPrefixExpression.cpp
+++ b/src/sksl/ir/SkSLPrefixExpression.cpp
@@ -11,9 +11,9 @@
 #include "src/sksl/ir/SkSLBoolLiteral.h"
 #include "src/sksl/ir/SkSLConstructor.h"
 #include "src/sksl/ir/SkSLConstructorArray.h"
+#include "src/sksl/ir/SkSLConstructorComposite.h"
 #include "src/sksl/ir/SkSLConstructorDiagonalMatrix.h"
 #include "src/sksl/ir/SkSLConstructorSplat.h"
-#include "src/sksl/ir/SkSLConstructorVector.h"
 #include "src/sksl/ir/SkSLFloatLiteral.h"
 #include "src/sksl/ir/SkSLIntLiteral.h"
 
@@ -76,11 +76,11 @@
             }
             break;
 
-        case Expression::Kind::kConstructorVector:
+        case Expression::Kind::kConstructorComposite:
             // Convert `-vecN(literal, ...)` into `vecN(-literal, ...)`.
             if (context.fConfig->fSettings.fOptimize && value->isCompileTimeConstant()) {
-                ConstructorVector& ctor = operand->as<ConstructorVector>();
-                return ConstructorVector::Make(
+                ConstructorComposite& ctor = operand->as<ConstructorComposite>();
+                return ConstructorComposite::Make(
                         context, ctor.fOffset, ctor.type(),
                         negate_operands(context, std::move(ctor.arguments())));
             }