[metal-writer] Stub out the Metal Shading Language backend.

This CL adds the basis of the Metal Shading Language backend.

Bug: tint:8
Change-Id: I85976250eb41ac12203a5db116444e993c3d09d4
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/23700
Reviewed-by: David Neto <dneto@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 39d66df..a8f0c6a 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -56,6 +56,12 @@
     defines += [ "TINT_BUILD_WGSL_WRITER=0" ]
   }
 
+  if (tint_build_msl_writer) {
+    defines += [ "TINT_BUILD_MSL_WRITER=1" ]
+  } else {
+    defines += [ "TINT_BUILD_MSL_WRITER=0" ]
+  }
+
   include_dirs = [
     "${tint_root_dir}/",
     "${tint_root_dir}/include/",
@@ -353,6 +359,10 @@
     "src/validator.h",
     "src/validator_impl.cc",
     "src/validator_impl.h",
+    "src/writer/text.cc",
+    "src/writer/text.h",
+    "src/writer/text_generator.cc",
+    "src/writer/text_generator.h",
     "src/writer/writer.cc",
     "src/writer/writer.h",
   ]
@@ -472,6 +482,23 @@
   }
 }
 
+source_set("libtint_msl_writer_src") {
+  sources = [
+    "src/writer/msl/generator.cc",
+    "src/writer/msl/generator.h",
+    "src/writer/msl/generator_impl.cc",
+    "src/wrtier/msl/generator_impl.h",
+  ]
+
+  configs += [ ":tint_common_config" ]
+  public_configs += [ ":tint_public_config" ]
+
+  if (build_with_chromium) {
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+}
+
 source_set("libtint") {
   deps = [ ":libtint_core_src" ]
 
@@ -491,6 +518,10 @@
     deps += [ ":libtint_wgsl_writer_src" ]
   }
 
+  if (tint_build_msl_writer) {
+    deps += [ ":libtint_msl_writer_src" ]
+  }
+
   configs += [ ":tint_common_config" ]
   public_configs = [ ":tint_public_config" ]
 
@@ -852,6 +883,21 @@
   }
 }
 
+source_set("tint_unittests_msl_writer_src") {
+  sources = [ "src/writer/msl/generator_impl_test.cc" ]
+
+  configs += [
+    ":tint_common_config",
+    ":tint_unittests_config",
+  ]
+  public_configs = [ ":tint_public_config" ]
+
+  if (build_with_chromium) {
+    configs -= [ "//build/config/compiler:chromium_code" ]
+    configs += [ "//build/config/compiler:no_chromium_code" ]
+  }
+}
+
 source_set("tint_unittests_src") {
   deps = [ ":tint_unittests_core_src" ]
 
@@ -871,6 +917,10 @@
     deps += [ ":tint_unittests_wgsl_writer_src" ]
   }
 
+  if (tint_build_msl_writer) {
+    deps += [ ":tint_unittests_msl_writer_src" ]
+  }
+
   configs += [
     ":tint_common_config",
     ":tint_unittests_config",
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b039a60..e248444 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -29,6 +29,7 @@
 option(TINT_BUILD_DOCS "Build documentation" ON)
 option(TINT_BUILD_SPV_READER "Build the SPIR-V input reader" ON)
 option(TINT_BUILD_WGSL_READER "Builde the WGSL input reader" ON)
+option(TINT_BUILD_MSL_WRITER "Build the MSL output writer" ON)
 option(TINT_BUILD_SPV_WRITER "Build the SPIR-V output writer" ON)
 option(TINT_BUILD_WGSL_WRITER "Build the WGSL output writer" ON)
 option(TINT_BUILD_FUZZERS "Build fuzzers" OFF)
@@ -50,6 +51,7 @@
 message(STATUS "Tint build docs: ${TINT_BUILD_DOCS}")
 message(STATUS "Tint build SPIR-V reader: ${TINT_BUILD_SPV_READER}")
 message(STATUS "Tint build WGSL reader: ${TINT_BUILD_WGSL_READER}")
+message(STATUS "Tint build MSL writer: ${TINT_BUILD_MSL_WRITER}")
 message(STATUS "Tint build SPIR-V writer: ${TINT_BUILD_SPV_WRITER}")
 message(STATUS "Tint build WGSL writer: ${TINT_BUILD_WGSL_WRITER}")
 message(STATUS "Tint build fuzzers: ${TINT_BUILD_FUZZERS}")
@@ -117,6 +119,8 @@
   target_compile_definitions(${TARGET} PRIVATE
       -DTINT_BUILD_WGSL_READER=$<BOOL:${TINT_BUILD_WGSL_READER}>)
   target_compile_definitions(${TARGET} PRIVATE
+    -DTINT_BUILD_MSL_WRITER=$<BOOL:${TINT_BUILD_MSL_WRITER}>)
+  target_compile_definitions(${TARGET} PRIVATE
       -DTINT_BUILD_SPV_WRITER=$<BOOL:${TINT_BUILD_SPV_WRITER}>)
   target_compile_definitions(${TARGET} PRIVATE
       -DTINT_BUILD_WGSL_WRITER=$<BOOL:${TINT_BUILD_WGSL_WRITER}>)
diff --git a/include/tint/tint.h b/include/tint/tint.h
index 7749d8b..6ff5947 100644
--- a/include/tint/tint.h
+++ b/include/tint/tint.h
@@ -42,4 +42,8 @@
 #include "src/writer/wgsl/generator.h"
 #endif  // TINT_BUILD_WGSL_WRITER
 
+#if TINT_BUILD_MSL_WRITER
+#include "src/writer/msl/generator.h"
+#endif  // TINT_BUILD_MSL_WRITER
+
 #endif  // INCLUDE_TINT_TINT_H_
diff --git a/samples/main.cc b/samples/main.cc
index 46a59a1..bd1190f 100644
--- a/samples/main.cc
+++ b/samples/main.cc
@@ -32,6 +32,7 @@
   kSpirv,
   kSpvAsm,
   kWgsl,
+  kMsl,
 };
 
 struct Options {
@@ -49,12 +50,13 @@
 const char kUsage[] = R"(Usage: tint [options] <input-file>
 
  options:
-  --format <spirv|spvasm|wgsl>  -- Output format.
+  --format <spirv|spvasm|wgsl|msl>  -- Output format.
                                If not provided, will be inferred from output
                                filename extension:
                                    .spvasm -> spvasm
                                    .spv -> spirv
                                    .wgsl -> wgsl
+                                   .metal -> msl
                                If none matches, then default to SPIR-V assembly.
   --output-file <name>      -- Output file name.  Use "-" for standard output
   -o <name>                 -- Output file name.  Use "-" for standard output
@@ -79,6 +81,11 @@
     return Format::kWgsl;
 #endif  // TINT_BUILD_WGSL_WRITER
 
+#if TINT_BUILD_MSL_WRITER
+  if (fmt == "msl")
+    return Format::kMsl;
+#endif  // TINT_BUILD_MSL_WRITER
+
   return Format::kNone;
 }
 
