sem::Type: Replace GetDefaultAlignAndSize() with Size() and Align()

This is a cleaner API, and the implementation doesn't have to know a bunch of information about all the derived types.

Change-Id: I96bebcb9f3ceda86fa34bd8e70961dee63fd7e13
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/59301
Commit-Queue: Ben Clayton <bclayton@google.com>
Auto-Submit: Ben Clayton <bclayton@google.com>
Kokoro: Ben Clayton <bclayton@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
diff --git a/src/inspector/inspector.cc b/src/inspector/inspector.cc
index dd2bdb2..84a1eee 100644
--- a/src/inspector/inspector.cc
+++ b/src/inspector/inspector.cc
@@ -546,9 +546,9 @@
   auto* func_sem = program_->Sem().Get(func);
   for (const sem::Variable* var : func_sem->ReferencedModuleVariables()) {
     if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
-      uint32_t align = 0;
-      uint32_t size = 0;
-      var->Type()->UnwrapRef()->GetDefaultAlignAndSize(align, size);
+      auto* ty = var->Type()->UnwrapRef();
+      uint32_t align = ty->Align();
+      uint32_t size = ty->Size();
 
       // This essentially matches std430 layout rules from GLSL, which are in
       // turn specified as an upper bound for Vulkan layout sizing. Since D3D
diff --git a/src/resolver/resolver.cc b/src/resolver/resolver.cc
index 93012fd..f7bb4e4 100644
--- a/src/resolver/resolver.cc
+++ b/src/resolver/resolver.cc
@@ -693,9 +693,7 @@
   };
 
   auto required_alignment_of = [&](const sem::Type* ty) {
-    uint32_t actual_align = 0;
-    uint32_t actual_size = 0;
-    ty->GetDefaultAlignAndSize(actual_align, actual_size);
+    uint32_t actual_align = ty->Align();
     uint32_t required_align = actual_align;
     if (is_uniform_struct_or_array(ty)) {
       required_align = utils::RoundUp(16u, actual_align);
@@ -3813,9 +3811,8 @@
     return nullptr;
   }
 
-  uint32_t el_align = 0;
-  uint32_t el_size = 0;
-  el_ty->GetDefaultAlignAndSize(el_align, el_size);
+  uint32_t el_align = el_ty->Align();
+  uint32_t el_size = el_ty->Size();
 
   if (!ValidateNoDuplicateDecorations(arr->decorations())) {
     return nullptr;
@@ -4035,9 +4032,8 @@
     }
 
     uint32_t offset = struct_size;
-    uint32_t align = 0;
-    uint32_t size = 0;
-    type->GetDefaultAlignAndSize(align, size);
+    uint32_t align = type->Align();
+    uint32_t size = type->Size();
 
     if (!ValidateNoDuplicateDecorations(member->decorations())) {
       return nullptr;
diff --git a/src/sem/array.cc b/src/sem/array.cc
index 8e3a734..a5757be 100644
--- a/src/sem/array.cc
+++ b/src/sem/array.cc
@@ -68,5 +68,13 @@
   return out.str();
 }
 
+uint32_t Array::Align() const {
+  return align_;
+}
+
+uint32_t Array::Size() const {
+  return size_;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/array.h b/src/sem/array.h
index 592c538..8ae7a97 100644
--- a/src/sem/array.h
+++ b/src/sem/array.h
@@ -62,12 +62,12 @@
   /// @returns the byte alignment of the array
   /// @note this may differ from the alignment of a structure member of this
   /// array type, if the member is annotated with the `[[align(n)]]` decoration.
-  uint32_t Align() const { return align_; }
+  uint32_t Align() const override;
 
   /// @returns the byte size of the array
   /// @note this may differ from the size of a structure member of this array
   /// type, if the member is annotated with the `[[size(n)]]` decoration.
-  uint32_t SizeInBytes() const { return size_; }
+  uint32_t Size() const override;
 
   /// @returns the number of bytes from the start of one element of the
   /// array to the start of the next element
diff --git a/src/sem/atomic_type.cc b/src/sem/atomic_type.cc
index 4444768..18fc5d2 100644
--- a/src/sem/atomic_type.cc
+++ b/src/sem/atomic_type.cc
@@ -38,6 +38,14 @@
   return out.str();
 }
 
+uint32_t Atomic::Size() const {
+  return subtype_->Size();
+}
+
+uint32_t Atomic::Align() const {
+  return subtype_->Align();
+}
+
 Atomic::Atomic(Atomic&&) = default;
 
 Atomic::~Atomic() = default;
diff --git a/src/sem/atomic_type.h b/src/sem/atomic_type.h
index 8c4a4ff..c284d1d 100644
--- a/src/sem/atomic_type.h
+++ b/src/sem/atomic_type.h
@@ -44,6 +44,12 @@
   /// declared in WGSL.
   std::string FriendlyName(const SymbolTable& symbols) const override;
 
+  /// @returns the size in bytes of the type.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type.
+  uint32_t Align() const override;
+
  private:
   sem::Type const* const subtype_;
 };
