resolver: Be const-correct with sem::Types

Make all the sem::Type pointers const.
The later stages still have not been fixed up, so there's liberal usage of const_cast where we create semantic nodes.

Bug: tint:745
Change-Id: I160b791f2b7944f8966bc961e061d1e5996c1973
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/49343
Auto-Submit: Ben Clayton <bclayton@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/intrinsic_table.cc b/src/intrinsic_table.cc
index 2652ab8..8a59ab5 100644
--- a/src/intrinsic_table.cc
+++ b/src/intrinsic_table.cc
@@ -78,7 +78,7 @@
     /// The map of open types. A new entry is assigned the first time an
     /// OpenType is encountered. If the OpenType is encountered again, a
     /// comparison is made to see if the type is consistent.
-    std::unordered_map<OpenType, sem::Type*> open_types;
+    std::unordered_map<OpenType, const sem::Type*> open_types;
     /// The map of open numbers. A new entry is assigned the first time an
     /// OpenNumber is encountered. If the OpenNumber is encountered again, a
     /// comparison is made to see if the number is consistent.
@@ -92,7 +92,7 @@
   /// Aliases are automatically unwrapped before matching.
   /// Match may add to, or compare against the open types and numbers in state.
   /// @returns true if the argument type is as expected.
-  bool Match(MatchState& state, sem::Type* argument_type) const {
+  bool Match(MatchState& state, const sem::Type* argument_type) const {
     auto* unwrapped = argument_type->UnwrapAliasIfNeeded();
     return MatchUnwrapped(state, unwrapped);
   }
@@ -111,12 +111,12 @@
   /// Match may add to, or compare against the open types and numbers in state.
   /// @returns true if the argument type is as expected.
   virtual bool MatchUnwrapped(MatchState& state,
-                              sem::Type* argument_type) const = 0;
+                              const sem::Type* argument_type) const = 0;
 
   /// Checks `state.open_type` to see if the OpenType `t` is equal to the type
   /// `ty`. If `state.open_type` does not contain an entry for `t`, then `ty`
   /// is added and returns true.
-  bool MatchOpenType(MatchState& state, OpenType t, sem::Type* ty) const {
+  bool MatchOpenType(MatchState& state, OpenType t, const sem::Type* ty) const {
     auto it = state.open_types.find(t);
     if (it != state.open_types.end()) {
       return it->second == ty;
@@ -148,7 +148,7 @@
     /// The type manager used to construct new types
     sem::Manager& ty_mgr;
     /// The final resolved list of open types
-    std::unordered_map<OpenType, sem::Type*> const open_types;
+    std::unordered_map<OpenType, const sem::Type*> const open_types;
     /// The final resolved list of open numbers
     std::unordered_map<OpenNumber, uint32_t> const open_numbers;
   };
@@ -157,7 +157,7 @@
   ~Builder() override = default;
 
   /// Constructs and returns the expected type
-  virtual sem::Type* Build(BuildState& state) const = 0;
+  virtual const sem::Type* Build(BuildState& state) const = 0;
 };
 
 /// OpenTypeBuilder is a Matcher / Builder for an open type (T etc).
@@ -167,11 +167,11 @@
  public:
   explicit OpenTypeBuilder(OpenType open_type) : open_type_(open_type) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     return MatchOpenType(state, open_type_, ty);
   }
 