@@ -95,16 +102,32 @@
 
 /// @param filename the filename to inspect
 /// @returns the inferred format for the filename suffix
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-parameter"
 Format infer_format(const std::string& filename) {
+#pragma clang diagnostic pop
+
+#if TINT_BUILD_SPV_WRITER
   if (ends_with(filename, ".spv")) {
     return Format::kSpirv;
   }
   if (ends_with(filename, ".spvasm")) {
     return Format::kSpvAsm;
   }
+#endif  // TINT_BUILD_SPV_WRITER
+
+#if TINT_BUILD_WGSL_WRITER
   if (ends_with(filename, ".wgsl")) {
     return Format::kWgsl;
   }
+#endif  // TINT_BUILD_WGSL_WRITER
+
+#if TINT_BUILD_MSL_WRITER
+  if (ends_with(filename, ".metal")) {
+    return Format::kMsl;
+  }
+#endif  // TINT_BUILD_WGSL_WRITER
+
   return Format::kNone;
 }
 
@@ -417,6 +440,12 @@
   }
 #endif  // TINT_BUILD_WGSL_WRITER
 
+#if TINT_BUILD_MSL_WRITER
+  if (options.format == Format::kMsl) {
+    writer = std::make_unique<tint::writer::msl::Generator>(std::move(mod));
+  }
+#endif  // TINT_BUILDER_MSL_WRITER
+
   if (!writer) {
     std::cerr << "Unknown output format specified" << std::endl;
     return 1;
@@ -443,14 +472,14 @@
   }
 #endif  // TINT_BUILD_SPV_WRITER
 
-#if TINT_BUILD_WGSL_WRITER
-  if (options.format == Format::kWgsl) {
-    auto* w = static_cast<tint::writer::wgsl::Generator*>(writer.get());
+#if TINT_BUILD_WGSL_WRITER || TINT_BUILD_MSL_WRITER
+  if (options.format == Format::kWgsl || options.format == Format::kMsl) {
+    auto* w = static_cast<tint::writer::Text*>(writer.get());
     if (!WriteFile(options.output_file, "w", w->result())) {
       return 1;
     }
   }
-#endif  // TINT_BUILD_WGSL_WRITER
+#endif  // TINT_BUILD_WGSL_WRITER || TINT_BUILD_MSL_WRITER
 
   return 0;
 }
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 6c1138b..aca6b5a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -184,6 +184,10 @@
   validator.h
   validator_impl.cc
   validator_impl.h
+  writer/text.cc
+  writer/text.h
+  writer/text_generator.cc
+  writer/text_generator.h
   writer/writer.cc
   writer/writer.h
 )
@@ -245,6 +249,15 @@
   )
 endif()
 
+if(${TINT_BUILD_MSL_WRITER})
+  list(APPEND TINT_LIB_SRCS
+    writer/msl/generator.cc
+    writer/msl/generator.h
+    writer/msl/generator_impl.cc
+    writer/msl/generator_impl.h
+  )
+endif()
+
 set(TINT_TEST_SRCS
   ast/array_accessor_expression_test.cc
   ast/as_expression_test.cc
@@ -477,6 +490,13 @@
     writer/wgsl/generator_impl_variable_test.cc
   )
 endif()
+
+if(${TINT_BUILD_MSL_WRITER})
+  list(APPEND TINT_TEST_SRCS
+    writer/msl/generator_impl_test.cc
+  )
+endif()
+
 add_executable(tint_unittests ${TINT_TEST_SRCS})
 
 if(NOT MSVC)
diff --git a/src/reader/spirv/function_misc_test.cc b/src/reader/spirv/function_misc_test.cc
index 56f4da0..3cb8ddfc 100644
--- a/src/reader/spirv/function_misc_test.cc
+++ b/src/reader/spirv/function_misc_test.cc
@@ -135,6 +135,8 @@
     {
       TypeConstructor{
         __vec_2__u32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   }
@@ -147,6 +149,8 @@
     {
       TypeConstructor{
         __vec_2__i32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   }
@@ -159,6 +163,8 @@
     {
       TypeConstructor{
         __vec_2__f32
+        ScalarConstructor{0.000000}
+        ScalarConstructor{0.000000}
       }
     }
   }
@@ -189,6 +195,16 @@
     {
       TypeConstructor{
         __mat_2_2__f32
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
       }
     }
   }
@@ -220,6 +236,8 @@
     {
       TypeConstructor{
         __array__u32_2
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   }
@@ -250,6 +268,10 @@
     {
       TypeConstructor{
         __alias_S__struct_S
+        ScalarConstructor{false}
+        ScalarConstructor{0}
+        ScalarConstructor{0}
+        ScalarConstructor{0.000000}
       }
     }
   }
diff --git a/src/reader/spirv/function_var_test.cc b/src/reader/spirv/function_var_test.cc
index 0a97ef5..dd9f283 100644
--- a/src/reader/spirv/function_var_test.cc
+++ b/src/reader/spirv/function_var_test.cc
@@ -492,6 +492,8 @@
     {
       TypeConstructor{
         __array__u32_2
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   }
@@ -524,6 +526,8 @@
     {
       TypeConstructor{
         __alias_Arr__array__u32_2_16
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   }
@@ -595,6 +599,13 @@
     {
       TypeConstructor{
         __alias_S__struct_S
+        ScalarConstructor{0}
+        ScalarConstructor{0.000000}
+        TypeConstructor{
+          __array__u32_2
+          ScalarConstructor{0}
+          ScalarConstructor{0}
+        }
       }
     }
   }
diff --git a/src/reader/spirv/parser_impl.cc b/src/reader/spirv/parser_impl.cc
index 5a42b77..4ad72ee 100644
--- a/src/reader/spirv/parser_impl.cc
+++ b/src/reader/spirv/parser_impl.cc
@@ -38,7 +38,6 @@
 #include "src/ast/bool_literal.h"
 #include "src/ast/builtin_decoration.h"
 #include "src/ast/decorated_variable.h"
-#include "src/ast/expression.h"
 #include "src/ast/float_literal.h"
 #include "src/ast/scalar_constructor_expression.h"
 #include "src/ast/sint_literal.h"
@@ -970,6 +969,10 @@
 
 std::unique_ptr<ast::Expression> ParserImpl::MakeNullValue(
     ast::type::Type* type) {
+  // TODO(dneto): Use the no-operands constructor syntax when it becomes
+  // available in Tint.
+  // https://github.com/gpuweb/gpuweb/issues/685
+  // https://bugs.chromium.org/p/tint/issues/detail?id=34
 
   if (!type) {
     Fail() << "trying to create null value for a null type";
@@ -995,10 +998,45 @@
     return std::make_unique<ast::ScalarConstructorExpression>(
         std::make_unique<ast::FloatLiteral>(type, 0.0f));
   }
-  if (type->IsVector() || type->IsMatrix() || type->IsArray() ||
-      type->IsStruct()) {
+  if (type->IsVector()) {
+    const auto* vec_ty = type->AsVector();
+    ast::ExpressionList ast_components;
+    for (size_t i = 0; i < vec_ty->size(); ++i) {
+      ast_components.emplace_back(MakeNullValue(vec_ty->type()));
+    }
     return std::make_unique<ast::TypeConstructorExpression>(
-        original_type, ast::ExpressionList{});
+        type, std::move(ast_components));
+  }
+  if (type->IsMatrix()) {
+    const auto* mat_ty = type->AsMatrix();
+    // Matrix components are columns
+    auto* column_ty =
+        ctx_.type_mgr().Get(std::make_unique<ast::type::VectorType>(
+            mat_ty->type(), mat_ty->rows()));
+    ast::ExpressionList ast_components;
+    for (size_t i = 0; i < mat_ty->columns(); ++i) {
+      ast_components.emplace_back(MakeNullValue(column_ty));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        type, std::move(ast_components));
+  }
+  if (type->IsArray()) {
+    auto* arr_ty = type->AsArray();
+    ast::ExpressionList ast_components;
+    for (size_t i = 0; i < arr_ty->size(); ++i) {
+      ast_components.emplace_back(MakeNullValue(arr_ty->type()));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        original_type, std::move(ast_components));
+  }
+  if (type->IsStruct()) {
+    auto* struct_ty = type->AsStruct();
+    ast::ExpressionList ast_components;
+    for (auto& member : struct_ty->impl()->members()) {
+      ast_components.emplace_back(MakeNullValue(member->type()));
+    }
+    return std::make_unique<ast::TypeConstructorExpression>(
+        original_type, std::move(ast_components));
   }
   Fail() << "can't make null value for type: " << type->type_name();
   return nullptr;
diff --git a/src/reader/spirv/parser_impl_module_var_test.cc b/src/reader/spirv/parser_impl_module_var_test.cc
index d4c5b87..b0c9d00 100644
--- a/src/reader/spirv/parser_impl_module_var_test.cc
+++ b/src/reader/spirv/parser_impl_module_var_test.cc
@@ -382,6 +382,8 @@
     {
       TypeConstructor{
         __vec_2__bool
+        ScalarConstructor{false}
+        ScalarConstructor{false}
       }
     }
   })"));