diff --git a/src/sem/bool_type.cc b/src/sem/bool_type.cc
index aec4f6b..eeb3100 100644
--- a/src/sem/bool_type.cc
+++ b/src/sem/bool_type.cc
@@ -39,5 +39,13 @@
   return true;
 }
 
+uint32_t Bool::Size() const {
+  return 4;
+}
+
+uint32_t Bool::Align() const {
+  return 4;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/bool_type.h b/src/sem/bool_type.h
index b9fa24d..c143989 100644
--- a/src/sem/bool_type.h
+++ b/src/sem/bool_type.h
@@ -48,6 +48,16 @@
   /// @returns true if constructible as per
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
   bool IsConstructible() const override;
+
+  /// @returns the size in bytes of the type.
+  /// @note: booleans are not host-sharable, but still may exist in workgroup
+  /// storage.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type.
+  /// @note: booleans are not host-sharable, but still may exist in workgroup
+  /// storage.
+  uint32_t Align() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/f32_type.cc b/src/sem/f32_type.cc
index 49d8666..3935fe3a 100644
--- a/src/sem/f32_type.cc
+++ b/src/sem/f32_type.cc
@@ -39,5 +39,13 @@
   return true;
 }
 
+uint32_t F32::Size() const {
+  return 4;
+}
+
+uint32_t F32::Align() const {
+  return 4;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/f32_type.h b/src/sem/f32_type.h
index 2d35982..16fe521 100644
--- a/src/sem/f32_type.h
+++ b/src/sem/f32_type.h
@@ -42,6 +42,12 @@
   /// @returns true if constructible as per
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
   bool IsConstructible() const override;
+
+  /// @returns the size in bytes of the type.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type.
+  uint32_t Align() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/i32_type.cc b/src/sem/i32_type.cc
index 0b24e87..daacf27 100644
--- a/src/sem/i32_type.cc
+++ b/src/sem/i32_type.cc
@@ -39,5 +39,13 @@
   return true;
 }
 
+uint32_t I32::Size() const {
+  return 4;
+}
+
+uint32_t I32::Align() const {
+  return 4;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/i32_type.h b/src/sem/i32_type.h
index 4621306..6818559 100644
--- a/src/sem/i32_type.h
+++ b/src/sem/i32_type.h
@@ -42,6 +42,12 @@
   /// @returns true if constructible as per
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
   bool IsConstructible() const override;
+
+  /// @returns the size in bytes of the type.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type.
+  uint32_t Align() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/matrix_type.cc b/src/sem/matrix_type.cc
index f9fed56..34de689 100644
--- a/src/sem/matrix_type.cc
+++ b/src/sem/matrix_type.cc
@@ -53,5 +53,17 @@
   return true;
 }
 
