Moved SkSL type into IRNode, now accessed via a method

This change doesn't accomplish anything by itself, but is a necessary
prerequisite for followup changes to node handling. Eventually all data
is going to be stored within IRNode itself, and the subclasses will not
add any fields; this is just the first step in that process.

Change-Id: If2bea4c62bd8f680e9d9f39248bb9679332b245b
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/315867
Reviewed-by: John Stiles <johnstiles@google.com>
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Ethan Nicholas <ethannicholas@google.com>
Auto-Submit: Ethan Nicholas <ethannicholas@google.com>
diff --git a/src/core/SkRuntimeEffect.cpp b/src/core/SkRuntimeEffect.cpp
index 6485a68..e868785 100644
--- a/src/core/SkRuntimeEffect.cpp
+++ b/src/core/SkRuntimeEffect.cpp
@@ -160,16 +160,17 @@
             for (const auto& varDecl : varDecls.fVars) {
                 const SkSL::Variable& var =
                         *(static_cast<const SkSL::VarDeclaration&>(*varDecl).fVar);
+                const SkSL::Type& varType = var.type();
 
                 // Varyings (only used in conjunction with drawVertices)
                 if (var.fModifiers.fFlags & SkSL::Modifiers::kVarying_Flag) {
                     varyings.push_back({var.fName,
-                                       var.fType.typeKind() == SkSL::Type::TypeKind::kVector
-                                               ? var.fType.columns()
+                                        varType.typeKind() == SkSL::Type::TypeKind::kVector
+                                               ? varType.columns()
                                                : 1});
                 }
                 // Fragment Processors (aka 'shader'): These are child effects
-                else if (&var.fType == ctx.fFragmentProcessor_Type.get()) {
+                else if (&varType == ctx.fFragmentProcessor_Type.get()) {
                     children.push_back(var.fName);
                     sampleUsages.push_back(SkSL::Analysis::GetSampleUsage(*program, var));
                 }
@@ -180,7 +181,7 @@
                     uni.fFlags = 0;
                     uni.fCount = 1;
 
-                    const SkSL::Type* type = &var.fType;
+                    const SkSL::Type* type = &var.type();
                     if (type->typeKind() == SkSL::Type::TypeKind::kArray) {
                         uni.fFlags |= Uniform::kArray_Flag;
                         uni.fCount = type->columns();
diff --git a/src/sksl/SkSLAnalysis.cpp b/src/sksl/SkSLAnalysis.cpp
index 3140ee1..6496965 100644
--- a/src/sksl/SkSLAnalysis.cpp
+++ b/src/sksl/SkSLAnalysis.cpp
@@ -95,9 +95,9 @@
                 // Determine the type of call at this site, and merge it with the accumulated state
                 const Expression* lastArg = fc.fArguments.back().get();
 
-                if (lastArg->fType == *fContext.fFloat2_Type) {
+                if (lastArg->type() == *fContext.fFloat2_Type) {
                     fUsage.merge(SampleUsage::Explicit());
-                } else if (lastArg->fType == *fContext.fFloat3x3_Type) {
+                } else if (lastArg->type() == *fContext.fFloat3x3_Type) {
                     // Determine the type of matrix for this call site
                     if (lastArg->isConstantOrUniform()) {
                         if (lastArg->kind() == Expression::Kind::kVariableReference ||
diff --git a/src/sksl/SkSLByteCodeGenerator.cpp b/src/sksl/SkSLByteCodeGenerator.cpp
index 6712336..a213ca4 100644
--- a/src/sksl/SkSLByteCodeGenerator.cpp
+++ b/src/sksl/SkSLByteCodeGenerator.cpp
@@ -162,16 +162,16 @@
                 const VarDeclarations& decl = e.as<VarDeclarations>();
                 for (const auto& v : decl.fVars) {
                     const Variable* declVar = v->as<VarDeclaration>().fVar;
-                    if (declVar->fType == *fContext.fFragmentProcessor_Type) {
+                    if (declVar->type() == *fContext.fFragmentProcessor_Type) {
                         fOutput->fChildFPCount++;
                     }
                     if (declVar->fModifiers.fLayout.fBuiltin >= 0 || is_in(*declVar)) {
                         continue;
                     }
                     if (is_uniform(*declVar)) {
-                        this->gatherUniforms(declVar->fType, declVar->fName);
+                        this->gatherUniforms(declVar->type(), declVar->fName);
                     } else {
-                        fOutput->fGlobalSlotCount += SlotCount(declVar->fType);
+                        fOutput->fGlobalSlotCount += SlotCount(declVar->type());
                     }
                 }
                 break;
@@ -434,7 +434,7 @@
             }
             int result = fParameterCount + fLocals.size();
             fLocals.push_back(&var);
-            for (int i = 0; i < SlotCount(var.fType) - 1; ++i) {
+            for (int i = 0; i < SlotCount(var.type()) - 1; ++i) {
                 fLocals.push_back(nullptr);
             }
             SkASSERT(result <= 255);
@@ -447,20 +447,20 @@
                     SkASSERT(offset <= 255);
                     return { offset, Storage::kLocal };
                 }
-                offset += SlotCount(p->fType);
+                offset += SlotCount(p->type());
             }
             SkASSERT(false);
             return Location::MakeInvalid();
         }
         case Variable::kGlobal_Storage: {
-            if (var.fType == *fContext.fFragmentProcessor_Type) {
+            if (var.type() == *fContext.fFragmentProcessor_Type) {
                 int offset = 0;
                 for (const auto& e : fProgram) {
                     if (e.kind() == ProgramElement::Kind::kVar) {
                         const VarDeclarations& decl = e.as<VarDeclarations>();
                         for (const auto& v : decl.fVars) {
                             const Variable* declVar = v->as<VarDeclaration>().fVar;
-                            if (declVar->fType != *fContext.fFragmentProcessor_Type) {
+                            if (declVar->type() != *fContext.fFragmentProcessor_Type) {
                                 continue;
                             }
                             if (declVar == &var) {
@@ -500,7 +500,7 @@
                             SkASSERT(offset <= 255);
                             return  { offset, isUniform ? Storage::kUniform : Storage::kGlobal };
                         }
-                        offset += SlotCount(declVar->fType);
+                        offset += SlotCount(declVar->type());
                     }
                 }
             }
@@ -520,7 +520,7 @@
             Location baseLoc = this->getLocation(*f.fBase);
             int offset = 0;
             for (int i = 0; i < f.fFieldIndex; ++i) {
-                offset += SlotCount(*f.fBase->fType.fields()[i].fType);
+                offset += SlotCount(*f.fBase->type().fields()[i].fType);
             }
             if (baseLoc.isOnStack()) {
                 if (offset != 0) {
@@ -535,8 +535,8 @@
         }
         case Expression::Kind::kIndex: {
             const IndexExpression& i = expr.as<IndexExpression>();
-            int stride = SlotCount(i.fType);
-            int length = i.fBase->fType.columns();
+            int stride = SlotCount(i.type());
+            int length = i.fBase->type().columns();
             SkASSERT(length <= 255);
             int offset = -1;
             if (i.fIndex->isCompileTimeConstant()) {
@@ -688,8 +688,8 @@
         discard = false;
         return discard;
     }
-    const Type& lType = b.fLeft->fType;
-    const Type& rType = b.fRight->fType;
+    const Type& lType = b.fLeft->type();
+    const Type& rType = b.fRight->type();
     bool lVecOrMtx = (lType.typeKind() == Type::TypeKind::kVector ||
                       lType.typeKind() == Type::TypeKind::kMatrix);
     bool rVecOrMtx = (rType.typeKind() == Type::TypeKind::kVector ||
@@ -776,7 +776,7 @@
         !(lType.typeKind() == Type::TypeKind::kVector &&
           rType.typeKind() == Type::TypeKind::kVector)) {
         this->write(ByteCodeInstruction::kMatrixMultiply,
-                    SlotCount(b.fType) - (SlotCount(lType) + SlotCount(rType)));
+                    SlotCount(b.type()) - (SlotCount(lType) + SlotCount(rType)));
         int rCols = rType.columns(),
             rRows = rType.rows(),
             lCols = lType.columns(),
@@ -786,7 +786,7 @@
             std::swap(rCols, rRows);
         }
         SkASSERT(lCols == rRows);
-        SkASSERT(SlotCount(b.fType) == lRows * rCols);
+        SkASSERT(SlotCount(b.type()) == lRows * rCols);
         this->write8(lCols);
         this->write8(lRows);
         this->write8(rCols);
@@ -908,8 +908,8 @@
         this->writeExpression(*arg);
     }
     if (c.fArguments.size() == 1) {
-        const Type& inType = c.fArguments[0]->fType;
-        const Type& outType = c.fType;
+        const Type& inType = c.fArguments[0]->type();
+        const Type& outType = c.type();
         TypeCategory inCategory = type_category(inType);
         TypeCategory outCategory = type_category(outType);
         int inCount = SlotCount(inType);
@@ -959,12 +959,12 @@
     int argumentCount = 0;
     for (const auto& arg : f.fArguments) {
         this->writeExpression(*arg);
-        argumentCount += SlotCount(arg->fType);
+        argumentCount += SlotCount(arg->type());
     }
-    this->write(ByteCodeInstruction::kCallExternal, SlotCount(f.fType) - argumentCount);
+    this->write(ByteCodeInstruction::kCallExternal, SlotCount(f.type()) - argumentCount);
     SkASSERT(argumentCount <= 255);
     this->write8(argumentCount);
-    this->write8(SlotCount(f.fType));
+    this->write8(SlotCount(f.type()));
     int index = fOutput->fExternalValues.size();
     fOutput->fExternalValues.push_back(f.fFunction);
     SkASSERT(index <= 255);
@@ -995,7 +995,7 @@
     }
 
     Location location = this->getLocation(expr);
-    int count = SlotCount(expr.fType);
+    int count = SlotCount(expr.type());
     if (count == 0) {
         return;
     }
@@ -1042,7 +1042,7 @@
     const size_t nargs = args.size();
     SkASSERT(nargs >= 1);
 
-    int count = SlotCount(args[0]->fType);
+    int count = SlotCount(args[0]->type());
 
     // Several intrinsics have variants where one argument is either scalar, or the same size as
     // the first argument. Call dupSmallerType(SlotCount(argType)) to ensure equal component count.
@@ -1055,9 +1055,9 @@
 
     if (intrin.is_special && intrin.special == SpecialIntrinsic::kSample) {
         // Sample is very special, the first argument is an FP, which can't be pushed to the stack.
-        if (nargs > 2 || args[0]->fType != *fContext.fFragmentProcessor_Type ||
-            (nargs == 2 && (args[1]->fType != *fContext.fFloat2_Type &&
-                            args[1]->fType != *fContext.fFloat3x3_Type))) {
+        if (nargs > 2 || args[0]->type() != *fContext.fFragmentProcessor_Type ||
+            (nargs == 2 && (args[1]->type() != *fContext.fFloat2_Type &&
+                            args[1]->type() != *fContext.fFloat3x3_Type))) {
             fErrors.error(c.fOffset, "Unsupported form of sample");
             return;
         }
@@ -1065,7 +1065,7 @@
         if (nargs == 2) {
             // Write our coords or matrix
             this->writeExpression(*args[1]);
-            this->write(args[1]->fType == *fContext.fFloat3x3_Type
+            this->write(args[1]->type() == *fContext.fFloat3x3_Type
                                 ? ByteCodeInstruction::kSampleMatrix
                                 : ByteCodeInstruction::kSampleExplicit);
         } else {
@@ -1083,7 +1083,7 @@
         // These intrinsics are extra-special, we need instructions interleaved with arguments
         bool saturate = (intrin.special == SpecialIntrinsic::kSaturate);
         SkASSERT(nargs == (saturate ? 1 : 3));
-        int limitCount = saturate ? 1 : SlotCount(args[1]->fType);
+        int limitCount = saturate ? 1 : SlotCount(args[1]->type());
 
         // 'x'
         this->writeExpression(*args[0]);
@@ -1096,7 +1096,7 @@
             this->writeExpression(*args[1]);
         }
         dupSmallerType(limitCount);
-        this->writeTypedInstruction(args[0]->fType,
+        this->writeTypedInstruction(args[0]->type(),
                                     ByteCodeInstruction::kMaxS,
                                     ByteCodeInstruction::kMaxS,
                                     ByteCodeInstruction::kMaxF,
@@ -1107,11 +1107,11 @@
             this->write(ByteCodeInstruction::kPushImmediate);
             this->write32(float_to_bits(1.0f));
         } else {
-            SkASSERT(limitCount == SlotCount(args[2]->fType));
+            SkASSERT(limitCount == SlotCount(args[2]->type()));
             this->writeExpression(*args[2]);
         }
         dupSmallerType(limitCount);
-        this->writeTypedInstruction(args[0]->fType,
+        this->writeTypedInstruction(args[0]->type(),
                                     ByteCodeInstruction::kMinS,
                                     ByteCodeInstruction::kMinS,
                                     ByteCodeInstruction::kMinF,
@@ -1140,7 +1140,7 @@
 
             case SpecialIntrinsic::kDot: {
                 SkASSERT(nargs == 2);
-                SkASSERT(count == SlotCount(args[1]->fType));
+                SkASSERT(count == SlotCount(args[1]->type()));
                 this->write(ByteCodeInstruction::kMultiplyF, count);
                 for (int i = count-1; i --> 0;) {
                     this->write(ByteCodeInstruction::kAddF, 1);
@@ -1161,15 +1161,15 @@
             case SpecialIntrinsic::kMin: {
                 SkASSERT(nargs == 2);
                 // There are variants where the second argument is scalar
-                dupSmallerType(SlotCount(args[1]->fType));
+                dupSmallerType(SlotCount(args[1]->type()));
                 if (intrin.special == SpecialIntrinsic::kMax) {
-                    this->writeTypedInstruction(args[0]->fType,
+                    this->writeTypedInstruction(args[0]->type(),
                                                 ByteCodeInstruction::kMaxS,
                                                 ByteCodeInstruction::kMaxS,
                                                 ByteCodeInstruction::kMaxF,
                                                 count);
                 } else {
-                    this->writeTypedInstruction(args[0]->fType,
+                    this->writeTypedInstruction(args[0]->type(),
                                                 ByteCodeInstruction::kMinS,
                                                 ByteCodeInstruction::kMinS,
                                                 ByteCodeInstruction::kMinF,
@@ -1180,10 +1180,10 @@
             case SpecialIntrinsic::kMix: {
                 // Two main variants of mix to handle
                 SkASSERT(nargs == 3);
-                SkASSERT(count == SlotCount(args[1]->fType));
-                int selectorCount = SlotCount(args[2]->fType);
+                SkASSERT(count == SlotCount(args[1]->type()));
+                int selectorCount = SlotCount(args[2]->type());
 
-                if (is_generic_type(&args[2]->fType, fContext.fGenBType_Type.get())) {
+                if (is_generic_type(&args[2]->type(), fContext.fGenBType_Type.get())) {
                     // mix(genType, genType, genBoolType)
                     SkASSERT(selectorCount == count);
                     this->write(ByteCodeInstruction::kMix, count);
@@ -1225,7 +1225,7 @@
             }
 
             default:
-                this->writeTypedInstruction(args[0]->fType,
+                this->writeTypedInstruction(args[0]->type(),
                                             intrin.inst_s,
                                             intrin.inst_u,
                                             intrin.inst_f,
@@ -1260,7 +1260,7 @@
     }
 
     // We may need to deal with out parameters, so the sequence is tricky
-    if (int returnCount = SlotCount(f.fType)) {
+    if (int returnCount = SlotCount(f.type())) {
         this->write(ByteCodeInstruction::kReserve, returnCount);
     }
 
@@ -1307,7 +1307,7 @@
             lvalues.back()->store(true);
             lvalues.pop_back();
         } else {
-            popCount += SlotCount(arg->fType);
+            popCount += SlotCount(arg->type());
         }
     }
     pop();
@@ -1327,19 +1327,20 @@
     switch (p.fOperator) {
         case Token::Kind::TK_PLUSPLUS: // fall through
         case Token::Kind::TK_MINUSMINUS: {
-            SkASSERT(SlotCount(p.fOperand->fType) == 1);
+            SkASSERT(SlotCount(p.fOperand->type()) == 1);
             std::unique_ptr<LValue> lvalue = this->getLValue(*p.fOperand);
             lvalue->load();
             this->write(ByteCodeInstruction::kPushImmediate);
-            this->write32(type_category(p.fType) == TypeCategory::kFloat ? float_to_bits(1.0f) : 1);
+            this->write32(type_category(p.type()) == TypeCategory::kFloat ? float_to_bits(1.0f)
+                                                                          : 1);
             if (p.fOperator == Token::Kind::TK_PLUSPLUS) {
-                this->writeTypedInstruction(p.fType,
+                this->writeTypedInstruction(p.type(),
                                             ByteCodeInstruction::kAddI,
                                             ByteCodeInstruction::kAddI,
                                             ByteCodeInstruction::kAddF,
                                             1);
             } else {
-                this->writeTypedInstruction(p.fType,
+                this->writeTypedInstruction(p.type(),
                                             ByteCodeInstruction::kSubtractI,
                                             ByteCodeInstruction::kSubtractI,
                                             ByteCodeInstruction::kSubtractF,
@@ -1351,17 +1352,17 @@
         }
         case Token::Kind::TK_MINUS: {
             this->writeExpression(*p.fOperand);
-            this->writeTypedInstruction(p.fType,
+            this->writeTypedInstruction(p.type(),
                                         ByteCodeInstruction::kNegateI,
                                         ByteCodeInstruction::kNegateI,
                                         ByteCodeInstruction::kNegateF,
-                                        SlotCount(p.fOperand->fType));
+                                        SlotCount(p.fOperand->type()));
             break;
         }
         case Token::Kind::TK_LOGICALNOT:
         case Token::Kind::TK_BITWISENOT: {
-            SkASSERT(SlotCount(p.fOperand->fType) == 1);
-            SkDEBUGCODE(TypeCategory tc = type_category(p.fOperand->fType));
+            SkASSERT(SlotCount(p.fOperand->type()) == 1);
+            SkDEBUGCODE(TypeCategory tc = type_category(p.fOperand->type()));
             SkASSERT((p.fOperator == Token::Kind::TK_LOGICALNOT && tc == TypeCategory::kBool) ||
                      (p.fOperator == Token::Kind::TK_BITWISENOT && (tc == TypeCategory::kSigned ||
                                                                  tc == TypeCategory::kUnsigned)));
@@ -1379,7 +1380,7 @@
     switch (p.fOperator) {
         case Token::Kind::TK_PLUSPLUS: // fall through
         case Token::Kind::TK_MINUSMINUS: {
-            SkASSERT(SlotCount(p.fOperand->fType) == 1);
+            SkASSERT(SlotCount(p.fOperand->type()) == 1);
             std::unique_ptr<LValue> lvalue = this->getLValue(*p.fOperand);
             lvalue->load();
             // If we're not supposed to discard the result, then make a copy *before* the +/-
@@ -1387,15 +1388,16 @@
                 this->write(ByteCodeInstruction::kDup, 1);
             }
             this->write(ByteCodeInstruction::kPushImmediate);
-            this->write32(type_category(p.fType) == TypeCategory::kFloat ? float_to_bits(1.0f) : 1);
+            this->write32(type_category(p.type()) == TypeCategory::kFloat ? float_to_bits(1.0f)
+                                                                          : 1);
             if (p.fOperator == Token::Kind::TK_PLUSPLUS) {
-                this->writeTypedInstruction(p.fType,
+                this->writeTypedInstruction(p.type(),
                                             ByteCodeInstruction::kAddI,
                                             ByteCodeInstruction::kAddI,
                                             ByteCodeInstruction::kAddF,
                                             1);
             } else {
-                this->writeTypedInstruction(p.fType,
+                this->writeTypedInstruction(p.type(),
                                             ByteCodeInstruction::kSubtractI,
                                             ByteCodeInstruction::kSubtractI,
                                             ByteCodeInstruction::kSubtractF,
@@ -1419,8 +1421,8 @@
     }
 
     this->writeExpression(*s.fBase);
-    this->write(ByteCodeInstruction::kSwizzle, s.fComponents.size() - s.fBase->fType.columns());
-    this->write8(s.fBase->fType.columns());
+    this->write(ByteCodeInstruction::kSwizzle, s.fComponents.size() - s.fBase->type().columns());
+    this->write8(s.fBase->type().columns());
     this->write8(s.fComponents.size());
     for (int c : s.fComponents) {
         this->write8(c);
@@ -1428,9 +1430,9 @@
 }
 
 void ByteCodeGenerator::writeTernaryExpression(const TernaryExpression& t) {
-    int count = SlotCount(t.fType);
-    SkASSERT(count == SlotCount(t.fIfTrue->fType));
-    SkASSERT(count == SlotCount(t.fIfFalse->fType));
+    int count = SlotCount(t.type());
+    SkASSERT(count == SlotCount(t.fIfTrue->type()));
+    SkASSERT(count == SlotCount(t.fIfFalse->type()));
 
     this->writeExpression(*t.fTest);
     this->write(ByteCodeInstruction::kMaskPush);
@@ -1493,7 +1495,7 @@
             SkASSERT(false);
     }
     if (discard) {
-        int count = SlotCount(e.fType);
+        int count = SlotCount(e.type());
         if (count > 0) {
             this->write(ByteCodeInstruction::kPop, count);
         }
@@ -1588,7 +1590,7 @@
     }
 
     void store(bool discard) override {
-        int count = ByteCodeGenerator::SlotCount(fExpression.fType);
+        int count = ByteCodeGenerator::SlotCount(fExpression.type());
         if (!discard) {
             fGenerator.write(ByteCodeInstruction::kDup, count);
         }
@@ -1734,7 +1736,7 @@
         fErrors.error(r.fOffset, "return not allowed inside conditional or loop");
         return;
     }
-    int count = SlotCount(r.fExpression->fType);
+    int count = SlotCount(r.fExpression->type());
     this->writeExpression(*r.fExpression);
 
     // Technically, the kReturn also pops fOutput->fLocalCount values from the stack, too, but we
@@ -1757,7 +1759,7 @@
         Location location = this->getLocation(*decl.fVar);
         if (decl.fValue) {
             this->writeExpression(*decl.fValue);
-            int count = SlotCount(decl.fValue->fType);
+            int count = SlotCount(decl.fValue->type());
             this->write(ByteCodeInstruction::kStore, count);
             this->write8(location.fSlot);
         }
@@ -1829,7 +1831,7 @@
         : fName(declaration->fName) {
     fParameterCount = 0;
     for (const auto& p : declaration->fParameters) {
-        int slots = ByteCodeGenerator::SlotCount(p->fType);
+        int slots = ByteCodeGenerator::SlotCount(p->type());
         fParameters.push_back({ slots, (bool)(p->fModifiers.fFlags & Modifiers::kOut_Flag) });
         fParameterCount += slots;
     }
diff --git a/src/sksl/SkSLCPPCodeGenerator.cpp b/src/sksl/SkSLCPPCodeGenerator.cpp
index db959be..3eacb80 100644
--- a/src/sksl/SkSLCPPCodeGenerator.cpp
+++ b/src/sksl/SkSLCPPCodeGenerator.cpp
@@ -21,7 +21,7 @@
 
 static bool needs_uniform_var(const Variable& var) {
     return (var.fModifiers.fFlags & Modifiers::kUniform_Flag) &&
-           var.fType.typeKind() != Type::TypeKind::kSampler;
+           var.type().typeKind() != Type::TypeKind::kSampler;
 }
 
 CPPCodeGenerator::CPPCodeGenerator(const Context* context, const Program* program,
@@ -90,8 +90,8 @@
         } else {
             var = &b.fRight->as<VariableReference>().fVariable;
         }
-        SkASSERT(var->fType.typeKind() == Type::TypeKind::kNullable &&
-                 var->fType.componentType() == *fContext.fFragmentProcessor_Type);
+        SkASSERT(var->type().typeKind() == Type::TypeKind::kNullable &&
+                 var->type().componentType() == *fContext.fFragmentProcessor_Type);
         this->write("%s");
         const char* op = "";
         switch (b.fOperator) {
@@ -128,7 +128,7 @@
     if (var.fModifiers.fLayout.fCType == SkSL::Layout::CType::kSkPMColor4f) {
         return "{SK_FloatNaN, SK_FloatNaN, SK_FloatNaN, SK_FloatNaN}";
     }
-    return default_value(var.fType);
+    return default_value(var.type());
 }
 
 static bool is_private(const Variable& var) {
@@ -141,7 +141,7 @@
 static bool is_uniform_in(const Variable& var) {
     return (var.fModifiers.fFlags & Modifiers::kUniform_Flag) &&
            (var.fModifiers.fFlags & Modifiers::kIn_Flag) &&
-           var.fType.typeKind() != Type::TypeKind::kSampler;
+           var.type().typeKind() != Type::TypeKind::kSampler;
 }
 
 String CPPCodeGenerator::formatRuntimeValue(const Type& type,
@@ -257,7 +257,7 @@
 
 void CPPCodeGenerator::writeVarInitializer(const Variable& var, const Expression& value) {
     if (is_private(var)) {
-        this->writeRuntimeValue(var.fType, var.fModifiers.fLayout, var.fName);
+        this->writeRuntimeValue(var.type(), var.fModifiers.fLayout, var.fName);
     } else {
         this->writeExpression(value, kTopLevel_Precedence);
     }
@@ -269,7 +269,7 @@
         if (&var == param) {
             return "args.fTexSamplers[" + to_string(samplerCount) + "]";
         }
-        if (param->fType.typeKind() == Type::TypeKind::kSampler) {
+        if (param->type().typeKind() == Type::TypeKind::kSampler) {
             ++samplerCount;
         }
     }
@@ -327,7 +327,7 @@
             this->write("sk_Height");
             break;
         default:
-            if (ref.fVariable.fType.typeKind() == Type::TypeKind::kSampler) {
+            if (ref.fVariable.type().typeKind() == Type::TypeKind::kSampler) {
                 this->write("%s");
                 fFormatArgs.push_back("fragBuilder->getProgramBuilder()->samplerVariable(" +
                                       this->getSamplerHandle(ref.fVariable) + ")");
@@ -343,14 +343,14 @@
                     code = String::printf("%sVar.isValid() ? %s : \"%s\"",
                                           HCodeGenerator::FieldName(name.c_str()).c_str(),
                                           var.c_str(),
-                                          default_value(ref.fVariable.fType).c_str());
+                                          default_value(ref.fVariable.type()).c_str());
                 } else {
                     code = var;
                 }
                 fFormatArgs.push_back(code);
             } else if (SectionAndParameterHelper::IsParameter(ref.fVariable)) {
                 String name(ref.fVariable.fName);
-                this->writeRuntimeValue(ref.fVariable.fType, ref.fVariable.fModifiers.fLayout,
+                this->writeRuntimeValue(ref.fVariable.type(), ref.fVariable.fModifiers.fLayout,
                                         String::printf("_outer.%s", name.c_str()).c_str());
             } else {
                 this->write(ref.fVariable.fName);
@@ -380,7 +380,7 @@
 }
 
 void CPPCodeGenerator::writeFieldAccess(const FieldAccess& access) {
-    if (access.fBase->fType.name() == "fragmentProcessor") {
+    if (access.fBase->type().name() == "fragmentProcessor") {
         // Special field access on fragment processors are converted into function calls on
         // GrFragmentProcessor's getters.
         if (access.fBase->kind() != Expression::Kind::kVariableReference) {
@@ -414,7 +414,7 @@
                 const VarDeclaration& decl = raw->as<VarDeclaration>();
                 if (decl.fVar == &var) {
                     found = true;
-                } else if (decl.fVar->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+                } else if (decl.fVar->type().nonnullable() == *fContext.fFragmentProcessor_Type) {
                     ++index;
                 }
             }
@@ -429,11 +429,11 @@
 
 void CPPCodeGenerator::writeFunctionCall(const FunctionCall& c) {
     if (c.fFunction.fBuiltin && c.fFunction.fName == "sample" &&
-        c.fArguments[0]->fType.typeKind() != Type::TypeKind::kSampler) {
+        c.fArguments[0]->type().typeKind() != Type::TypeKind::kSampler) {
         // Validity checks that are detected by function definition in sksl_fp.inc
         SkASSERT(c.fArguments.size() >= 1 && c.fArguments.size() <= 3);
-        SkASSERT("fragmentProcessor"  == c.fArguments[0]->fType.name() ||
-                 "fragmentProcessor?" == c.fArguments[0]->fType.name());
+        SkASSERT("fragmentProcessor"  == c.fArguments[0]->type().name() ||
+                 "fragmentProcessor?" == c.fArguments[0]->type().name());
 
         // Actually fail during compilation if arguments with valid types are
         // provided that are not variable references, since sample() is a
@@ -450,7 +450,7 @@
         this->newExtraEmitCodeBlock();
 
         String inputColor;
-        if (c.fArguments.size() > 1 && c.fArguments[1]->fType.name() == "half4") {
+        if (c.fArguments.size() > 1 && c.fArguments[1]->type().name() == "half4") {
             // Use the invokeChild() variant that accepts an input color, so convert the 2nd
             // argument's expression into C++ code that produces sksl stored in an SkString.
             String inputColorName = "_input" + to_string(c.fOffset);
@@ -462,12 +462,12 @@
 
         String inputCoord;
         String invokeFunction = "invokeChild";
-        if (c.fArguments.back()->fType.name() == "float2") {
+        if (c.fArguments.back()->type().name() == "float2") {
             // Invoking child with explicit coordinates at this call site
             inputCoord = "_coords" + to_string(c.fOffset);
             addExtraEmitCodeLine(convertSKSLExpressionToCPP(*c.fArguments.back(), inputCoord));
             inputCoord.append(".c_str()");
-        } else if (c.fArguments.back()->fType.name() == "float3x3") {
+        } else if (c.fArguments.back()->type().name() == "float3x3") {
             // Invoking child with a matrix, sampling relative to the input coords.
             invokeFunction = "invokeChildWithMatrix";
             SampleUsage usage = Analysis::GetSampleUsage(fProgram, child);
@@ -616,7 +616,7 @@
         const char* separator = "";
         for (const auto& param : decl.fParameters) {
             args += String(separator) + "GrShaderVar(\"" + param->fName + "\", " +
-                    glsltype_string(fContext, param->fType) + ")";
+                    glsltype_string(fContext, param->type()) + ")";
             separator = ", ";
         }
         args += "};";
@@ -681,19 +681,19 @@
         this->writef("        if (%s) {\n    ", String(var.fModifiers.fLayout.fWhen).c_str());
     }
     String name(var.fName);
-    if (var.fType.typeKind() != Type::TypeKind::kArray) {
+    if (var.type().typeKind() != Type::TypeKind::kArray) {
         this->writef("        %sVar = args.fUniformHandler->addUniform(&_outer, "
                      "kFragment_GrShaderFlag, %s, \"%s\");\n",
                      HCodeGenerator::FieldName(name.c_str()).c_str(),
-                     glsltype_string(fContext, var.fType),
+                     glsltype_string(fContext, var.type()),
                      name.c_str());
     } else {
         this->writef("        %sVar = args.fUniformHandler->addUniformArray(&_outer, "
                      "kFragment_GrShaderFlag, %s, \"%s\", %d);\n",
                      HCodeGenerator::FieldName(name.c_str()).c_str(),
-                     glsltype_string(fContext, var.fType.componentType()),
+                     glsltype_string(fContext, var.type().componentType()),
                      name.c_str(),
-                     var.fType.columns());
+                     var.type().columns());
     }
     if (var.fModifiers.fLayout.fWhen.fLength) {
         this->write("        }\n");
@@ -710,13 +710,13 @@
             for (const auto& raw : decls.fVars) {
                 VarDeclaration& decl = raw->as<VarDeclaration>();
                 if (is_private(*decl.fVar)) {
-                    if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
+                    if (decl.fVar->type() == *fContext.fFragmentProcessor_Type) {
                         fErrors.error(decl.fOffset,
                                       "fragmentProcessor variables must be declared 'in'");
                         return;
                     }
                     this->writef("%s %s = %s;\n",
-                                 HCodeGenerator::FieldType(fContext, decl.fVar->fType,
+                                 HCodeGenerator::FieldType(fContext, decl.fVar->type(),
                                                            decl.fVar->fModifiers.fLayout).c_str(),
                                  String(decl.fVar->fName).c_str(),
                                  default_value(*decl.fVar).c_str());
@@ -762,7 +762,7 @@
 }
 
 static bool is_accessible(const Variable& var) {
-    const Type& type = var.fType.nonnullable();
+    const Type& type = var.type().nonnullable();
     return Type::TypeKind::kSampler != type.typeKind() &&
            Type::TypeKind::kOther != type.typeKind();
 }
@@ -1040,7 +1040,7 @@
             if (needsValueDeclaration) {
                 valueVar.appendf("%sValue", name);
                 // Use AccessType since that will match the return type of _outer's public API.
-                String valueType = HCodeGenerator::AccessType(fContext, u->fType,
+                String valueType = HCodeGenerator::AccessType(fContext, u->type(),
                                                               u->fModifiers.fLayout);
                 this->writef("%s%s %s = _outer.%s;\n",
                              indent.c_str(), valueType.c_str(), valueVar.c_str(), name);
@@ -1085,7 +1085,7 @@
                     const Variable& variable = *decl.fVar;
                     String nameString(variable.fName);
                     const char* name = nameString.c_str();
-                    if (variable.fType.typeKind() == Type::TypeKind::kSampler) {
+                    if (variable.type().typeKind() == Type::TypeKind::kSampler) {
                         this->writef("        const GrSurfaceProxyView& %sView = "
                                      "_outer.textureSampler(%d).view();\n",
                                      name, samplerIndex);
@@ -1098,14 +1098,14 @@
                                      "        (void) %s;\n",
                                      name, HCodeGenerator::FieldName(name).c_str(), name);
                     } else if (SectionAndParameterHelper::IsParameter(variable) &&
-                               variable.fType != *fContext.fFragmentProcessor_Type) {
+                               variable.type() != *fContext.fFragmentProcessor_Type) {
                         if (!wroteProcessor) {
                             this->writef("        const %s& _outer = _proc.cast<%s>();\n", fullName,
                                          fullName);
                             wroteProcessor = true;
                         }
 
-                        if (variable.fType.nonnullable() != *fContext.fFragmentProcessor_Type) {
+                        if (variable.type().nonnullable() != *fContext.fFragmentProcessor_Type) {
                             this->writef("        auto %s = _outer.%s;\n"
                                          "        (void) %s;\n",
                                          name, name, name);
@@ -1122,7 +1122,7 @@
 void CPPCodeGenerator::writeOnTextureSampler() {
     bool foundSampler = false;
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-        if (param->fType.typeKind() == Type::TypeKind::kSampler) {
+        if (param->type().typeKind() == Type::TypeKind::kSampler) {
             if (!foundSampler) {
                 this->writef(
                         "const GrFragmentProcessor::TextureSampler& %s::onTextureSampler(int "
@@ -1153,7 +1153,7 @@
                      fFullName.c_str(), fFullName.c_str(), fFullName.c_str());
         for (const Variable* param : fSectionAndParameterHelper.getParameters()) {
             String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
-            if (param->fType.nonnullable() != *fContext.fFragmentProcessor_Type) {
+            if (param->type().nonnullable() != *fContext.fFragmentProcessor_Type) {
                 this->writef("\n, %s(src.%s)",
                              fieldName.c_str(),
                              fieldName.c_str());
@@ -1163,7 +1163,7 @@
         this->writef("        this->cloneAndRegisterAllChildProcessors(src);\n");
         int samplerCount = 0;
         for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-            if (param->fType.typeKind() == Type::TypeKind::kSampler) {
+            if (param->type().typeKind() == Type::TypeKind::kSampler) {
                 ++samplerCount;
             }
         }
@@ -1197,14 +1197,16 @@
 
         for (const Variable* param : fSectionAndParameterHelper.getParameters()) {
             // dumpInfo() doesn't need to log child FPs.
-            if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+            if (param->type().nonnullable() == *fContext.fFragmentProcessor_Type) {
                 continue;
             }
 
             // Add this field onto the format string and argument list.
             String fieldName = HCodeGenerator::FieldName(String(param->fName).c_str());
-            String runtimeValue = this->formatRuntimeValue(param->fType, param->fModifiers.fLayout,
-                                                           param->fName, &argumentList);
+            String runtimeValue = this->formatRuntimeValue(param->type(),
+                                                           param->fModifiers.fLayout,
+                                                           param->fName,
+                                                           &argumentList);
             formatString.appendf("%s%s=%s",
                                  formatString.empty() ? "" : ", ",
                                  fieldName.c_str(),
@@ -1257,6 +1259,7 @@
             for (const auto& raw : decls.fVars) {
                 const VarDeclaration& decl = raw->as<VarDeclaration>();
                 const Variable& var = *decl.fVar;
+                const Type& varType = var.type();
                 String nameString(var.fName);
                 const char* name = nameString.c_str();
                 if (var.fModifiers.fLayout.fKey != Layout::kNo_Key &&
@@ -1268,7 +1271,7 @@
                     case Layout::kKey_Key:
                         if (is_private(var)) {
                             this->writef("%s %s =",
-                                         HCodeGenerator::FieldType(fContext, var.fType,
+                                         HCodeGenerator::FieldType(fContext, varType,
                                                                    var.fModifiers.fLayout).c_str(),
                                          String(var.fName).c_str());
                             if (decl.fValue) {
@@ -1283,7 +1286,7 @@
                         if (var.fModifiers.fLayout.fWhen.fLength) {
                             this->writef("if (%s) {", String(var.fModifiers.fLayout.fWhen).c_str());
                         }
-                        if (var.fType == *fContext.fHalf4_Type) {
+                        if (varType == *fContext.fHalf4_Type) {
                             this->writef("    uint16_t red = SkFloatToHalf(%s.fR);\n",
                                          HCodeGenerator::FieldName(name).c_str());
                             this->writef("    uint16_t green = SkFloatToHalf(%s.fG);\n",
@@ -1294,24 +1297,24 @@
                                          HCodeGenerator::FieldName(name).c_str());
                             this->write("    b->add32(((uint32_t)red << 16) | green);\n");
                             this->write("    b->add32(((uint32_t)blue << 16) | alpha);\n");
-                        } else if (var.fType == *fContext.fHalf_Type ||
-                                   var.fType == *fContext.fFloat_Type) {
+                        } else if (varType == *fContext.fHalf_Type ||
+                                   varType == *fContext.fFloat_Type) {
                             this->writef("    b->add32(sk_bit_cast<uint32_t>(%s));\n",
                                          HCodeGenerator::FieldName(name).c_str());
-                        } else if (var.fType.isInteger() || var.fType == *fContext.fBool_Type ||
-                                   var.fType.typeKind() == Type::TypeKind::kEnum) {
+                        } else if (varType.isInteger() || varType == *fContext.fBool_Type ||
+                                   varType.typeKind() == Type::TypeKind::kEnum) {
                             this->writef("    b->add32((uint32_t) %s);\n",
                                          HCodeGenerator::FieldName(name).c_str());
                         } else {
                             ABORT("NOT YET IMPLEMENTED: automatic key handling for %s\n",
-                                  var.fType.displayName().c_str());
+                                  varType.displayName().c_str());
                         }
                         if (var.fModifiers.fLayout.fWhen.fLength) {
                             this->write("}");
                         }
                         break;
                     case Layout::kIdentity_Key:
-                        if (var.fType.typeKind() != Type::TypeKind::kMatrix) {
+                        if (varType.typeKind() != Type::TypeKind::kMatrix) {
                             fErrors.error(var.fOffset,
                                           "layout(key=identity) requires matrix type");
                         }
@@ -1335,7 +1338,7 @@
             for (const auto& raw : decls.fVars) {
                 VarDeclaration& decl = raw->as<VarDeclaration>();
                 if ((decl.fVar->fModifiers.fFlags & Modifiers::kUniform_Flag) &&
-                           decl.fVar->fType.typeKind() != Type::TypeKind::kSampler) {
+                           decl.fVar->type().typeKind() != Type::TypeKind::kSampler) {
                     uniforms.push_back(decl.fVar);
                 }
 
@@ -1411,7 +1414,7 @@
                  "    (void) that;\n",
                  fullName, fullName, fullName);
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-        if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+        if (param->type().nonnullable() == *fContext.fFragmentProcessor_Type) {
             continue;
         }
         String nameString(param->fName);
diff --git a/src/sksl/SkSLCPPUniformCTypes.h b/src/sksl/SkSLCPPUniformCTypes.h
index c6d3686..79ea4ac 100644
--- a/src/sksl/SkSLCPPUniformCTypes.h
+++ b/src/sksl/SkSLCPPUniformCTypes.h
@@ -50,7 +50,7 @@
                                          const Layout& layout);
 
     static const UniformCTypeMapper* Get(const Context& context, const Variable& variable) {
-        return Get(context, variable.fType, variable.fModifiers.fLayout);
+        return Get(context, variable.type(), variable.fModifiers.fLayout);
     }
 
     // The C++ type name that this mapper applies to
diff --git a/src/sksl/SkSLCompiler.cpp b/src/sksl/SkSLCompiler.cpp
index 31737d5..749815f 100644
--- a/src/sksl/SkSLCompiler.cpp
+++ b/src/sksl/SkSLCompiler.cpp
@@ -237,7 +237,7 @@
     fIRGenerator->fSymbolTable->add(
             skCapsName,
             std::make_unique<Variable>(/*offset=*/-1, Modifiers(), skCapsName,
-                                       *fContext->fSkCaps_Type, Variable::kGlobal_Storage));
+                                       fContext->fSkCaps_Type.get(), Variable::kGlobal_Storage));
 
     fIRGenerator->fIntrinsics = fGPUIntrinsics.get();
     std::vector<std::unique_ptr<ProgramElement>> gpuIntrinsics;
@@ -660,12 +660,13 @@
         case Expression::Kind::kConstructor: {
             const Constructor& constructor = expr.as<Constructor>();
             if (constructor.isCompileTimeConstant()) {
-                bool isFloat = constructor.fType.columns() > 1
-                                       ? constructor.fType.componentType().isFloat()
-                                       : constructor.fType.isFloat();
-                switch (constructor.fType.typeKind()) {
+                const Type& constructorType = constructor.type();
+                bool isFloat = constructorType.columns() > 1
+                                       ? constructorType.componentType().isFloat()
+                                       : constructorType.isFloat();
+                switch (constructorType.typeKind()) {
                     case Type::TypeKind::kVector:
-                        for (int i = 0; i < constructor.fType.columns(); ++i) {
+                        for (int i = 0; i < constructorType.columns(); ++i) {
                             if (isFloat) {
                                 if (constructor.getFVecComponent(i) != value) {
                                     return false;
@@ -765,7 +766,7 @@
 /**
  * Constructs the specified type using a single argument.
  */
-static std::unique_ptr<Expression> construct(const Type& type, std::unique_ptr<Expression> v) {
+static std::unique_ptr<Expression> construct(const Type* type, std::unique_ptr<Expression> v) {
     std::vector<std::unique_ptr<Expression>> args;
     args.push_back(std::move(v));
     std::unique_ptr<Expression> result = std::make_unique<Constructor>(-1, type, std::move(args));
@@ -784,14 +785,14 @@
                       bool* outNeedsRescan) {
     SkASSERT((*(*iter)->expression())->kind() == Expression::Kind::kBinary);
     SkASSERT(type.typeKind() == Type::TypeKind::kVector);
-    SkASSERT((*otherExpression)->fType.typeKind() == Type::TypeKind::kScalar);
+    SkASSERT((*otherExpression)->type().typeKind() == Type::TypeKind::kScalar);
     *outUpdated = true;
     std::unique_ptr<Expression>* target = (*iter)->expression();
     if (!b->tryRemoveExpression(iter)) {
-        *target = construct(type, std::move(*otherExpression));
+        *target = construct(&type, std::move(*otherExpression));
         *outNeedsRescan = true;
     } else {
-        *target = construct(type, std::move(*otherExpression));
+        *target = construct(&type, std::move(*otherExpression));
         if (!b->tryInsertExpression(iter, target)) {
             *outNeedsRescan = true;
         }
@@ -807,7 +808,7 @@
                            bool* outUpdated,
                            bool* outNeedsRescan) {
     BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
-    vectorize(b, iter, bin.fRight->fType, &bin.fLeft, outUpdated, outNeedsRescan);
+    vectorize(b, iter, bin.fRight->type(), &bin.fLeft, outUpdated, outNeedsRescan);
 }
 
 /**
@@ -819,7 +820,7 @@
                             bool* outUpdated,
                             bool* outNeedsRescan) {
     BinaryExpression& bin = (*(*iter)->expression())->as<BinaryExpression>();
-    vectorize(b, iter, bin.fLeft->fType, &bin.fRight, outUpdated, outNeedsRescan);
+    vectorize(b, iter, bin.fLeft->type(), &bin.fRight, outUpdated, outNeedsRescan);
 }
 
 // Mark that an expression which we were writing to is no longer being written to
@@ -856,7 +857,7 @@
         std::unique_ptr<Expression> optimized = expr->constantPropagate(*fIRGenerator, definitions);
         if (optimized) {
             *outUpdated = true;
-            optimized = fIRGenerator->coerce(std::move(optimized), expr->fType);
+            optimized = fIRGenerator->coerce(std::move(optimized), expr->type());
             SkASSERT(optimized);
             if (!try_replace_expression(&b, iter, &optimized)) {
                 *outNeedsRescan = true;
@@ -901,18 +902,20 @@
                 delete_left(&b, iter, outUpdated, outNeedsRescan);
                 break;
             }
+            const Type& leftType = bin->fLeft->type();
+            const Type& rightType = bin->fRight->type();
             // collapse useless expressions like x * 1 or x + 0
-            if (((bin->fLeft->fType.typeKind() != Type::TypeKind::kScalar) &&
-                 (bin->fLeft->fType.typeKind() != Type::TypeKind::kVector)) ||
-                ((bin->fRight->fType.typeKind() != Type::TypeKind::kScalar) &&
-                 (bin->fRight->fType.typeKind() != Type::TypeKind::kVector))) {
+            if (((leftType.typeKind() != Type::TypeKind::kScalar) &&
+                 (leftType.typeKind() != Type::TypeKind::kVector)) ||
+                ((rightType.typeKind() != Type::TypeKind::kScalar) &&
+                 (rightType.typeKind() != Type::TypeKind::kVector))) {
                 break;
             }
             switch (bin->fOperator) {
                 case Token::Kind::TK_STAR:
                     if (is_constant(*bin->fLeft, 1)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kVector &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kScalar) {
+                        if (leftType.typeKind() == Type::TypeKind::kVector &&
+                            rightType.typeKind() == Type::TypeKind::kScalar) {
                             // float4(1) * x -> float4(x)
                             vectorize_right(&b, iter, outUpdated, outNeedsRescan);
                         } else {
@@ -923,8 +926,8 @@
                         }
                     }
                     else if (is_constant(*bin->fLeft, 0)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kScalar &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kVector &&
+                        if (leftType.typeKind() == Type::TypeKind::kScalar &&
+                            rightType.typeKind() == Type::TypeKind::kVector &&
                             !bin->fRight->hasSideEffects()) {
                             // 0 * float4(x) -> float4(0)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
@@ -938,8 +941,8 @@
                         }
                     }
                     else if (is_constant(*bin->fRight, 1)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kScalar &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kVector) {
+                        if (leftType.typeKind() == Type::TypeKind::kScalar &&
+                            rightType.typeKind() == Type::TypeKind::kVector) {
                             // x * float4(1) -> float4(x)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
                         } else {
@@ -950,8 +953,8 @@
                         }
                     }
                     else if (is_constant(*bin->fRight, 0)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kVector &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kScalar &&
+                        if (leftType.typeKind() == Type::TypeKind::kVector &&
+                            rightType.typeKind() == Type::TypeKind::kScalar &&
                             !bin->fLeft->hasSideEffects()) {
                             // float4(x) * 0 -> float4(0)
                             vectorize_right(&b, iter, outUpdated, outNeedsRescan);
@@ -967,8 +970,8 @@
                     break;
                 case Token::Kind::TK_PLUS:
                     if (is_constant(*bin->fLeft, 0)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kVector &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kScalar) {
+                        if (leftType.typeKind() == Type::TypeKind::kVector &&
+                            rightType.typeKind() == Type::TypeKind::kScalar) {
                             // float4(0) + x -> float4(x)
                             vectorize_right(&b, iter, outUpdated, outNeedsRescan);
                         } else {
@@ -978,8 +981,8 @@
                             delete_left(&b, iter, outUpdated, outNeedsRescan);
                         }
                     } else if (is_constant(*bin->fRight, 0)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kScalar &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kVector) {
+                        if (leftType.typeKind() == Type::TypeKind::kScalar &&
+                            rightType.typeKind() == Type::TypeKind::kVector) {
                             // x + float4(0) -> float4(x)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
                         } else {
@@ -992,8 +995,8 @@
                     break;
                 case Token::Kind::TK_MINUS:
                     if (is_constant(*bin->fRight, 0)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kScalar &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kVector) {
+                        if (leftType.typeKind() == Type::TypeKind::kScalar &&
+                            rightType.typeKind() == Type::TypeKind::kVector) {
                             // x - float4(0) -> float4(x)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
                         } else {
@@ -1006,8 +1009,8 @@
                     break;
                 case Token::Kind::TK_SLASH:
                     if (is_constant(*bin->fRight, 1)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kScalar &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kVector) {
+                        if (leftType.typeKind() == Type::TypeKind::kScalar &&
+                            rightType.typeKind() == Type::TypeKind::kVector) {
                             // x / float4(1) -> float4(x)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
                         } else {
@@ -1017,8 +1020,8 @@
                             delete_right(&b, iter, outUpdated, outNeedsRescan);
                         }
                     } else if (is_constant(*bin->fLeft, 0)) {
-                        if (bin->fLeft->fType.typeKind() == Type::TypeKind::kScalar &&
-                            bin->fRight->fType.typeKind() == Type::TypeKind::kVector &&
+                        if (leftType.typeKind() == Type::TypeKind::kScalar &&
+                            rightType.typeKind() == Type::TypeKind::kVector &&
                             !bin->fRight->hasSideEffects()) {
                             // 0 / float4(x) -> float4(0)
                             vectorize_left(&b, iter, outUpdated, outNeedsRescan);
@@ -1064,7 +1067,7 @@
         case Expression::Kind::kSwizzle: {
             Swizzle& s = expr->as<Swizzle>();
             // detect identity swizzles like foo.rgba
-            if ((int) s.fComponents.size() == s.fBase->fType.columns()) {
+            if ((int) s.fComponents.size() == s.fBase->type().columns()) {
                 bool identity = true;
                 for (int i = 0; i < (int) s.fComponents.size(); ++i) {
                     if (s.fComponents[i] != i) {
diff --git a/src/sksl/SkSLContext.h b/src/sksl/SkSLContext.h
index 992d74c..e7208ea 100644
--- a/src/sksl/SkSLContext.h
+++ b/src/sksl/SkSLContext.h
@@ -186,7 +186,7 @@
                                      fBool3_Type.get(), fBool4_Type.get() }))
     , fSkCaps_Type(new Type("$sk_Caps"))
     , fFragmentProcessor_Type(fp_type(fInt_Type.get(), fBool_Type.get()))
-    , fDefined_Expression(new Defined(*fInvalid_Type)) {}
+    , fDefined_Expression(new Defined(fInvalid_Type.get())) {}
 
     static std::vector<const Type*> static_type(const Type& t) {
         return { &t, &t, &t, &t };
@@ -353,7 +353,7 @@
     public:
         static constexpr Kind kExpressionKind = Kind::kDefined;
 
-        Defined(const Type& type)
+        Defined(const Type* type)
         : INHERITED(-1, kExpressionKind, type) {}
 
         bool hasProperty(Property property) const override {
@@ -365,7 +365,7 @@
         }
 
         std::unique_ptr<Expression> clone() const override {
-            return std::unique_ptr<Expression>(new Defined(fType));
+            return std::unique_ptr<Expression>(new Defined(&this->type()));
         }
 
         using INHERITED = Expression;
diff --git a/src/sksl/SkSLDehydrator.cpp b/src/sksl/SkSLDehydrator.cpp
index b9b0646..0035a21 100644
--- a/src/sksl/SkSLDehydrator.cpp
+++ b/src/sksl/SkSLDehydrator.cpp
@@ -210,7 +210,7 @@
             this->writeId(&v);
             this->write(v.fModifiers);
             this->write(v.fName);
-            this->write(v.fType);
+            this->write(v.type());
             this->writeU8(v.fStorage);
             break;
         }
@@ -261,7 +261,7 @@
                 this->write(b.fLeft.get());
                 this->writeU8((int) b.fOperator);
                 this->write(b.fRight.get());
-                this->write(b.fType);
+                this->write(b.type());
                 break;
             }
             case Expression::Kind::kBoolLiteral: {
@@ -273,7 +273,7 @@
             case Expression::Kind::kConstructor: {
                 const Constructor& c = e->as<Constructor>();
                 this->writeU8(Rehydrator::kConstructor_Command);
-                this->write(c.fType);
+                this->write(c.type());
                 this->writeU8(c.fArguments.size());
                 for (const auto& a : c.fArguments) {
                     this->write(a.get());
@@ -305,7 +305,7 @@
             case Expression::Kind::kFunctionCall: {
                 const FunctionCall& f = e->as<FunctionCall>();
                 this->writeU8(Rehydrator::kFunctionCall_Command);
-                this->write(f.fType);
+                this->write(f.type());
                 this->writeId(&f.fFunction);
                 this->writeU8(f.fArguments.size());
                 for (const auto& a : f.fArguments) {
diff --git a/src/sksl/SkSLGLSLCodeGenerator.cpp b/src/sksl/SkSLGLSLCodeGenerator.cpp
index 92c3a87..8863901 100644
--- a/src/sksl/SkSLGLSLCodeGenerator.cpp
+++ b/src/sksl/SkSLGLSLCodeGenerator.cpp
@@ -251,10 +251,10 @@
     SkASSERT(!fProgram.fSettings.fCaps->canUseMinAndAbsTogether());
     String tmpVar1 = "minAbsHackVar" + to_string(fVarCount++);
     String tmpVar2 = "minAbsHackVar" + to_string(fVarCount++);
-    this->fFunctionHeader += String("    ") + this->getTypePrecision(absExpr.fType) +
-                             this->getTypeName(absExpr.fType) + " " + tmpVar1 + ";\n";
-    this->fFunctionHeader += String("    ") + this->getTypePrecision(otherExpr.fType) +
-                             this->getTypeName(otherExpr.fType) + " " + tmpVar2 + ";\n";
+    this->fFunctionHeader += String("    ") + this->getTypePrecision(absExpr.type()) +
+                             this->getTypeName(absExpr.type()) + " " + tmpVar1 + ";\n";
+    this->fFunctionHeader += String("    ") + this->getTypePrecision(otherExpr.type()) +
+                             this->getTypeName(otherExpr.type()) + " " + tmpVar2 + ";\n";
     this->write("((" + tmpVar1 + " = ");
     this->writeExpression(absExpr, kTopLevel_Precedence);
     this->write(") < (" + tmpVar2 + " = ");
@@ -270,7 +270,8 @@
 
 void GLSLCodeGenerator::writeDeterminantHack(const Expression& mat) {
     String name;
-    if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
+    const Type& type = mat.type();
+    if (type == *fContext.fFloat2x2_Type || type == *fContext.fHalf2x2_Type) {
         name = "_determinant2";
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
@@ -281,7 +282,7 @@
             ).c_str());
         }
     }
-    else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
+    else if (type == *fContext.fFloat3x3_Type || type == *fContext.fHalf3x3_Type) {
         name = "_determinant3";
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
@@ -298,7 +299,7 @@
             ).c_str());
         }
     }
-    else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
+    else if (type == *fContext.fFloat4x4_Type || type == *fContext.fHalf4x4_Type) {
         name = "_determinant3";
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
@@ -335,7 +336,8 @@
 
 void GLSLCodeGenerator::writeInverseHack(const Expression& mat) {
     String name;
-    if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
+    const Type& type = mat.type();
+    if (type == *fContext.fFloat2x2_Type || type == *fContext.fHalf2x2_Type) {
         name = "_inverse2";
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
@@ -347,7 +349,7 @@
             ).c_str());
         }
     }
-    else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
+    else if (type == *fContext.fFloat3x3_Type || type == *fContext.fHalf3x3_Type) {
         name = "_inverse3";
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
@@ -367,7 +369,7 @@
             ).c_str());
         }
     }
-    else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
+    else if (type == *fContext.fFloat4x4_Type || type == *fContext.fHalf4x4_Type) {
         name = "_inverse4";
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
@@ -421,19 +423,20 @@
 }
 
 void GLSLCodeGenerator::writeTransposeHack(const Expression& mat) {
-    String name = "transpose" + to_string(mat.fType.columns()) + to_string(mat.fType.rows());
+    const Type& type = mat.type();
+    String name = "transpose" + to_string(type.columns()) + to_string(type.rows());
     if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
         fWrittenIntrinsics.insert(name);
-        String type = this->getTypeName(mat.fType);
-        const Type& base = mat.fType.componentType();
+        String typeName = this->getTypeName(type);
+        const Type& base = type.componentType();
         String transposed =  this->getTypeName(base.toCompound(fContext,
-                                                               mat.fType.rows(),
-                                                               mat.fType.columns()));
-        fExtraFunctions.writeText((transposed + " " + name + "(" + type + " m) {\nreturn " +
+                                                               type.rows(),
+                                                               type.columns()));
+        fExtraFunctions.writeText((transposed + " " + name + "(" + typeName + " m) {\nreturn " +
                                   transposed + "(").c_str());
         const char* separator = "";
-        for (int row = 0; row < mat.fType.rows(); ++row) {
-            for (int column = 0; column < mat.fType.columns(); ++column) {
+        for (int row = 0; row < type.rows(); ++row) {
+            for (int column = 0; column < type.columns(); ++column) {
                 fExtraFunctions.writeText(separator);
                 fExtraFunctions.writeText(("m[" + to_string(column) + "][" + to_string(row) +
                                            "]").c_str());
@@ -487,7 +490,7 @@
                 if (!fProgram.fSettings.fCaps->emulateAbsIntFunction())
                     break;
                 SkASSERT(c.fArguments.size() == 1);
-                if (c.fArguments[0]->fType != *fContext.fInt_Type)
+                if (c.fArguments[0]->type() != *fContext.fInt_Type)
                   break;
                 // abs(int) on Intel OSX is incorrect, so emulate it:
                 String name = "_absemulation";
@@ -618,36 +621,38 @@
             case FunctionClass::kTexture: {
                 const char* dim = "";
                 bool proj = false;
-                switch (c.fArguments[0]->fType.dimensions()) {
+                const Type& arg0Type = c.fArguments[0]->type();
+                const Type& arg1Type = c.fArguments[1]->type();
+                switch (arg0Type.dimensions()) {
                     case SpvDim1D:
                         dim = "1D";
                         isTextureFunctionWithBias = true;
-                        if (c.fArguments[1]->fType == *fContext.fFloat_Type) {
+                        if (arg1Type == *fContext.fFloat_Type) {
                             proj = false;
                         } else {
-                            SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
+                            SkASSERT(arg1Type == *fContext.fFloat2_Type);
                             proj = true;
                         }
                         break;
                     case SpvDim2D:
                         dim = "2D";
-                        if (c.fArguments[0]->fType != *fContext.fSamplerExternalOES_Type) {
+                        if (arg0Type != *fContext.fSamplerExternalOES_Type) {
                             isTextureFunctionWithBias = true;
                         }
-                        if (c.fArguments[1]->fType == *fContext.fFloat2_Type) {
+                        if (arg1Type == *fContext.fFloat2_Type) {
                             proj = false;
                         } else {
-                            SkASSERT(c.fArguments[1]->fType == *fContext.fFloat3_Type);
+                            SkASSERT(arg1Type == *fContext.fFloat3_Type);
                             proj = true;
                         }
                         break;
                     case SpvDim3D:
                         dim = "3D";
                         isTextureFunctionWithBias = true;
-                        if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
+                        if (arg1Type == *fContext.fFloat3_Type) {
                             proj = false;
                         } else {
-                            SkASSERT(c.fArguments[1]->fType == *fContext.fFloat4_Type);
+                            SkASSERT(arg1Type == *fContext.fFloat4_Type);
                             proj = true;
                         }
                         break;
@@ -712,16 +717,16 @@
 
 void GLSLCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
     if (c.fArguments.size() == 1 &&
-        (this->getTypeName(c.fType) == this->getTypeName(c.fArguments[0]->fType) ||
-        (c.fType.typeKind() == Type::TypeKind::kScalar &&
-         c.fArguments[0]->fType == *fContext.fFloatLiteral_Type))) {
+        (this->getTypeName(c.type()) == this->getTypeName(c.fArguments[0]->type()) ||
+        (c.type().typeKind() == Type::TypeKind::kScalar &&
+         c.fArguments[0]->type() == *fContext.fFloatLiteral_Type))) {
         // in cases like half(float), they're different types as far as SkSL is concerned but the
         // same type as far as GLSL is concerned. We avoid a redundant float(float) by just writing
         // out the inner expression here.
         this->writeExpression(*c.fArguments[0], parentPrecedence);
         return;
     }
-    this->writeType(c.fType);
+    this->writeType(c.type());
     this->write("(");
     const char* separator = "";
     for (const auto& arg : c.fArguments) {
@@ -832,7 +837,7 @@
 }
 
 bool is_sk_position(const FieldAccess& f) {
-    return "sk_Position" == f.fBase->fType.fields()[f.fFieldIndex].fName;
+    return "sk_Position" == f.fBase->type().fields()[f.fFieldIndex].fName;
 }
 
 void GLSLCodeGenerator::writeFieldAccess(const FieldAccess& f) {
@@ -840,24 +845,25 @@
         this->writeExpression(*f.fBase, kPostfix_Precedence);
         this->write(".");
     }
-    switch (f.fBase->fType.fields()[f.fFieldIndex].fModifiers.fLayout.fBuiltin) {
+    const Type& baseType = f.fBase->type();
+    switch (baseType.fields()[f.fFieldIndex].fModifiers.fLayout.fBuiltin) {
         case SK_CLIPDISTANCE_BUILTIN:
             this->write("gl_ClipDistance");
             break;
         default:
-            StringFragment name = f.fBase->fType.fields()[f.fFieldIndex].fName;
+            StringFragment name = baseType.fields()[f.fFieldIndex].fName;
             if (name == "sk_Position") {
                 this->write("gl_Position");
             } else if (name == "sk_PointSize") {
                 this->write("gl_PointSize");
             } else {
-                this->write(f.fBase->fType.fields()[f.fFieldIndex].fName);
+                this->write(baseType.fields()[f.fFieldIndex].fName);
             }
     }
 }
 
 void GLSLCodeGenerator::writeConstantSwizzle(const Swizzle& swizzle, const String& constants) {
-    this->writeType(swizzle.fType);
+    this->writeType(swizzle.type());
     this->write("(");
     this->write(constants);
     this->write(")");
@@ -872,7 +878,7 @@
 void GLSLCodeGenerator::writeSwizzleConstructor(const Swizzle& swizzle, const String& constants,
                                                 const String& mask,
                                                 GLSLCodeGenerator::SwizzleOrder order) {
-    this->writeType(swizzle.fType);
+    this->writeType(swizzle.type());
     this->write("(");
     if (order == SwizzleOrder::CONSTANTS_FIRST) {
         this->write(constants);
@@ -1190,11 +1196,12 @@
 }
 
 void GLSLCodeGenerator::writeIntLiteral(const IntLiteral& i) {
-    if (i.fType == *fContext.fUInt_Type) {
+    const Type& type = i.type();
+    if (type == *fContext.fUInt_Type) {
         this->write(to_string(i.fValue & 0xffffffff) + "u");
-    } else if (i.fType == *fContext.fUShort_Type) {
+    } else if (type == *fContext.fUShort_Type) {
         this->write(to_string(i.fValue & 0xffff) + "u");
-    } else if (i.fType == *fContext.fUByte_Type) {
+    } else if (type == *fContext.fUByte_Type) {
         this->write(to_string(i.fValue & 0xff) + "u");
     } else {
         this->write(to_string((int32_t) i.fValue));
@@ -1226,7 +1233,7 @@
         separator = ", ";
         this->writeModifiers(param->fModifiers, false);
         std::vector<int> sizes;
-        const Type* type = &param->fType;
+        const Type* type = &param->type();
         while (type->typeKind() == Type::TypeKind::kArray) {
             sizes.push_back(type->columns());
             type = &type->componentType();
@@ -1349,7 +1356,7 @@
     this->writeModifiers(intf.fVariable.fModifiers, true);
     this->writeLine(intf.fTypeName + " {");
     fIndentation++;
-    const Type* structType = &intf.fVariable.fType;
+    const Type* structType = &intf.fVariable.type();
     while (structType->typeKind() == Type::TypeKind::kArray) {
         structType = &structType->componentType();
     }
@@ -1441,7 +1448,7 @@
             this->write(" = ");
             this->writeVarInitializer(*var.fVar, *var.fValue);
         }
-        if (!fFoundExternalSamplerDecl && var.fVar->fType == *fContext.fSamplerExternalOES_Type) {
+        if (!fFoundExternalSamplerDecl && var.fVar->type() == *fContext.fSamplerExternalOES_Type) {
             if (fProgram.fSettings.fCaps->externalTextureExtensionString()) {
                 this->writeExtension(fProgram.fSettings.fCaps->externalTextureExtensionString());
             }
@@ -1451,7 +1458,7 @@
             }
             fFoundExternalSamplerDecl = true;
         }
-        if (!fFoundRectSamplerDecl && var.fVar->fType == *fContext.fSampler2DRect_Type) {
+        if (!fFoundRectSamplerDecl && var.fVar->type() == *fContext.fSampler2DRect_Type) {
             fFoundRectSamplerDecl = true;
         }
     }
@@ -1555,7 +1562,7 @@
             std::unique_ptr<Expression> and_true(new BinaryExpression(
                     -1, f.fTest->clone(), Token::Kind::TK_LOGICALAND,
                     std::make_unique<BoolLiteral>(fContext, -1, true),
-                    *fContext.fBool_Type));
+                    fContext.fBool_Type.get()));
             this->writeExpression(*and_true, kTopLevel_Precedence);
         } else {
             this->writeExpression(*f.fTest, kTopLevel_Precedence);
diff --git a/src/sksl/SkSLHCodeGenerator.cpp b/src/sksl/SkSLHCodeGenerator.cpp
index 8841435..e5bdd94 100644
--- a/src/sksl/SkSLHCodeGenerator.cpp
+++ b/src/sksl/SkSLHCodeGenerator.cpp
@@ -189,7 +189,7 @@
         this->writef("    static std::unique_ptr<GrFragmentProcessor> Make(");
         separator = "";
         for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-            this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
+            this->writef("%s%s %s", separator, ParameterType(fContext, param->type(),
                                                              param->fModifiers.fLayout).c_str(),
                          String(param->fName).c_str());
             separator = ", ";
@@ -200,8 +200,8 @@
                      fFullName.c_str());
         separator = "";
         for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-            if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type ||
-                param->fType.nonnullable().typeKind() == Type::TypeKind::kSampler) {
+            if (param->type().nonnullable() == *fContext.fFragmentProcessor_Type ||
+                param->type().nonnullable().typeKind() == Type::TypeKind::kSampler) {
                 this->writef("%sstd::move(%s)", separator, String(param->fName).c_str());
             } else {
                 this->writef("%s%s", separator, String(param->fName).c_str());
@@ -233,7 +233,7 @@
     this->writef("    %s(", fFullName.c_str());
     const char* separator = "";
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-        this->writef("%s%s %s", separator, ParameterType(fContext, param->fType,
+        this->writef("%s%s %s", separator, ParameterType(fContext, param->type(),
                                                          param->fModifiers.fLayout).c_str(),
                      String(param->fName).c_str());
         separator = ", ";
@@ -249,7 +249,7 @@
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         String nameString(param->fName);
         const char* name = nameString.c_str();
-        const Type& type = param->fType.nonnullable();
+        const Type& type = param->type().nonnullable();
         if (type.typeKind() == Type::TypeKind::kSampler) {
             this->writef("\n    , %s(std::move(%s)", FieldName(name).c_str(), name);
             for (const Section* s : fSectionAndParameterHelper.getSections(
@@ -274,10 +274,11 @@
 
     int samplerCount = 0;
     for (const Variable* param : fSectionAndParameterHelper.getParameters()) {
-        if (param->fType.typeKind() == Type::TypeKind::kSampler) {
+        const Type& paramType = param->type();
+        if (paramType.typeKind() == Type::TypeKind::kSampler) {
             ++samplerCount;
-        } else if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
-            if (param->fType.typeKind() != Type::TypeKind::kNullable) {
+        } else if (paramType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+            if (paramType.typeKind() != Type::TypeKind::kNullable) {
                 this->writef("        SkASSERT(%s);", String(param->fName).c_str());
             }
 
@@ -310,10 +311,10 @@
     this->writeSection(kFieldsSection);
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
         String name = FieldName(String(param->fName).c_str());
-        if (param->fType.nonnullable() == *fContext.fFragmentProcessor_Type) {
+        if (param->type().nonnullable() == *fContext.fFragmentProcessor_Type) {
             // Don't need to write any fields, FPs are held as children
         } else {
-            this->writef("    %s %s;\n", FieldType(fContext, param->fType,
+            this->writef("    %s %s;\n", FieldType(fContext, param->type(),
                                                    param->fModifiers.fLayout).c_str(),
                                          name.c_str());
         }
@@ -374,7 +375,7 @@
                                                 "GrProcessorKeyBuilder*) const override;\n"
                  "    bool onIsEqual(const GrFragmentProcessor&) const override;\n");
     for (const auto& param : fSectionAndParameterHelper.getParameters()) {
-        if (param->fType.typeKind() == Type::TypeKind::kSampler) {
+        if (param->type().typeKind() == Type::TypeKind::kSampler) {
             this->writef("    const TextureSampler& onTextureSampler(int) const override;");
             break;
         }
diff --git a/src/sksl/SkSLIRGenerator.cpp b/src/sksl/SkSLIRGenerator.cpp
index c12b7ed..d2dec9b 100644
--- a/src/sksl/SkSLIRGenerator.cpp
+++ b/src/sksl/SkSLIRGenerator.cpp
@@ -442,11 +442,11 @@
                 sizes.push_back(nullptr);
             }
         }
-        auto var = std::make_unique<Variable>(varDecl.fOffset, modifiers, varData.fName, *type,
+        auto var = std::make_unique<Variable>(varDecl.fOffset, modifiers, varData.fName, type,
                                               storage);
         if (var->fName == Compiler::RTADJUST_NAME) {
             SkASSERT(!fRTAdjust);
-            SkASSERT(var->fType == *fContext.fFloat4_Type);
+            SkASSERT(var->type() == *fContext.fFloat4_Type);
             fRTAdjust = var.get();
         }
         std::unique_ptr<Expression> value;
@@ -674,8 +674,8 @@
     if (!value) {
         return nullptr;
     }
-    if (value->fType != *fContext.fUInt_Type &&
-        value->fType.typeKind() != Type::TypeKind::kEnum) {
+    if (value->type() != *fContext.fUInt_Type &&
+        value->type().typeKind() != Type::TypeKind::kEnum) {
         value = this->coerce(std::move(value), *fContext.fInt_Type);
         if (!value) {
             return nullptr;
@@ -694,7 +694,7 @@
             if (!caseValue) {
                 return nullptr;
             }
-            caseValue = this->coerce(std::move(caseValue), value->fType);
+            caseValue = this->coerce(std::move(caseValue), value->type());
             if (!caseValue) {
                 return nullptr;
             }
@@ -809,7 +809,7 @@
                     std::unique_ptr<Expression>(new VariableReference(-1, *loopIdx)),
                     Token::Kind::TK_LT,
                     std::make_unique<IntLiteral>(fContext, -1, fInvocations),
-                    *fContext.fBool_Type));
+                    fContext.fBool_Type.get()));
     std::unique_ptr<Expression> next(new PostfixExpression(
                 std::unique_ptr<Expression>(
                                       new VariableReference(-1,
@@ -835,7 +835,7 @@
                                                                 VariableReference::kWrite_RefKind)),
                     Token::Kind::TK_EQ,
                     std::make_unique<IntLiteral>(fContext, -1, 0),
-                    *fContext.fInt_Type));
+                    fContext.fInt_Type.get()));
     std::unique_ptr<Statement> initializer(new ExpressionStatement(std::move(assignment)));
     std::unique_ptr<Statement> loop = std::unique_ptr<Statement>(
                 new ForStatement(-1,
@@ -869,7 +869,7 @@
                                                                        { __VA_ARGS__ }))
     #define OP(left, op, right) std::unique_ptr<Expression>( \
                                    new BinaryExpression(-1, left, op, right, \
-                                                        *fContext.fFloat2_Type))
+                                                        fContext.fFloat2_Type.get()))
     std::vector<std::unique_ptr<Expression>> children;
     children.push_back(OP(OP(SWIZZLE(POS, 0, 1), Token::Kind::TK_STAR, SWIZZLE(ADJUST, 0, 2)),
                           Token::Kind::TK_PLUS,
@@ -877,9 +877,10 @@
     children.push_back(std::unique_ptr<Expression>(new FloatLiteral(fContext, -1, 0.0)));
     children.push_back(SWIZZLE(POS, 3));
     std::unique_ptr<Expression> result = OP(POS, Token::Kind::TK_EQ,
-                                 std::unique_ptr<Expression>(new Constructor(-1,
-                                                                             *fContext.fFloat4_Type,
-                                                                             std::move(children))));
+                                 std::unique_ptr<Expression>(new Constructor(
+                                                                        -1,
+                                                                        fContext.fFloat4_Type.get(),
+                                                                        std::move(children))));
     return std::unique_ptr<Statement>(new ExpressionStatement(std::move(result)));
 }
 
@@ -984,12 +985,12 @@
         }
         StringFragment name = pd.fName;
         const Variable* var = fSymbolTable->takeOwnershipOfSymbol(std::make_unique<Variable>(
-                param.fOffset, pd.fModifiers, name, *type, Variable::kParameter_Storage));
+                param.fOffset, pd.fModifiers, name, type, Variable::kParameter_Storage));
         parameters.push_back(var);
     }
 
     auto paramIsCoords = [&](int idx) {
-        return parameters[idx]->fType == *fContext.fFloat2_Type &&
+        return parameters[idx]->type() == *fContext.fFloat2_Type &&
                parameters[idx]->fModifiers.fFlags == 0;
     };
 
@@ -1046,7 +1047,7 @@
             if (parameters.size() == other->fParameters.size()) {
                 bool match = true;
                 for (size_t i = 0; i < parameters.size(); i++) {
-                    if (parameters[i]->fType != other->fParameters[i]->fType) {
+                    if (parameters[i]->type() != other->fParameters[i]->type()) {
                         match = false;
                         break;
                     }
@@ -1164,17 +1165,17 @@
             }
             if (vd.fVar == fRTAdjust) {
                 foundRTAdjust = true;
-                SkASSERT(vd.fVar->fType == *fContext.fFloat4_Type);
+                SkASSERT(vd.fVar->type() == *fContext.fFloat4_Type);
                 fRTAdjustFieldIndex = fields.size();
             }
             fields.push_back(Type::Field(vd.fVar->fModifiers, vd.fVar->fName,
-                                         &vd.fVar->fType));
+                                         &vd.fVar->type()));
             if (vd.fValue) {
                 fErrors.error(decl->fOffset,
                               "initializers are not permitted on interface block fields");
             }
-            if (vd.fVar->fType.typeKind() == Type::TypeKind::kArray &&
-                vd.fVar->fType.columns() == -1) {
+            if (vd.fVar->type().typeKind() == Type::TypeKind::kArray &&
+                vd.fVar->type().columns() == -1) {
                 haveRuntimeArray = true;
             }
         }
@@ -1215,7 +1216,7 @@
             std::make_unique<Variable>(intf.fOffset,
                                        id.fModifiers,
                                        id.fInstanceName.fLength ? id.fInstanceName : id.fTypeName,
-                                       *type,
+                                       type,
                                        Variable::kGlobal_Storage));
     if (foundRTAdjust) {
         fRTAdjustInterfaceBlock = var;
@@ -1285,7 +1286,7 @@
         value = std::unique_ptr<Expression>(new IntLiteral(fContext, e.fOffset, currentValue));
         ++currentValue;
         fSymbolTable->add(child.getString(),
-                          std::make_unique<Variable>(e.fOffset, modifiers, child.getString(), *type,
+                          std::make_unique<Variable>(e.fOffset, modifiers, child.getString(), type,
                                                      Variable::kGlobal_Storage, value.get()));
         fSymbolTable->takeOwnershipOfIRNode(std::move(value));
     }
@@ -1419,8 +1420,8 @@
                 !(var->fModifiers.fFlags & Modifiers::kUniform_Flag) &&
                 !var->fModifiers.fLayout.fKey &&
                 var->fModifiers.fLayout.fBuiltin == -1 &&
-                var->fType.nonnullable() != *fContext.fFragmentProcessor_Type &&
-                var->fType.typeKind() != Type::TypeKind::kSampler) {
+                var->type().nonnullable() != *fContext.fFragmentProcessor_Type &&
+                var->type().typeKind() != Type::TypeKind::kSampler) {
                 bool valid = false;
                 for (const auto& decl : fFile->root()) {
                     if (decl.fKind == ASTNode::Kind::kSection) {
@@ -1481,16 +1482,16 @@
     if (!expr) {
         return nullptr;
     }
-    if (expr->fType == type) {
+    if (expr->type() == type) {
         return expr;
     }
     this->checkValid(*expr);
-    if (expr->fType == *fContext.fInvalid_Type) {
+    if (expr->type() == *fContext.fInvalid_Type) {
         return nullptr;
     }
     if (expr->coercionCost(type) == INT_MAX) {
         fErrors.error(expr->fOffset, "expected '" + type.displayName() + "', but found '" +
-                                        expr->fType.displayName() + "'");
+                                     expr->type().displayName() + "'");
         return nullptr;
     }
     if (type.typeKind() == Type::TypeKind::kScalar) {
@@ -1515,11 +1516,11 @@
     }
     if (expr->kind() == Expression::Kind::kNullLiteral) {
         SkASSERT(type.typeKind() == Type::TypeKind::kNullable);
-        return std::unique_ptr<Expression>(new NullLiteral(expr->fOffset, type));
+        return std::unique_ptr<Expression>(new NullLiteral(expr->fOffset, &type));
     }
     std::vector<std::unique_ptr<Expression>> args;
     args.push_back(std::move(expr));
-    return std::unique_ptr<Expression>(new Constructor(-1, type, std::move(args)));
+    return std::unique_ptr<Expression>(new Constructor(-1, &type, std::move(args)));
 }
 
 static bool is_matrix_multiply(const Type& left, const Type& right) {
@@ -1850,16 +1851,18 @@
             default:                   return nullptr;
         }
     }
-    if (left.fType.typeKind() == Type::TypeKind::kVector && left.fType.componentType().isFloat() &&
-        left.fType == right.fType) {
+    const Type& leftType = left.type();
+    const Type& rightType = right.type();
+    if (leftType.typeKind() == Type::TypeKind::kVector && leftType.componentType().isFloat() &&
+        leftType == rightType) {
         std::vector<std::unique_ptr<Expression>> args;
         #define RETURN_VEC_COMPONENTWISE_RESULT(op)                              \
-            for (int i = 0; i < left.fType.columns(); i++) {                     \
+            for (int i = 0; i < leftType.columns(); i++) {                       \
                 float value = left.getFVecComponent(i) op                        \
                               right.getFVecComponent(i);                         \
                 args.emplace_back(new FloatLiteral(fContext, -1, value));        \
             }                                                                    \
-            return std::unique_ptr<Expression>(new Constructor(-1, left.fType,   \
+            return std::unique_ptr<Expression>(new Constructor(-1, &leftType,    \
                                                                std::move(args)))
         switch (op) {
             case Token::Kind::TK_EQEQ:
@@ -1872,7 +1875,7 @@
             case Token::Kind::TK_MINUS: RETURN_VEC_COMPONENTWISE_RESULT(-);
             case Token::Kind::TK_STAR:  RETURN_VEC_COMPONENTWISE_RESULT(*);
             case Token::Kind::TK_SLASH:
-                for (int i = 0; i < left.fType.columns(); i++) {
+                for (int i = 0; i < leftType.columns(); i++) {
                     SKSL_FLOAT rvalue = right.getFVecComponent(i);
                     if (rvalue == 0.0) {
                         fErrors.error(right.fOffset, "division by zero");
@@ -1881,14 +1884,14 @@
                     float value = left.getFVecComponent(i) / rvalue;
                     args.emplace_back(new FloatLiteral(fContext, -1, value));
                 }
-                return std::unique_ptr<Expression>(new Constructor(-1, left.fType,
+                return std::unique_ptr<Expression>(new Constructor(-1, &leftType,
                                                                    std::move(args)));
             default:
                 return nullptr;
         }
     }
-    if (left.fType.typeKind() == Type::TypeKind::kMatrix &&
-        right.fType.typeKind() == Type::TypeKind::kMatrix &&
+    if (leftType.typeKind() == Type::TypeKind::kMatrix &&
+        rightType.typeKind() == Type::TypeKind::kMatrix &&
         left.kind() == right.kind()) {
         switch (op) {
             case Token::Kind::TK_EQEQ:
@@ -1928,23 +1931,23 @@
     const Type* rightType;
     const Type* resultType;
     const Type* rawLeftType;
-    if (left->kind() == Expression::Kind::kIntLiteral && right->fType.isInteger()) {
-        rawLeftType = &right->fType;
+    if (left->kind() == Expression::Kind::kIntLiteral && right->type().isInteger()) {
+        rawLeftType = &right->type();
     } else {
-        rawLeftType = &left->fType;
+        rawLeftType = &left->type();
     }
     const Type* rawRightType;
-    if (right->kind() == Expression::Kind::kIntLiteral && left->fType.isInteger()) {
-        rawRightType = &left->fType;
+    if (right->kind() == Expression::Kind::kIntLiteral && left->type().isInteger()) {
+        rawRightType = &left->type();
     } else {
-        rawRightType = &right->fType;
+        rawRightType = &right->type();
     }
     if (!determine_binary_type(fContext, op, *rawLeftType, *rawRightType,
                                &leftType, &rightType, &resultType)) {
         fErrors.error(expression.fOffset, String("type mismatch: '") +
                                           Compiler::OperatorName(expression.getToken().fKind) +
-                                          "' cannot operate on '" + left->fType.displayName() +
-                                          "', '" + right->fType.displayName() + "'");
+                                          "' cannot operate on '" + left->type().displayName() +
+                                          "', '" + right->type().displayName() + "'");
         return nullptr;
     }
     if (Compiler::IsAssignment(op)) {
@@ -1962,7 +1965,7 @@
     std::unique_ptr<Expression> result = this->constantFold(*left, op, *right);
     if (!result) {
         result = std::make_unique<BinaryExpression>(expression.fOffset, std::move(left), op,
-                                                    std::move(right), *resultType);
+                                                    std::move(right), resultType);
     }
     return result;
 }
@@ -1991,11 +1994,11 @@
     const Type* trueType;
     const Type* falseType;
     const Type* resultType;
-    if (!determine_binary_type(fContext, Token::Kind::TK_EQEQ, ifTrue->fType, ifFalse->fType,
+    if (!determine_binary_type(fContext, Token::Kind::TK_EQEQ, ifTrue->type(), ifFalse->type(),
                                &trueType, &falseType, &resultType) || trueType != falseType) {
         fErrors.error(node.fOffset, "ternary operator result mismatch: '" +
-                                    ifTrue->fType.displayName() + "', '" +
-                                    ifFalse->fType.displayName() + "'");
+                                    ifTrue->type().displayName() + "', '" +
+                                    ifFalse->type().displayName() + "'");
         return nullptr;
     }
     if (trueType->nonnullable() == *fContext.fFragmentProcessor_Type) {
@@ -2072,7 +2075,7 @@
         for (size_t i = 0; i < arguments.size(); i++) {
             msg += separator;
             separator = ", ";
-            msg += arguments[i]->fType.displayName();
+            msg += arguments[i]->type().displayName();
         }
         msg += ")";
         fErrors.error(offset, msg);
@@ -2091,7 +2094,7 @@
         }
     }
 
-    auto funcCall = std::make_unique<FunctionCall>(offset, *returnType, function,
+    auto funcCall = std::make_unique<FunctionCall>(offset, returnType, function,
                                                    std::move(arguments));
     if (fCanInline && fInliner->isSafeToInline(*funcCall, fSettings->fInlineThreshold)) {
         Inliner::InlinedCall inlinedCall = fInliner->inlineCall(funcCall.get(), fSymbolTable.get());
@@ -2161,7 +2164,7 @@
                     return nullptr;
                 }
             }
-            return std::make_unique<ExternalFunctionCall>(offset, v->callReturnType(), v,
+            return std::make_unique<ExternalFunctionCall>(offset, &v->callReturnType(), v,
                                                           std::move(arguments));
         }
         case Expression::Kind::kFunctionReference: {
@@ -2184,7 +2187,7 @@
                 for (size_t i = 0; i < arguments.size(); i++) {
                     msg += separator;
                     separator = ", ";
-                    msg += arguments[i]->fType.displayName();
+                    msg += arguments[i]->type().displayName();
                 }
                 msg += ")";
                 fErrors.error(offset, msg);
@@ -2209,7 +2212,8 @@
                               to_string((uint64_t) args.size()) + ")");
         return nullptr;
     }
-    if (type == args[0]->fType) {
+    const Type& argType = args[0]->type();
+    if (type == argType) {
         return std::move(args[0]);
     }
     if (type.isFloat() && args.size() == 1 && args[0]->kind() == Expression::Kind::kFloatLiteral) {
@@ -2226,7 +2230,7 @@
                                                           args[0]->as<IntLiteral>().fValue,
                                                           &type));
     }
-    if (args[0]->fType == *fContext.fBool_Type) {
+    if (argType == *fContext.fBool_Type) {
         std::unique_ptr<IntLiteral> zero(new IntLiteral(fContext, offset, 0));
         std::unique_ptr<IntLiteral> one(new IntLiteral(fContext, offset, 1));
         return std::unique_ptr<Expression>(
@@ -2235,13 +2239,13 @@
                                                            this->coerce(std::move(zero),
                                                                         type)));
     }
-    if (!args[0]->fType.isNumber()) {
+    if (!argType.isNumber()) {
         fErrors.error(offset, "invalid argument to '" + type.displayName() +
                               "' constructor (expected a number or bool, but found '" +
-                              args[0]->fType.displayName() + "')");
+                              argType.displayName() + "')");
         return nullptr;
     }
-    return std::unique_ptr<Expression>(new Constructor(offset, type, std::move(args)));
+    return std::unique_ptr<Expression>(new Constructor(offset, &type, std::move(args)));
 }
 
 static int component_count(const Type& type) {
@@ -2262,25 +2266,26 @@
     SkASSERT(type.typeKind() == Type::TypeKind::kVector ||
              type.typeKind() == Type::TypeKind::kMatrix);
     if (type.typeKind() == Type::TypeKind::kMatrix && args.size() == 1 &&
-        args[0]->fType.typeKind() == Type::TypeKind::kMatrix) {
+        args[0]->type().typeKind() == Type::TypeKind::kMatrix) {
         // matrix from matrix is always legal
-        return std::unique_ptr<Expression>(new Constructor(offset, type, std::move(args)));
+        return std::unique_ptr<Expression>(new Constructor(offset, &type, std::move(args)));
     }
     int actual = 0;
     int expected = type.rows() * type.columns();
-    if (args.size() != 1 || expected != component_count(args[0]->fType) ||
-        type.componentType().isNumber() != args[0]->fType.componentType().isNumber()) {
+    if (args.size() != 1 || expected != component_count(args[0]->type()) ||
+        type.componentType().isNumber() != args[0]->type().componentType().isNumber()) {
         for (size_t i = 0; i < args.size(); i++) {
-            if (args[i]->fType.typeKind() == Type::TypeKind::kVector) {
+            const Type& argType = args[i]->type();
+            if (argType.typeKind() == Type::TypeKind::kVector) {
                 if (type.componentType().isNumber() !=
-                    args[i]->fType.componentType().isNumber()) {
-                    fErrors.error(offset, "'" + args[i]->fType.displayName() + "' is not a valid "
+                    argType.componentType().isNumber()) {
+                    fErrors.error(offset, "'" + argType.displayName() + "' is not a valid "
                                           "parameter to '" + type.displayName() +
                                           "' constructor");
                     return nullptr;
                 }
-                actual += args[i]->fType.columns();
-            } else if (args[i]->fType.typeKind() == Type::TypeKind::kScalar) {
+                actual += argType.columns();
+            } else if (argType.typeKind() == Type::TypeKind::kScalar) {
                 actual += 1;
                 if (type.typeKind() != Type::TypeKind::kScalar) {
                     args[i] = this->coerce(std::move(args[i]), type.componentType());
@@ -2289,7 +2294,7 @@
                     }
                 }
             } else {
-                fErrors.error(offset, "'" + args[i]->fType.displayName() + "' is not a valid "
+                fErrors.error(offset, "'" + argType.displayName() + "' is not a valid "
                                       "parameter to '" + type.displayName() + "' constructor");
                 return nullptr;
             }
@@ -2301,7 +2306,7 @@
             return nullptr;
         }
     }
-    return std::unique_ptr<Expression>(new Constructor(offset, type, std::move(args)));
+    return std::unique_ptr<Expression>(new Constructor(offset, &type, std::move(args)));
 }
 
 std::unique_ptr<Expression> IRGenerator::convertConstructor(
@@ -2309,7 +2314,7 @@
                                                     const Type& type,
                                                     std::vector<std::unique_ptr<Expression>> args) {
     // FIXME: add support for structs
-    if (args.size() == 1 && args[0]->fType == type &&
+    if (args.size() == 1 && args[0]->type() == type &&
         type.nonnullable() != *fContext.fFragmentProcessor_Type) {
         // argument is already the right type, just return it
         return std::move(args[0]);
@@ -2325,7 +2330,7 @@
                 return nullptr;
             }
         }
-        return std::make_unique<Constructor>(offset, type, std::move(args));
+        return std::make_unique<Constructor>(offset, &type, std::move(args));
     } else if (kind == Type::TypeKind::kVector || kind == Type::TypeKind::kMatrix) {
         return this->convertCompoundConstructor(offset, type, std::move(args));
     } else {
@@ -2340,12 +2345,13 @@
     if (!base) {
         return nullptr;
     }
+    const Type& baseType = base->type();
     switch (expression.getToken().fKind) {
         case Token::Kind::TK_PLUS:
-            if (!base->fType.isNumber() && base->fType.typeKind() != Type::TypeKind::kVector &&
-                base->fType != *fContext.fFloatLiteral_Type) {
+            if (!baseType.isNumber() && baseType.typeKind() != Type::TypeKind::kVector &&
+                baseType != *fContext.fFloatLiteral_Type) {
                 fErrors.error(expression.fOffset,
-                              "'+' cannot operate on '" + base->fType.displayName() + "'");
+                              "'+' cannot operate on '" + baseType.displayName() + "'");
                 return nullptr;
             }
             return base;
@@ -2359,36 +2365,36 @@
                 return std::unique_ptr<Expression>(new FloatLiteral(fContext, base->fOffset,
                                                                     value));
             }
-            if (!base->fType.isNumber() && base->fType.typeKind() != Type::TypeKind::kVector) {
+            if (!baseType.isNumber() && baseType.typeKind() != Type::TypeKind::kVector) {
                 fErrors.error(expression.fOffset,
-                              "'-' cannot operate on '" + base->fType.displayName() + "'");
+                              "'-' cannot operate on '" + baseType.displayName() + "'");
                 return nullptr;
             }
             return std::unique_ptr<Expression>(new PrefixExpression(Token::Kind::TK_MINUS,
                                                                     std::move(base)));
         case Token::Kind::TK_PLUSPLUS:
-            if (!base->fType.isNumber()) {
+            if (!baseType.isNumber()) {
                 fErrors.error(expression.fOffset,
                               String("'") + Compiler::OperatorName(expression.getToken().fKind) +
-                              "' cannot operate on '" + base->fType.displayName() + "'");
+                              "' cannot operate on '" + baseType.displayName() + "'");
                 return nullptr;
             }
             this->setRefKind(*base, VariableReference::kReadWrite_RefKind);
             break;
         case Token::Kind::TK_MINUSMINUS:
-            if (!base->fType.isNumber()) {
+            if (!baseType.isNumber()) {
                 fErrors.error(expression.fOffset,
                               String("'") + Compiler::OperatorName(expression.getToken().fKind) +
-                              "' cannot operate on '" + base->fType.displayName() + "'");
+                              "' cannot operate on '" + baseType.displayName() + "'");
                 return nullptr;
             }
             this->setRefKind(*base, VariableReference::kReadWrite_RefKind);
             break;
         case Token::Kind::TK_LOGICALNOT:
-            if (base->fType != *fContext.fBool_Type) {
+            if (baseType != *fContext.fBool_Type) {
                 fErrors.error(expression.fOffset,
                               String("'") + Compiler::OperatorName(expression.getToken().fKind) +
-                              "' cannot operate on '" + base->fType.displayName() + "'");
+                              "' cannot operate on '" + baseType.displayName() + "'");
                 return nullptr;
             }
             if (base->kind() == Expression::Kind::kBoolLiteral) {
@@ -2397,10 +2403,10 @@
             }
             break;
         case Token::Kind::TK_BITWISENOT:
-            if (base->fType != *fContext.fInt_Type && base->fType != *fContext.fUInt_Type) {
+            if (baseType != *fContext.fInt_Type && baseType != *fContext.fUInt_Type) {
                 fErrors.error(expression.fOffset,
                               String("'") + Compiler::OperatorName(expression.getToken().fKind) +
-                              "' cannot operate on '" + base->fType.displayName() + "'");
+                              "' cannot operate on '" + baseType.displayName() + "'");
                 return nullptr;
             }
             break;
@@ -2427,10 +2433,11 @@
             return nullptr;
         }
     }
-    if (base->fType.typeKind() != Type::TypeKind::kArray &&
-        base->fType.typeKind() != Type::TypeKind::kMatrix &&
-        base->fType.typeKind() != Type::TypeKind::kVector) {
-        fErrors.error(base->fOffset, "expected array, but found '" + base->fType.displayName() +
+    const Type& baseType = base->type();
+    if (baseType.typeKind() != Type::TypeKind::kArray &&
+        baseType.typeKind() != Type::TypeKind::kMatrix &&
+        baseType.typeKind() != Type::TypeKind::kVector) {
+        fErrors.error(base->fOffset, "expected array, but found '" + baseType.displayName() +
                                      "'");
         return nullptr;
     }
@@ -2438,7 +2445,7 @@
     if (!converted) {
         return nullptr;
     }
-    if (converted->fType != *fContext.fUInt_Type) {
+    if (converted->type() != *fContext.fUInt_Type) {
         converted = this->coerce(std::move(converted), *fContext.fInt_Type);
         if (!converted) {
             return nullptr;
@@ -2459,14 +2466,15 @@
         }
         return std::unique_ptr<Expression>(new ExternalValueReference(base->fOffset, result));
     }
-    auto fields = base->fType.fields();
+    const Type& baseType = base->type();
+    auto fields = baseType.fields();
     for (size_t i = 0; i < fields.size(); i++) {
         if (fields[i].fName == field) {
             return std::unique_ptr<Expression>(new FieldAccess(std::move(base), (int) i));
         }
     }
-    fErrors.error(base->fOffset, "type '" + base->fType.displayName() + "' does not have a "
-                                 "field named '" + field + "");
+    fErrors.error(base->fOffset, "type '" + baseType.displayName() + "' does not have a field "
+                                 "named '" + field + "");
     return nullptr;
 }
 
@@ -2487,8 +2495,9 @@
 
 std::unique_ptr<Expression> IRGenerator::convertSwizzle(std::unique_ptr<Expression> base,
                                                         StringFragment fields) {
-    if (base->fType.typeKind() != Type::TypeKind::kVector && !base->fType.isNumber()) {
-        fErrors.error(base->fOffset, "cannot swizzle value of type '" + base->fType.displayName() +
+    const Type& baseType = base->type();
+    if (baseType.typeKind() != Type::TypeKind::kVector && !baseType.isNumber()) {
+        fErrors.error(base->fOffset, "cannot swizzle value of type '" + baseType.displayName() +
                                      "'");
         return nullptr;
     }
@@ -2514,7 +2523,7 @@
             case 'g':
             case 't':
             case 'T':
-                if (base->fType.columns() >= 2) {
+                if (baseType.columns() >= 2) {
                     swizzleComponents.push_back(1);
                     break;
                 }
@@ -2523,7 +2532,7 @@
             case 'b':
             case 'p':
             case 'R':
-                if (base->fType.columns() >= 3) {
+                if (baseType.columns() >= 3) {
                     swizzleComponents.push_back(2);
                     break;
                 }
@@ -2532,7 +2541,7 @@
             case 'a':
             case 'q':
             case 'B':
-                if (base->fType.columns() >= 4) {
+                if (baseType.columns() >= 4) {
                     swizzleComponents.push_back(3);
                     break;
                 }
@@ -2552,7 +2561,7 @@
         fErrors.error(base->fOffset, "swizzle must refer to base expression");
         return nullptr;
     }
-    if (base->fType.isNumber()) {
+    if (baseType.isNumber()) {
         // Swizzling a single scalar. Something like foo.x0x1 is equivalent to float4(foo, 0, foo,
         // 1)
         int offset = base->fOffset;
@@ -2583,14 +2592,14 @@
                             std::make_unique<Variable>(offset,
                                                        Modifiers(),
                                                        namePtr->c_str(),
-                                                       base->fType,
+                                                       &baseType,
                                                        Variable::kLocal_Storage,
                                                        base.get()));
                     expr = std::make_unique<VariableReference>(offset, *var);
                     std::vector<std::unique_ptr<VarDeclaration>> variables;
                     variables.emplace_back(new VarDeclaration(var, {}, std::move(base)));
                     fExtraStatements.emplace_back(new VarDeclarationsStatement(
-                            std::make_unique<VarDeclarations>(offset, &expr->fType,
+                            std::make_unique<VarDeclarations>(offset, &expr->type(),
                                                               std::move(variables))));
                 }
         }
@@ -2608,9 +2617,9 @@
                         std::vector<std::unique_ptr<Expression>> constructorArgs;
                         constructorArgs.push_back(std::move(args.back()));
                         args.pop_back();
-                        args.emplace_back(new Constructor(offset, expr->fType.toCompound(fContext,
-                                                                                         count,
-                                                                                         1),
+                        args.emplace_back(new Constructor(offset, &expr->type().toCompound(fContext,
+                                                                                           count,
+                                                                                           1),
                                                           std::move(constructorArgs)));
                     }
                     break;
@@ -2624,7 +2633,7 @@
             }
         }
         return std::unique_ptr<Expression>(new Constructor(offset,
-                                                           expr->fType.toCompound(
+                                                           &expr->type().toCompound(
                                                                            fContext,
                                                                            swizzleComponents.size(),
                                                                            1),
@@ -2735,13 +2744,14 @@
         return nullptr;
     }
     StringFragment field = fieldNode.getString();
-    if (base->fType == *fContext.fSkCaps_Type) {
+    const Type& baseType = base->type();
+    if (baseType == *fContext.fSkCaps_Type) {
         return this->getCap(fieldNode.fOffset, field);
     }
     if (base->kind() == Expression::Kind::kExternalValue) {
         return this->convertField(std::move(base), field);
     }
-    switch (base->fType.typeKind()) {
+    switch (baseType.typeKind()) {
         case Type::TypeKind::kOther:
         case Type::TypeKind::kStruct:
             return this->convertField(std::move(base), field);
@@ -2768,10 +2778,11 @@
     if (!base) {
         return nullptr;
     }
-    if (!base->fType.isNumber()) {
+    const Type& baseType = base->type();
+    if (!baseType.isNumber()) {
         fErrors.error(expression.fOffset,
                       "'" + String(Compiler::OperatorName(expression.getToken().fKind)) +
-                      "' cannot operate on '" + base->fType.displayName() + "'");
+                      "' cannot operate on '" + baseType.displayName() + "'");
         return nullptr;
     }
     this->setRefKind(*base, VariableReference::kReadWrite_RefKind);
@@ -2788,7 +2799,7 @@
             fErrors.error(expr.fOffset, "expected '(' to begin constructor invocation");
             break;
         default:
-            if (expr.fType == *fContext.fInvalid_Type) {
+            if (expr.type() == *fContext.fInvalid_Type) {
                 fErrors.error(expr.fOffset, "invalid expression");
             }
     }
diff --git a/src/sksl/SkSLInliner.cpp b/src/sksl/SkSLInliner.cpp
index 459362f..4270261 100644
--- a/src/sksl/SkSLInliner.cpp
+++ b/src/sksl/SkSLInliner.cpp
@@ -237,7 +237,7 @@
                                                       expr(b.fLeft),
                                                       b.fOperator,
                                                       expr(b.fRight),
-                                                      b.fType);
+                                                      &b.type());
         }
         case Expression::Kind::kBoolLiteral:
         case Expression::Kind::kIntLiteral:
@@ -246,12 +246,12 @@
             return expression.clone();
         case Expression::Kind::kConstructor: {
             const Constructor& constructor = expression.as<Constructor>();
-            return std::make_unique<Constructor>(offset, constructor.fType,
+            return std::make_unique<Constructor>(offset, &constructor.type(),
                                                  argList(constructor.fArguments));
         }
         case Expression::Kind::kExternalFunctionCall: {
             const ExternalFunctionCall& externalCall = expression.as<ExternalFunctionCall>();
-            return std::make_unique<ExternalFunctionCall>(offset, externalCall.fType,
+            return std::make_unique<ExternalFunctionCall>(offset, &externalCall.type(),
                                                           externalCall.fFunction,
                                                           argList(externalCall.fArguments));
         }
@@ -263,7 +263,7 @@
         }
         case Expression::Kind::kFunctionCall: {
             const FunctionCall& funcCall = expression.as<FunctionCall>();
-            return std::make_unique<FunctionCall>(offset, funcCall.fType, funcCall.fFunction,
+            return std::make_unique<FunctionCall>(offset, &funcCall.type(), funcCall.fFunction,
                                                   argList(funcCall.fArguments));
         }
         case Expression::Kind::kFunctionReference:
@@ -376,7 +376,7 @@
                                                                 VariableReference::kWrite_RefKind),
                             Token::Kind::TK_EQ,
                             expr(r.fExpression),
-                            returnVar->fType));
+                            &returnVar->type()));
                 if (haveEarlyReturns) {
                     std::vector<std::unique_ptr<Statement>> block;
                     block.push_back(std::move(assignment));
@@ -416,12 +416,12 @@
             // its symbols
             std::unique_ptr<String> name(new String(old->fName));
             const String* namePtr = symbolTableForStatement->takeOwnershipOfString(std::move(name));
-            const Type* typePtr = copy_if_needed(&old->fType, *symbolTableForStatement);
+            const Type* typePtr = copy_if_needed(&old->type(), *symbolTableForStatement);
             const Variable* clone = symbolTableForStatement->takeOwnershipOfSymbol(
                     std::make_unique<Variable>(offset,
                                                old->fModifiers,
                                                namePtr->c_str(),
-                                               *typePtr,
+                                               typePtr,
                                                old->fStorage,
                                                initialValue.get()));
             (*varMap)[old] = clone;
@@ -521,7 +521,7 @@
         StringFragment nameFrag{namePtr->c_str(), namePtr->length()};
 
         // Add our new variable to the symbol table.
-        auto newVar = std::make_unique<Variable>(/*offset=*/-1, Modifiers(), nameFrag, *type,
+        auto newVar = std::make_unique<Variable>(/*offset=*/-1, Modifiers(), nameFrag, type,
                                                  Variable::kLocal_Storage, initialValue->get());
         const Variable* variableSymbol = symbolTableForCall->add(nameFrag, std::move(newVar));
 
@@ -569,8 +569,8 @@
             }
         }
 
-        varMap[param] = makeInlineVar(String(param->fName), &arguments[i]->fType, param->fModifiers,
-                                      &arguments[i]);
+        varMap[param] = makeInlineVar(String(param->fName), &arguments[i]->type(),
+                                      param->fModifiers, &arguments[i]);
     }
 
     const Block& body = function.fBody->as<Block>();
@@ -612,7 +612,7 @@
                                                        arguments[i]->clone(),
                                                        Token::Kind::TK_EQ,
                                                        std::move(varRef),
-                                                       arguments[i]->fType)));
+                                                       &arguments[i]->type())));
         }
     }
 
diff --git a/src/sksl/SkSLMetalCodeGenerator.cpp b/src/sksl/SkSLMetalCodeGenerator.cpp
index ecb2d70..96af1a0 100644
--- a/src/sksl/SkSLMetalCodeGenerator.cpp
+++ b/src/sksl/SkSLMetalCodeGenerator.cpp
@@ -271,9 +271,10 @@
 }
 
 void MetalCodeGenerator::writeInverseHack(const Expression& mat) {
-    String typeName = mat.fType.name();
+    const Type& type = mat.type();
+    const String& typeName = type.name();
     String name = typeName + "_inverse";
-    if (mat.fType == *fContext.fFloat2x2_Type || mat.fType == *fContext.fHalf2x2_Type) {
+    if (type == *fContext.fFloat2x2_Type || type == *fContext.fHalf2x2_Type) {
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
             fExtraFunctions.writeText((
@@ -283,7 +284,7 @@
             ).c_str());
         }
     }
-    else if (mat.fType == *fContext.fFloat3x3_Type || mat.fType == *fContext.fHalf3x3_Type) {
+    else if (type == *fContext.fFloat3x3_Type || type == *fContext.fHalf3x3_Type) {
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
             fExtraFunctions.writeText((
@@ -304,7 +305,7 @@
             ).c_str());
         }
     }
-    else if (mat.fType == *fContext.fFloat4x4_Type || mat.fType == *fContext.fHalf4x4_Type) {
+    else if (type == *fContext.fFloat4x4_Type || type == *fContext.fHalf4x4_Type) {
         if (fWrittenIntrinsics.find(name) == fWrittenIntrinsics.end()) {
             fWrittenIntrinsics.insert(name);
             fExtraFunctions.writeText((
@@ -352,32 +353,33 @@
 
 void MetalCodeGenerator::writeSpecialIntrinsic(const FunctionCall & c, SpecialIntrinsic kind) {
     switch (kind) {
-        case kTexture_SpecialIntrinsic:
+        case kTexture_SpecialIntrinsic: {
             this->writeExpression(*c.fArguments[0], kSequence_Precedence);
             this->write(".sample(");
             this->writeExpression(*c.fArguments[0], kSequence_Precedence);
             this->write(SAMPLER_SUFFIX);
             this->write(", ");
-            if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
+            const Type& arg1Type = c.fArguments[1]->type();
+            if (arg1Type == *fContext.fFloat3_Type) {
                 // have to store the vector in a temp variable to avoid double evaluating it
                 String tmpVar = "tmpCoord" + to_string(fVarCount++);
-                this->fFunctionHeader += "    " + this->typeName(c.fArguments[1]->fType) + " " +
-                                         tmpVar + ";\n";
+                this->fFunctionHeader += "    " + this->typeName(arg1Type) + " " + tmpVar + ";\n";
                 this->write("(" + tmpVar + " = ");
                 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
                 this->write(", " + tmpVar + ".xy / " + tmpVar + ".z))");
             } else {
-                SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
+                SkASSERT(arg1Type == *fContext.fFloat2_Type);
                 this->writeExpression(*c.fArguments[1], kSequence_Precedence);
                 this->write(")");
             }
             break;
+        }
         case kMod_SpecialIntrinsic: {
             // fmod(x, y) in metal calculates x - y * trunc(x / y) instead of x - y * floor(x / y)
             String tmpX = "tmpX" + to_string(fVarCount++);
             String tmpY = "tmpY" + to_string(fVarCount++);
-            this->fFunctionHeader += "    " + this->typeName(c.fArguments[0]->fType) + " " + tmpX +
-                                     ", " + tmpY + ";\n";
+            this->fFunctionHeader += "    " + this->typeName(c.fArguments[0]->type()) +
+                                     " " + tmpX + ", " + tmpY + ";\n";
             this->write("(" + tmpX + " = ");
             this->writeExpression(*c.fArguments[0], kSequence_Precedence);
             this->write(", " + tmpY + " = ");
@@ -446,7 +448,7 @@
             rowSeparator = ", ";
 
             if (argIndex < args.size()) {
-                const Type& argType = args[argIndex]->fType;
+                const Type& argType = args[argIndex]->type();
                 switch (argType.typeKind()) {
                     case Type::TypeKind::kScalar: {
                         fExtraFunctions.printf("x%zu", argIndex);
@@ -494,7 +496,7 @@
 // constructor for any given permutation of input argument types. Returns the name of the
 // generated constructor method.
 String MetalCodeGenerator::getMatrixConstructHelper(const Constructor& c) {
-    const Type& matrix = c.fType;
+    const Type& matrix = c.type();
     int columns = matrix.columns();
     int rows = matrix.rows();
     const std::vector<std::unique_ptr<Expression>>& args = c.fArguments;
@@ -503,7 +505,7 @@
     String name;
     name.appendf("float%dx%d_from", columns, rows);
     for (const std::unique_ptr<Expression>& expr : args) {
-        name.appendf("_%s", expr->fType.displayName().c_str());
+        name.appendf("_%s", expr->type().displayName().c_str());
     }
 
     // If a helper-method has already been synthesized, we don't need to synthesize it again.
@@ -521,14 +523,14 @@
     const char* argSeparator = "";
     for (const std::unique_ptr<Expression>& expr : args) {
         fExtraFunctions.printf("%s%s x%zu", argSeparator,
-                               expr->fType.displayName().c_str(), argIndex++);
+                               expr->type().displayName().c_str(), argIndex++);
         argSeparator = ", ";
     }
 
     fExtraFunctions.printf(") {\n    return float%dx%d(", columns, rows);
 
-    if (args.size() == 1 && args.front()->fType.typeKind() == Type::TypeKind::kMatrix) {
-        this->assembleMatrixFromMatrix(args.front()->fType, rows, columns);
+    if (args.size() == 1 && args.front()->type().typeKind() == Type::TypeKind::kMatrix) {
+        this->assembleMatrixFromMatrix(args.front()->type(), rows, columns);
     } else {
         this->assembleMatrixFromExpressions(args, rows, columns);
     }
@@ -549,7 +551,7 @@
 
 bool MetalCodeGenerator::matrixConstructHelperIsNeeded(const Constructor& c) {
     // A matrix construct helper is only necessary if we are, in fact, constructing a matrix.
-    if (c.fType.typeKind() != Type::TypeKind::kMatrix) {
+    if (c.type().typeKind() != Type::TypeKind::kMatrix) {
         return false;
     }
 
@@ -577,15 +579,15 @@
     int position = 0;
     for (const std::unique_ptr<Expression>& expr : c.fArguments) {
         // If an input argument is a matrix, we need a helper function.
-        if (expr->fType.typeKind() == Type::TypeKind::kMatrix) {
+        if (expr->type().typeKind() == Type::TypeKind::kMatrix) {
             return true;
         }
-        position += expr->fType.columns();
-        if (position > c.fType.rows()) {
+        position += expr->type().columns();
+        if (position > c.type().rows()) {
             // An input argument would span multiple rows; a helper function is required.
             return true;
         }
-        if (position == c.fType.rows()) {
+        if (position == c.type().rows()) {
             // We've advanced to the end of a row. Wrap to the start of the next row.
             position = 0;
         }
@@ -595,19 +597,21 @@
 }
 
 void MetalCodeGenerator::writeConstructor(const Constructor& c, Precedence parentPrecedence) {
+    const Type& constructorType = c.type();
     // Handle special cases for single-argument constructors.
     if (c.fArguments.size() == 1) {
         // If the type is coercible, emit it directly.
         const Expression& arg = *c.fArguments.front();
-        if (this->canCoerce(c.fType, arg.fType)) {
+        const Type& argType = arg.type();
+        if (this->canCoerce(constructorType, argType)) {
             this->writeExpression(arg, parentPrecedence);
             return;
         }
 
         // Metal supports creating matrices with a scalar on the diagonal via the single-argument
         // matrix constructor.
-        if (c.fType.typeKind() == Type::TypeKind::kMatrix && arg.fType.isNumber()) {
-            const Type& matrix = c.fType;
+        if (constructorType.typeKind() == Type::TypeKind::kMatrix && argType.isNumber()) {
+            const Type& matrix = constructorType;
             this->write("float");
             this->write(to_string(matrix.columns()));
             this->write("x");
@@ -634,25 +638,26 @@
     }
 
     // Explicitly invoke the constructor, passing in the necessary arguments.
-    this->writeType(c.fType);
+    this->writeType(constructorType);
     this->write("(");
     const char* separator = "";
     int scalarCount = 0;
     for (const std::unique_ptr<Expression>& arg : c.fArguments) {
+        const Type& argType = arg->type();
         this->write(separator);
         separator = ", ";
-        if (c.fType.typeKind() == Type::TypeKind::kMatrix &&
-            arg->fType.columns() < c.fType.rows()) {
+        if (constructorType.typeKind() == Type::TypeKind::kMatrix &&
+            argType.columns() < constructorType.rows()) {
             // Merge scalars and smaller vectors together.
             if (!scalarCount) {
-                this->writeType(c.fType.componentType());
-                this->write(to_string(c.fType.rows()));
+                this->writeType(constructorType.componentType());
+                this->write(to_string(constructorType.rows()));
                 this->write("(");
             }
-            scalarCount += arg->fType.columns();
+            scalarCount += argType.columns();
         }
         this->writeExpression(*arg, kSequence_Precedence);
-        if (scalarCount && scalarCount == c.fType.rows()) {
+        if (scalarCount && scalarCount == constructorType.rows()) {
             this->write(")");
             scalarCount = 0;
         }
@@ -696,7 +701,7 @@
                 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
                     this->write("_out->");
                 } else if (ref.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
-                           ref.fVariable.fType.typeKind() != Type::TypeKind::kSampler) {
+                           ref.fVariable.type().typeKind() != Type::TypeKind::kSampler) {
                     this->write("_uniforms.");
                 } else {
                     this->write("_globals->");
@@ -714,7 +719,7 @@
 }
 
 void MetalCodeGenerator::writeFieldAccess(const FieldAccess& f) {
-    const Type::Field* field = &f.fBase->fType.fields()[f.fFieldIndex];
+    const Type::Field* field = &f.fBase->type().fields()[f.fFieldIndex];
     if (FieldAccess::kDefault_OwnerKind == f.fOwnerKind) {
         this->writeExpression(*f.fBase, kPostfix_Precedence);
         this->write(".");
@@ -743,7 +748,7 @@
 void MetalCodeGenerator::writeSwizzle(const Swizzle& swizzle) {
     int last = swizzle.fComponents.back();
     if (last == SKSL_SWIZZLE_0 || last == SKSL_SWIZZLE_1) {
-        this->writeType(swizzle.fType);
+        this->writeType(swizzle.type());
         this->write("(");
     }
     this->writeExpression(*swizzle.fBase, kPostfix_Precedence);
@@ -815,17 +820,19 @@
 
 void MetalCodeGenerator::writeBinaryExpression(const BinaryExpression& b,
                                                Precedence parentPrecedence) {
+    const Type& leftType = b.fLeft->type();
+    const Type& rightType = b.fRight->type();
     Precedence precedence = GetBinaryPrecedence(b.fOperator);
     bool needParens = precedence >= parentPrecedence;
     switch (b.fOperator) {
         case Token::Kind::TK_EQEQ:
-            if (b.fLeft->fType.typeKind() == Type::TypeKind::kVector) {
+            if (leftType.typeKind() == Type::TypeKind::kVector) {
                 this->write("all");
                 needParens = true;
             }
             break;
         case Token::Kind::TK_NEQ:
-            if (b.fLeft->fType.typeKind() == Type::TypeKind::kVector) {
+            if (leftType.typeKind() == Type::TypeKind::kVector) {
                 this->write("any");
                 needParens = true;
             }
@@ -845,9 +852,9 @@
         this->write("*");
     }
     if (b.fOperator == Token::Kind::TK_STAREQ &&
-        b.fLeft->fType.typeKind() == Type::TypeKind::kMatrix &&
-        b.fRight->fType.typeKind() == Type::TypeKind::kMatrix) {
-        this->writeMatrixTimesEqualHelper(b.fLeft->fType, b.fRight->fType, b.fType);
+        leftType.typeKind() == Type::TypeKind::kMatrix &&
+        rightType.typeKind() == Type::TypeKind::kMatrix) {
+        this->writeMatrixTimesEqualHelper(leftType, rightType, b.type());
     }
     this->writeExpression(*b.fLeft, precedence);
     if (b.fOperator != Token::Kind::TK_EQ && Compiler::IsAssignment(b.fOperator) &&
@@ -918,7 +925,7 @@
 }
 
 void MetalCodeGenerator::writeIntLiteral(const IntLiteral& i) {
-    if (i.fType == *fContext.fUInt_Type) {
+    if (i.type() == *fContext.fUInt_Type) {
         this->write(to_string(i.fValue & 0xffffffff) + "u");
     } else {
         this->write(to_string((int32_t) i.fValue));
@@ -960,7 +967,7 @@
                 }
                 for (const auto& stmt: decls.fVars) {
                     VarDeclaration& var = stmt->as<VarDeclaration>();
-                    if (var.fVar->fType.typeKind() == Type::TypeKind::kSampler) {
+                    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=...)'");
@@ -984,7 +991,7 @@
                     continue;
                 }
                 this->write(", constant ");
-                this->writeType(intf.fVariable.fType);
+                this->writeType(intf.fVariable.type());
                 this->write("& " );
                 this->write(fInterfaceBlockNameMap[&intf]);
                 this->write(" [[buffer(");
@@ -1039,7 +1046,7 @@
         separator = ", ";
         this->writeModifiers(param->fModifiers, false);
         std::vector<int> sizes;
-        const Type* type = &param->fType;
+        const Type* type = &param->type();
         while (type->typeKind() == Type::TypeKind::kArray) {
             sizes.push_back(type->columns());
             type = &type->componentType();
@@ -1112,7 +1119,7 @@
     this->writeModifiers(intf.fVariable.fModifiers, true);
     this->write("struct ");
     this->writeLine(intf.fTypeName + " {");
-    const Type* structType = &intf.fVariable.fType;
+    const Type* structType = &intf.fVariable.type();
     fWrittenStructs.push_back(structType);
     while (structType->typeKind() == Type::TypeKind::kArray) {
         structType = &structType->componentType();
@@ -1408,7 +1415,7 @@
             }
             const Variable& first = *decls.fVars[0]->as<VarDeclaration>().fVar;
             if (first.fModifiers.fFlags & Modifiers::kUniform_Flag &&
-                first.fType.typeKind() != Type::TypeKind::kSampler) {
+                first.type().typeKind() != Type::TypeKind::kSampler) {
                 if (-1 == fUniformBuffer) {
                     this->write("struct Uniforms {\n");
                     fUniformBuffer = first.fModifiers.fLayout.fSet;
@@ -1422,7 +1429,7 @@
                     }
                 }
                 this->write("    ");
-                this->writeType(first.fType);
+                this->writeType(first.type());
                 this->write(" ");
                 for (const auto& stmt : decls.fVars) {
                     const VarDeclaration& var = stmt->as<VarDeclaration>();
@@ -1449,7 +1456,7 @@
             if (first.fModifiers.fFlags & Modifiers::kIn_Flag &&
                 -1 == first.fModifiers.fLayout.fBuiltin) {
                 this->write("    ");
-                this->writeType(first.fType);
+                this->writeType(first.type());
                 this->write(" ");
                 for (const auto& stmt : decls.fVars) {
                     const VarDeclaration& var = stmt->as<VarDeclaration>();
@@ -1488,7 +1495,7 @@
             if (first.fModifiers.fFlags & Modifiers::kOut_Flag &&
                 -1 == first.fModifiers.fLayout.fBuiltin) {
                 this->write("    ");
-                this->writeType(first.fType);
+                this->writeType(first.type());
                 this->write(" ");
                 for (const auto& stmt : decls.fVars) {
                     const VarDeclaration& var = stmt->as<VarDeclaration>();
@@ -1546,14 +1553,14 @@
         }
         const Variable& first = *((VarDeclaration&) *decls.fVars[0]).fVar;
         if ((!first.fModifiers.fFlags && -1 == first.fModifiers.fLayout.fBuiltin) ||
-            first.fType.typeKind() == Type::TypeKind::kSampler) {
+            first.type().typeKind() == Type::TypeKind::kSampler) {
             for (const auto& stmt : decls.fVars) {
                 VarDeclaration& var = static_cast<VarDeclaration&>(*stmt);
 
-                if (var.fVar->fType.typeKind() == Type::TypeKind::kSampler) {
+                if (var.fVar->type().typeKind() == Type::TypeKind::kSampler) {
                     // Samplers are represented as a "texture/sampler" duo in the global struct.
-                    visitor->VisitTexture(first.fType, var.fVar->fName);
-                    visitor->VisitSampler(first.fType, String(var.fVar->fName) + SAMPLER_SUFFIX);
+                    visitor->VisitTexture(first.type(), var.fVar->fName);
+                    visitor->VisitSampler(first.type(), String(var.fVar->fName) + SAMPLER_SUFFIX);
                 } else {
                     // Visit a regular variable.
                     visitor->VisitVariable(*var.fVar, var.fValue.get());
@@ -1591,7 +1598,7 @@
         void VisitVariable(const Variable& var, const Expression* value) override {
             this->AddElement();
             fCodeGen->write("    ");
-            fCodeGen->writeType(var.fType);
+            fCodeGen->writeType(var.type());
             fCodeGen->write(" ");
             fCodeGen->writeName(var.fName);
             fCodeGen->write(";\n");
@@ -1762,7 +1769,7 @@
                 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kOut_Flag) {
                     result = kOutputs_Requirement;
                 } else if (v.fVariable.fModifiers.fFlags & Modifiers::kUniform_Flag &&
-                           v.fVariable.fType.typeKind() != Type::TypeKind::kSampler) {
+                           v.fVariable.type().typeKind() != Type::TypeKind::kSampler) {
                     result = kUniforms_Requirement;
                 } else {
                     result = kGlobals_Requirement;
diff --git a/src/sksl/SkSLPipelineStageCodeGenerator.cpp b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
index a45ba6e..b1cccce 100644
--- a/src/sksl/SkSLPipelineStageCodeGenerator.cpp
+++ b/src/sksl/SkSLPipelineStageCodeGenerator.cpp
@@ -34,10 +34,11 @@
 
 void PipelineStageCodeGenerator::writeFunctionCall(const FunctionCall& c) {
     if (c.fFunction.fBuiltin && c.fFunction.fName == "sample" &&
-        c.fArguments[0]->fType.typeKind() != Type::TypeKind::kSampler) {
+        c.fArguments[0]->type().typeKind() != Type::TypeKind::kSampler) {
         SkASSERT(c.fArguments.size() <= 2);
-        SkASSERT("fragmentProcessor"  == c.fArguments[0]->fType.name() ||
-                 "fragmentProcessor?" == c.fArguments[0]->fType.name());
+        SkDEBUGCODE(const Type& arg0Type = c.fArguments[0]->type());
+        SkASSERT("fragmentProcessor"  == arg0Type.name() ||
+                 "fragmentProcessor?" == arg0Type.name());
         SkASSERT(c.fArguments[0]->kind() == Expression::Kind::kVariableReference);
         int index = 0;
         bool found = false;
@@ -48,7 +49,7 @@
                     VarDeclaration& decl = raw->as<VarDeclaration>();
                     if (decl.fVar == &c.fArguments[0]->as<VariableReference>().fVariable) {
                         found = true;
-                    } else if (decl.fVar->fType == *fContext.fFragmentProcessor_Type) {
+                    } else if (decl.fVar->type() == *fContext.fFragmentProcessor_Type) {
                         ++index;
                     }
                 }
@@ -61,7 +62,7 @@
         size_t childCallIndex = fArgs->fFormatArgs.size();
         this->write(Compiler::kFormatArgPlaceholderStr);
         bool matrixCall = c.fArguments.size() == 2 &&
-                          c.fArguments[1]->fType.typeKind() == Type::TypeKind::kMatrix;
+                          c.fArguments[1]->type().typeKind() == Type::TypeKind::kMatrix;
         fArgs->fFormatArgs.push_back(Compiler::FormatArg(
                 matrixCall ? Compiler::FormatArg::Kind::kChildProcessorWithMatrix
                            : Compiler::FormatArg::Kind::kChildProcessor,
@@ -194,7 +195,7 @@
         result.fName = decl.fName;
         for (const Variable* v : decl.fParameters) {
             GrSLType paramSLType;
-            if (!type_to_grsltype(fContext, v->fType, &paramSLType)) {
+            if (!type_to_grsltype(fContext, v->type(), &paramSLType)) {
                 fErrors.error(v->fOffset, "unsupported parameter type");
                 return;
             }
diff --git a/src/sksl/SkSLRehydrator.cpp b/src/sksl/SkSLRehydrator.cpp
index 0f51e27..b49d5f8 100644
--- a/src/sksl/SkSLRehydrator.cpp
+++ b/src/sksl/SkSLRehydrator.cpp
@@ -232,7 +232,7 @@
             const Type* type = this->type();
             Variable::Storage storage = (Variable::Storage) this->readU8();
             const Variable* result = fSymbolTable->takeOwnershipOfSymbol(
-                    std::make_unique<Variable>(/*offset=*/-1, m, name, *type, storage));
+                    std::make_unique<Variable>(/*offset=*/-1, m, name, type, storage));
             this->addSymbol(id, result);
             return result;
         }
@@ -466,12 +466,12 @@
             Token::Kind op = (Token::Kind) this->readU8();
             std::unique_ptr<Expression> right = this->expression();
             const Type* type = this->type();
-            return std::unique_ptr<Expression>(new BinaryExpression(-1, std::move(left), op,
-                                                                    std::move(right), *type));
+            return std::make_unique<BinaryExpression>(-1, std::move(left), op, std::move(right),
+                                                      type);
         }
         case Rehydrator::kBoolLiteral_Command: {
             bool value = this->readU8();
-            return std::unique_ptr<Expression>(new BoolLiteral(fContext, -1, value));
+            return std::make_unique<BoolLiteral>(fContext, -1, value);
         }
         case Rehydrator::kConstructor_Command: {
             const Type* type = this->type();
@@ -481,21 +481,21 @@
             for (int i = 0; i < argCount; ++i) {
                 args.push_back(this->expression());
             }
-            return std::unique_ptr<Expression>(new Constructor(-1, *type, std::move(args)));
+            return std::make_unique<Constructor>(-1, type, std::move(args));
         }
         case Rehydrator::kFieldAccess_Command: {
             std::unique_ptr<Expression> base = this->expression();
             int index = this->readU8();
             FieldAccess::OwnerKind ownerKind = (FieldAccess::OwnerKind) this->readU8();
-            return std::unique_ptr<Expression>(new FieldAccess(std::move(base), index, ownerKind));
+            return std::make_unique<FieldAccess>(std::move(base), index, ownerKind);
         }
         case Rehydrator::kFloatLiteral_Command: {
             FloatIntUnion u;
             u.fInt = this->readS32();
-            return std::unique_ptr<Expression>(new FloatLiteral(fContext, -1, u.fFloat));
+            return std::make_unique<FloatLiteral>(fContext, -1, u.fFloat);
         }
         case Rehydrator::kFunctionCall_Command: {
-            const Type& type = *this->type();
+            const Type* type = this->type();
             const FunctionDeclaration* f = this->symbolRef<FunctionDeclaration>(
                                                                 Symbol::Kind::kFunctionDeclaration);
             uint8_t argCount = this->readU8();
@@ -509,29 +509,28 @@
         case Rehydrator::kIndex_Command: {
             std::unique_ptr<Expression> base = this->expression();
             std::unique_ptr<Expression> index = this->expression();
-            return std::unique_ptr<Expression>(new IndexExpression(fContext, std::move(base),
-                                                                   std::move(index)));
+            return std::make_unique<IndexExpression>(fContext, std::move(base), std::move(index));
         }
         case Rehydrator::kIntLiteral_Command: {
             int value = this->readS32();
-            return std::unique_ptr<Expression>(new IntLiteral(fContext, -1, value));
+            return std::make_unique<IntLiteral>(fContext, -1, value);
         }
         case Rehydrator::kNullLiteral_Command:
-            return std::unique_ptr<Expression>(new NullLiteral(fContext, -1));
+            return std::make_unique<NullLiteral>(fContext, -1);
         case Rehydrator::kPostfix_Command: {
             Token::Kind op = (Token::Kind) this->readU8();
             std::unique_ptr<Expression> operand = this->expression();
-            return std::unique_ptr<Expression>(new PostfixExpression(std::move(operand), op));
+            return std::make_unique<PostfixExpression>(std::move(operand), op);
         }
         case Rehydrator::kPrefix_Command: {
             Token::Kind op = (Token::Kind) this->readU8();
             std::unique_ptr<Expression> operand = this->expression();
-            return std::unique_ptr<Expression>(new PrefixExpression(op, std::move(operand)));
+            return std::make_unique<PrefixExpression>(op, std::move(operand));
         }
         case Rehydrator::kSetting_Command: {
             StringFragment name = this->readString();
             std::unique_ptr<Expression> value = this->expression();
-            return std::unique_ptr<Expression>(new Setting(-1, name, std::move(value)));
+            return std::make_unique<Setting>(-1, name, std::move(value));
         }
         case Rehydrator::kSwizzle_Command: {
             std::unique_ptr<Expression> base = this->expression();
@@ -541,21 +540,19 @@
             for (int i = 0; i < count; ++i) {
                 components.push_back(this->readU8());
             }
-            return std::unique_ptr<Expression>(new Swizzle(fContext, std::move(base),
-                                                           std::move(components)));
+            return std::make_unique<Swizzle>(fContext, std::move(base), std::move(components));
         }
         case Rehydrator::kTernary_Command: {
             std::unique_ptr<Expression> test = this->expression();
             std::unique_ptr<Expression> ifTrue = this->expression();
             std::unique_ptr<Expression> ifFalse = this->expression();
-            return std::unique_ptr<Expression>(new TernaryExpression(-1, std::move(test),
-                                                                     std::move(ifFalse),
-                                                                     std::move(ifTrue)));
+            return std::make_unique<TernaryExpression>(-1, std::move(test), std::move(ifFalse),
+                                                       std::move(ifTrue));
         }
         case Rehydrator::kVariableReference_Command: {
             const Variable* var = this->symbolRef<Variable>(Symbol::Kind::kVariable);
             VariableReference::RefKind refKind = (VariableReference::RefKind) this->readU8();
-            return std::unique_ptr<Expression>(new VariableReference(-1, *var, refKind));
+            return std::make_unique<VariableReference>(-1, *var, refKind);
         }
         case Rehydrator::kVoid_Command:
             return nullptr;
diff --git a/src/sksl/SkSLSPIRVCodeGenerator.cpp b/src/sksl/SkSLSPIRVCodeGenerator.cpp
index da91466..e5d1126 100644
--- a/src/sksl/SkSLSPIRVCodeGenerator.cpp
+++ b/src/sksl/SkSLSPIRVCodeGenerator.cpp
@@ -583,7 +583,7 @@
     for (size_t i = 0; i < function.fParameters.size(); i++) {
         key += separator;
         separator = ", ";
-        key += to_string(this->getType(function.fParameters[i]->fType));
+        key += to_string(this->getType(function.fParameters[i]->type()));
     }
     key += ")";
     auto entry = fTypeMap.find(key);
@@ -616,7 +616,7 @@
             // as glslang does, fixes it. It's entirely possible I simply missed whichever part of
             // the spec makes this make sense.
 //            if (is_out(function->fParameters[i])) {
-                parameterTypes.push_back(this->getPointerType(function.fParameters[i]->fType,
+                parameterTypes.push_back(this->getPointerType(function.fParameters[i]->type(),
                                                               SpvStorageClassFunction));
 //            } else {
 //                parameterTypes.push_back(this->getType(function.fParameters[i]->fType));
@@ -695,7 +695,7 @@
     SkASSERT(intrinsic != fIntrinsicMap.end());
     int32_t intrinsicId;
     if (c.fArguments.size() > 0) {
-        const Type& type = c.fArguments[0]->fType;
+        const Type& type = c.fArguments[0]->type();
         if (std::get<0>(intrinsic->second) == kSpecial_IntrinsicKind || is_float(fContext, type)) {
             intrinsicId = std::get<1>(intrinsic->second);
         } else if (is_signed(fContext, type)) {
@@ -722,7 +722,7 @@
                 }
             }
             this->writeOpCode(SpvOpExtInst, 5 + (int32_t) arguments.size(), out);
-            this->writeWord(this->getType(c.fType), out);
+            this->writeWord(this->getType(c.type()), out);
             this->writeWord(result, out);
             this->writeWord(fGLSLExtendedInstructions, out);
             this->writeWord(intrinsicId, out);
@@ -741,9 +741,9 @@
                     arguments.push_back(this->writeExpression(*c.fArguments[i], out));
                 }
             }
-            if (c.fType != *fContext.fVoid_Type) {
+            if (c.type() != *fContext.fVoid_Type) {
                 this->writeOpCode((SpvOp_) intrinsicId, 3 + (int32_t) arguments.size(), out);
-                this->writeWord(this->getType(c.fType), out);
+                this->writeWord(this->getType(c.type()), out);
                 this->writeWord(result, out);
             } else {
                 this->writeOpCode((SpvOp_) intrinsicId, 1 + (int32_t) arguments.size(), out);
@@ -765,27 +765,28 @@
                                                OutputStream& out) {
     int vectorSize = 0;
     for (const auto& a : args) {
-        if (a->fType.typeKind() == Type::TypeKind::kVector) {
+        if (a->type().typeKind() == Type::TypeKind::kVector) {
             if (vectorSize) {
-                SkASSERT(a->fType.columns() == vectorSize);
+                SkASSERT(a->type().columns() == vectorSize);
             }
             else {
-                vectorSize = a->fType.columns();
+                vectorSize = a->type().columns();
             }
         }
     }
     std::vector<SpvId> result;
-    for (const auto& a : args) {
-        SpvId raw = this->writeExpression(*a, out);
-        if (vectorSize && a->fType.typeKind() == Type::TypeKind::kScalar) {
+    for (const auto& arg : args) {
+        const Type& argType = arg->type();
+        SpvId raw = this->writeExpression(*arg, out);
+        if (vectorSize && argType.typeKind() == Type::TypeKind::kScalar) {
             SpvId vector = this->nextId();
             this->writeOpCode(SpvOpCompositeConstruct, 3 + vectorSize, out);
-            this->writeWord(this->getType(a->fType.toCompound(fContext, vectorSize, 1)), out);
+            this->writeWord(this->getType(argType.toCompound(fContext, vectorSize, 1)), out);
             this->writeWord(vector, out);
             for (int i = 0; i < vectorSize; i++) {
                 this->writeWord(raw, out);
             }
-            this->writePrecisionModifier(a->fType, vector);
+            this->writePrecisionModifier(argType, vector);
             result.push_back(vector);
         } else {
             result.push_back(raw);
@@ -820,6 +821,7 @@
 SpvId SPIRVCodeGenerator::writeSpecialIntrinsic(const FunctionCall& c, SpecialIntrinsic kind,
                                                 OutputStream& out) {
     SpvId result = this->nextId();
+    const Type& callType = c.type();
     switch (kind) {
         case kAtan_SpecialIntrinsic: {
             std::vector<SpvId> arguments;
@@ -827,7 +829,7 @@
                 arguments.push_back(this->writeExpression(*c.fArguments[i], out));
             }
             this->writeOpCode(SpvOpExtInst, 5 + (int32_t) arguments.size(), out);
-            this->writeWord(this->getType(c.fType), out);
+            this->writeWord(this->getType(callType), out);
             this->writeWord(result, out);
             this->writeWord(fGLSLExtendedInstructions, out);
             this->writeWord(arguments.size() == 2 ? GLSLstd450Atan2 : GLSLstd450Atan, out);
@@ -841,7 +843,7 @@
             SpvId img = this->writeExpression(*c.fArguments[0], out);
             SpvId sampler = this->writeExpression(*c.fArguments[1], out);
             this->writeInstruction(SpvOpSampledImage,
-                                   this->getType(c.fType),
+                                   this->getType(callType),
                                    result,
                                    img,
                                    sampler,
@@ -853,11 +855,11 @@
             std::vector<std::unique_ptr<Expression>> args;
             args.emplace_back(new IntLiteral(fContext, -1, 0));
             args.emplace_back(new IntLiteral(fContext, -1, 0));
-            Constructor ctor(-1, *fContext.fInt2_Type, std::move(args));
+            Constructor ctor(-1, fContext.fInt2_Type.get(), std::move(args));
             SpvId coords = this->writeConstantVector(ctor);
             if (1 == c.fArguments.size()) {
                 this->writeInstruction(SpvOpImageRead,
-                                       this->getType(c.fType),
+                                       this->getType(callType),
                                        result,
                                        img,
                                        coords,
@@ -866,7 +868,7 @@
                 SkASSERT(2 == c.fArguments.size());
                 SpvId sample = this->writeExpression(*c.fArguments[1], out);
                 this->writeInstruction(SpvOpImageRead,
-                                       this->getType(c.fType),
+                                       this->getType(callType),
                                        result,
                                        img,
                                        coords,
@@ -878,26 +880,27 @@
         }
         case kTexture_SpecialIntrinsic: {
             SpvOp_ op = SpvOpImageSampleImplicitLod;
-            switch (c.fArguments[0]->fType.dimensions()) {
+            const Type& arg1Type = c.fArguments[1]->type();
+            switch (c.fArguments[0]->type().dimensions()) {
                 case SpvDim1D:
-                    if (c.fArguments[1]->fType == *fContext.fFloat2_Type) {
+                    if (arg1Type == *fContext.fFloat2_Type) {
                         op = SpvOpImageSampleProjImplicitLod;
                     } else {
-                        SkASSERT(c.fArguments[1]->fType == *fContext.fFloat_Type);
+                        SkASSERT(arg1Type == *fContext.fFloat_Type);
                     }
                     break;
                 case SpvDim2D:
-                    if (c.fArguments[1]->fType == *fContext.fFloat3_Type) {
+                    if (arg1Type == *fContext.fFloat3_Type) {
                         op = SpvOpImageSampleProjImplicitLod;
                     } else {
-                        SkASSERT(c.fArguments[1]->fType == *fContext.fFloat2_Type);
+                        SkASSERT(arg1Type == *fContext.fFloat2_Type);
                     }
                     break;
                 case SpvDim3D:
-                    if (c.fArguments[1]->fType == *fContext.fFloat4_Type) {
+                    if (arg1Type == *fContext.fFloat4_Type) {
                         op = SpvOpImageSampleProjImplicitLod;
                     } else {
-                        SkASSERT(c.fArguments[1]->fType == *fContext.fFloat3_Type);
+                        SkASSERT(arg1Type == *fContext.fFloat3_Type);
                     }
                     break;
                 case SpvDimCube:   // fall through
@@ -906,7 +909,7 @@
                 case SpvDimSubpassData:
                     break;
             }
-            SpvId type = this->getType(c.fType);
+            SpvId type = this->getType(callType);
             SpvId sampler = this->writeExpression(*c.fArguments[0], out);
             SpvId uv = this->writeExpression(*c.fArguments[1], out);
             if (c.fArguments.size() == 3) {
@@ -932,7 +935,7 @@
         case kMod_SpecialIntrinsic: {
             std::vector<SpvId> args = this->vectorize(c.fArguments, out);
             SkASSERT(args.size() == 2);
-            const Type& operandType = c.fArguments[0]->fType;
+            const Type& operandType = c.fArguments[0]->type();
             SpvOp_ op;
             if (is_float(fContext, operandType)) {
                 op = SpvOpFMod;
@@ -954,14 +957,15 @@
         case kDFdy_SpecialIntrinsic: {
             SpvId fn = this->writeExpression(*c.fArguments[0], out);
             this->writeOpCode(SpvOpDPdy, 4, out);
-            this->writeWord(this->getType(c.fType), out);
+            this->writeWord(this->getType(callType), out);
             this->writeWord(result, out);
             this->writeWord(fn, out);
             if (fProgram.fSettings.fFlipY) {
                 // Flipping Y also negates the Y derivatives.
                 SpvId flipped = this->nextId();
-                this->writeInstruction(SpvOpFNegate, this->getType(c.fType), flipped, result, out);
-                this->writePrecisionModifier(c.fType, flipped);
+                this->writeInstruction(SpvOpFNegate, this->getType(callType), flipped, result,
+                                       out);
+                this->writePrecisionModifier(callType, flipped);
                 return flipped;
             }
             break;
@@ -969,28 +973,28 @@
         case kClamp_SpecialIntrinsic: {
             std::vector<SpvId> args = this->vectorize(c.fArguments, out);
             SkASSERT(args.size() == 3);
-            this->writeGLSLExtendedInstruction(c.fType, result, GLSLstd450FClamp, GLSLstd450SClamp,
+            this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FClamp, GLSLstd450SClamp,
                                                GLSLstd450UClamp, args, out);
             break;
         }
         case kMax_SpecialIntrinsic: {
             std::vector<SpvId> args = this->vectorize(c.fArguments, out);
             SkASSERT(args.size() == 2);
-            this->writeGLSLExtendedInstruction(c.fType, result, GLSLstd450FMax, GLSLstd450SMax,
+            this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMax, GLSLstd450SMax,
                                                GLSLstd450UMax, args, out);
             break;
         }
         case kMin_SpecialIntrinsic: {
             std::vector<SpvId> args = this->vectorize(c.fArguments, out);
             SkASSERT(args.size() == 2);
-            this->writeGLSLExtendedInstruction(c.fType, result, GLSLstd450FMin, GLSLstd450SMin,
+            this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMin, GLSLstd450SMin,
                                                GLSLstd450UMin, args, out);
             break;
         }
         case kMix_SpecialIntrinsic: {
             std::vector<SpvId> args = this->vectorize(c.fArguments, out);
             SkASSERT(args.size() == 3);
-            this->writeGLSLExtendedInstruction(c.fType, result, GLSLstd450FMix, SpvOpUndef,
+            this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FMix, SpvOpUndef,
                                                SpvOpUndef, args, out);
             break;
         }
@@ -1001,7 +1005,7 @@
             finalArgs.emplace_back(new FloatLiteral(fContext, -1, 0));
             finalArgs.emplace_back(new FloatLiteral(fContext, -1, 1));
             std::vector<SpvId> spvArgs = this->vectorize(finalArgs, out);
-            this->writeGLSLExtendedInstruction(c.fType, result, GLSLstd450FClamp, GLSLstd450SClamp,
+            this->writeGLSLExtendedInstruction(callType, result, GLSLstd450FClamp, GLSLstd450SClamp,
                                                GLSLstd450UClamp, spvArgs, out);
             break;
         }
@@ -1035,7 +1039,7 @@
                 // update the lvalue.
                 tmpValueId = lv->load(out);
                 tmpVar = this->nextId();
-                lvalues.push_back(std::make_tuple(tmpVar, &c.fArguments[i]->fType, std::move(lv)));
+                lvalues.push_back(std::make_tuple(tmpVar, &c.fArguments[i]->type(), std::move(lv)));
             }
         } else {
             // see getFunctionType for an explanation of why we're always using pointer parameters
@@ -1043,7 +1047,7 @@
             tmpVar = this->nextId();
         }
         this->writeInstruction(SpvOpVariable,
-                               this->getPointerType(c.fArguments[i]->fType,
+                               this->getPointerType(c.fArguments[i]->type(),
                                                     SpvStorageClassFunction),
                                tmpVar,
                                SpvStorageClassFunction,
@@ -1053,7 +1057,7 @@
     }
     SpvId result = this->nextId();
     this->writeOpCode(SpvOpFunctionCall, 4 + (int32_t) c.fArguments.size(), out);
-    this->writeWord(this->getType(c.fType), out);
+    this->writeWord(this->getType(c.type()), out);
     this->writeWord(result, out);
     this->writeWord(entry->second, out);
     for (SpvId id : arguments) {
@@ -1072,25 +1076,26 @@
 }
 
 SpvId SPIRVCodeGenerator::writeConstantVector(const Constructor& c) {
-    SkASSERT(c.fType.typeKind() == Type::TypeKind::kVector && c.isCompileTimeConstant());
+    const Type& type = c.type();
+    SkASSERT(type.typeKind() == Type::TypeKind::kVector && c.isCompileTimeConstant());
     SpvId result = this->nextId();
     std::vector<SpvId> arguments;
     for (size_t i = 0; i < c.fArguments.size(); i++) {
         arguments.push_back(this->writeExpression(*c.fArguments[i], fConstantBuffer));
     }
-    SpvId type = this->getType(c.fType);
+    SpvId typeId = this->getType(type);
     if (c.fArguments.size() == 1) {
         // with a single argument, a vector will have all of its entries equal to the argument
-        this->writeOpCode(SpvOpConstantComposite, 3 + c.fType.columns(), fConstantBuffer);
-        this->writeWord(type, fConstantBuffer);
+        this->writeOpCode(SpvOpConstantComposite, 3 + type.columns(), fConstantBuffer);
+        this->writeWord(typeId, fConstantBuffer);
         this->writeWord(result, fConstantBuffer);
-        for (int i = 0; i < c.fType.columns(); i++) {
+        for (int i = 0; i < type.columns(); i++) {
             this->writeWord(arguments[0], fConstantBuffer);
         }
     } else {
         this->writeOpCode(SpvOpConstantComposite, 3 + (int32_t) c.fArguments.size(),
                           fConstantBuffer);
-        this->writeWord(type, fConstantBuffer);
+        this->writeWord(typeId, fConstantBuffer);
         this->writeWord(result, fConstantBuffer);
         for (SpvId id : arguments) {
             this->writeWord(id, fConstantBuffer);
@@ -1100,52 +1105,58 @@
 }
 
 SpvId SPIRVCodeGenerator::writeFloatConstructor(const Constructor& c, OutputStream& out) {
-    SkASSERT(c.fType.isFloat());
+    const Type& constructorType = c.type();
     SkASSERT(c.fArguments.size() == 1);
-    SkASSERT(c.fArguments[0]->fType.isNumber());
+    const Type& argType = c.fArguments[0]->type();
+    SkASSERT(constructorType.isFloat());
+    SkASSERT(argType.isNumber());
     SpvId result = this->nextId();
     SpvId parameter = this->writeExpression(*c.fArguments[0], out);
-    if (c.fArguments[0]->fType.isSigned()) {
-        this->writeInstruction(SpvOpConvertSToF, this->getType(c.fType), result, parameter,
+    if (argType.isSigned()) {
+        this->writeInstruction(SpvOpConvertSToF, this->getType(constructorType), result, parameter,
                                out);
     } else {
-        SkASSERT(c.fArguments[0]->fType.isUnsigned());
-        this->writeInstruction(SpvOpConvertUToF, this->getType(c.fType), result, parameter,
+        SkASSERT(argType.isUnsigned());
+        this->writeInstruction(SpvOpConvertUToF, this->getType(constructorType), result, parameter,
                                out);
     }
     return result;
 }
 
 SpvId SPIRVCodeGenerator::writeIntConstructor(const Constructor& c, OutputStream& out) {
-    SkASSERT(c.fType.isSigned());
+    const Type& constructorType = c.type();
     SkASSERT(c.fArguments.size() == 1);
-    SkASSERT(c.fArguments[0]->fType.isNumber());
+    const Type& argType = c.fArguments[0]->type();
+    SkASSERT(constructorType.isSigned());
+    SkASSERT(argType.isNumber());
     SpvId result = this->nextId();
     SpvId parameter = this->writeExpression(*c.fArguments[0], out);
-    if (c.fArguments[0]->fType.isFloat()) {
-        this->writeInstruction(SpvOpConvertFToS, this->getType(c.fType), result, parameter,
+    if (argType.isFloat()) {
+        this->writeInstruction(SpvOpConvertFToS, this->getType(constructorType), result, parameter,
                                out);
     }
     else {
-        SkASSERT(c.fArguments[0]->fType.isUnsigned());
-        this->writeInstruction(SpvOpBitcast, this->getType(c.fType), result, parameter,
+        SkASSERT(argType.isUnsigned());
+        this->writeInstruction(SpvOpBitcast, this->getType(constructorType), result, parameter,
                                out);
     }
     return result;
 }
 
 SpvId SPIRVCodeGenerator::writeUIntConstructor(const Constructor& c, OutputStream& out) {
-    SkASSERT(c.fType.isUnsigned());
+    const Type& constructorType = c.type();
     SkASSERT(c.fArguments.size() == 1);
-    SkASSERT(c.fArguments[0]->fType.isNumber());
+    const Type& argType = c.fArguments[0]->type();
+    SkASSERT(constructorType.isUnsigned());
+    SkASSERT(argType.isNumber());
     SpvId result = this->nextId();
     SpvId parameter = this->writeExpression(*c.fArguments[0], out);
-    if (c.fArguments[0]->fType.isFloat()) {
-        this->writeInstruction(SpvOpConvertFToU, this->getType(c.fType), result, parameter,
+    if (argType.isFloat()) {
+        this->writeInstruction(SpvOpConvertFToU, this->getType(constructorType), result, parameter,
                                out);
     } else {
-        SkASSERT(c.fArguments[0]->fType.isSigned());
-        this->writeInstruction(SpvOpBitcast, this->getType(c.fType), result, parameter,
+        SkASSERT(argType.isSigned());
+        this->writeInstruction(SpvOpBitcast, this->getType(constructorType), result, parameter,
                                out);
     }
     return result;
@@ -1289,7 +1300,10 @@
 }
 
 SpvId SPIRVCodeGenerator::writeMatrixConstructor(const Constructor& c, OutputStream& out) {
-    SkASSERT(c.fType.typeKind() == Type::TypeKind::kMatrix);
+    const Type& type = c.type();
+    SkASSERT(type.typeKind() == Type::TypeKind::kMatrix);
+    SkASSERT(c.fArguments.size() > 0);
+    const Type& arg0Type = c.fArguments[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;
@@ -1297,51 +1311,51 @@
         arguments.push_back(this->writeExpression(*c.fArguments[i], out));
     }
     SpvId result = this->nextId();
-    int rows = c.fType.rows();
-    int columns = c.fType.columns();
-    if (arguments.size() == 1 && c.fArguments[0]->fType.typeKind() == Type::TypeKind::kScalar) {
-        this->writeUniformScaleMatrix(result, arguments[0], c.fType, out);
+    int rows = type.rows();
+    int columns = type.columns();
+    if (arguments.size() == 1 && arg0Type.typeKind() == Type::TypeKind::kScalar) {
+        this->writeUniformScaleMatrix(result, arguments[0], type, out);
+    } else if (arguments.size() == 1 && arg0Type.typeKind() == Type::TypeKind::kMatrix) {
+        this->writeMatrixCopy(result, arguments[0], arg0Type, type, out);
     } else if (arguments.size() == 1 &&
-               c.fArguments[0]->fType.typeKind() == Type::TypeKind::kMatrix) {
-        this->writeMatrixCopy(result, arguments[0], c.fArguments[0]->fType, c.fType, out);
-    } else if (arguments.size() == 1 &&
-               c.fArguments[0]->fType.typeKind() == Type::TypeKind::kVector) {
-        SkASSERT(c.fType.rows() == 2 && c.fType.columns() == 2);
-        SkASSERT(c.fArguments[0]->fType.columns() == 4);
-        SpvId componentType = this->getType(c.fType.componentType());
+               arg0Type.typeKind() == Type::TypeKind::kVector) {
+        SkASSERT(type.rows() == 2 && type.columns() == 2);
+        SkASSERT(arg0Type.columns() == 4);
+        SpvId componentType = this->getType(type.componentType());
         SpvId v[4];
         for (int i = 0; i < 4; ++i) {
             v[i] = this->nextId();
-            this->writeInstruction(SpvOpCompositeExtract, componentType, v[i], arguments[0], i, out);
+            this->writeInstruction(SpvOpCompositeExtract, componentType, v[i], arguments[0], i,
+                                   out);
         }
-        SpvId columnType = this->getType(c.fType.componentType().toCompound(fContext, 2, 1));
+        SpvId columnType = this->getType(type.componentType().toCompound(fContext, 2, 1));
         SpvId column1 = this->nextId();
         this->writeInstruction(SpvOpCompositeConstruct, columnType, column1, v[0], v[1], out);
         SpvId column2 = this->nextId();
         this->writeInstruction(SpvOpCompositeConstruct, columnType, column2, v[2], v[3], out);
-        this->writeInstruction(SpvOpCompositeConstruct, this->getType(c.fType), result, column1,
+        this->writeInstruction(SpvOpCompositeConstruct, this->getType(type), result, column1,
                                column2, out);
     } else {
-        SpvId columnType = this->getType(c.fType.componentType().toCompound(fContext, rows, 1));
+        SpvId columnType = this->getType(type.componentType().toCompound(fContext, rows, 1));
         std::vector<SpvId> columnIds;
         // ids of vectors and scalars we have written to the current column so far
         std::vector<SpvId> currentColumn;
         // the total number of scalars represented by currentColumn's entries
         int currentCount = 0;
-        Precision precision = c.fType.highPrecision() ? Precision::kHigh : Precision::kLow;
+        Precision precision = type.highPrecision() ? Precision::kHigh : Precision::kLow;
         for (size_t i = 0; i < arguments.size(); i++) {
-            if (currentCount == 0 &&
-                c.fArguments[i]->fType.typeKind() == Type::TypeKind::kVector &&
-                c.fArguments[i]->fType.columns() == c.fType.rows()) {
+            const Type& argType = c.fArguments[i]->type();
+            if (currentCount == 0 && argType.typeKind() == Type::TypeKind::kVector &&
+                argType.columns() == type.rows()) {
                 // this is a complete column by itself
                 columnIds.push_back(arguments[i]);
             } else {
-                if (c.fArguments[i]->fType.columns() == 1) {
+                if (argType.columns() == 1) {
                     this->addColumnEntry(columnType, precision, &currentColumn, &columnIds,
                                          &currentCount, rows, arguments[i], out);
                 } else {
-                    SpvId componentType = this->getType(c.fArguments[i]->fType.componentType());
-                    for (int j = 0; j < c.fArguments[i]->fType.columns(); ++j) {
+                    SpvId componentType = this->getType(argType.componentType());
+                    for (int j = 0; j < argType.columns(); ++j) {
                         SpvId swizzle = this->nextId();
                         this->writeInstruction(SpvOpCompositeExtract, componentType, swizzle,
                                                arguments[i], j, out);
@@ -1353,18 +1367,19 @@
         }
         SkASSERT(columnIds.size() == (size_t) columns);
         this->writeOpCode(SpvOpCompositeConstruct, 3 + columns, out);
-        this->writeWord(this->getType(c.fType), out);
+        this->writeWord(this->getType(type), out);
         this->writeWord(result, out);
         for (SpvId id : columnIds) {
             this->writeWord(id, out);
         }
     }
-    this->writePrecisionModifier(c.fType, result);
+    this->writePrecisionModifier(type, result);
     return result;
 }
 
 SpvId SPIRVCodeGenerator::writeVectorConstructor(const Constructor& c, OutputStream& out) {
-    SkASSERT(c.fType.typeKind() == Type::TypeKind::kVector);
+    const Type& type = c.type();
+    SkASSERT(type.typeKind() == Type::TypeKind::kVector);
     if (c.isCompileTimeConstant()) {
         return this->writeConstantVector(c);
     }
@@ -1372,7 +1387,8 @@
     // an instruction
     std::vector<SpvId> arguments;
     for (size_t i = 0; i < c.fArguments.size(); i++) {
-        if (c.fArguments[i]->fType.typeKind() == Type::TypeKind::kVector) {
+        const Type& argType = c.fArguments[i]->type();
+        if (argType.typeKind() == Type::TypeKind::kVector) {
             // SPIR-V doesn't support vector(vector-of-different-type) directly, so we need to
             // extract the components and convert them in that case manually. On top of that,
             // as of this writing there's a bug in the Intel Vulkan driver where OpCreateComposite
@@ -1380,8 +1396,8 @@
             // pass them into OpCreateComposite individually.
             SpvId vec = this->writeExpression(*c.fArguments[i], out);
             SpvOp_ op = SpvOpUndef;
-            const Type& src = c.fArguments[i]->fType.componentType();
-            const Type& dst = c.fType.componentType();
+            const Type& src = argType.componentType();
+            const Type& dst = type.componentType();
             if (dst == *fContext.fFloat_Type || dst == *fContext.fHalf_Type) {
                 if (src == *fContext.fFloat_Type || src == *fContext.fHalf_Type) {
                     if (c.fArguments.size() == 1) {
@@ -1435,7 +1451,7 @@
                     SkASSERT(false);
                 }
             }
-            for (int j = 0; j < c.fArguments[i]->fType.columns(); j++) {
+            for (int j = 0; j < argType.columns(); j++) {
                 SpvId swizzle = this->nextId();
                 this->writeInstruction(SpvOpCompositeExtract, this->getType(src), swizzle, vec, j,
                                        out);
@@ -1452,17 +1468,17 @@
         }
     }
     SpvId result = this->nextId();
-    if (arguments.size() == 1 && c.fArguments[0]->fType.typeKind() == Type::TypeKind::kScalar) {
-        this->writeOpCode(SpvOpCompositeConstruct, 3 + c.fType.columns(), out);
-        this->writeWord(this->getType(c.fType), out);
+    if (arguments.size() == 1 && c.fArguments[0]->type().typeKind() == Type::TypeKind::kScalar) {
+        this->writeOpCode(SpvOpCompositeConstruct, 3 + type.columns(), out);
+        this->writeWord(this->getType(type), out);
         this->writeWord(result, out);
-        for (int i = 0; i < c.fType.columns(); i++) {
+        for (int i = 0; i < type.columns(); i++) {
             this->writeWord(arguments[0], out);
         }
     } else {
         SkASSERT(arguments.size() > 1);
         this->writeOpCode(SpvOpCompositeConstruct, 3 + (int32_t) arguments.size(), out);
-        this->writeWord(this->getType(c.fType), out);
+        this->writeWord(this->getType(type), out);
         this->writeWord(result, out);
         for (SpvId id : arguments) {
             this->writeWord(id, out);
@@ -1472,7 +1488,8 @@
 }
 
 SpvId SPIRVCodeGenerator::writeArrayConstructor(const Constructor& c, OutputStream& out) {
-    SkASSERT(c.fType.typeKind() == Type::TypeKind::kArray);
+    const Type& type = c.type();
+    SkASSERT(type.typeKind() == Type::TypeKind::kArray);
     // 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;
@@ -1481,7 +1498,7 @@
     }
     SpvId result = this->nextId();
     this->writeOpCode(SpvOpCompositeConstruct, 3 + (int32_t) c.fArguments.size(), out);
-    this->writeWord(this->getType(c.fType), out);
+    this->writeWord(this->getType(type), out);
     this->writeWord(result, out);
     for (SpvId id : arguments) {
         this->writeWord(id, out);
@@ -1490,22 +1507,23 @@
 }
 
 SpvId SPIRVCodeGenerator::writeConstructor(const Constructor& c, OutputStream& out) {
+    const Type& type = c.type();
     if (c.fArguments.size() == 1 &&
-        this->getActualType(c.fType) == this->getActualType(c.fArguments[0]->fType)) {
+        this->getActualType(type) == this->getActualType(c.fArguments[0]->type())) {
         return this->writeExpression(*c.fArguments[0], out);
     }
-    if (c.fType == *fContext.fFloat_Type || c.fType == *fContext.fHalf_Type) {
+    if (type == *fContext.fFloat_Type || type == *fContext.fHalf_Type) {
         return this->writeFloatConstructor(c, out);
-    } else if (c.fType == *fContext.fInt_Type ||
-               c.fType == *fContext.fShort_Type ||
-               c.fType == *fContext.fByte_Type) {
+    } else if (type == *fContext.fInt_Type ||
+               type == *fContext.fShort_Type ||
+               type == *fContext.fByte_Type) {
         return this->writeIntConstructor(c, out);
-    } else if (c.fType == *fContext.fUInt_Type ||
-               c.fType == *fContext.fUShort_Type ||
-               c.fType == *fContext.fUByte_Type) {
+    } else if (type == *fContext.fUInt_Type ||
+               type == *fContext.fUShort_Type ||
+               type == *fContext.fUByte_Type) {
         return this->writeUIntConstructor(c, out);
     }
-    switch (c.fType.typeKind()) {
+    switch (type.typeKind()) {
         case Type::TypeKind::kVector:
             return this->writeVectorConstructor(c, out);
         case Type::TypeKind::kMatrix:
@@ -1698,22 +1716,23 @@
 
 std::unique_ptr<SPIRVCodeGenerator::LValue> SPIRVCodeGenerator::getLValue(const Expression& expr,
                                                                           OutputStream& out) {
-    Precision precision = expr.fType.highPrecision() ? Precision::kHigh : Precision::kLow;
+    const Type& type = expr.type();
+    Precision precision = type.highPrecision() ? Precision::kHigh : Precision::kLow;
     switch (expr.kind()) {
         case Expression::Kind::kVariableReference: {
-            SpvId type;
+            SpvId typeId;
             const Variable& var = ((VariableReference&) expr).fVariable;
             if (var.fModifiers.fLayout.fBuiltin == SK_IN_BUILTIN) {
-                type = this->getType(Type("sk_in", Type::TypeKind::kArray,
-                                          var.fType.componentType(), fSkInCount));
+                typeId = this->getType(Type("sk_in", Type::TypeKind::kArray,
+                                            var.type().componentType(), fSkInCount));
             } else {
-                type = this->getType(expr.fType);
+                typeId = this->getType(type);
             }
             auto entry = fVariableMap.find(&var);
             SkASSERT(entry != fVariableMap.end());
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(*this,
                                                                                  entry->second,
-                                                                                 type,
+                                                                                 typeId,
                                                                                  precision));
         }
         case Expression::Kind::kIndex: // fall through
@@ -1721,16 +1740,16 @@
             std::vector<SpvId> chain = this->getAccessChain(expr, out);
             SpvId member = this->nextId();
             this->writeOpCode(SpvOpAccessChain, (SpvId) (3 + chain.size()), out);
-            this->writeWord(this->getPointerType(expr.fType, get_storage_class(expr)), out);
+            this->writeWord(this->getPointerType(type, get_storage_class(expr)), out);
             this->writeWord(member, out);
             for (SpvId idx : chain) {
                 this->writeWord(idx, out);
             }
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
-                                                                        *this,
-                                                                        member,
-                                                                        this->getType(expr.fType),
-                                                                        precision));
+                                                                                *this,
+                                                                                member,
+                                                                                this->getType(type),
+                                                                                precision));
         }
         case Expression::Kind::kSwizzle: {
             Swizzle& swizzle = (Swizzle&) expr;
@@ -1741,25 +1760,25 @@
                 IntLiteral index(fContext, -1, swizzle.fComponents[0]);
                 SpvId member = this->nextId();
                 this->writeInstruction(SpvOpAccessChain,
-                                       this->getPointerType(swizzle.fType,
+                                       this->getPointerType(type,
                                                             get_storage_class(*swizzle.fBase)),
                                        member,
                                        base,
                                        this->writeIntLiteral(index),
                                        out);
                 return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
-                                                                       *this,
-                                                                       member,
-                                                                       this->getType(expr.fType),
-                                                                       precision));
+                                                                                *this,
+                                                                                member,
+                                                                                this->getType(type),
+                                                                                precision));
             } else {
                 return std::unique_ptr<SPIRVCodeGenerator::LValue>(new SwizzleLValue(
-                                                                              *this,
-                                                                              base,
-                                                                              swizzle.fComponents,
-                                                                              swizzle.fBase->fType,
-                                                                              expr.fType,
-                                                                              precision));
+                                                                             *this,
+                                                                             base,
+                                                                             swizzle.fComponents,
+                                                                             swizzle.fBase->type(),
+                                                                             type,
+                                                                             precision));
             }
         }
         case Expression::Kind::kTernary: {
@@ -1783,26 +1802,27 @@
             this->writeInstruction(SpvOpPhi, this->getType(*fContext.fBool_Type), result, ifTrue,
                        ifTrueLabel, ifFalse, ifFalseLabel, out);
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
-                                                                       *this,
-                                                                       result,
-                                                                       this->getType(expr.fType),
-                                                                       precision));
+                                                                                *this,
+                                                                                result,
+                                                                                this->getType(type),
+                                                                                precision));
         }
-        default:
+        default: {
             // expr isn't actually an lvalue, create a dummy variable for it. This case happens due
             // to the need to store values in temporary variables during function calls (see
             // comments in getFunctionType); erroneous uses of rvalues as lvalues should have been
             // caught by IRGenerator
             SpvId result = this->nextId();
-            SpvId type = this->getPointerType(expr.fType, SpvStorageClassFunction);
-            this->writeInstruction(SpvOpVariable, type, result, SpvStorageClassFunction,
+            SpvId pointerType = this->getPointerType(type, SpvStorageClassFunction);
+            this->writeInstruction(SpvOpVariable, pointerType, result, SpvStorageClassFunction,
                                    fVariableBuffer);
             this->writeInstruction(SpvOpStore, result, this->writeExpression(expr, out), out);
             return std::unique_ptr<SPIRVCodeGenerator::LValue>(new PointerLValue(
-                                                                       *this,
-                                                                       result,
-                                                                       this->getType(expr.fType),
-                                                                       precision));
+                                                                                *this,
+                                                                                result,
+                                                                                this->getType(type),
+                                                                                precision));
+        }
     }
 }
 
@@ -1811,8 +1831,8 @@
     auto entry = fVariableMap.find(&ref.fVariable);
     SkASSERT(entry != fVariableMap.end());
     SpvId var = entry->second;
-    this->writeInstruction(SpvOpLoad, this->getType(ref.fVariable.fType), result, var, out);
-    this->writePrecisionModifier(ref.fVariable.fType, result);
+    this->writeInstruction(SpvOpLoad, this->getType(ref.fVariable.type()), result, var, out);
+    this->writePrecisionModifier(ref.fVariable.type(), result);
     if (ref.fVariable.fModifiers.fLayout.fBuiltin == SK_FRAGCOORD_BUILTIN &&
         (fProgram.fSettings.fFlipY || fProgram.fSettings.fInverseW)) {
         // The x component never changes, so just grab it
@@ -1854,7 +1874,7 @@
                         std::make_unique<Variable>(/*offset=*/-1,
                                                    Modifiers(layout, Modifiers::kUniform_Flag),
                                                    name,
-                                                   intfStruct,
+                                                   &intfStruct,
                                                    Variable::kGlobal_Storage));
                 InterfaceBlock intf(-1, intfVar, name, String(""),
                                     std::vector<std::unique_ptr<Expression>>(), st);
@@ -1933,11 +1953,11 @@
 }
 
 SpvId SPIRVCodeGenerator::writeIndexExpression(const IndexExpression& expr, OutputStream& out) {
-    if (expr.fBase->fType.typeKind() == Type::TypeKind::kVector) {
+    if (expr.fBase->type().typeKind() == Type::TypeKind::kVector) {
         SpvId base = this->writeExpression(*expr.fBase, out);
         SpvId index = this->writeExpression(*expr.fIndex, out);
         SpvId result = this->nextId();
-        this->writeInstruction(SpvOpVectorExtractDynamic, this->getType(expr.fType), result, base,
+        this->writeInstruction(SpvOpVectorExtractDynamic, this->getType(expr.type()), result, base,
                                index, out);
         return result;
     }
@@ -1953,11 +1973,11 @@
     SpvId result = this->nextId();
     size_t count = swizzle.fComponents.size();
     if (count == 1) {
-        this->writeInstruction(SpvOpCompositeExtract, this->getType(swizzle.fType), result, base,
+        this->writeInstruction(SpvOpCompositeExtract, this->getType(swizzle.type()), result, base,
                                swizzle.fComponents[0], out);
     } else {
         this->writeOpCode(SpvOpVectorShuffle, 5 + (int32_t) count, out);
-        this->writeWord(this->getType(swizzle.fType), out);
+        this->writeWord(this->getType(swizzle.type()), out);
         this->writeWord(result, out);
         this->writeWord(base, out);
         SpvId other = base;
@@ -1981,11 +2001,12 @@
             }
         }
         this->writeWord(other, out);
+        int baseColumns = swizzle.fBase->type().columns();
         for (int component : swizzle.fComponents) {
             if (component == SKSL_SWIZZLE_0) {
-                this->writeWord(swizzle.fBase->fType.columns(), out);
+                this->writeWord(baseColumns, out);
             } else if (component == SKSL_SWIZZLE_1) {
-                this->writeWord(swizzle.fBase->fType.columns() + 1, out);
+                this->writeWord(baseColumns + 1, out);
             } else {
                 this->writeWord(component, out);
             }
@@ -2333,9 +2354,9 @@
         lhs = this->writeExpression(*b.fLeft, out);
     }
     SpvId rhs = this->writeExpression(*b.fRight, out);
-    SpvId result = this->writeBinaryExpression(b.fLeft->fType, lhs,
+    SpvId result = this->writeBinaryExpression(b.fLeft->type(), lhs,
                                                Compiler::RemoveAssignment(b.fOperator),
-                                               b.fRight->fType, rhs, b.fType, out);
+                                               b.fRight->type(), rhs, b.type(), out);
     if (lvalue) {
         lvalue->store(result, out);
     }
@@ -2385,22 +2406,23 @@
 }
 
 SpvId SPIRVCodeGenerator::writeTernaryExpression(const TernaryExpression& t, OutputStream& out) {
+    const Type& type = t.type();
     SpvId test = this->writeExpression(*t.fTest, out);
-    if (t.fIfTrue->fType.columns() == 1 &&
+    if (t.fIfTrue->type().columns() == 1 &&
         t.fIfTrue->isCompileTimeConstant() &&
         t.fIfFalse->isCompileTimeConstant()) {
         // both true and false are constants, can just use OpSelect
         SpvId result = this->nextId();
         SpvId trueId = this->writeExpression(*t.fIfTrue, out);
         SpvId falseId = this->writeExpression(*t.fIfFalse, out);
-        this->writeInstruction(SpvOpSelect, this->getType(t.fType), result, test, trueId, falseId,
+        this->writeInstruction(SpvOpSelect, this->getType(type), result, test, trueId, falseId,
                                out);
         return result;
     }
     // was originally using OpPhi to choose the result, but for some reason that is crashing on
     // Adreno. Switched to storing the result in a temp variable as glslang does.
     SpvId var = this->nextId();
-    this->writeInstruction(SpvOpVariable, this->getPointerType(t.fType, SpvStorageClassFunction),
+    this->writeInstruction(SpvOpVariable, this->getPointerType(type, SpvStorageClassFunction),
                            var, SpvStorageClassFunction, fVariableBuffer);
     SpvId trueLabel = this->nextId();
     SpvId falseLabel = this->nextId();
@@ -2415,26 +2437,27 @@
     this->writeInstruction(SpvOpBranch, end, out);
     this->writeLabel(end, out);
     SpvId result = this->nextId();
-    this->writeInstruction(SpvOpLoad, this->getType(t.fType), result, var, out);
-    this->writePrecisionModifier(t.fType, result);
+    this->writeInstruction(SpvOpLoad, this->getType(type), result, var, out);
+    this->writePrecisionModifier(type, result);
     return result;
 }
 
 SpvId SPIRVCodeGenerator::writePrefixExpression(const PrefixExpression& p, OutputStream& out) {
+    const Type& type = p.type();
     if (p.fOperator == Token::Kind::TK_MINUS) {
         SpvId result = this->nextId();
-        SpvId typeId = this->getType(p.fType);
+        SpvId typeId = this->getType(type);
         SpvId expr = this->writeExpression(*p.fOperand, out);
-        if (is_float(fContext, p.fType)) {
+        if (is_float(fContext, type)) {
             this->writeInstruction(SpvOpFNegate, typeId, result, expr, out);
-        } else if (is_signed(fContext, p.fType)) {
+        } else if (is_signed(fContext, type)) {
             this->writeInstruction(SpvOpSNegate, typeId, result, expr, out);
         } else {
 #ifdef SK_DEBUG
             ABORT("unsupported prefix expression %s", p.description().c_str());
 #endif
         }
-        this->writePrecisionModifier(p.fType, result);
+        this->writePrecisionModifier(type, result);
         return result;
     }
     switch (p.fOperator) {
@@ -2442,8 +2465,8 @@
             return this->writeExpression(*p.fOperand, out);
         case Token::Kind::TK_PLUSPLUS: {
             std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
-            SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
-            SpvId result = this->writeBinaryOperation(p.fType, p.fType, lv->load(out), one,
+            SpvId one = this->writeExpression(*create_literal_1(fContext, type), out);
+            SpvId result = this->writeBinaryOperation(type, type, lv->load(out), one,
                                                       SpvOpFAdd, SpvOpIAdd, SpvOpIAdd, SpvOpUndef,
                                                       out);
             lv->store(result, out);
@@ -2451,23 +2474,22 @@
         }
         case Token::Kind::TK_MINUSMINUS: {
             std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
-            SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
-            SpvId result = this->writeBinaryOperation(p.fType, p.fType, lv->load(out), one,
-                                                      SpvOpFSub, SpvOpISub, SpvOpISub, SpvOpUndef,
-                                                      out);
+            SpvId one = this->writeExpression(*create_literal_1(fContext, type), out);
+            SpvId result = this->writeBinaryOperation(type, type, lv->load(out), one, SpvOpFSub,
+                                                      SpvOpISub, SpvOpISub, SpvOpUndef, out);
             lv->store(result, out);
             return result;
         }
         case Token::Kind::TK_LOGICALNOT: {
-            SkASSERT(p.fOperand->fType == *fContext.fBool_Type);
+            SkASSERT(p.fOperand->type() == *fContext.fBool_Type);
             SpvId result = this->nextId();
-            this->writeInstruction(SpvOpLogicalNot, this->getType(p.fOperand->fType), result,
+            this->writeInstruction(SpvOpLogicalNot, this->getType(p.fOperand->type()), result,
                                    this->writeExpression(*p.fOperand, out), out);
             return result;
         }
         case Token::Kind::TK_BITWISENOT: {
             SpvId result = this->nextId();
-            this->writeInstruction(SpvOpNot, this->getType(p.fOperand->fType), result,
+            this->writeInstruction(SpvOpNot, this->getType(p.fOperand->type()), result,
                                    this->writeExpression(*p.fOperand, out), out);
             return result;
         }
@@ -2480,18 +2502,19 @@
 }
 
 SpvId SPIRVCodeGenerator::writePostfixExpression(const PostfixExpression& p, OutputStream& out) {
+    const Type& type = p.type();
     std::unique_ptr<LValue> lv = this->getLValue(*p.fOperand, out);
     SpvId result = lv->load(out);
-    SpvId one = this->writeExpression(*create_literal_1(fContext, p.fType), out);
+    SpvId one = this->writeExpression(*create_literal_1(fContext, type), out);
     switch (p.fOperator) {
         case Token::Kind::TK_PLUSPLUS: {
-            SpvId temp = this->writeBinaryOperation(p.fType, p.fType, result, one, SpvOpFAdd,
+            SpvId temp = this->writeBinaryOperation(type, type, result, one, SpvOpFAdd,
                                                     SpvOpIAdd, SpvOpIAdd, SpvOpUndef, out);
             lv->store(temp, out);
             return result;
         }
         case Token::Kind::TK_MINUSMINUS: {
-            SpvId temp = this->writeBinaryOperation(p.fType, p.fType, result, one, SpvOpFSub,
+            SpvId temp = this->writeBinaryOperation(type, type, result, one, SpvOpFSub,
                                                     SpvOpISub, SpvOpISub, SpvOpUndef, out);
             lv->store(temp, out);
             return result;
@@ -2508,14 +2531,14 @@
     if (b.fValue) {
         if (fBoolTrue == 0) {
             fBoolTrue = this->nextId();
-            this->writeInstruction(SpvOpConstantTrue, this->getType(b.fType), fBoolTrue,
+            this->writeInstruction(SpvOpConstantTrue, this->getType(b.type()), fBoolTrue,
                                    fConstantBuffer);
         }
         return fBoolTrue;
     } else {
         if (fBoolFalse == 0) {
             fBoolFalse = this->nextId();
-            this->writeInstruction(SpvOpConstantFalse, this->getType(b.fType), fBoolFalse,
+            this->writeInstruction(SpvOpConstantFalse, this->getType(b.type()), fBoolFalse,
                                    fConstantBuffer);
         }
         return fBoolFalse;
@@ -2523,23 +2546,24 @@
 }
 
 SpvId SPIRVCodeGenerator::writeIntLiteral(const IntLiteral& i) {
-    ConstantType type;
-    if (i.fType == *fContext.fInt_Type) {
-        type = ConstantType::kInt;
-    } else if (i.fType == *fContext.fUInt_Type) {
-        type = ConstantType::kUInt;
-    } else if (i.fType == *fContext.fShort_Type || i.fType == *fContext.fByte_Type) {
-        type = ConstantType::kShort;
-    } else if (i.fType == *fContext.fUShort_Type || i.fType == *fContext.fUByte_Type) {
-        type = ConstantType::kUShort;
+    const Type& type = i.type();
+    ConstantType constantType;
+    if (type == *fContext.fInt_Type) {
+        constantType = ConstantType::kInt;
+    } else if (type == *fContext.fUInt_Type) {
+        constantType = ConstantType::kUInt;
+    } else if (type == *fContext.fShort_Type || type == *fContext.fByte_Type) {
+        constantType = ConstantType::kShort;
+    } else if (type == *fContext.fUShort_Type || type == *fContext.fUByte_Type) {
+        constantType = ConstantType::kUShort;
     } else {
         SkASSERT(false);
     }
-    std::pair<ConstantValue, ConstantType> key(i.fValue, type);
+    std::pair<ConstantValue, ConstantType> key(i.fValue, constantType);
     auto entry = fNumberConstants.find(key);
     if (entry == fNumberConstants.end()) {
         SpvId result = this->nextId();
-        this->writeInstruction(SpvOpConstant, this->getType(i.fType), result, (SpvId) i.fValue,
+        this->writeInstruction(SpvOpConstant, this->getType(type), result, (SpvId) i.fValue,
                                fConstantBuffer);
         fNumberConstants[key] = result;
         return result;
@@ -2548,21 +2572,22 @@
 }
 
 SpvId SPIRVCodeGenerator::writeFloatLiteral(const FloatLiteral& f) {
-    ConstantType type;
-    if (f.fType == *fContext.fHalf_Type) {
-        type = ConstantType::kHalf;
+    const Type& type = f.type();
+    ConstantType constantType;
+    if (type == *fContext.fHalf_Type) {
+        constantType = ConstantType::kHalf;
     } else {
-        type = ConstantType::kFloat;
+        constantType = ConstantType::kFloat;
     }
     float value = (float) f.fValue;
-    std::pair<ConstantValue, ConstantType> key(f.fValue, type);
+    std::pair<ConstantValue, ConstantType> key(f.fValue, constantType);
     auto entry = fNumberConstants.find(key);
     if (entry == fNumberConstants.end()) {
         SpvId result = this->nextId();
         uint32_t bits;
         SkASSERT(sizeof(bits) == sizeof(value));
         memcpy(&bits, &value, sizeof(bits));
-        this->writeInstruction(SpvOpConstant, this->getType(f.fType), result, bits,
+        this->writeInstruction(SpvOpConstant, this->getType(type), result, bits,
                                fConstantBuffer);
         fNumberConstants[key] = result;
         return result;
@@ -2579,7 +2604,7 @@
         SpvId id = this->nextId();
         fVariableMap[f.fParameters[i]] = id;
         SpvId type;
-        type = this->getPointerType(f.fParameters[i]->fType, SpvStorageClassFunction);
+        type = this->getPointerType(f.fParameters[i]->type(), SpvStorageClassFunction);
         this->writeInstruction(SpvOpFunctionParameter, type, id, out);
     }
     return result;
@@ -2693,7 +2718,7 @@
                                 MemoryLayout(MemoryLayout::k430_Standard) :
                                 fDefaultLayout;
     SpvId result = this->nextId();
-    const Type* type = &intf.fVariable.fType;
+    const Type* type = &intf.fVariable.type();
     if (fProgram.fInputs.fRTHeight && appendRTHeight) {
         SkASSERT(fRTHeightStructId == (SpvId) -1);
         SkASSERT(fRTHeightFieldIndex == (SpvId) -1);
@@ -2712,7 +2737,7 @@
             }
         }
         typeId = this->getType(Type("sk_in", Type::TypeKind::kArray,
-                                    intf.fVariable.fType.componentType(),
+                                    intf.fVariable.type().componentType(),
                                     fSkInCount),
                                memoryLayout);
     } else {
@@ -2792,15 +2817,16 @@
         if (is_dead(*var)) {
             continue;
         }
+        const Type& type = var->type();
         SpvStorageClass_ storageClass;
         if (var->fModifiers.fFlags & Modifiers::kIn_Flag) {
             storageClass = SpvStorageClassInput;
         } else if (var->fModifiers.fFlags & Modifiers::kOut_Flag) {
             storageClass = SpvStorageClassOutput;
         } else if (var->fModifiers.fFlags & Modifiers::kUniform_Flag) {
-            if (var->fType.typeKind() == Type::TypeKind::kSampler ||
-                var->fType.typeKind() == Type::TypeKind::kSeparateSampler ||
-                var->fType.typeKind() == Type::TypeKind::kTexture) {
+            if (type.typeKind() == Type::TypeKind::kSampler ||
+                type.typeKind() == Type::TypeKind::kSeparateSampler ||
+                type.typeKind() == Type::TypeKind::kTexture) {
                 storageClass = SpvStorageClassUniformConstant;
             } else {
                 storageClass = SpvStorageClassUniform;
@@ -2810,17 +2836,17 @@
         }
         SpvId id = this->nextId();
         fVariableMap[var] = id;
-        SpvId type;
+        SpvId typeId;
         if (var->fModifiers.fLayout.fBuiltin == SK_IN_BUILTIN) {
-            type = this->getPointerType(Type("sk_in", Type::TypeKind::kArray,
-                                             var->fType.componentType(), fSkInCount),
+            typeId = this->getPointerType(Type("sk_in", Type::TypeKind::kArray,
+                                             type.componentType(), fSkInCount),
                                         storageClass);
         } else {
-            type = this->getPointerType(var->fType, storageClass);
+            typeId = this->getPointerType(type, storageClass);
         }
-        this->writeInstruction(SpvOpVariable, type, id, storageClass, fConstantBuffer);
+        this->writeInstruction(SpvOpVariable, typeId, id, storageClass, fConstantBuffer);
         this->writeInstruction(SpvOpName, id, var->fName, fNameBuffer);
-        this->writePrecisionModifier(var->fType, id);
+        this->writePrecisionModifier(type, id);
         if (varDecl.fValue) {
             SkASSERT(!fCurrentBlock);
             fCurrentBlock = -1;
@@ -2853,7 +2879,7 @@
                                            Modifiers::kRestrict_Flag)));
         SpvId id = this->nextId();
         fVariableMap[var] = id;
-        SpvId type = this->getPointerType(var->fType, SpvStorageClassFunction);
+        SpvId type = this->getPointerType(var->type(), SpvStorageClassFunction);
         this->writeInstruction(SpvOpVariable, type, id, SpvStorageClassFunction, fVariableBuffer);
         this->writeInstruction(SpvOpName, id, var->fName, fNameBuffer);
         if (varDecl.fValue) {
diff --git a/src/sksl/ir/SkSLBinaryExpression.h b/src/sksl/ir/SkSLBinaryExpression.h
index 3a927fc..18ce2b7 100644
--- a/src/sksl/ir/SkSLBinaryExpression.h
+++ b/src/sksl/ir/SkSLBinaryExpression.h
@@ -50,7 +50,7 @@
     static constexpr Kind kExpressionKind = Kind::kBinary;
 
     BinaryExpression(int offset, std::unique_ptr<Expression> left, Token::Kind op,
-                     std::unique_ptr<Expression> right, const Type& type)
+                     std::unique_ptr<Expression> right, const Type* type)
     : INHERITED(offset, kExpressionKind, type)
     , fLeft(std::move(left))
     , fOperator(op)
@@ -79,7 +79,7 @@
 
     std::unique_ptr<Expression> clone() const override {
         return std::unique_ptr<Expression>(new BinaryExpression(fOffset, fLeft->clone(), fOperator,
-                                                                fRight->clone(), fType));
+                                                                fRight->clone(), &this->type()));
     }
 
     String description() const override {
diff --git a/src/sksl/ir/SkSLBoolLiteral.h b/src/sksl/ir/SkSLBoolLiteral.h
index 8e0c21a..c82014e 100644
--- a/src/sksl/ir/SkSLBoolLiteral.h
+++ b/src/sksl/ir/SkSLBoolLiteral.h
@@ -20,7 +20,7 @@
     static constexpr Kind kExpressionKind = Kind::kBoolLiteral;
 
     BoolLiteral(const Context& context, int offset, bool value)
-    : INHERITED(offset, kExpressionKind, *context.fBool_Type)
+    : INHERITED(offset, kExpressionKind, context.fBool_Type.get())
     , fValue(value) {}
 
     String description() const override {
@@ -41,7 +41,7 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new BoolLiteral(fOffset, fValue, &fType));
+        return std::unique_ptr<Expression>(new BoolLiteral(fOffset, fValue, &this->type()));
     }
 
     const bool fValue;
@@ -50,7 +50,7 @@
 
 private:
     BoolLiteral(int offset, bool value, const Type* type)
-    : INHERITED(offset, kExpressionKind, *type)
+    : INHERITED(offset, kExpressionKind, type)
     , fValue(value) {}
 };
 
diff --git a/src/sksl/ir/SkSLConstructor.h b/src/sksl/ir/SkSLConstructor.h
index 051bda1..60abe0a 100644
--- a/src/sksl/ir/SkSLConstructor.h
+++ b/src/sksl/ir/SkSLConstructor.h
@@ -28,25 +28,26 @@
 struct Constructor : public Expression {
     static constexpr Kind kExpressionKind = Kind::kConstructor;
 
-    Constructor(int offset, const Type& type, std::vector<std::unique_ptr<Expression>> arguments)
+    Constructor(int offset, const Type* type, std::vector<std::unique_ptr<Expression>> arguments)
     : INHERITED(offset, kExpressionKind, type)
     , fArguments(std::move(arguments)) {}
 
     std::unique_ptr<Expression> constantPropagate(const IRGenerator& irGenerator,
                                                   const DefinitionMap& definitions) override {
         if (fArguments.size() == 1 && fArguments[0]->kind() == Expression::Kind::kIntLiteral) {
-            if (fType.isFloat()) {
+            const Type& type = this->type();
+            if (type.isFloat()) {
                 // promote float(1) to 1.0
                 int64_t intValue = fArguments[0]->as<IntLiteral>().fValue;
                 return std::unique_ptr<Expression>(new FloatLiteral(irGenerator.fContext,
                                                                     fOffset,
                                                                     intValue));
-            } else if (fType.isInteger()) {
+            } else if (type.isInteger()) {
                 // promote uint(1) to 1u
                 int64_t intValue = fArguments[0]->as<IntLiteral>().fValue;
                 return std::unique_ptr<Expression>(new IntLiteral(fOffset,
                                                                   intValue,
-                                                                  &fType));
+                                                                  &type));
             }
         }
         return nullptr;
@@ -66,11 +67,12 @@
         for (const auto& arg : fArguments) {
             cloned.push_back(arg->clone());
         }
-        return std::unique_ptr<Expression>(new Constructor(fOffset, fType, std::move(cloned)));
+        return std::unique_ptr<Expression>(new Constructor(fOffset, &this->type(),
+                                                           std::move(cloned)));
     }
 
     String description() const override {
-        String result = fType.description() + "(";
+        String result = this->type().description() + "(";
         String separator;
         for (size_t i = 0; i < fArguments.size(); i++) {
             result += separator;
@@ -100,12 +102,14 @@
     }
 
     bool compareConstant(const Context& context, const Expression& other) const override {
-        SkASSERT(other.kind() == Expression::Kind::kConstructor && other.fType == fType);
-        Constructor& c = (Constructor&) other;
-        if (c.fType.typeKind() == Type::TypeKind::kVector) {
-            bool isFloat = c.fType.columns() > 1 ? c.fType.componentType().isFloat()
-                                                 : c.fType.isFloat();
-            for (int i = 0; i < fType.columns(); i++) {
+        const Constructor& c = other.as<Constructor>();
+        const Type& myType = this->type();
+        const Type& otherType = c.type();
+        SkASSERT(myType == otherType);
+        if (otherType.typeKind() == Type::TypeKind::kVector) {
+            bool isFloat = otherType.columns() > 1 ? otherType.componentType().isFloat()
+                                                 : otherType.isFloat();
+            for (int i = 0; i < myType.columns(); i++) {
                 if (isFloat) {
                     if (this->getFVecComponent(i) != c.getFVecComponent(i)) {
                         return false;
@@ -119,9 +123,9 @@
         // shouldn't be possible to have a constant constructor that isn't a vector or matrix;
         // a constant scalar constructor should have been collapsed down to the appropriate
         // literal
-        SkASSERT(fType.typeKind() == Type::TypeKind::kMatrix);
-        for (int col = 0; col < fType.columns(); col++) {
-            for (int row = 0; row < fType.rows(); row++) {
+        SkASSERT(myType.typeKind() == Type::TypeKind::kMatrix);
+        for (int col = 0; col < myType.columns(); col++) {
+            for (int row = 0; row < myType.rows(); row++) {
                 if (getMatComponent(col, row) != c.getMatComponent(col, row)) {
                     return false;
                 }
@@ -132,8 +136,9 @@
 
     template <typename type>
     type getVecComponent(int index) const {
-        SkASSERT(fType.typeKind() == Type::TypeKind::kVector);
-        if (fArguments.size() == 1 && fArguments[0]->fType.typeKind() == Type::TypeKind::kScalar) {
+        SkASSERT(this->type().typeKind() == Type::TypeKind::kVector);
+        if (fArguments.size() == 1 &&
+            fArguments[0]->type().typeKind() == Type::TypeKind::kScalar) {
             // This constructor just wraps a scalar. Propagate out the value.
             if (std::is_floating_point<type>::value) {
                 return fArguments[0]->getConstantFloat();
@@ -150,7 +155,7 @@
                 break;
             }
 
-            if (arg->fType.typeKind() == Type::TypeKind::kScalar) {
+            if (arg->type().typeKind() == Type::TypeKind::kScalar) {
                 if (index == current) {
                     // We're on the proper argument, and it's a scalar; fetch it.
                     if (std::is_floating_point<type>::value) {
@@ -166,10 +171,10 @@
             switch (arg->kind()) {
                 case Kind::kConstructor: {
                     const Constructor& constructor = static_cast<const Constructor&>(*arg);
-                    if (current + constructor.fType.columns() > index) {
+                    if (current + constructor.type().columns() > index) {
                         // We've found a constructor that overlaps the proper argument. Descend into
                         // it, honoring the type.
-                        if (constructor.fType.componentType().isFloat()) {
+                        if (constructor.type().componentType().isFloat()) {
                             return type(constructor.getVecComponent<SKSL_FLOAT>(index - current));
                         } else {
                             return type(constructor.getVecComponent<SKSL_INT>(index - current));
@@ -179,7 +184,7 @@
                 }
                 case Kind::kPrefix: {
                     const PrefixExpression& prefix = static_cast<const PrefixExpression&>(*arg);
-                    if (current + prefix.fType.columns() > index) {
+                    if (current + prefix.type().columns() > index) {
                         // We found a prefix operator that contains the proper argument. Descend
                         // into it. We only support for constant propagation of the unary minus, so
                         // we shouldn't see any other tokens here.
@@ -191,7 +196,7 @@
                                 static_cast<const Constructor&>(*prefix.fOperand);
 
                         // Descend into this constructor, honoring the type.
-                        if (constructor.fType.componentType().isFloat()) {
+                        if (constructor.type().componentType().isFloat()) {
                             return -type(constructor.getVecComponent<SKSL_FLOAT>(index - current));
                         } else {
                             return -type(constructor.getVecComponent<SKSL_INT>(index - current));
@@ -206,7 +211,7 @@
                 }
             }
 
-            current += arg->fType.columns();
+            current += arg->type().columns();
         }
 
         SkDEBUGFAILF("failed to find vector component %d in %s\n", index, description().c_str());
@@ -226,11 +231,13 @@
     }
 
     SKSL_FLOAT getMatComponent(int col, int row) const override {
+        SkDEBUGCODE(const Type& myType = this->type();)
         SkASSERT(this->isCompileTimeConstant());
-        SkASSERT(fType.typeKind() == Type::TypeKind::kMatrix);
-        SkASSERT(col < fType.columns() && row < fType.rows());
+        SkASSERT(myType.typeKind() == Type::TypeKind::kMatrix);
+        SkASSERT(col < myType.columns() && row < myType.rows());
         if (fArguments.size() == 1) {
-            if (fArguments[0]->fType.typeKind() == Type::TypeKind::kScalar) {
+            const Type& argType = fArguments[0]->type();
+            if (argType.typeKind() == Type::TypeKind::kScalar) {
                 // single scalar argument, so matrix is of the form:
                 // x 0 0
                 // 0 x 0
@@ -238,10 +245,9 @@
                 // return x if col == row
                 return col == row ? fArguments[0]->getConstantFloat() : 0.0;
             }
-            if (fArguments[0]->fType.typeKind() == Type::TypeKind::kMatrix) {
+            if (argType.typeKind() == Type::TypeKind::kMatrix) {
                 SkASSERT(fArguments[0]->kind() == Expression::Kind::kConstructor);
                 // single matrix argument. make sure we're within the argument's bounds.
-                const Type& argType = ((Constructor&) *fArguments[0]).fType;
                 if (col < argType.columns() && row < argType.rows()) {
                     // within bounds, defer to argument
                     return ((Constructor&) *fArguments[0]).getMatComponent(col, row);
@@ -251,18 +257,19 @@
             }
         }
         int currentIndex = 0;
-        int targetIndex = col * fType.rows() + row;
+        int targetIndex = col * this->type().rows() + row;
         for (const auto& arg : fArguments) {
+            const Type& argType = arg->type();
             SkASSERT(targetIndex >= currentIndex);
-            SkASSERT(arg->fType.rows() == 1);
-            if (currentIndex + arg->fType.columns() > targetIndex) {
-                if (arg->fType.columns() == 1) {
+            SkASSERT(argType.rows() == 1);
+            if (currentIndex + argType.columns() > targetIndex) {
+                if (argType.columns() == 1) {
                     return arg->getConstantFloat();
                 } else {
                     return arg->getFVecComponent(targetIndex - currentIndex);
                 }
             }
-            currentIndex += arg->fType.columns();
+            currentIndex += argType.columns();
         }
         ABORT("can't happen, matrix component out of bounds");
     }
diff --git a/src/sksl/ir/SkSLExpression.h b/src/sksl/ir/SkSLExpression.h
index 2203aae..4e6c17c 100644
--- a/src/sksl/ir/SkSLExpression.h
+++ b/src/sksl/ir/SkSLExpression.h
@@ -56,9 +56,8 @@
         kContainsRTAdjust
     };
 
-    Expression(int offset, Kind kind, const Type& type)
-    : INHERITED(offset, (int) kind)
-    , fType(std::move(type)) {
+    Expression(int offset, Kind kind, const Type* type)
+    : INHERITED(offset, (int) kind, type) {
         SkASSERT(kind >= Kind::kFirst && kind <= Kind::kLast);
     }
 
@@ -156,7 +155,7 @@
     }
 
     virtual int coercionCost(const Type& target) const {
-        return fType.coercionCost(target);
+        return this->type().coercionCost(target);
     }
 
     /**
@@ -189,8 +188,6 @@
 
     virtual std::unique_ptr<Expression> clone() const = 0;
 
-    const Type& fType;
-
     using INHERITED = IRNode;
 };
 
diff --git a/src/sksl/ir/SkSLExternalFunctionCall.h b/src/sksl/ir/SkSLExternalFunctionCall.h
index 6e8942c..3776cb0 100644
--- a/src/sksl/ir/SkSLExternalFunctionCall.h
+++ b/src/sksl/ir/SkSLExternalFunctionCall.h
@@ -20,7 +20,7 @@
 struct ExternalFunctionCall : public Expression {
     static constexpr Kind kExpressionKind = Kind::kExternalFunctionCall;
 
-    ExternalFunctionCall(int offset, const Type& type, const ExternalValue* function,
+    ExternalFunctionCall(int offset, const Type* type, const ExternalValue* function,
                          std::vector<std::unique_ptr<Expression>> arguments)
     : INHERITED(offset, kExpressionKind, type)
     , fFunction(function)
@@ -44,7 +44,7 @@
             cloned.push_back(arg->clone());
         }
         return std::unique_ptr<Expression>(new ExternalFunctionCall(fOffset,
-                                                                    fType,
+                                                                    &this->type(),
                                                                     fFunction,
                                                                     std::move(cloned)));
     }
diff --git a/src/sksl/ir/SkSLExternalValueReference.h b/src/sksl/ir/SkSLExternalValueReference.h
index a68c7ee..49d383c 100644
--- a/src/sksl/ir/SkSLExternalValueReference.h
+++ b/src/sksl/ir/SkSLExternalValueReference.h
@@ -20,7 +20,7 @@
     static constexpr Kind kExpressionKind = Kind::kExternalValue;
 
     ExternalValueReference(int offset, const ExternalValue* ev)
-    : INHERITED(offset, kExpressionKind, ev->type())
+    : INHERITED(offset, kExpressionKind, &ev->type())
     , fValue(ev) {}
 
     bool hasProperty(Property property) const override {
diff --git a/src/sksl/ir/SkSLField.h b/src/sksl/ir/SkSLField.h
index 33ac904..78872c5 100644
--- a/src/sksl/ir/SkSLField.h
+++ b/src/sksl/ir/SkSLField.h
@@ -25,12 +25,12 @@
     static constexpr Kind kSymbolKind = Kind::kField;
 
     Field(int offset, const Variable& owner, int fieldIndex)
-    : INHERITED(offset, kSymbolKind, owner.fType.fields()[fieldIndex].fName)
+    : INHERITED(offset, kSymbolKind, owner.type().fields()[fieldIndex].fName)
     , fOwner(owner)
     , fFieldIndex(fieldIndex) {}
 
     String description() const override {
-        return fOwner.description() + "." + fOwner.fType.fields()[fFieldIndex].fName;
+        return fOwner.description() + "." + fOwner.type().fields()[fFieldIndex].fName;
     }
 
     const Variable& fOwner;
diff --git a/src/sksl/ir/SkSLFieldAccess.h b/src/sksl/ir/SkSLFieldAccess.h
index c78bb42..ee14638 100644
--- a/src/sksl/ir/SkSLFieldAccess.h
+++ b/src/sksl/ir/SkSLFieldAccess.h
@@ -28,7 +28,7 @@
 
     FieldAccess(std::unique_ptr<Expression> base, int fieldIndex,
                 OwnerKind ownerKind = kDefault_OwnerKind)
-    : INHERITED(base->fOffset, kExpressionKind, *base->fType.fields()[fieldIndex].fType)
+    : INHERITED(base->fOffset, kExpressionKind, base->type().fields()[fieldIndex].fType)
     , fBase(std::move(base))
     , fFieldIndex(fieldIndex)
     , fOwnerKind(ownerKind) {}
@@ -43,7 +43,7 @@
     }
 
     String description() const override {
-        return fBase->description() + "." + fBase->fType.fields()[fFieldIndex].fName;
+        return fBase->description() + "." + fBase->type().fields()[fFieldIndex].fName;
     }
 
     std::unique_ptr<Expression> fBase;
diff --git a/src/sksl/ir/SkSLFloatLiteral.h b/src/sksl/ir/SkSLFloatLiteral.h
index dbfb726..0bbdc14 100644
--- a/src/sksl/ir/SkSLFloatLiteral.h
+++ b/src/sksl/ir/SkSLFloatLiteral.h
@@ -20,11 +20,11 @@
     static constexpr Kind kExpressionKind = Kind::kFloatLiteral;
 
     FloatLiteral(const Context& context, int offset, double value)
-    : INHERITED(offset, kExpressionKind, *context.fFloatLiteral_Type)
+    : INHERITED(offset, kExpressionKind, context.fFloatLiteral_Type.get())
     , fValue(value) {}
 
     FloatLiteral(int offset, double value, const Type* type)
-    : INHERITED(offset, kExpressionKind, *type)
+    : INHERITED(offset, kExpressionKind, type)
     , fValue(value) {}
 
     String description() const override {
@@ -55,7 +55,7 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new FloatLiteral(fOffset, fValue, &fType));
+        return std::unique_ptr<Expression>(new FloatLiteral(fOffset, fValue, &this->type()));
     }
 
     const double fValue;
diff --git a/src/sksl/ir/SkSLFunctionCall.h b/src/sksl/ir/SkSLFunctionCall.h
index 119066b..64e9270 100644
--- a/src/sksl/ir/SkSLFunctionCall.h
+++ b/src/sksl/ir/SkSLFunctionCall.h
@@ -19,7 +19,7 @@
 struct FunctionCall : public Expression {
     static constexpr Kind kExpressionKind = Kind::kFunctionCall;
 
-    FunctionCall(int offset, const Type& type, const FunctionDeclaration& function,
+    FunctionCall(int offset, const Type* type, const FunctionDeclaration& function,
                  std::vector<std::unique_ptr<Expression>> arguments)
     : INHERITED(offset, kExpressionKind, type)
     , fFunction(std::move(function))
@@ -49,7 +49,7 @@
         for (const auto& arg : fArguments) {
             cloned.push_back(arg->clone());
         }
-        return std::unique_ptr<Expression>(new FunctionCall(fOffset, fType, fFunction,
+        return std::unique_ptr<Expression>(new FunctionCall(fOffset, &this->type(), fFunction,
                                                             std::move(cloned)));
     }
 
diff --git a/src/sksl/ir/SkSLFunctionDeclaration.h b/src/sksl/ir/SkSLFunctionDeclaration.h
index 7d1974d..2635d9a 100644
--- a/src/sksl/ir/SkSLFunctionDeclaration.h
+++ b/src/sksl/ir/SkSLFunctionDeclaration.h
@@ -43,7 +43,7 @@
         for (auto p : fParameters) {
             result += separator;
             separator = ", ";
-            result += p->fType.displayName();
+            result += p->type().displayName();
         }
         result += ")";
         return result;
@@ -57,7 +57,7 @@
             return false;
         }
         for (size_t i = 0; i < fParameters.size(); i++) {
-            if (fParameters[i]->fType != f.fParameters[i]->fType) {
+            if (fParameters[i]->type() != f.fParameters[i]->type()) {
                 return false;
             }
         }
@@ -81,11 +81,12 @@
         SkASSERT(arguments.size() == fParameters.size());
         int genericIndex = -1;
         for (size_t i = 0; i < arguments.size(); i++) {
-            if (fParameters[i]->fType.typeKind() == Type::TypeKind::kGeneric) {
-                std::vector<const Type*> types = fParameters[i]->fType.coercibleTypes();
+            const Type& parameterType = fParameters[i]->type();
+            if (parameterType.typeKind() == Type::TypeKind::kGeneric) {
+                std::vector<const Type*> types = parameterType.coercibleTypes();
                 if (genericIndex == -1) {
                     for (size_t j = 0; j < types.size(); j++) {
-                        if (arguments[i]->fType.canCoerceTo(*types[j])) {
+                        if (arguments[i]->type().canCoerceTo(*types[j])) {
                             genericIndex = j;
                             break;
                         }
@@ -96,7 +97,7 @@
                 }
                 outParameterTypes->push_back(types[genericIndex]);
             } else {
-                outParameterTypes->push_back(&fParameters[i]->fType);
+                outParameterTypes->push_back(&parameterType);
             }
         }
         if (fReturnType.typeKind() == Type::TypeKind::kGeneric) {
diff --git a/src/sksl/ir/SkSLFunctionReference.h b/src/sksl/ir/SkSLFunctionReference.h
index af8fff0..da002f2 100644
--- a/src/sksl/ir/SkSLFunctionReference.h
+++ b/src/sksl/ir/SkSLFunctionReference.h
@@ -23,7 +23,7 @@
 
     FunctionReference(const Context& context, int offset,
                       std::vector<const FunctionDeclaration*> function)
-    : INHERITED(offset, kExpressionKind, *context.fInvalid_Type)
+    : INHERITED(offset, kExpressionKind, context.fInvalid_Type.get())
     , fFunctions(function) {}
 
     bool hasProperty(Property property) const override {
@@ -31,7 +31,8 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new FunctionReference(fOffset, fFunctions, &fType));
+        return std::unique_ptr<Expression>(new FunctionReference(fOffset, fFunctions,
+                                                                 &this->type()));
     }
 
     String description() const override {
@@ -45,7 +46,7 @@
 private:
     FunctionReference(int offset, std::vector<const FunctionDeclaration*> function,
                       const Type* type)
-    : INHERITED(offset, kExpressionKind, *type)
+    : INHERITED(offset, kExpressionKind, type)
     , fFunctions(function) {}};
 
 }  // namespace SkSL
diff --git a/src/sksl/ir/SkSLIRNode.h b/src/sksl/ir/SkSLIRNode.h
index 9549cd2..4a6aa90 100644
--- a/src/sksl/ir/SkSLIRNode.h
+++ b/src/sksl/ir/SkSLIRNode.h
@@ -13,14 +13,18 @@
 
 namespace SkSL {
 
+class Type;
+
 /**
  * Represents a node in the intermediate representation (IR) tree. The IR is a fully-resolved
  * version of the program (all types determined, everything validated), ready for code generation.
  */
-struct IRNode {
-    IRNode(int offset, int kind)
+class IRNode {
+public:
+    IRNode(int offset, int kind, const Type* type = nullptr)
     : fOffset(offset)
-    , fKind(kind) {}
+    , fKind(kind)
+    , fType(type) {}
 
     virtual ~IRNode() {}
 
@@ -30,8 +34,16 @@
     // purposes
     int fOffset;
 
+    const Type& type() const {
+        SkASSERT(fType);
+        return *fType;
+    }
+
 protected:
     int fKind;
+
+private:
+    const Type* fType;
 };
 
 }  // namespace SkSL
diff --git a/src/sksl/ir/SkSLIndexExpression.h b/src/sksl/ir/SkSLIndexExpression.h
index 6be205d..7911bb1 100644
--- a/src/sksl/ir/SkSLIndexExpression.h
+++ b/src/sksl/ir/SkSLIndexExpression.h
@@ -46,10 +46,10 @@
 
     IndexExpression(const Context& context, std::unique_ptr<Expression> base,
                     std::unique_ptr<Expression> index)
-    : INHERITED(base->fOffset, kExpressionKind, index_type(context, base->fType))
+    : INHERITED(base->fOffset, kExpressionKind, &index_type(context, base->type()))
     , fBase(std::move(base))
     , fIndex(std::move(index)) {
-        SkASSERT(fIndex->fType == *context.fInt_Type || fIndex->fType == *context.fUInt_Type);
+        SkASSERT(fIndex->type() == *context.fInt_Type || fIndex->type() == *context.fUInt_Type);
     }
 
     bool hasProperty(Property property) const override {
@@ -58,7 +58,7 @@
 
     std::unique_ptr<Expression> clone() const override {
         return std::unique_ptr<Expression>(new IndexExpression(fBase->clone(), fIndex->clone(),
-                                                               &fType));
+                                                               &this->type()));
     }
 
     String description() const override {
@@ -73,7 +73,7 @@
 private:
     IndexExpression(std::unique_ptr<Expression> base, std::unique_ptr<Expression> index,
                     const Type* type)
-    : INHERITED(base->fOffset, Kind::kIndex, *type)
+    : INHERITED(base->fOffset, Kind::kIndex, type)
     , fBase(std::move(base))
     , fIndex(std::move(index)) {}
 };
diff --git a/src/sksl/ir/SkSLIntLiteral.h b/src/sksl/ir/SkSLIntLiteral.h
index 70b92e6..1ec16d5 100644
--- a/src/sksl/ir/SkSLIntLiteral.h
+++ b/src/sksl/ir/SkSLIntLiteral.h
@@ -22,11 +22,11 @@
     // FIXME: we will need to revisit this if/when we add full support for both signed and unsigned
     // 64-bit integers, but for right now an int64_t will hold every value we care about
     IntLiteral(const Context& context, int offset, int64_t value)
-    : INHERITED(offset, kExpressionKind, *context.fInt_Type)
+    : INHERITED(offset, kExpressionKind, context.fInt_Type.get())
     , fValue(value) {}
 
     IntLiteral(int offset, int64_t value, const Type* type = nullptr)
-    : INHERITED(offset, kExpressionKind, *type)
+    : INHERITED(offset, kExpressionKind, type)
     , fValue(value) {}
 
     String description() const override {
@@ -58,7 +58,7 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new IntLiteral(fOffset, fValue, &fType));
+        return std::unique_ptr<Expression>(new IntLiteral(fOffset, fValue, &this->type()));
     }
 
     const int64_t fValue;
diff --git a/src/sksl/ir/SkSLInterfaceBlock.h b/src/sksl/ir/SkSLInterfaceBlock.h
index 41dbd8f..d8f6415 100644
--- a/src/sksl/ir/SkSLInterfaceBlock.h
+++ b/src/sksl/ir/SkSLInterfaceBlock.h
@@ -50,7 +50,7 @@
 
     String description() const override {
         String result = fVariable.fModifiers.description() + fTypeName + " {\n";
-        const Type* structType = &fVariable.fType;
+        const Type* structType = &fVariable.type();
         while (structType->typeKind() == Type::TypeKind::kArray) {
             structType = &structType->componentType();
         }
diff --git a/src/sksl/ir/SkSLNullLiteral.h b/src/sksl/ir/SkSLNullLiteral.h
index 57b9010..2754ec3 100644
--- a/src/sksl/ir/SkSLNullLiteral.h
+++ b/src/sksl/ir/SkSLNullLiteral.h
@@ -20,9 +20,9 @@
     static constexpr Kind kExpressionKind = Kind::kNullLiteral;
 
     NullLiteral(const Context& context, int offset)
-    : INHERITED(offset, kExpressionKind, *context.fNull_Type) {}
+    : INHERITED(offset, kExpressionKind, context.fNull_Type.get()) {}
 
-    NullLiteral(int offset, const Type& type)
+    NullLiteral(int offset, const Type* type)
     : INHERITED(offset, kExpressionKind, type) {}
 
     String description() const override {
@@ -42,7 +42,7 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new NullLiteral(fOffset, fType));
+        return std::unique_ptr<Expression>(new NullLiteral(fOffset, &this->type()));
     }
 
     using INHERITED = Expression;
diff --git a/src/sksl/ir/SkSLPostfixExpression.h b/src/sksl/ir/SkSLPostfixExpression.h
index b635013..6364fa3 100644
--- a/src/sksl/ir/SkSLPostfixExpression.h
+++ b/src/sksl/ir/SkSLPostfixExpression.h
@@ -21,7 +21,7 @@
     static constexpr Kind kExpressionKind = Kind::kPostfix;
 
     PostfixExpression(std::unique_ptr<Expression> operand, Token::Kind op)
-    : INHERITED(operand->fOffset, kExpressionKind, operand->fType)
+    : INHERITED(operand->fOffset, kExpressionKind, &operand->type())
     , fOperand(std::move(operand))
     , fOperator(op) {}
 
diff --git a/src/sksl/ir/SkSLPrefixExpression.h b/src/sksl/ir/SkSLPrefixExpression.h
index c92b7ca..8bc72b7 100644
--- a/src/sksl/ir/SkSLPrefixExpression.h
+++ b/src/sksl/ir/SkSLPrefixExpression.h
@@ -23,7 +23,7 @@
     static constexpr Kind kExpressionKind = Kind::kPrefix;
 
     PrefixExpression(Token::Kind op, std::unique_ptr<Expression> operand)
-    : INHERITED(operand->fOffset, kExpressionKind, operand->fType)
+    : INHERITED(operand->fOffset, kExpressionKind, &operand->type())
     , fOperand(std::move(operand))
     , fOperator(op) {}
 
diff --git a/src/sksl/ir/SkSLSetting.h b/src/sksl/ir/SkSLSetting.h
index 9b8d528..81eb5a7 100644
--- a/src/sksl/ir/SkSLSetting.h
+++ b/src/sksl/ir/SkSLSetting.h
@@ -21,7 +21,7 @@
     static constexpr Kind kExpressionKind = Kind::kSetting;
 
     Setting(int offset, String name, std::unique_ptr<Expression> value)
-    : INHERITED(offset, kExpressionKind, value->fType)
+    : INHERITED(offset, kExpressionKind, &value->type())
     , fName(std::move(name))
     , fValue(std::move(value)) {
         SkASSERT(fValue->isCompileTimeConstant());
diff --git a/src/sksl/ir/SkSLSwizzle.h b/src/sksl/ir/SkSLSwizzle.h
index bace1e5..879e7c4 100644
--- a/src/sksl/ir/SkSLSwizzle.h
+++ b/src/sksl/ir/SkSLSwizzle.h
@@ -28,7 +28,7 @@
  * swizzle with more components than the source vector, as in 'float2(1).xxxx'.
  */
 static const Type& get_type(const Context& context, Expression& value, size_t count) {
-    const Type& base = value.fType.componentType();
+    const Type& base = value.type().componentType();
     if (count == 1) {
         return base;
     }
@@ -90,7 +90,7 @@
 #ifdef SK_DEBUG
     ABORT("cannot swizzle %s\n", value.description().c_str());
 #endif
-    return value.fType;
+    return value.type();
 }
 
 /**
@@ -100,7 +100,7 @@
     static constexpr Kind kExpressionKind = Kind::kSwizzle;
 
     Swizzle(const Context& context, std::unique_ptr<Expression> base, std::vector<int> components)
-    : INHERITED(base->fOffset, kExpressionKind, get_type(context, *base, components.size()))
+    : INHERITED(base->fOffset, kExpressionKind, &get_type(context, *base, components.size()))
     , fBase(std::move(base))
     , fComponents(std::move(components)) {
         SkASSERT(fComponents.size() >= 1 && fComponents.size() <= 4);
@@ -112,12 +112,13 @@
             Constructor& constructor = static_cast<Constructor&>(*fBase);
             if (constructor.isCompileTimeConstant()) {
                 // we're swizzling a constant vector, e.g. float4(1).x. Simplify it.
-                if (fType.isInteger()) {
+                const Type& type = this->type();
+                if (type.isInteger()) {
                     SkASSERT(fComponents.size() == 1);
                     int64_t value = constructor.getIVecComponent(fComponents[0]);
                     return std::make_unique<IntLiteral>(irGenerator.fContext, constructor.fOffset,
                                                         value);
-                } else if (fType.isFloat()) {
+                } else if (type.isFloat()) {
                     SkASSERT(fComponents.size() == 1);
                     double value = constructor.getFVecComponent(fComponents[0]);
                     return std::make_unique<FloatLiteral>(irGenerator.fContext, constructor.fOffset,
@@ -133,7 +134,7 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new Swizzle(fType, fBase->clone(), fComponents));
+        return std::unique_ptr<Expression>(new Swizzle(&this->type(), fBase->clone(), fComponents));
     }
 
     String description() const override {
@@ -150,7 +151,7 @@
     using INHERITED = Expression;
 
 private:
-    Swizzle(const Type& type, std::unique_ptr<Expression> base, std::vector<int> components)
+    Swizzle(const Type* type, std::unique_ptr<Expression> base, std::vector<int> components)
     : INHERITED(base->fOffset, kExpressionKind, type)
     , fBase(std::move(base))
     , fComponents(std::move(components)) {
diff --git a/src/sksl/ir/SkSLSymbol.h b/src/sksl/ir/SkSLSymbol.h
index 9f400a8..1decc00 100644
--- a/src/sksl/ir/SkSLSymbol.h
+++ b/src/sksl/ir/SkSLSymbol.h
@@ -29,8 +29,8 @@
         kLast = kVariable
     };
 
-    Symbol(int offset, Kind kind, StringFragment name)
-    : INHERITED(offset, (int) kind)
+    Symbol(int offset, Kind kind, StringFragment name, const Type* type = nullptr)
+    : INHERITED(offset, (int) kind, type)
     , fName(name) {
         SkASSERT(kind >= Kind::kFirst && kind <= Kind::kLast);
     }
diff --git a/src/sksl/ir/SkSLTernaryExpression.h b/src/sksl/ir/SkSLTernaryExpression.h
index cf2faac..f590bf8 100644
--- a/src/sksl/ir/SkSLTernaryExpression.h
+++ b/src/sksl/ir/SkSLTernaryExpression.h
@@ -21,11 +21,11 @@
 
     TernaryExpression(int offset, std::unique_ptr<Expression> test,
                       std::unique_ptr<Expression> ifTrue, std::unique_ptr<Expression> ifFalse)
-    : INHERITED(offset, kExpressionKind, ifTrue->fType)
+    : INHERITED(offset, kExpressionKind, &ifTrue->type())
     , fTest(std::move(test))
     , fIfTrue(std::move(ifTrue))
     , fIfFalse(std::move(ifFalse)) {
-        SkASSERT(fIfTrue->fType == fIfFalse->fType);
+        SkASSERT(fIfTrue->type() == fIfFalse->type());
     }
 
     bool hasProperty(Property property) const override {
diff --git a/src/sksl/ir/SkSLTypeReference.h b/src/sksl/ir/SkSLTypeReference.h
index 7efb49e..4f738d8 100644
--- a/src/sksl/ir/SkSLTypeReference.h
+++ b/src/sksl/ir/SkSLTypeReference.h
@@ -21,7 +21,7 @@
     static constexpr Kind kExpressionKind = Kind::kTypeReference;
 
     TypeReference(const Context& context, int offset, const Type* value)
-    : INHERITED(offset, kExpressionKind, *context.fInvalid_Type)
+    : INHERITED(offset, kExpressionKind, context.fInvalid_Type.get())
     , fValue(*value) {}
 
     bool hasProperty(Property property) const override {
@@ -33,7 +33,7 @@
     }
 
     std::unique_ptr<Expression> clone() const override {
-        return std::unique_ptr<Expression>(new TypeReference(fOffset, fValue, &fType));
+        return std::unique_ptr<Expression>(new TypeReference(fOffset, fValue, &this->type()));
     }
 
     const Type& fValue;
@@ -42,7 +42,7 @@
 
 private:
     TypeReference(int offset, const Type& value, const Type* type)
-    : INHERITED(offset, kExpressionKind, *type)
+    : INHERITED(offset, kExpressionKind, type)
     , fValue(value) {}
 };
 
diff --git a/src/sksl/ir/SkSLVarDeclarations.h b/src/sksl/ir/SkSLVarDeclarations.h
index 13c02f7..8a94441 100644
--- a/src/sksl/ir/SkSLVarDeclarations.h
+++ b/src/sksl/ir/SkSLVarDeclarations.h
@@ -45,7 +45,7 @@
     }
 
     String description() const override {
-        String result = fVar->fModifiers.description() + fVar->fType.name() + " " + fVar->fName;
+        String result = fVar->fModifiers.description() + fVar->type().name() + " " + fVar->fName;
         for (const auto& size : fSizes) {
             if (size) {
                 result += "[" + size->description() + "]";
diff --git a/src/sksl/ir/SkSLVariable.h b/src/sksl/ir/SkSLVariable.h
index e822aa8..033c9eb 100644
--- a/src/sksl/ir/SkSLVariable.h
+++ b/src/sksl/ir/SkSLVariable.h
@@ -32,11 +32,10 @@
         kParameter_Storage
     };
 
-    Variable(int offset, Modifiers modifiers, StringFragment name, const Type& type,
+    Variable(int offset, Modifiers modifiers, StringFragment name, const Type* type,
              Storage storage, Expression* initialValue = nullptr)
-    : INHERITED(offset, kSymbolKind, name)
+    : INHERITED(offset, kSymbolKind, name, type)
     , fModifiers(modifiers)
-    , fType(type)
     , fStorage(storage)
     , fInitialValue(initialValue)
     , fReadCount(0)
@@ -51,7 +50,7 @@
     }
 
     String description() const override {
-        return fModifiers.description() + fType.fName + " " + fName;
+        return fModifiers.description() + this->type().fName + " " + fName;
     }
 
     bool dead() const {
@@ -66,7 +65,6 @@
     }
 
     mutable Modifiers fModifiers;
-    const Type& fType;
     const Storage fStorage;
 
     const Expression* fInitialValue = nullptr;
diff --git a/src/sksl/ir/SkSLVariableReference.cpp b/src/sksl/ir/SkSLVariableReference.cpp
index 7bbff25..d1544ac 100644
--- a/src/sksl/ir/SkSLVariableReference.cpp
+++ b/src/sksl/ir/SkSLVariableReference.cpp
@@ -15,7 +15,7 @@
 namespace SkSL {
 
 VariableReference::VariableReference(int offset, const Variable& variable, RefKind refKind)
-: INHERITED(offset, kExpressionKind, variable.fType)
+: INHERITED(offset, kExpressionKind, &variable.type())
 , fVariable(variable)
 , fRefKind(refKind) {
     if (refKind != kRead_RefKind) {
@@ -74,7 +74,7 @@
             for (const auto& arg : c->fArguments) {
                 args.push_back(copy_constant(irGenerator, arg.get()));
             }
-            return std::unique_ptr<Expression>(new Constructor(-1, c->fType,
+            return std::unique_ptr<Expression>(new Constructor(-1, &c->type(),
                                                                std::move(args)));
         }
         case Expression::Kind::kSetting: {
@@ -95,7 +95,7 @@
     }
     if ((fVariable.fModifiers.fFlags & Modifiers::kConst_Flag) && fVariable.fInitialValue &&
         fVariable.fInitialValue->isCompileTimeConstant() &&
-        fType.typeKind() != Type::TypeKind::kArray) {
+        this->type().typeKind() != Type::TypeKind::kArray) {
         return copy_constant(irGenerator, fVariable.fInitialValue);
     }
     auto exprIter = definitions.find(&fVariable);