-  sem::Type* Build(BuildState& state) const override {
+  const sem::Type* Build(BuildState& state) const override {
     return state.open_types.at(open_type_);
   }
 
@@ -184,7 +184,7 @@
 /// VoidBuilder is a Matcher / Builder for void types.
 class VoidBuilder : public Builder {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::Void>();
   }
   sem::Type* Build(BuildState& state) const override {
@@ -196,7 +196,7 @@
 /// BoolBuilder is a Matcher / Builder for boolean types.
 class BoolBuilder : public Builder {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::Bool>();
   }
   sem::Type* Build(BuildState& state) const override {
@@ -208,7 +208,7 @@
 /// F32Builder is a Matcher / Builder for f32 types.
 class F32Builder : public Builder {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::F32>();
   }
   sem::Type* Build(BuildState& state) const override {
@@ -220,7 +220,7 @@
 /// U32Builder is a Matcher / Builder for u32 types.
 class U32Builder : public Builder {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::U32>();
   }
   sem::Type* Build(BuildState& state) const override {
@@ -232,7 +232,7 @@
 /// I32Builder is a Matcher / Builder for i32 types.
 class I32Builder : public Builder {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::I32>();
   }
   sem::Type* Build(BuildState& state) const override {
@@ -244,7 +244,7 @@
 /// IU32Matcher is a Matcher for i32 or u32 types.
 class IU32Matcher : public Matcher {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::I32>() || ty->Is<sem::U32>();
   }
   std::string str() const override { return "i32 or u32"; }
@@ -253,7 +253,7 @@
 /// FIU32Matcher is a Matcher for f32, i32 or u32 types.
 class FIU32Matcher : public Matcher {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::F32>() || ty->Is<sem::I32>() || ty->Is<sem::U32>();
   }
   std::string str() const override { return "f32, i32 or u32"; }
@@ -262,7 +262,7 @@
 /// ScalarMatcher is a Matcher for f32, i32, u32 or boolean types.
 class ScalarMatcher : public Matcher {
  public:
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->is_scalar();
   }
   std::string str() const override { return "scalar"; }
@@ -275,7 +275,7 @@
   OpenSizeVecBuilder(OpenNumber size, Builder* element_builder)
       : size_(size), element_builder_(element_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* vec = ty->As<sem::Vector>()) {
       if (!MatchOpenNumber(state, size_, vec->size())) {
         return false;
@@ -307,7 +307,7 @@
   VecBuilder(uint32_t size, Builder* element_builder)
       : size_(size), element_builder_(element_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* vec = ty->As<sem::Vector>()) {
       if (vec->size() == size_) {
         return element_builder_->Match(state, vec->type());
@@ -339,7 +339,7 @@
                      Builder* element_builder)
       : columns_(columns), rows_(rows), element_builder_(element_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* mat = ty->As<sem::Matrix>()) {
       if (!MatchOpenNumber(state, columns_, mat->columns())) {
         return false;
@@ -356,7 +356,8 @@
     auto* el = element_builder_->Build(state);
     auto columns = state.open_numbers.at(columns_);
     auto rows = state.open_numbers.at(rows_);
-    return state.ty_mgr.Get<sem::Matrix>(el, rows, columns);
+    return state.ty_mgr.Get<sem::Matrix>(const_cast<sem::Type*>(el), rows,
+                                         columns);
   }
 
   std::string str() const override {
@@ -376,7 +377,7 @@
   explicit PtrBuilder(Builder* element_builder)
       : element_builder_(element_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* ptr = ty->As<sem::Pointer>()) {
       return element_builder_->Match(state, ptr->type());
     }
@@ -385,7 +386,8 @@
 
   sem::Type* Build(BuildState& state) const override {
     auto* el = element_builder_->Build(state);
-    return state.ty_mgr.Get<sem::Pointer>(el, ast::StorageClass::kNone);
+    return state.ty_mgr.Get<sem::Pointer>(const_cast<sem::Type*>(el),
+                                          ast::StorageClass::kNone);
   }
 
   bool ExpectsPointer() const override { return true; }
@@ -404,7 +406,7 @@
   explicit ArrayBuilder(Builder* element_builder)
       : element_builder_(element_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* arr = ty->As<sem::ArrayType>()) {
       if (arr->size() == 0) {
         return element_builder_->Match(state, arr->type());
@@ -415,7 +417,8 @@
 
   sem::Type* Build(BuildState& state) const override {
     auto* el = element_builder_->Build(state);
-    return state.ty_mgr.Get<sem::ArrayType>(el, 0, ast::DecorationList{});
+    return state.ty_mgr.Get<sem::ArrayType>(const_cast<sem::Type*>(el), 0,
+                                            ast::DecorationList{});
   }
 
   std::string str() const override {
@@ -433,7 +436,7 @@
                                  Builder* type_builder)
       : dimensions_(dimensions), type_builder_(type_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* tex = ty->As<sem::SampledTexture>()) {
       if (tex->dim() == dimensions_) {
         return type_builder_->Match(state, tex->type());
@@ -466,7 +469,7 @@
                                       Builder* type_builder)
       : dimensions_(dimensions), type_builder_(type_builder) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* tex = ty->As<sem::MultisampledTexture>()) {
       if (tex->dim() == dimensions_) {
         return type_builder_->Match(state, tex->type());
@@ -498,7 +501,7 @@
   explicit DepthTextureBuilder(ast::TextureDimension dimensions)
       : dimensions_(dimensions) {}
 
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     if (auto* tex = ty->As<sem::DepthTexture>()) {
       return tex->dim() == dimensions_;
     }
@@ -531,7 +534,7 @@
         texel_format_(texel_format),
         channel_format_(channel_format) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* ac = ty->As<sem::AccessControl>()) {
       // If we have an storage texture argument that's got an access control
       // type wrapped around it, accept it. Signatures that don't include an
@@ -555,8 +558,8 @@
     auto texel_format =
         static_cast<ast::ImageFormat>(state.open_numbers.at(texel_format_));
     auto* channel_format = state.open_types.at(channel_format_);
-    return state.ty_mgr.Get<sem::StorageTexture>(dimensions_, texel_format,
-                                                 channel_format);
+    return state.ty_mgr.Get<sem::StorageTexture>(
+        dimensions_, texel_format, const_cast<sem::Type*>(channel_format));
   }
 
   std::string str() const override {
@@ -576,7 +579,7 @@
  public:
   ExternalTextureBuilder() {}
 
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     return ty->Is<sem::ExternalTexture>();
   }
 
@@ -592,7 +595,7 @@
  public:
   explicit SamplerBuilder(ast::SamplerKind kind) : kind_(kind) {}
 
-  bool MatchUnwrapped(MatchState&, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState&, const sem::Type* ty) const override {
     if (auto* sampler = ty->As<sem::Sampler>()) {
       return sampler->kind() == kind_;
     }
@@ -624,7 +627,7 @@
                                 Builder* type)
       : access_control_(access_control), type_(type) {}
 
-  bool MatchUnwrapped(MatchState& state, sem::Type* ty) const override {
+  bool MatchUnwrapped(MatchState& state, const sem::Type* ty) const override {
     if (auto* ac = ty->As<sem::AccessControl>()) {
       if (ac->access_control() == access_control_) {
         return type_->Match(state, ty);
@@ -656,7 +659,7 @@
 
   IntrinsicTable::Result Lookup(ProgramBuilder& builder,
                                 sem::IntrinsicType type,
-                                const std::vector<sem::Type*>& args,
+                                const std::vector<const sem::Type*>& args,
                                 const Source& source) const override;
 
   /// Holds the information about a single overload parameter used for matching
@@ -678,7 +681,7 @@
     /// (positive representing a greater match), and nullptr is returned.
     sem::Intrinsic* Match(ProgramBuilder& builder,
                           sem::IntrinsicType type,
-                          const std::vector<sem::Type*>& arg_types,
+                          const std::vector<const sem::Type*>& arg_types,
                           diag::List& diagnostics,
                           int& match_score) const;
 
@@ -1328,7 +1331,7 @@
 /// types.
 std::string CallSignature(ProgramBuilder& builder,
                           sem::IntrinsicType type,
-                          const std::vector<sem::Type*>& args) {
+                          const std::vector<const sem::Type*>& args) {
   std::stringstream ss;
   ss << sem::str(type) << "(";
   {
@@ -1348,7 +1351,7 @@
 
 IntrinsicTable::Result Impl::Lookup(ProgramBuilder& builder,
                                     sem::IntrinsicType type,
-                                    const std::vector<sem::Type*>& args,
+                                    const std::vector<const sem::Type*>& args,
                                     const Source& source) const {
   diag::List diagnostics;
   // Candidate holds information about a mismatched overload that could be what
@@ -1398,7 +1401,7 @@
 
 sem::Intrinsic* Impl::Overload::Match(ProgramBuilder& builder,
                                       sem::IntrinsicType intrinsic,
-                                      const std::vector<sem::Type*>& args,
+                                      const std::vector<const sem::Type*>& args,
                                       diag::List& diagnostics,
                                       int& match_score) const {
   if (type != intrinsic) {
@@ -1495,10 +1498,12 @@
   for (size_t i = 0; i < args.size(); i++) {
     auto& parameter = parameters[i];
     auto* ty = parameter.matcher->Build(builder_state);
-    params.emplace_back(sem::Parameter{ty, parameter.usage});
+    params.emplace_back(
+        sem::Parameter{const_cast<sem::Type*>(ty), parameter.usage});
   }
 
-  return builder.create<sem::Intrinsic>(intrinsic, ret, params);
+  return builder.create<sem::Intrinsic>(intrinsic, const_cast<sem::Type*>(ret),
+                                        params);
 }
 
 }  // namespace
diff --git a/src/intrinsic_table.h b/src/intrinsic_table.h
index 0425255..d908296 100644
--- a/src/intrinsic_table.h
+++ b/src/intrinsic_table.h
@@ -51,7 +51,7 @@
   /// @return the semantic intrinsic if found, otherwise nullptr
   virtual Result Lookup(ProgramBuilder& builder,
                         sem::IntrinsicType type,
-                        const std::vector<sem::Type*>& args,
+                        const std::vector<const sem::Type*>& args,
                         const Source& source) const = 0;
 };
 
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 19acd68..353e7f2 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -161,15 +161,15 @@
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
-bool Resolver::IsStorable(sem::Type* type) {
+bool Resolver::IsStorable(const sem::Type* type) {
   type = type->UnwrapIfNeeded();
   if (type->is_scalar() || type->Is<sem::Vector>() || type->Is<sem::Matrix>()) {
     return true;
   }
-  if (sem::ArrayType* arr = type->As<sem::ArrayType>()) {
+  if (auto* arr = type->As<sem::ArrayType>()) {
     return IsStorable(arr->type());
   }
-  if (sem::StructType* str = type->As<sem::StructType>()) {
+  if (auto* str = type->As<sem::StructType>()) {
     for (const auto* member : str->impl()->members()) {
       if (!IsStorable(member->type())) {
         return false;
@@ -181,7 +181,7 @@
 }
 
 // https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
-bool Resolver::IsHostShareable(sem::Type* type) {
+bool Resolver::IsHostShareable(const sem::Type* type) {
   type = type->UnwrapIfNeeded();
   if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
     return true;
@@ -206,7 +206,7 @@
   return false;
 }
 
-bool Resolver::IsValidAssignment(sem::Type* lhs, sem::Type* rhs) {
+bool Resolver::IsValidAssignment(const sem::Type* lhs, const sem::Type* rhs) {
   // TODO(crbug.com/tint/659): This is a rough approximation, and is missing
   // checks for writability of pointer storage class, access control, etc.
   // This will need to be fixed after WGSL agrees the behavior of pointers /
@@ -273,7 +273,7 @@
   return true;
 }
 
-sem::Type* Resolver::Type(ast::Type* ty) {
+sem::Type* Resolver::Type(const ast::Type* ty) {
   Mark(ty);
   sem::Type* s = nullptr;
   if (ty->Is<ast::Void>()) {
@@ -305,7 +305,7 @@
     auto* el = Type(ptr->type());
     s = builder_->create<sem::Pointer>(el, ptr->storage_class());
   } else if (auto* str = ty->As<ast::Struct>()) {
-    s = builder_->create<sem::StructType>(str);
+    s = builder_->create<sem::StructType>(const_cast<ast::Struct*>(str));
   } else if (auto* sampler = ty->As<ast::Sampler>()) {
     s = builder_->create<sem::Sampler>(sampler->kind());
   } else if (auto* sampled_tex = ty->As<ast::SampledTexture>()) {
@@ -342,8 +342,9 @@
   return true;
 }
 
-Resolver::VariableInfo* Resolver::Variable(ast::Variable* var,
-                                           sem::Type* type /*=nullptr*/) {
+Resolver::VariableInfo* Resolver::Variable(
+    ast::Variable* var,
+    const sem::Type* type /* = nullptr*/) {
   auto it = variable_to_info_.find(var);
   if (it != variable_to_info_.end()) {
     return it->second;
@@ -658,7 +659,7 @@
   };
   // Inner lambda that is applied to a type and all of its members.
   auto validate_entry_point_decorations_inner =
-      [&](const ast::DecorationList& decos, sem::Type* ty, Source source,
+      [&](const ast::DecorationList& decos, const sem::Type* ty, Source source,
           ParamOrRetType param_or_ret, bool is_struct_member) {
         // Scan decorations for pipeline IO attributes.
         // Check for overlap with attributes that have been seen previously.
@@ -739,7 +740,8 @@
 
   // Outer lambda for validating the entry point decorations for a type.
   auto validate_entry_point_decorations = [&](const ast::DecorationList& decos,
-                                              sem::Type* ty, Source source,
+                                              const sem::Type* ty,
+                                              Source source,
                                               ParamOrRetType param_or_ret) {
     // Validate the decorations for the type.
     if (!validate_entry_point_decorations_inner(decos, ty, source, param_or_ret,
@@ -1274,7 +1276,7 @@
 
 bool Resolver::IntrinsicCall(ast::CallExpression* call,
                              sem::IntrinsicType intrinsic_type) {
-  std::vector<sem::Type*> arg_tys;
+  std::vector<const sem::Type*> arg_tys;
   arg_tys.reserve(call->params().size());
   for (auto* expr : call->params()) {
     arg_tys.emplace_back(TypeOf(expr));
@@ -1325,10 +1327,10 @@
 
 bool Resolver::ValidateVectorConstructor(const sem::Vector* vec_type,
                                          const ast::ExpressionList& values) {
-  sem::Type* elem_type = vec_type->type()->UnwrapAll();
+  auto* elem_type = vec_type->type()->UnwrapAll();
   size_t value_cardinality_sum = 0;
   for (auto* value : values) {
-    sem::Type* value_type = TypeOf(value)->UnwrapAll();
+    auto* value_type = TypeOf(value)->UnwrapAll();
     if (value_type->is_scalar()) {
       if (elem_type != value_type) {
         diagnostics_.add_error(
@@ -1398,7 +1400,7 @@
     return true;
   }
 
-  sem::Type* elem_type = matrix_type->type()->UnwrapAll();
+  auto* elem_type = matrix_type->type()->UnwrapAll();
   if (matrix_type->columns() != values.size()) {
     const Source& values_start = values[0]->source();
     const Source& values_end = values[values.size() - 1]->source();
@@ -1412,7 +1414,7 @@
   }
 
   for (auto* value : values) {
-    sem::Type* value_type = TypeOf(value)->UnwrapAll();
+    auto* value_type = TypeOf(value)->UnwrapAll();
     auto* value_vec = value_type->As<sem::Vector>();
 
     if (!value_vec || value_vec->size() != matrix_type->rows() ||
@@ -1442,8 +1444,8 @@
     } else if (var->type->Is<sem::Pointer>()) {
       SetType(expr, var->type);
     } else {
-      SetType(expr,
-              builder_->create<sem::Pointer>(var->type, var->storage_class));
+      SetType(expr, builder_->create<sem::Pointer>(
+                        const_cast<sem::Type*>(var->type), var->storage_class));
     }
 
     var->users.push_back(expr);
@@ -1830,7 +1832,7 @@
     auto* rhs_mat = rhs_type->As<sem::Matrix>();
     auto* lhs_vec = lhs_type->As<sem::Vector>();
     auto* rhs_vec = rhs_type->As<sem::Vector>();
-    sem::Type* result_type;
+    const sem::Type* result_type = nullptr;
     if (lhs_mat && rhs_mat) {
       result_type = builder_->create<sem::Matrix>(
           lhs_mat->type(), lhs_mat->rows(), rhs_mat->columns());
@@ -1884,7 +1886,7 @@
   ast::Variable* var = stmt->variable();
   Mark(var);
 
-  sem::Type* type = var->declared_type();
+  const sem::Type* type = var->declared_type();
 
   bool is_global = false;
   if (variable_stack_.get(var->symbol(), nullptr, &is_global)) {
@@ -1960,7 +1962,7 @@
   return true;
 }
 
-sem::Type* Resolver::TypeOf(ast::Expression* expr) {
+const sem::Type* Resolver::TypeOf(ast::Expression* expr) {
   auto it = expr_info_.find(expr);
   if (it != expr_info_.end()) {
     return it->second.type;
@@ -1968,7 +1970,7 @@
   return nullptr;
 }
 
-void Resolver::SetType(ast::Expression* expr, sem::Type* type) {
+void Resolver::SetType(ast::Expression* expr, const sem::Type* type) {
   if (expr_info_.count(expr)) {
     TINT_ICE(builder_->Diagnostics())
         << "SetType() called twice for the same expression";
@@ -2078,9 +2080,9 @@
     auto* info = it.second;
     builder_->Sem().Add(
         str, builder_->create<sem::Struct>(
-                 str, std::move(info->members), info->align, info->size,
-                 info->size_no_padding, info->storage_class_usage,
-                 info->pipeline_stage_uses));
+                 const_cast<sem::StructType*>(str), std::move(info->members),
+                 info->align, info->size, info->size_no_padding,
+                 info->storage_class_usage, info->pipeline_stage_uses));
   }
 }
 
@@ -2148,7 +2150,8 @@
   return false;
 }
 
-const sem::Array* Resolver::Array(sem::ArrayType* arr, const Source& source) {
+const sem::Array* Resolver::Array(const sem::ArrayType* arr,
+                                  const Source& source) {
   if (auto* sem = builder_->Sem().Get(arr)) {
     // Semantic info already constructed for this array type
     return sem;
@@ -2171,7 +2174,8 @@
     // WebGPU requires runtime arrays have at least one element, but the AST
     // records an element count of 0 for it.
     auto size = std::max<uint32_t>(arr->size(), 1) * stride;
-    auto* sem = builder_->create<sem::Array>(arr, align, size, stride);
+    auto* sem = builder_->create<sem::Array>(const_cast<sem::ArrayType*>(arr),
+                                             align, size, stride);
     builder_->Sem().Add(arr, sem);
     return sem;
   };
@@ -2305,7 +2309,7 @@
   return true;
 }
 
-Resolver::StructInfo* Resolver::Structure(sem::StructType* str) {
+Resolver::StructInfo* Resolver::Structure(const sem::StructType* str) {
   auto info_it = struct_info_.find(str);
   if (info_it != struct_info_.end()) {
     // StructInfo already resolved for this structure type
@@ -2617,7 +2621,7 @@
 }
 
 bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
-                                            sem::Type* ty,
+                                            const sem::Type* ty,
                                             const Source& usage) {
   ty = ty->UnwrapIfNeeded();
 
@@ -2676,27 +2680,29 @@
   return vec_type.FriendlyName(builder_->Symbols());
 }
 
-sem::Type* Resolver::Canonical(sem::Type* type) {
+const sem::Type* Resolver::Canonical(const sem::Type* type) {
   using AccessControl = sem::AccessControl;
   using Alias = sem::Alias;
   using Matrix = sem::Matrix;
   using Type = sem::Type;
   using Vector = sem::Vector;
 
-  std::function<Type*(Type*)> make_canonical;
-  make_canonical = [&](Type* t) -> sem::Type* {
+  std::function<const Type*(const Type*)> make_canonical;
+  make_canonical = [&](const Type* t) -> const sem::Type* {
     // Unwrap alias sequence
-    Type* ct = t;
+    const Type* ct = t;
     while (auto* p = ct->As<Alias>()) {
       ct = p->type();
     }
 
     if (auto* v = ct->As<Vector>()) {
-      return builder_->create<Vector>(make_canonical(v->type()), v->size());
+      return builder_->create<Vector>(
+          const_cast<sem::Type*>(make_canonical(v->type())), v->size());
     }
     if (auto* m = ct->As<Matrix>()) {
-      return builder_->create<Matrix>(make_canonical(m->type()), m->rows(),
-                                      m->columns());
+      return builder_->create<Matrix>(
+          const_cast<sem::Type*>(make_canonical(m->type())), m->rows(),
+          m->columns());
     }
     if (auto* ac = ct->As<AccessControl>()) {
       return builder_->create<AccessControl>(ac->access_control(),
@@ -2709,7 +2715,7 @@
                             [&] { return make_canonical(type); });
 }
 
-void Resolver::Mark(ast::Node* node) {
+void Resolver::Mark(const ast::Node* node) {
   if (node == nullptr) {
     TINT_ICE(diagnostics_) << "Resolver::Mark() called with nullptr";
   }
@@ -2722,7 +2728,8 @@
       << "At: " << node->source();
 }
 
-Resolver::VariableInfo::VariableInfo(ast::Variable* decl, sem::Type* ctype)
+Resolver::VariableInfo::VariableInfo(const ast::Variable* decl,
+                                     const sem::Type* ctype)
     : declaration(decl),
       type(ctype),
       storage_class(decl->declared_storage_class()) {}
diff --git a/src/resolver/resolver.h b/src/resolver/resolver.h
index fc5f44f..2994033 100644
--- a/src/resolver/resolver.h
+++ b/src/resolver/resolver.h
@@ -73,34 +73,34 @@
 
   /// @param type the given type
   /// @returns true if the given type is storable
-  static bool IsStorable(sem::Type* type);
+  static bool IsStorable(const sem::Type* type);
 
   /// @param type the given type
   /// @returns true if the given type is host-shareable
-  static bool IsHostShareable(sem::Type* type);
+  static bool IsHostShareable(const sem::Type* type);
 
   /// @param lhs the assignment store type (non-pointer)
   /// @param rhs the assignment source type (non-pointer or pointer with
   /// auto-deref)
   /// @returns true an expression of type `rhs` can be assigned to a variable,
   /// structure member or array element of type `lhs`
-  static bool IsValidAssignment(sem::Type* lhs, sem::Type* rhs);
+  static bool IsValidAssignment(const sem::Type* lhs, const sem::Type* rhs);
 
   /// @param type the input type
   /// @returns the canonical type for `type`; that is, a type with all aliases
   /// removed. For example, `Canonical(alias<alias<vec3<alias<f32>>>>)` is
   /// `vec3<f32>`.
-  sem::Type* Canonical(sem::Type* type);
+  const sem::Type* Canonical(const sem::Type* type);
 
  private:
   /// Structure holding semantic information about a variable.
   /// Used to build the sem::Variable nodes at the end of resolving.
   struct VariableInfo {
-    VariableInfo(ast::Variable* decl, sem::Type* type);
+    VariableInfo(const ast::Variable* decl, const sem::Type* type);
     ~VariableInfo();
 
-    ast::Variable* const declaration;
-    sem::Type* type;
+    ast::Variable const* const declaration;
+    sem::Type const* type;
     ast::StorageClass storage_class;
     std::vector<ast::IdentifierExpression*> users;
   };
@@ -124,7 +124,7 @@
   /// Structure holding semantic information about an expression.
   /// Used to build the sem::Expression nodes at the end of resolving.
   struct ExpressionInfo {
-    sem::Type* type;
+    sem::Type const* type;
     sem::Statement* statement;
   };
 
@@ -260,25 +260,25 @@
   /// hasn't been constructed already. If an error is raised, nullptr is
   /// returned.
   /// @param ty the ast::Type
-  sem::Type* Type(ast::Type* ty);
+  sem::Type* Type(const ast::Type* ty);
 
   /// @returns the semantic information for the array `arr`, building it if it
   /// hasn't been constructed already. If an error is raised, nullptr is
   /// returned.
   /// @param arr the Array to get semantic information for
   /// @param source the Source of the ast node with this array as its type
-  const sem::Array* Array(sem::ArrayType* arr, const Source& source);
+  const sem::Array* Array(const sem::ArrayType* arr, const Source& source);
 
   /// @returns the StructInfo for the structure `str`, building it if it hasn't
   /// been constructed already. If an error is raised, nullptr is returned.
-  StructInfo* Structure(sem::StructType* str);
+  StructInfo* Structure(const sem::StructType* str);
 
   /// @returns the VariableInfo for the variable `var`, building it if it hasn't
   /// been constructed already. If an error is raised, nullptr is returned.
   /// @param var the variable to create or return the `VariableInfo` for
   /// @param type optional type of `var` to use instead of
   /// `var->declared_type()`. For type inference.
-  VariableInfo* Variable(ast::Variable* var, sem::Type* type = nullptr);
+  VariableInfo* Variable(ast::Variable* var, const sem::Type* type = nullptr);
 
   /// Records the storage class usage for the given type, and any transient
   /// dependencies of the type. Validates that the type can be used for the
@@ -289,7 +289,7 @@
   /// given type and storage class. Used for generating sensible error messages.
   /// @returns true on success, false on error
   bool ApplyStorageClassUsageToType(ast::StorageClass sc,
-                                    sem::Type* ty,
+                                    const sem::Type* ty,
                                     const Source& usage);
 
   /// @param align the output default alignment in bytes for the type `ty`
@@ -303,13 +303,13 @@
 
   /// @returns the resolved type of the ast::Expression `expr`
   /// @param expr the expression
-  sem::Type* TypeOf(ast::Expression* expr);
+  const sem::Type* TypeOf(ast::Expression* expr);
 
   /// Creates a sem::Expression node with the resolved type `type`, and
   /// assigns this semantic node to the expression `expr`.
   /// @param expr the expression
   /// @param type the resolved type
-  void SetType(ast::Expression* expr, sem::Type* type);
+  void SetType(ast::Expression* expr, const sem::Type* type);
 
   /// Constructs a new BlockInfo with the given type and with #current_block_ as
   /// its parent, assigns this to #current_block_, and then calls `callback`.
@@ -329,7 +329,7 @@
   /// Mark records that the given AST node has been visited, and asserts that
   /// the given node has not already been seen. Diamonds in the AST are illegal.
   /// @param node the AST node.
-  void Mark(ast::Node* node);
+  void Mark(const ast::Node* node);
 
   ProgramBuilder* const builder_;
   std::unique_ptr<IntrinsicTable> const intrinsic_table_;
@@ -341,9 +341,9 @@
   std::unordered_map<const ast::Variable*, VariableInfo*> variable_to_info_;
   std::unordered_map<ast::CallExpression*, FunctionCallInfo> function_calls_;
   std::unordered_map<ast::Expression*, ExpressionInfo> expr_info_;
-  std::unordered_map<sem::StructType*, StructInfo*> struct_info_;
-  std::unordered_map<sem::Type*, sem::Type*> type_to_canonical_;
-  std::unordered_set<ast::Node*> marked_;
+  std::unordered_map<const sem::StructType*, StructInfo*> struct_info_;
+  std::unordered_map<const sem::Type*, const sem::Type*> type_to_canonical_;
+  std::unordered_set<const ast::Node*> marked_;
   FunctionInfo* current_function_ = nullptr;
   sem::Statement* current_statement_ = nullptr;
   BlockAllocator<VariableInfo> variable_infos_;