+uint32_t Matrix::Size() const {
+  return column_type_->Align() * columns();
+}
+
+uint32_t Matrix::Align() const {
+  return column_type_->Align();
+}
+
+uint32_t Matrix::ColumnStride() const {
+  return column_type_->Align();
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/matrix_type.h b/src/sem/matrix_type.h
index d3724fa..9c69b79 100644
--- a/src/sem/matrix_type.h
+++ b/src/sem/matrix_type.h
@@ -58,6 +58,16 @@
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
   bool IsConstructible() const override;
 
+  /// @returns the size in bytes of the type. This may include tail padding.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type. This may include tail
+  /// padding.
+  uint32_t Align() const override;
+
+  /// @returns the number of bytes between columns of the matrix
+  uint32_t ColumnStride() const;
+
  private:
   Type* const subtype_;
   Vector* const column_type_;
diff --git a/src/sem/sem_array_test.cc b/src/sem/sem_array_test.cc
index 30c6fca..04207b6 100644
--- a/src/sem/sem_array_test.cc
+++ b/src/sem/sem_array_test.cc
@@ -27,7 +27,7 @@
   EXPECT_EQ(arr->ElemType(), &u32);
   EXPECT_EQ(arr->Count(), 2u);
   EXPECT_EQ(arr->Align(), 4u);
-  EXPECT_EQ(arr->SizeInBytes(), 8u);
+  EXPECT_EQ(arr->Size(), 8u);
   EXPECT_EQ(arr->Stride(), 32u);
   EXPECT_EQ(arr->ImplicitStride(), 16u);
   EXPECT_FALSE(arr->IsStrideImplicit());
@@ -40,7 +40,7 @@
   EXPECT_EQ(arr->ElemType(), &u32);
   EXPECT_EQ(arr->Count(), 0u);
   EXPECT_EQ(arr->Align(), 4u);
-  EXPECT_EQ(arr->SizeInBytes(), 8u);
+  EXPECT_EQ(arr->Size(), 8u);
   EXPECT_EQ(arr->Stride(), 32u);
   EXPECT_EQ(arr->ImplicitStride(), 32u);
   EXPECT_TRUE(arr->IsStrideImplicit());
diff --git a/src/sem/struct.cc b/src/sem/struct.cc
index 317402e..210a70e 100644
--- a/src/sem/struct.cc
+++ b/src/sem/struct.cc
@@ -60,6 +60,14 @@
   return declaration_->type_name();
 }
 
+uint32_t Struct::Align() const {
+  return align_;
+}
+
+uint32_t Struct::Size() const {
+  return size_;
+}
+
 std::string Struct::FriendlyName(const SymbolTable& symbols) const {
   return symbols.NameFor(declaration_->name());
 }
diff --git a/src/sem/struct.h b/src/sem/struct.h
index e4595c9..b5357cb 100644
--- a/src/sem/struct.h
+++ b/src/sem/struct.h
@@ -86,13 +86,13 @@
   /// @note this may differ from the alignment of a structure member of this
   /// structure type, if the member is annotated with the `[[align(n)]]`
   /// decoration.
-  uint32_t Align() const { return align_; }
+  uint32_t Align() const override;
 
   /// @returns the byte size of the structure
   /// @note this may differ from the size of a structure member of this
   /// structure type, if the member is annotated with the `[[size(n)]]`
   /// decoration.
-  uint32_t Size() const { return size_; }
+  uint32_t Size() const override;
 
   /// @returns the byte size of the members without the end of structure
   /// alignment padding
diff --git a/src/sem/type.cc b/src/sem/type.cc
index 16555f9..5f547a2 100644
--- a/src/sem/type.cc
+++ b/src/sem/type.cc
@@ -14,9 +14,6 @@
 
 #include "src/sem/type.h"
 
-#include "src/debug.h"
-#include "src/sem/array.h"
-#include "src/sem/atomic_type.h"
 #include "src/sem/bool_type.h"
 #include "src/sem/f32_type.h"
 #include "src/sem/i32_type.h"
@@ -24,7 +21,6 @@
 #include "src/sem/pointer_type.h"
 #include "src/sem/reference_type.h"
 #include "src/sem/sampler_type.h"
-#include "src/sem/struct.h"
 #include "src/sem/texture_type.h"
 #include "src/sem/u32_type.h"
 #include "src/sem/vector_type.h"
@@ -56,59 +52,12 @@
   return type;
 }
 