@@ -403,6 +405,8 @@
     {
       TypeConstructor{
         __vec_2__bool
+        ScalarConstructor{false}
+        ScalarConstructor{false}
       }
     }
   })"));
@@ -424,6 +428,8 @@
     {
       TypeConstructor{
         __vec_2__u32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   })"));
@@ -445,6 +451,8 @@
     {
       TypeConstructor{
         __vec_2__u32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   })"));
@@ -466,6 +474,8 @@
     {
       TypeConstructor{
         __vec_2__i32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   })"));
@@ -487,6 +497,8 @@
     {
       TypeConstructor{
         __vec_2__i32
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   })"));
@@ -508,6 +520,8 @@
     {
       TypeConstructor{
         __vec_2__f32
+        ScalarConstructor{0.000000}
+        ScalarConstructor{0.000000}
       }
     }
   })"));
@@ -529,6 +543,8 @@
     {
       TypeConstructor{
         __vec_2__f32
+        ScalarConstructor{0.000000}
+        ScalarConstructor{0.000000}
       }
     }
   })"));
@@ -592,6 +608,21 @@
     {
       TypeConstructor{
         __mat_2_3__f32
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
       }
     }
   })"));
@@ -613,6 +644,21 @@
     {
       TypeConstructor{
         __mat_2_3__f32
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
+        TypeConstructor{
+          __vec_2__f32
+          ScalarConstructor{0.000000}
+          ScalarConstructor{0.000000}
+        }
       }
     }
   })"));
@@ -658,6 +704,8 @@
     {
       TypeConstructor{
         __array__u32_2
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   })"));
@@ -679,6 +727,8 @@
     {
       TypeConstructor{
         __array__u32_2
+        ScalarConstructor{0}
+        ScalarConstructor{0}
       }
     }
   })"));
@@ -731,6 +781,13 @@
     {
       TypeConstructor{
         __alias_S__struct_S
+        ScalarConstructor{0}
+        ScalarConstructor{0.000000}
+        TypeConstructor{
+          __array__u32_2
+          ScalarConstructor{0}
+          ScalarConstructor{0}
+        }
       }
     }
   })"))
@@ -753,6 +810,13 @@
     {
       TypeConstructor{
         __alias_S__struct_S
+        ScalarConstructor{0}
+        ScalarConstructor{0.000000}
+        TypeConstructor{
+          __array__u32_2
+          ScalarConstructor{0}
+          ScalarConstructor{0}
+        }
       }
     }
   })"))