-void Type::GetDefaultAlignAndSize(uint32_t& align, uint32_t& size) const {
-  TINT_ASSERT(Semantic, !As<Reference>());
-  TINT_ASSERT(Semantic, !As<Pointer>());
+uint32_t Type::Size() const {
+  return 0;
+}
 
-  static constexpr uint32_t vector_size[] = {
-      /* padding */ 0,
-      /* padding */ 0,
-      /*vec2*/ 8,
-      /*vec3*/ 12,
-      /*vec4*/ 16,
-  };
-  static constexpr uint32_t vector_align[] = {
-      /* padding */ 0,
-      /* padding */ 0,
-      /*vec2*/ 8,
-      /*vec3*/ 16,
-      /*vec4*/ 16,
-  };
-
-  if (is_scalar()) {
-    // Note: Also captures booleans, but these are not host-shareable.
-    align = 4;
-    size = 4;
-    return;
-  }
-  if (auto* vec = As<Vector>()) {
-    TINT_ASSERT(Semantic, vec->Width() >= 2 && vec->Width() <= 4);
-    align = vector_align[vec->Width()];
-    size = vector_size[vec->Width()];
-    return;
-  }
-  if (auto* mat = As<Matrix>()) {
-    TINT_ASSERT(Semantic, mat->columns() >= 2 && mat->columns() <= 4);
-    TINT_ASSERT(Semantic, mat->rows() >= 2 && mat->rows() <= 4);
-    align = vector_align[mat->rows()];
-    size = vector_align[mat->rows()] * mat->columns();
-    return;
-  }
-  if (auto* s = As<Struct>()) {
-    align = s->Align();
-    size = s->Size();
-    return;
-  }
-  if (auto* a = As<Array>()) {
-    align = a->Align();
-    size = a->SizeInBytes();
-    return;
-  }
-  if (auto* a = As<Atomic>()) {
-    return a->Type()->GetDefaultAlignAndSize(align, size);
-  }
-
-  TINT_ASSERT(Semantic, false);
+uint32_t Type::Align() const {
+  return 0;
 }
 
 bool Type::IsConstructible() const {
diff --git a/src/sem/type.h b/src/sem/type.h
index 81a11d9..d38b5ef 100644
--- a/src/sem/type.h
+++ b/src/sem/type.h
@@ -52,9 +52,14 @@
   /// @returns the inner type if this is a reference, `this` otherwise
   const Type* UnwrapRef() const;
 
-  /// @param align the output default alignment in bytes for this type.
-  /// @param size the output default size in bytes for this type.
-  void GetDefaultAlignAndSize(uint32_t& align, uint32_t& size) const;
+  /// @returns the size in bytes of the type. This may include tail padding.
+  /// @note opaque types will return a size of 0.
+  virtual uint32_t Size() const;
+
+  /// @returns the alignment in bytes of the type. This may include tail
+  /// padding.
+  /// @note opaque types will return a size of 0.
+  virtual uint32_t Align() const;
 
   /// @returns true if constructible as per
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
diff --git a/src/sem/u32_type.cc b/src/sem/u32_type.cc
index dbc42b0..6383814 100644
--- a/src/sem/u32_type.cc
+++ b/src/sem/u32_type.cc
@@ -39,5 +39,13 @@
   return true;
 }
 
+uint32_t U32::Size() const {
+  return 4;
+}
+
+uint32_t U32::Align() const {
+  return 4;
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/u32_type.h b/src/sem/u32_type.h
index cf68da6..8d4bb06 100644
--- a/src/sem/u32_type.h
+++ b/src/sem/u32_type.h
@@ -42,6 +42,12 @@
   /// @returns true if constructible as per
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
   bool IsConstructible() const override;
+
+  /// @returns the size in bytes of the type.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type.
+  uint32_t Align() const override;
 };
 
 }  // namespace sem
diff --git a/src/sem/vector_type.cc b/src/sem/vector_type.cc
index 1a44a52..20faa13 100644
--- a/src/sem/vector_type.cc
+++ b/src/sem/vector_type.cc
@@ -45,5 +45,37 @@
   return true;
 }
 
+uint32_t Vector::Size() const {
+  return SizeOf(width_);
+}
+
+uint32_t Vector::Align() const {
+  return AlignOf(width_);
+}
+
+uint32_t Vector::SizeOf(uint32_t width) {
+  switch (width) {
+    case 2:
+      return 8;
+    case 3:
+      return 12;
+    case 4:
+      return 16;
+  }
+  return 0;  // Unreachable
+}
+
+uint32_t Vector::AlignOf(uint32_t width) {
+  switch (width) {
+    case 2:
+      return 8;
+    case 3:
+      return 16;
+    case 4:
+      return 16;
+  }
+  return 0;  // Unreachable
+}
+
 }  // namespace sem
 }  // namespace tint
diff --git a/src/sem/vector_type.h b/src/sem/vector_type.h
index 5697386..91633a7 100644
--- a/src/sem/vector_type.h
+++ b/src/sem/vector_type.h
@@ -39,9 +39,6 @@
   /// @returns the name for th type
   std::string type_name() const override;
 
-  /// @returns the width of the vector
-  uint32_t Width() const { return width_; }
-
   /// @param symbols the program's symbol table
   /// @returns the name for this type that closely resembles how it would be
   /// declared in WGSL.
@@ -51,6 +48,24 @@
   /// https://gpuweb.github.io/gpuweb/wgsl/#constructible-types
   bool IsConstructible() const override;
 
+  /// @returns the number of elements in the vector
+  uint32_t Width() const { return width_; }
+
+  /// @returns the size in bytes of the type. This may include tail padding.
+  uint32_t Size() const override;
+
+  /// @returns the alignment in bytes of the type. This may include tail
+  /// padding.
+  uint32_t Align() const override;
+
+  /// @param width the width of the vector
+  /// @returns the size in bytes of a vector of the given width.
+  static uint32_t SizeOf(uint32_t width);
+
+  /// @param width the width of the vector
+  /// @returns the alignment in bytes of a vector of the given width.
+  static uint32_t AlignOf(uint32_t width);
+
  private:
   Type const* const subtype_;
   uint32_t const width_;
diff --git a/src/transform/decompose_memory_access.cc b/src/transform/decompose_memory_access.cc
index 65bfec1..8f94f18 100644
--- a/src/transform/decompose_memory_access.cc
+++ b/src/transform/decompose_memory_access.cc
@@ -127,17 +127,6 @@
   };
 };
 