diff --git a/src/writer/msl/generator.cc b/src/writer/msl/generator.cc
new file mode 100644
index 0000000..6349ce8
--- /dev/null
+++ b/src/writer/msl/generator.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/msl/generator.h"
+
+#include <utility>
+
+namespace tint {
+namespace writer {
+namespace msl {
+
+Generator::Generator(ast::Module module) : Text(std::move(module)) {}
+
+Generator::~Generator() = default;
+
+bool Generator::Generate() {
+  return impl_.Generate(module_);
+}
+
+}  // namespace msl
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/msl/generator.h b/src/writer/msl/generator.h
new file mode 100644
index 0000000..2e5a577
--- /dev/null
+++ b/src/writer/msl/generator.h
@@ -0,0 +1,53 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_WRITER_MSL_GENERATOR_H_
+#define SRC_WRITER_MSL_GENERATOR_H_
+
+#include <string>
+
+#include "src/writer/msl/generator_impl.h"
+#include "src/writer/text.h"
+
+namespace tint {
+namespace writer {
+namespace msl {
+
+/// Class to generate WGSL source from a WGSL module
+class Generator : public Text {
+ public:
+  /// Constructor
+  /// @param module the module to convert
+  explicit Generator(ast::Module module);
+  ~Generator() override;
+
+  /// Generates the result data
+  /// @returns true on successful generation; false otherwise
+  bool Generate() override;
+
+  /// @returns the result data
+  std::string result() const override { return impl_.result(); }
+
+  /// @returns the error
+  std::string error() const { return impl_.error(); }
+
+ private:
+  GeneratorImpl impl_;
+};
+
+}  // namespace msl
+}  // namespace writer
+}  // namespace tint
+
+#endif  // SRC_WRITER_MSL_GENERATOR_H_
diff --git a/src/writer/msl/generator_impl.cc b/src/writer/msl/generator_impl.cc
new file mode 100644
index 0000000..eb8c8ba
--- /dev/null
+++ b/src/writer/msl/generator_impl.cc
@@ -0,0 +1,31 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/msl/generator_impl.h"
+
+namespace tint {
+namespace writer {
+namespace msl {
+
+GeneratorImpl::GeneratorImpl() = default;
+
+GeneratorImpl::~GeneratorImpl() = default;
+
+bool GeneratorImpl::Generate(const ast::Module&) {
+  return true;
+}
+
+}  // namespace msl
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/msl/generator_impl.h b/src/writer/msl/generator_impl.h
new file mode 100644
index 0000000..e71c703
--- /dev/null
+++ b/src/writer/msl/generator_impl.h
@@ -0,0 +1,45 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_WRITER_MSL_GENERATOR_IMPL_H_
+#define SRC_WRITER_MSL_GENERATOR_IMPL_H_
+
+#include <sstream>
+#include <string>
+
+#include "src/ast/module.h"
+#include "src/writer/text_generator.h"
+
+namespace tint {
+namespace writer {
+namespace msl {
+
+/// Implementation class for WGSL generator
+class GeneratorImpl : public TextGenerator {
+ public:
+  /// Constructor
+  GeneratorImpl();
+  ~GeneratorImpl();
+
+  /// Generates the result data
+  /// @param module the module to generate
+  /// @returns true on successful generation; false otherwise
+  bool Generate(const ast::Module& module);
+};
+
+}  // namespace msl
+}  // namespace writer
+}  // namespace tint
+
+#endif  // SRC_WRITER_MSL_GENERATOR_IMPL_H_
diff --git a/src/writer/msl/generator_impl_test.cc b/src/writer/msl/generator_impl_test.cc
new file mode 100644
index 0000000..05e7015
--- /dev/null
+++ b/src/writer/msl/generator_impl_test.cc
@@ -0,0 +1,54 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/msl/generator_impl.h"
+
+#include <memory>
+
+#include "gtest/gtest.h"
+#include "src/ast/entry_point.h"
+#include "src/ast/function.h"
+#include "src/ast/module.h"
+#include "src/ast/pipeline_stage.h"
+#include "src/ast/type/void_type.h"
+
+namespace tint {
+namespace writer {
+namespace msl {
+namespace {
+
+using MslGeneratorImplTest = testing::Test;
+
+TEST_F(MslGeneratorImplTest, DISABLED_Generate) {
+  ast::type::VoidType void_type;
+  ast::Module m;
+  m.AddFunction(std::make_unique<ast::Function>("my_func", ast::VariableList{},
+                                                &void_type));
+  m.AddEntryPoint(std::make_unique<ast::EntryPoint>(
+      ast::PipelineStage::kCompute, "my_func", ""));
+
+  GeneratorImpl g;
+
+  ASSERT_TRUE(g.Generate(m)) << g.error();
+  EXPECT_EQ(g.result(), R"(#import <metal_lib>
+
+compute void my_func() {
+}
+)");
+}
+
+}  // namespace
+}  // namespace msl
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/text.cc b/src/writer/text.cc
new file mode 100644
index 0000000..319f32e
--- /dev/null
+++ b/src/writer/text.cc
@@ -0,0 +1,27 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/text.h"
+
+#include <utility>
+
+namespace tint {
+namespace writer {
+
+Text::Text(ast::Module module) : Writer(std::move(module)) {}
+
+Text::~Text() = default;
+
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/text.h b/src/writer/text.h
new file mode 100644
index 0000000..7e4fffc
--- /dev/null
+++ b/src/writer/text.h
@@ -0,0 +1,40 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_WRITER_TEXT_H_
+#define SRC_WRITER_TEXT_H_
+
+#include <string>
+
+#include "src/writer/writer.h"
+
+namespace tint {
+namespace writer {
+
+/// Class to generate text source
+class Text : public Writer {
+ public:
+  /// Constructor
+  /// @param module the module to convert
+  explicit Text(ast::Module module);
+  ~Text() override;
+
+  /// @returns the result data
+  virtual std::string result() const = 0;
+};
+
+}  // namespace writer
+}  // namespace tint
+
+#endif  // SRC_WRITER_TEXT_H_
diff --git a/src/writer/text_generator.cc b/src/writer/text_generator.cc
new file mode 100644
index 0000000..2c94fdb
--- /dev/null
+++ b/src/writer/text_generator.cc
@@ -0,0 +1,33 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "src/writer/text_generator.h"
+
+#include <utility>
+
+namespace tint {
+namespace writer {
+
+TextGenerator::TextGenerator() = default;
+
+TextGenerator::~TextGenerator() = default;
+
+void TextGenerator::make_indent() {
+  for (size_t i = 0; i < indent_; i++) {
+    out_ << " ";
+  }
+}
+
+}  // namespace writer
+}  // namespace tint
diff --git a/src/writer/text_generator.h b/src/writer/text_generator.h
new file mode 100644
index 0000000..46da1bc
--- /dev/null
+++ b/src/writer/text_generator.h
@@ -0,0 +1,64 @@
+// Copyright 2020 The Tint Authors.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#ifndef SRC_WRITER_TEXT_GENERATOR_H_
+#define SRC_WRITER_TEXT_GENERATOR_H_
+
+#include <sstream>
+#include <string>
+
+namespace tint {
+namespace writer {
+
+/// Helper methods for generators which are creating text output
+class TextGenerator {
+ public:
+  /// Constructor
+  TextGenerator();
+  ~TextGenerator();
+
+  /// Increment the emitter indent level
+  void increment_indent() { indent_ += 2; }
+  /// Decrement the emiter indent level
+  void decrement_indent() {
+    if (indent_ < 2) {
+      indent_ = 0;
+      return;
+    }
+    indent_ -= 2;
+  }
+
+  /// Writes the current indent to the output stream
+  void make_indent();
+
+  /// @returns the result data
+  std::string result() const { return out_.str(); }
+
+  /// @returns the error
+  std::string error() const { return error_; }
+
+ protected:
+  /// The text output stream
+  std::ostringstream out_;
+  /// Error generated by the generator
+  std::string error_;
+
+ private:
+  size_t indent_ = 0;
+};
+
+}  // namespace writer
+}  // namespace tint
+
+#endif  // SRC_WRITER_TEXT_GENERATOR_H_
diff --git a/src/writer/wgsl/generator.cc b/src/writer/wgsl/generator.cc
index 1124e7a..ec4c8f7 100644
--- a/src/writer/wgsl/generator.cc
+++ b/src/writer/wgsl/generator.cc
@@ -20,7 +20,7 @@
 namespace writer {
 namespace wgsl {
 
-Generator::Generator(ast::Module module) : writer::Writer(std::move(module)) {}
+Generator::Generator(ast::Module module) : Text(std::move(module)) {}
 
 Generator::~Generator() = default;
 
diff --git a/src/writer/wgsl/generator.h b/src/writer/wgsl/generator.h
index 827a060..4a89f9a 100644
--- a/src/writer/wgsl/generator.h
+++ b/src/writer/wgsl/generator.h
@@ -17,15 +17,15 @@
 
 #include <string>
 
+#include "src/writer/text.h"
 #include "src/writer/wgsl/generator_impl.h"
-#include "src/writer/writer.h"
 
 namespace tint {
 namespace writer {
 namespace wgsl {
 
 /// Class to generate WGSL source from a WGSL module
-class Generator : public writer::Writer {
+class Generator : public Text {
  public:
   /// Constructor
   /// @param module the module to convert
@@ -37,7 +37,7 @@
   bool Generate() override;
 
   /// @returns the result data
-  std::string result() const { return impl_.result(); }
+  std::string result() const override { return impl_.result(); }
 
   /// @returns the error
   std::string error() const { return impl_.error(); }
diff --git a/src/writer/wgsl/generator_impl.cc b/src/writer/wgsl/generator_impl.cc
index df1506a..817da91 100644
--- a/src/writer/wgsl/generator_impl.cc
+++ b/src/writer/wgsl/generator_impl.cc
@@ -110,12 +110,6 @@
   return true;
 }
 
-void GeneratorImpl::make_indent() {
-  for (size_t i = 0; i < indent_; i++) {
-    out_ << " ";
-  }
-}
-
 bool GeneratorImpl::EmitAliasType(const ast::type::AliasType* alias) {
   make_indent();
   out_ << "type " << alias->name() << " = ";
diff --git a/src/writer/wgsl/generator_impl.h b/src/writer/wgsl/generator_impl.h
index a0ec427..9d394ad 100644
--- a/src/writer/wgsl/generator_impl.h
+++ b/src/writer/wgsl/generator_impl.h
@@ -29,13 +29,14 @@
 #include "src/ast/type/type.h"
 #include "src/ast/type_constructor_expression.h"
 #include "src/ast/variable.h"
+#include "src/writer/text_generator.h"
 
 namespace tint {
 namespace writer {
 namespace wgsl {
 
 /// Implementation class for WGSL generator
-class GeneratorImpl {
+class GeneratorImpl : public TextGenerator {
  public:
   /// Constructor
   GeneratorImpl();
@@ -46,26 +47,6 @@
   /// @returns true on successful generation; false otherwise
   bool Generate(const ast::Module& module);
 
-  /// @returns the result data
-  std::string result() const { return out_.str(); }
-
-  /// @returns the error from the generator
-  std::string error() const { return error_; }
-
-  /// Increment the emitter indent level
-  void increment_indent() { indent_ += 2; }
-  /// Decrement the emiter indent level
-  void decrement_indent() {
-    if (indent_ < 2) {
-      indent_ = 0;
-      return;
-    }
-    indent_ -= 2;
-  }
-
-  /// Writes the current indent to the output stream
-  void make_indent();
-
   /// Handles generating an alias
   /// @param alias the alias to generate
   /// @returns true if the alias was emitted
@@ -202,11 +183,6 @@
   /// @param var the decorated variable
   /// @returns true if the variable decoration was emitted
   bool EmitVariableDecorations(ast::DecoratedVariable* var);
-
- private:
-  size_t indent_ = 0;
-  std::ostringstream out_;
-  std::string error_;
 };
 
 }  // namespace wgsl
diff --git a/src/writer/wgsl/generator_impl_alias_type_test.cc b/src/writer/wgsl/generator_impl_alias_type_test.cc
index 70891dc..8608f10 100644
--- a/src/writer/wgsl/generator_impl_alias_type_test.cc
+++ b/src/writer/wgsl/generator_impl_alias_type_test.cc
@@ -27,9 +27,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitAliasType_F32) {
+TEST_F(WgslGeneratorImplTest, EmitAliasType_F32) {
   ast::type::F32Type f32;
   ast::type::AliasType alias("a", &f32);
 
@@ -39,7 +39,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitAliasType_Struct) {
+TEST_F(WgslGeneratorImplTest, EmitAliasType_Struct) {
   ast::type::I32Type i32;
   ast::type::F32Type f32;
 
diff --git a/src/writer/wgsl/generator_impl_array_accessor_test.cc b/src/writer/wgsl/generator_impl_array_accessor_test.cc
index 8e98eb8..452f582 100644
--- a/src/writer/wgsl/generator_impl_array_accessor_test.cc
+++ b/src/writer/wgsl/generator_impl_array_accessor_test.cc
@@ -27,9 +27,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitExpression_ArrayAccessor) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_ArrayAccessor) {
   ast::type::I32Type i32;
   auto lit = std::make_unique<ast::SintLiteral>(&i32, 5);
   auto idx = std::make_unique<ast::ScalarConstructorExpression>(std::move(lit));
@@ -42,7 +42,7 @@
   EXPECT_EQ(g.result(), "ary[5]");
 }
 
-TEST_F(GeneratorImplTest, EmitArrayAccessor) {
+TEST_F(WgslGeneratorImplTest, EmitArrayAccessor) {
   auto ary = std::make_unique<ast::IdentifierExpression>("ary");
   auto idx = std::make_unique<ast::IdentifierExpression>("idx");
 
diff --git a/src/writer/wgsl/generator_impl_as_test.cc b/src/writer/wgsl/generator_impl_as_test.cc
index 740dde0..958bff2 100644
--- a/src/writer/wgsl/generator_impl_as_test.cc
+++ b/src/writer/wgsl/generator_impl_as_test.cc
@@ -25,9 +25,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitExpression_As) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_As) {
   ast::type::F32Type f32;
   auto id = std::make_unique<ast::IdentifierExpression>("id");
   ast::AsExpression as(&f32, std::move(id));
diff --git a/src/writer/wgsl/generator_impl_assign_test.cc b/src/writer/wgsl/generator_impl_assign_test.cc
index d97238a..c3b7eee 100644
--- a/src/writer/wgsl/generator_impl_assign_test.cc
+++ b/src/writer/wgsl/generator_impl_assign_test.cc
@@ -25,9 +25,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Assign) {
+TEST_F(WgslGeneratorImplTest, Emit_Assign) {
   auto lhs = std::make_unique<ast::IdentifierExpression>("lhs");
   auto rhs = std::make_unique<ast::IdentifierExpression>("rhs");
   ast::AssignmentStatement assign(std::move(lhs), std::move(rhs));
diff --git a/src/writer/wgsl/generator_impl_break_test.cc b/src/writer/wgsl/generator_impl_break_test.cc
index 7bcc8e8..e8d3612 100644
--- a/src/writer/wgsl/generator_impl_break_test.cc
+++ b/src/writer/wgsl/generator_impl_break_test.cc
@@ -24,9 +24,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Break) {
+TEST_F(WgslGeneratorImplTest, Emit_Break) {
   ast::BreakStatement b;
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_call_test.cc b/src/writer/wgsl/generator_impl_call_test.cc
index 25f0083..91a5a4c 100644
--- a/src/writer/wgsl/generator_impl_call_test.cc
+++ b/src/writer/wgsl/generator_impl_call_test.cc
@@ -24,9 +24,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitExpression_Call_WithoutParams) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_Call_WithoutParams) {
   auto id = std::make_unique<ast::IdentifierExpression>("my_func");
   ast::CallExpression call(std::move(id), {});
 
@@ -35,7 +35,7 @@
   EXPECT_EQ(g.result(), "my_func()");
 }
 
-TEST_F(GeneratorImplTest, EmitExpression_Call_WithParams) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_Call_WithParams) {
   auto id = std::make_unique<ast::IdentifierExpression>("my_func");
   ast::ExpressionList params;
   params.push_back(std::make_unique<ast::IdentifierExpression>("param1"));
diff --git a/src/writer/wgsl/generator_impl_case_test.cc b/src/writer/wgsl/generator_impl_case_test.cc
index 99374a9..511e2bd 100644
--- a/src/writer/wgsl/generator_impl_case_test.cc
+++ b/src/writer/wgsl/generator_impl_case_test.cc
@@ -27,9 +27,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Case) {
+TEST_F(WgslGeneratorImplTest, Emit_Case) {
   ast::type::I32Type i32;
 
   ast::StatementList body;
@@ -49,7 +49,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_Case_MultipleSelectors) {
+TEST_F(WgslGeneratorImplTest, Emit_Case_MultipleSelectors) {
   ast::type::I32Type i32;
 
   ast::StatementList body;
@@ -70,7 +70,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_Case_Default) {
+TEST_F(WgslGeneratorImplTest, Emit_Case_Default) {
   ast::CaseStatement c;
 
   ast::StatementList body;
diff --git a/src/writer/wgsl/generator_impl_cast_test.cc b/src/writer/wgsl/generator_impl_cast_test.cc
index f011758..e388337 100644
--- a/src/writer/wgsl/generator_impl_cast_test.cc
+++ b/src/writer/wgsl/generator_impl_cast_test.cc
@@ -25,9 +25,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitExpression_Cast) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_Cast) {
   ast::type::F32Type f32;
   auto id = std::make_unique<ast::IdentifierExpression>("id");
   ast::CastExpression cast(&f32, std::move(id));
diff --git a/src/writer/wgsl/generator_impl_constructor_test.cc b/src/writer/wgsl/generator_impl_constructor_test.cc
index 9565ae8..e30478d 100644
--- a/src/writer/wgsl/generator_impl_constructor_test.cc
+++ b/src/writer/wgsl/generator_impl_constructor_test.cc
@@ -32,9 +32,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitConstructor_Bool) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Bool) {
   ast::type::BoolType bool_type;
   auto lit = std::make_unique<ast::BoolLiteral>(&bool_type, false);
   ast::ScalarConstructorExpression expr(std::move(lit));
@@ -44,7 +44,7 @@
   EXPECT_EQ(g.result(), "false");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Int) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Int) {
   ast::type::I32Type i32;
   auto lit = std::make_unique<ast::SintLiteral>(&i32, -12345);
   ast::ScalarConstructorExpression expr(std::move(lit));
@@ -54,7 +54,7 @@
   EXPECT_EQ(g.result(), "-12345");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_UInt) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_UInt) {
   ast::type::U32Type u32;
   auto lit = std::make_unique<ast::UintLiteral>(&u32, 56779);
   ast::ScalarConstructorExpression expr(std::move(lit));
@@ -64,7 +64,7 @@
   EXPECT_EQ(g.result(), "56779u");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Float) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Float) {
   ast::type::F32Type f32;
   auto lit = std::make_unique<ast::FloatLiteral>(&f32, 1.5e27);
   ast::ScalarConstructorExpression expr(std::move(lit));
@@ -74,7 +74,7 @@
   EXPECT_EQ(g.result(), "1.49999995e+27");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Float) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Float) {
   ast::type::F32Type f32;
 
   auto lit = std::make_unique<ast::FloatLiteral>(&f32, -1.2e-5);
@@ -89,7 +89,7 @@
   EXPECT_EQ(g.result(), "f32(-1.20000004e-05)");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Bool) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Bool) {
   ast::type::BoolType b;
 
   auto lit = std::make_unique<ast::BoolLiteral>(&b, true);
@@ -104,7 +104,7 @@
   EXPECT_EQ(g.result(), "bool(true)");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Int) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Int) {
   ast::type::I32Type i32;
 
   auto lit = std::make_unique<ast::SintLiteral>(&i32, -12345);
@@ -119,7 +119,7 @@
   EXPECT_EQ(g.result(), "i32(-12345)");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Uint) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Uint) {
   ast::type::U32Type u32;
 
   auto lit = std::make_unique<ast::UintLiteral>(&u32, 12345);
@@ -134,7 +134,7 @@
   EXPECT_EQ(g.result(), "u32(12345u)");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Vec) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Vec) {
   ast::type::F32Type f32;
   ast::type::VectorType vec(&f32, 3);
 
@@ -156,7 +156,7 @@
   EXPECT_EQ(g.result(), "vec3<f32>(1.00000000, 2.00000000, 3.00000000)");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Mat) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Mat) {
   ast::type::F32Type f32;
   ast::type::MatrixType mat(&f32, 3, 2);
 
@@ -190,7 +190,7 @@
                 "vec2<f32>(5.00000000, 6.00000000))");
 }
 
-TEST_F(GeneratorImplTest, EmitConstructor_Type_Array) {
+TEST_F(WgslGeneratorImplTest, EmitConstructor_Type_Array) {
   ast::type::F32Type f32;
   ast::type::VectorType vec(&f32, 3);
   ast::type::ArrayType ary(&vec, 3);
diff --git a/src/writer/wgsl/generator_impl_continue_test.cc b/src/writer/wgsl/generator_impl_continue_test.cc
index ffd5b93..6e8bde2 100644
--- a/src/writer/wgsl/generator_impl_continue_test.cc
+++ b/src/writer/wgsl/generator_impl_continue_test.cc
@@ -24,9 +24,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Continue) {
+TEST_F(WgslGeneratorImplTest, Emit_Continue) {
   ast::ContinueStatement c;
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_else_test.cc b/src/writer/wgsl/generator_impl_else_test.cc
index 52fc4a0..47869ba 100644
--- a/src/writer/wgsl/generator_impl_else_test.cc
+++ b/src/writer/wgsl/generator_impl_else_test.cc
@@ -25,9 +25,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Else) {
+TEST_F(WgslGeneratorImplTest, Emit_Else) {
   ast::StatementList body;
   body.push_back(std::make_unique<ast::KillStatement>());
 
@@ -42,7 +42,7 @@
   })");
 }
 
-TEST_F(GeneratorImplTest, Emit_ElseWithCondition) {
+TEST_F(WgslGeneratorImplTest, Emit_ElseWithCondition) {
   auto cond = std::make_unique<ast::IdentifierExpression>("cond");
 
   ast::StatementList body;
diff --git a/src/writer/wgsl/generator_impl_entry_point_test.cc b/src/writer/wgsl/generator_impl_entry_point_test.cc
index 372934f..bab9e91 100644
--- a/src/writer/wgsl/generator_impl_entry_point_test.cc
+++ b/src/writer/wgsl/generator_impl_entry_point_test.cc
@@ -20,9 +20,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitEntryPoint_NoName) {
+TEST_F(WgslGeneratorImplTest, EmitEntryPoint_NoName) {
   ast::EntryPoint ep(ast::PipelineStage::kFragment, "", "frag_main");
 
   GeneratorImpl g;
@@ -31,7 +31,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitEntryPoint_WithName) {
+TEST_F(WgslGeneratorImplTest, EmitEntryPoint_WithName) {
   ast::EntryPoint ep(ast::PipelineStage::kFragment, "main", "frag_main");
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_fallthrough_test.cc b/src/writer/wgsl/generator_impl_fallthrough_test.cc
index 63fac29..bbe3547 100644
--- a/src/writer/wgsl/generator_impl_fallthrough_test.cc
+++ b/src/writer/wgsl/generator_impl_fallthrough_test.cc
@@ -21,9 +21,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Fallthrough) {
+TEST_F(WgslGeneratorImplTest, Emit_Fallthrough) {
   ast::FallthroughStatement f;
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_function_test.cc b/src/writer/wgsl/generator_impl_function_test.cc
index 274f789..ab9ccb6 100644
--- a/src/writer/wgsl/generator_impl_function_test.cc
+++ b/src/writer/wgsl/generator_impl_function_test.cc
@@ -27,9 +27,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Function) {
+TEST_F(WgslGeneratorImplTest, Emit_Function) {
   ast::StatementList body;
   body.push_back(std::make_unique<ast::KillStatement>());
   body.push_back(std::make_unique<ast::ReturnStatement>());
@@ -49,7 +49,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_Function_WithParams) {
+TEST_F(WgslGeneratorImplTest, Emit_Function_WithParams) {
   ast::StatementList body;
   body.push_back(std::make_unique<ast::KillStatement>());
   body.push_back(std::make_unique<ast::ReturnStatement>());
diff --git a/src/writer/wgsl/generator_impl_identifier_test.cc b/src/writer/wgsl/generator_impl_identifier_test.cc
index 14e87e3..eb5e9c0 100644
--- a/src/writer/wgsl/generator_impl_identifier_test.cc
+++ b/src/writer/wgsl/generator_impl_identifier_test.cc
@@ -20,9 +20,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitExpression_Identifier) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_Identifier) {
   ast::IdentifierExpression i(std::vector<std::string>{"std", "glsl"});
 
   GeneratorImpl g;
@@ -30,7 +30,7 @@
   EXPECT_EQ(g.result(), "std::glsl");
 }
 
-TEST_F(GeneratorImplTest, EmitIdentifierExpression_Single) {
+TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_Single) {
   ast::IdentifierExpression i("glsl");
 
   GeneratorImpl g;
@@ -38,7 +38,7 @@
   EXPECT_EQ(g.result(), "glsl");
 }
 
-TEST_F(GeneratorImplTest, EmitIdentifierExpression_MultipleNames) {
+TEST_F(WgslGeneratorImplTest, EmitIdentifierExpression_MultipleNames) {
   ast::IdentifierExpression i({"std", "glsl", "init"});
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_if_test.cc b/src/writer/wgsl/generator_impl_if_test.cc
index 99609ec..f5a7575 100644
--- a/src/writer/wgsl/generator_impl_if_test.cc
+++ b/src/writer/wgsl/generator_impl_if_test.cc
@@ -24,9 +24,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_If) {
+TEST_F(WgslGeneratorImplTest, Emit_If) {
   auto cond = std::make_unique<ast::IdentifierExpression>("cond");
   ast::StatementList body;
   body.push_back(std::make_unique<ast::KillStatement>());
@@ -43,7 +43,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_IfWithElseIf) {
+TEST_F(WgslGeneratorImplTest, Emit_IfWithElseIf) {
   auto else_cond = std::make_unique<ast::IdentifierExpression>("else_cond");
 
   ast::StatementList else_body;
@@ -72,7 +72,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_IfWithElse) {
+TEST_F(WgslGeneratorImplTest, Emit_IfWithElse) {
   ast::StatementList else_body;
   else_body.push_back(std::make_unique<ast::KillStatement>());
 
@@ -98,7 +98,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_IfWithMultiple) {
+TEST_F(WgslGeneratorImplTest, Emit_IfWithMultiple) {
   auto else_cond = std::make_unique<ast::IdentifierExpression>("else_cond");
 
   ast::StatementList else_body;
diff --git a/src/writer/wgsl/generator_impl_import_test.cc b/src/writer/wgsl/generator_impl_import_test.cc
index 9972eb7..24d1980 100644
--- a/src/writer/wgsl/generator_impl_import_test.cc
+++ b/src/writer/wgsl/generator_impl_import_test.cc
@@ -20,9 +20,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitImport) {
+TEST_F(WgslGeneratorImplTest, EmitImport) {
   ast::Import import("GLSL.std.450", "std::glsl");
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_kill_test.cc b/src/writer/wgsl/generator_impl_kill_test.cc
index 4371452..6ebee1c 100644
--- a/src/writer/wgsl/generator_impl_kill_test.cc
+++ b/src/writer/wgsl/generator_impl_kill_test.cc
@@ -21,9 +21,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_kill) {
+TEST_F(WgslGeneratorImplTest, Emit_kill) {
   ast::KillStatement k;
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_loop_test.cc b/src/writer/wgsl/generator_impl_loop_test.cc
index bb21ff9..47fc3ef 100644
--- a/src/writer/wgsl/generator_impl_loop_test.cc
+++ b/src/writer/wgsl/generator_impl_loop_test.cc
@@ -24,9 +24,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Loop) {
+TEST_F(WgslGeneratorImplTest, Emit_Loop) {
   ast::StatementList body;
   body.push_back(std::make_unique<ast::KillStatement>());
 
@@ -42,7 +42,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, Emit_LoopWithContinuing) {
+TEST_F(WgslGeneratorImplTest, Emit_LoopWithContinuing) {
   ast::StatementList body;
   body.push_back(std::make_unique<ast::KillStatement>());
 
diff --git a/src/writer/wgsl/generator_impl_member_accessor_test.cc b/src/writer/wgsl/generator_impl_member_accessor_test.cc
index 88adbe3..4c9eaf6 100644
--- a/src/writer/wgsl/generator_impl_member_accessor_test.cc
+++ b/src/writer/wgsl/generator_impl_member_accessor_test.cc
@@ -24,9 +24,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitExpression_MemberAccessor) {
+TEST_F(WgslGeneratorImplTest, EmitExpression_MemberAccessor) {
   auto str = std::make_unique<ast::IdentifierExpression>("str");
   auto mem = std::make_unique<ast::IdentifierExpression>("mem");
 
diff --git a/src/writer/wgsl/generator_impl_relational_test.cc b/src/writer/wgsl/generator_impl_relational_test.cc
index 9006135..465c149 100644
--- a/src/writer/wgsl/generator_impl_relational_test.cc
+++ b/src/writer/wgsl/generator_impl_relational_test.cc
@@ -46,7 +46,7 @@
   EXPECT_EQ(g.result(), params.result);
 }
 INSTANTIATE_TEST_SUITE_P(
-    GeneratorImplTest,
+    WgslGeneratorImplTest,
     BinaryTest,
     testing::Values(
         BinaryData{"(left & right)", ast::BinaryOp::kAnd},
diff --git a/src/writer/wgsl/generator_impl_return_test.cc b/src/writer/wgsl/generator_impl_return_test.cc
index 1ef0cd1..4701813 100644
--- a/src/writer/wgsl/generator_impl_return_test.cc
+++ b/src/writer/wgsl/generator_impl_return_test.cc
@@ -25,9 +25,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Return) {
+TEST_F(WgslGeneratorImplTest, Emit_Return) {
   ast::ReturnStatement r;
 
   GeneratorImpl g;
@@ -37,7 +37,7 @@
   EXPECT_EQ(g.result(), "  return;\n");
 }
 
-TEST_F(GeneratorImplTest, Emit_ReturnWithValue) {
+TEST_F(WgslGeneratorImplTest, Emit_ReturnWithValue) {
   auto expr = std::make_unique<ast::IdentifierExpression>("expr");
   ast::ReturnStatement r(std::move(expr));
 
diff --git a/src/writer/wgsl/generator_impl_switch_test.cc b/src/writer/wgsl/generator_impl_switch_test.cc
index b018da0..090380e 100644
--- a/src/writer/wgsl/generator_impl_switch_test.cc
+++ b/src/writer/wgsl/generator_impl_switch_test.cc
@@ -28,9 +28,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_Switch) {
+TEST_F(WgslGeneratorImplTest, Emit_Switch) {
   auto def = std::make_unique<ast::CaseStatement>();
   ast::StatementList def_body;
   def_body.push_back(std::make_unique<ast::BreakStatement>());
diff --git a/src/writer/wgsl/generator_impl_test.cc b/src/writer/wgsl/generator_impl_test.cc
index 7bd50f6..b4420d6 100644
--- a/src/writer/wgsl/generator_impl_test.cc
+++ b/src/writer/wgsl/generator_impl_test.cc
@@ -23,9 +23,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Generate) {
+TEST_F(WgslGeneratorImplTest, Generate) {
   ast::Module m;
   m.AddImport(std::make_unique<ast::Import>("GLSL.std.430", "a"));
 
diff --git a/src/writer/wgsl/generator_impl_type_test.cc b/src/writer/wgsl/generator_impl_type_test.cc
index b9705b8..1cbc223 100644
--- a/src/writer/wgsl/generator_impl_type_test.cc
+++ b/src/writer/wgsl/generator_impl_type_test.cc
@@ -35,9 +35,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitType_Alias) {
+TEST_F(WgslGeneratorImplTest, EmitType_Alias) {
   ast::type::F32Type f32;
   ast::type::AliasType alias("alias", &f32);
 
@@ -46,7 +46,7 @@
   EXPECT_EQ(g.result(), "alias");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Array) {
+TEST_F(WgslGeneratorImplTest, EmitType_Array) {
   ast::type::BoolType b;
   ast::type::ArrayType a(&b, 4);
 
@@ -55,7 +55,7 @@
   EXPECT_EQ(g.result(), "array<bool, 4>");
 }
 
-TEST_F(GeneratorImplTest, EmitType_RuntimeArray) {
+TEST_F(WgslGeneratorImplTest, EmitType_RuntimeArray) {
   ast::type::BoolType b;
   ast::type::ArrayType a(&b);
 
@@ -64,7 +64,7 @@
   EXPECT_EQ(g.result(), "array<bool>");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Bool) {
+TEST_F(WgslGeneratorImplTest, EmitType_Bool) {
   ast::type::BoolType b;
 
   GeneratorImpl g;
@@ -72,7 +72,7 @@
   EXPECT_EQ(g.result(), "bool");
 }
 
-TEST_F(GeneratorImplTest, EmitType_F32) {
+TEST_F(WgslGeneratorImplTest, EmitType_F32) {
   ast::type::F32Type f32;
 
   GeneratorImpl g;
@@ -80,7 +80,7 @@
   EXPECT_EQ(g.result(), "f32");
 }
 
-TEST_F(GeneratorImplTest, EmitType_I32) {
+TEST_F(WgslGeneratorImplTest, EmitType_I32) {
   ast::type::I32Type i32;
 
   GeneratorImpl g;
@@ -88,7 +88,7 @@
   EXPECT_EQ(g.result(), "i32");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Matrix) {
+TEST_F(WgslGeneratorImplTest, EmitType_Matrix) {
   ast::type::F32Type f32;
   ast::type::MatrixType m(&f32, 3, 2);
 
@@ -97,7 +97,7 @@
   EXPECT_EQ(g.result(), "mat2x3<f32>");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Pointer) {
+TEST_F(WgslGeneratorImplTest, EmitType_Pointer) {
   ast::type::F32Type f32;
   ast::type::PointerType p(&f32, ast::StorageClass::kWorkgroup);
 
@@ -106,7 +106,7 @@
   EXPECT_EQ(g.result(), "ptr<workgroup, f32>");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Struct) {
+TEST_F(WgslGeneratorImplTest, EmitType_Struct) {
   ast::type::I32Type i32;
   ast::type::F32Type f32;
 
@@ -132,7 +132,7 @@
 })");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Struct_WithDecoration) {
+TEST_F(WgslGeneratorImplTest, EmitType_Struct_WithDecoration) {
   ast::type::I32Type i32;
   ast::type::F32Type f32;
 
@@ -159,7 +159,7 @@
 })");
 }
 
-TEST_F(GeneratorImplTest, EmitType_U32) {
+TEST_F(WgslGeneratorImplTest, EmitType_U32) {
   ast::type::U32Type u32;
 
   GeneratorImpl g;
@@ -167,7 +167,7 @@
   EXPECT_EQ(g.result(), "u32");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Vector) {
+TEST_F(WgslGeneratorImplTest, EmitType_Vector) {
   ast::type::F32Type f32;
   ast::type::VectorType v(&f32, 3);
 
@@ -176,7 +176,7 @@
   EXPECT_EQ(g.result(), "vec3<f32>");
 }
 
-TEST_F(GeneratorImplTest, EmitType_Void) {
+TEST_F(WgslGeneratorImplTest, EmitType_Void) {
   ast::type::VoidType v;
 
   GeneratorImpl g;
diff --git a/src/writer/wgsl/generator_impl_unary_op_test.cc b/src/writer/wgsl/generator_impl_unary_op_test.cc
index ee9ee47..73d835d 100644
--- a/src/writer/wgsl/generator_impl_unary_op_test.cc
+++ b/src/writer/wgsl/generator_impl_unary_op_test.cc
@@ -44,7 +44,7 @@
   ASSERT_TRUE(g.EmitExpression(&op)) << g.error();
   EXPECT_EQ(g.result(), std::string(params.name) + "(expr)");
 }
-INSTANTIATE_TEST_SUITE_P(GeneratorImplTest,
+INSTANTIATE_TEST_SUITE_P(WgslGeneratorImplTest,
                          UnaryOpTest,
                          testing::Values(UnaryOpData{"!", ast::UnaryOp::kNot},
                                          UnaryOpData{"-",
diff --git a/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc b/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
index 03fbff3..4f705a4 100644
--- a/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_decl_statement_test.cc
@@ -26,9 +26,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, Emit_VariableDeclStatement) {
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement) {
   ast::type::F32Type f32;
   auto var =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kNone, &f32);
@@ -42,7 +42,7 @@
   EXPECT_EQ(g.result(), "  var a : f32;\n");
 }
 
-TEST_F(GeneratorImplTest, Emit_VariableDeclStatement_Function) {
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Function) {
   // Variable declarations with Function storage class don't mention their
   // storage class.  Rely on defaulting.
   // https://github.com/gpuweb/gpuweb/issues/654
@@ -59,7 +59,7 @@
   EXPECT_EQ(g.result(), "  var a : f32;\n");
 }
 
-TEST_F(GeneratorImplTest, Emit_VariableDeclStatement_Private) {
+TEST_F(WgslGeneratorImplTest, Emit_VariableDeclStatement_Private) {
   ast::type::F32Type f32;
   auto var =
       std::make_unique<ast::Variable>("a", ast::StorageClass::kPrivate, &f32);
diff --git a/src/writer/wgsl/generator_impl_variable_test.cc b/src/writer/wgsl/generator_impl_variable_test.cc
index 8d2d2bb..64a9eed 100644
--- a/src/writer/wgsl/generator_impl_variable_test.cc
+++ b/src/writer/wgsl/generator_impl_variable_test.cc
@@ -29,9 +29,9 @@
 namespace wgsl {
 namespace {
 
-using GeneratorImplTest = testing::Test;
+using WgslGeneratorImplTest = testing::Test;
 
-TEST_F(GeneratorImplTest, EmitVariable) {
+TEST_F(WgslGeneratorImplTest, EmitVariable) {
   ast::type::F32Type f32;
   ast::Variable v("a", ast::StorageClass::kNone, &f32);
 
@@ -41,7 +41,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitVariable_StorageClass) {
+TEST_F(WgslGeneratorImplTest, EmitVariable_StorageClass) {
   ast::type::F32Type f32;
   ast::Variable v("a", ast::StorageClass::kInput, &f32);
 
@@ -51,7 +51,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitVariable_Decorated) {
+TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated) {
   ast::type::F32Type f32;
 
   ast::VariableDecorationList decos;
@@ -68,7 +68,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitVariable_Decorated_Multiple) {
+TEST_F(WgslGeneratorImplTest, EmitVariable_Decorated_Multiple) {
   ast::type::F32Type f32;
 
   ast::VariableDecorationList decos;
@@ -90,7 +90,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitVariable_Constructor) {
+TEST_F(WgslGeneratorImplTest, EmitVariable_Constructor) {
   auto ident = std::make_unique<ast::IdentifierExpression>("initializer");
 
   ast::type::F32Type f32;
@@ -103,7 +103,7 @@
 )");
 }
 
-TEST_F(GeneratorImplTest, EmitVariable_Const) {
+TEST_F(WgslGeneratorImplTest, EmitVariable_Const) {
   auto ident = std::make_unique<ast::IdentifierExpression>("initializer");
 
   ast::type::F32Type f32;
diff --git a/tint_overrides_with_defaults.gni b/tint_overrides_with_defaults.gni
index 5a70186..eaad186 100644
--- a/tint_overrides_with_defaults.gni
+++ b/tint_overrides_with_defaults.gni
@@ -56,4 +56,9 @@
   if (!defined(tint_build_wgsl_writer)) {
     tint_build_wgsl_writer = false
   }
+
+  # Build the MSL output writer
+  if (!defined(tint_build_msl_writer)) {
+    tint_build_msl_writer = false
+  }
 }