-/// @returns the size in bytes of a scalar
-uint32_t ScalarSize(const sem::Type*) {
-  // TODO(bclayton): Assumes 32-bit elements
-  return 4;
-}
-
-/// @returns the number of bytes between columns of the given matrix
-uint32_t MatrixColumnStride(const sem::Matrix* mat) {
-  return ScalarSize(mat->type()) * ((mat->rows() == 2) ? 2 : 4);
-}
-
 bool IntrinsicDataTypeFor(const sem::Type* ty,
                           DecomposeMemoryAccess::Intrinsic::DataType& out) {
   if (ty->Is<sem::I32>()) {
@@ -525,7 +514,7 @@
               auto* vec_ty = mat_ty->ColumnType();
               Symbol load = LoadFunc(buf_ty, vec_ty, var_user);
               for (uint32_t i = 0; i < mat_ty->columns(); i++) {
-                auto* offset = b.Add("offset", i * MatrixColumnStride(mat_ty));
+                auto* offset = b.Add("offset", i * mat_ty->ColumnStride());
                 values.emplace_back(b.Call(load, "buffer", offset));
               }
             } else if (auto* str = el_ty->As<sem::Struct>()) {
@@ -624,7 +613,7 @@
               auto* vec_ty = mat_ty->ColumnType();
               Symbol store = StoreFunc(buf_ty, vec_ty, var_user);
               for (uint32_t i = 0; i < mat_ty->columns(); i++) {
-                auto* offset = b.Add("offset", i * MatrixColumnStride(mat_ty));
+                auto* offset = b.Add("offset", i * mat_ty->ColumnStride());
                 auto* access = b.IndexAccessor("value", i);
                 auto* call = b.Call(store, "buffer", offset, access);
                 body.emplace_back(b.create<ast::CallStatement>(call));
@@ -845,7 +834,7 @@
           if (auto access = state.TakeAccess(accessor->structure())) {
             auto* vec_ty = access.type->As<sem::Vector>();
             auto* offset =
-                state.Mul(ScalarSize(vec_ty->type()), swizzle->Indices()[0]);
+                state.Mul(vec_ty->type()->Size(), swizzle->Indices()[0]);
             state.AddAccess(accessor, {
                                           access.var,
                                           state.Add(access.offset, offset),
@@ -882,7 +871,7 @@
         }
         if (auto* vec_ty = access.type->As<sem::Vector>()) {
           auto* offset =
-              state.Mul(ScalarSize(vec_ty->type()), accessor->idx_expr());
+              state.Mul(vec_ty->type()->Size(), accessor->idx_expr());
           state.AddAccess(accessor, {
                                         access.var,
                                         state.Add(access.offset, offset),
@@ -892,7 +881,7 @@
         }
         if (auto* mat_ty = access.type->As<sem::Matrix>()) {
           auto* offset =
-              state.Mul(MatrixColumnStride(mat_ty), accessor->idx_expr());
+              state.Mul(mat_ty->ColumnStride(), accessor->idx_expr());
           state.AddAccess(accessor, {
                                         access.var,
                                         state.Add(access.offset, offset),
diff --git a/src/transform/zero_init_workgroup_memory.cc b/src/transform/zero_init_workgroup_memory.cc
index bc820fa..f866298 100644
--- a/src/transform/zero_init_workgroup_memory.cc
+++ b/src/transform/zero_init_workgroup_memory.cc
@@ -130,7 +130,7 @@
     // we need to return true for these arrays.
     // See https://github.com/gpuweb/gpuweb/pull/1792
     return (cfg.init_arrays_with_loop_size_threshold != 0) &&
-           (array->SizeInBytes() >= cfg.init_arrays_with_loop_size_threshold);
+           (array->Size() >= cfg.init_arrays_with_loop_size_threshold);
   }
 };
 
diff --git a/src/writer/hlsl/generator_impl.cc b/src/writer/hlsl/generator_impl.cc
index e853aee..e3ed808 100644
--- a/src/writer/hlsl/generator_impl.cc
+++ b/src/writer/hlsl/generator_impl.cc
@@ -2970,21 +2970,21 @@
   } else if (type->Is<sem::U32>()) {
     out << "uint";
   } else if (auto* vec = type->As<sem::Vector>()) {
-    auto size = vec->Width();
-    if (vec->type()->Is<sem::F32>() && size >= 1 && size <= 4) {
-      out << "float" << size;
-    } else if (vec->type()->Is<sem::I32>() && size >= 1 && size <= 4) {
-      out << "int" << size;
-    } else if (vec->type()->Is<sem::U32>() && size >= 1 && size <= 4) {
-      out << "uint" << size;
-    } else if (vec->type()->Is<sem::Bool>() && size >= 1 && size <= 4) {
-      out << "bool" << size;
+    auto width = vec->Width();
+    if (vec->type()->Is<sem::F32>() && width >= 1 && width <= 4) {
+      out << "float" << width;
+    } else if (vec->type()->Is<sem::I32>() && width >= 1 && width <= 4) {
+      out << "int" << width;
+    } else if (vec->type()->Is<sem::U32>() && width >= 1 && width <= 4) {
+      out << "uint" << width;
+    } else if (vec->type()->Is<sem::Bool>() && width >= 1 && width <= 4) {
+      out << "bool" << width;
     } else {
       out << "vector<";
       if (!EmitType(out, vec->type(), storage_class, access, "")) {
         return false;
       }
-      out << ", " << size << ">";
+      out << ", " << width << ">";
     }
   } else if (auto* atomic = type->As<sem::Atomic>()) {
     if (!EmitType(out, atomic->Type(), storage_class, access, name)) {