[tint] Use std::string_view for generated enum strings

Instead of 'const char*'.

The string length can be calculated and stored at compile time.
This also allows us to remove some 'const char*' -> std::string_view
conversions.

Also drop 'const' from std::string_view in a couple of places. The view
content is already immutable.

Change-Id: Iea88f54f8872d2fdce22b9d9164cfc2cf64c25a4
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/159780
Kokoro: Kokoro <noreply+kokoro@google.com>
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
diff --git a/src/tint/lang/core/access.h b/src/tint/lang/core/access.h
index 2dfea06..9ace935 100644
--- a/src/tint/lang/core/access.h
+++ b/src/tint/lang/core/access.h
@@ -68,7 +68,7 @@
 /// @returns the parsed enum, or Access::kUndefined if the string could not be parsed.
 Access ParseAccess(std::string_view str);
 
-constexpr const char* kAccessStrings[] = {
+constexpr std::string_view kAccessStrings[] = {
     "read",
     "read_write",
     "write",
diff --git a/src/tint/lang/core/address_space.h b/src/tint/lang/core/address_space.h
index bb2ac7a..8418574 100644
--- a/src/tint/lang/core/address_space.h
+++ b/src/tint/lang/core/address_space.h
@@ -75,7 +75,7 @@
 /// @returns the parsed enum, or AddressSpace::kUndefined if the string could not be parsed.
 AddressSpace ParseAddressSpace(std::string_view str);
 
-constexpr const char* kAddressSpaceStrings[] = {
+constexpr std::string_view kAddressSpaceStrings[] = {
     "__in",          "__out",   "function", "pixel_local", "private",
     "push_constant", "storage", "uniform",  "workgroup",
 };
diff --git a/src/tint/lang/core/attribute.h b/src/tint/lang/core/attribute.h
index 606f4b6..3e7b087 100644
--- a/src/tint/lang/core/attribute.h
+++ b/src/tint/lang/core/attribute.h
@@ -84,7 +84,7 @@
 /// @returns the parsed enum, or Attribute::kUndefined if the string could not be parsed.
 Attribute ParseAttribute(std::string_view str);
 
-constexpr const char* kAttributeStrings[] = {
+constexpr std::string_view kAttributeStrings[] = {
     "align",    "binding", "builtin", "compute",        "diagnostic", "fragment",
     "group",    "id",      "index",   "interpolate",    "invariant",  "location",
     "must_use", "size",    "vertex",  "workgroup_size",
diff --git a/src/tint/lang/core/builtin_type.h b/src/tint/lang/core/builtin_type.h
index 6817791..ac0a22e 100644
--- a/src/tint/lang/core/builtin_type.h
+++ b/src/tint/lang/core/builtin_type.h
@@ -161,7 +161,7 @@
 /// @returns the parsed enum, or BuiltinType::kUndefined if the string could not be parsed.
 BuiltinType ParseBuiltinType(std::string_view str);
 
-constexpr const char* kBuiltinTypeStrings[] = {
+constexpr std::string_view kBuiltinTypeStrings[] = {
     "__atomic_compare_exchange_result_i32",
     "__atomic_compare_exchange_result_u32",
     "__frexp_result_abstract",
diff --git a/src/tint/lang/core/builtin_value.h b/src/tint/lang/core/builtin_value.h
index 917ba14..1af2585 100644
--- a/src/tint/lang/core/builtin_value.h
+++ b/src/tint/lang/core/builtin_value.h
@@ -80,7 +80,7 @@
 /// @returns the parsed enum, or BuiltinValue::kUndefined if the string could not be parsed.
 BuiltinValue ParseBuiltinValue(std::string_view str);
 
-constexpr const char* kBuiltinValueStrings[] = {
+constexpr std::string_view kBuiltinValueStrings[] = {
     "__point_size",           "frag_depth",     "front_facing",
     "global_invocation_id",   "instance_index", "local_invocation_id",
     "local_invocation_index", "num_workgroups", "position",
diff --git a/src/tint/lang/core/interpolation_sampling.h b/src/tint/lang/core/interpolation_sampling.h
index ebec530..ca7e075 100644
--- a/src/tint/lang/core/interpolation_sampling.h
+++ b/src/tint/lang/core/interpolation_sampling.h
@@ -70,7 +70,7 @@
 /// parsed.
 InterpolationSampling ParseInterpolationSampling(std::string_view str);
 
-constexpr const char* kInterpolationSamplingStrings[] = {
+constexpr std::string_view kInterpolationSamplingStrings[] = {
     "center",
     "centroid",
     "sample",
diff --git a/src/tint/lang/core/interpolation_type.h b/src/tint/lang/core/interpolation_type.h
index e1eeb15..a71af66 100644
--- a/src/tint/lang/core/interpolation_type.h
+++ b/src/tint/lang/core/interpolation_type.h
@@ -69,7 +69,7 @@
 /// @returns the parsed enum, or InterpolationType::kUndefined if the string could not be parsed.
 InterpolationType ParseInterpolationType(std::string_view str);
 
-constexpr const char* kInterpolationTypeStrings[] = {
+constexpr std::string_view kInterpolationTypeStrings[] = {
     "flat",
     "linear",
     "perspective",
diff --git a/src/tint/lang/core/texel_format.h b/src/tint/lang/core/texel_format.h
index f99504b..da6791d 100644
--- a/src/tint/lang/core/texel_format.h
+++ b/src/tint/lang/core/texel_format.h
@@ -82,7 +82,7 @@
 /// @returns the parsed enum, or TexelFormat::kUndefined if the string could not be parsed.
 TexelFormat ParseTexelFormat(std::string_view str);
 
-constexpr const char* kTexelFormatStrings[] = {
+constexpr std::string_view kTexelFormatStrings[] = {
     "bgra8unorm", "r32float",    "r32sint",    "r32uint",    "rg32float",   "rg32sint",
     "rg32uint",   "rgba16float", "rgba16sint", "rgba16uint", "rgba32float", "rgba32sint",
     "rgba32uint", "rgba8sint",   "rgba8snorm", "rgba8uint",  "rgba8unorm",
diff --git a/src/tint/lang/wgsl/ast/transform/renamer_test.cc b/src/tint/lang/wgsl/ast/transform/renamer_test.cc
index d31f0a6..fca0b22 100644
--- a/src/tint/lang/wgsl/ast/transform/renamer_test.cc
+++ b/src/tint/lang/wgsl/ast/transform/renamer_test.cc
@@ -1791,22 +1791,21 @@
     return std::string(name);
 }
 
-std::vector<const char*> ConstructableTypes() {
-    std::vector<const char*> out;
-    for (auto* ty : core::kBuiltinTypeStrings) {
-        std::string_view type(ty);
+std::vector<std::string_view> ConstructableTypes() {
+    std::vector<std::string_view> out;
+    for (auto type : core::kBuiltinTypeStrings) {
         if (type != "ptr" && type != "atomic" && !tint::HasPrefix(type, "sampler") &&
             !tint::HasPrefix(type, "texture") && !tint::HasPrefix(type, "__")) {
-            out.push_back(ty);
+            out.push_back(type);
         }
     }
     return out;
 }
 
-using RenamerBuiltinTypeTest = TransformTestWithParam<const char*>;
+using RenamerBuiltinTypeTest = TransformTestWithParam<std::string_view>;
 
 TEST_P(RenamerBuiltinTypeTest, PreserveTypeUsage) {
-    auto expand = [&](const char* source) {
+    auto expand = [&](std::string_view source) {
         return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
     };
 
@@ -1845,7 +1844,7 @@
     EXPECT_EQ(expect, str(got));
 }
 TEST_P(RenamerBuiltinTypeTest, PreserveTypeInitializer) {
-    auto expand = [&](const char* source) {
+    auto expand = [&](std::string_view source) {
         return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
     };
 
@@ -1877,7 +1876,7 @@
         return;  // Cannot value convert arrays.
     }
 
-    auto expand = [&](const char* source) {
+    auto expand = [&](std::string_view source) {
         return tint::ReplaceAll(source, "$type", ExpandBuiltinType(GetParam()));
     };
 
@@ -1929,7 +1928,7 @@
 }
 
 TEST_P(RenamerBuiltinTypeTest, RenameShadowedByAlias) {
-    auto expand = [&](const char* source) {
+    auto expand = [&](std::string_view source) {
         std::string_view ty = GetParam();
         auto out = tint::ReplaceAll(source, "$name", ty);
         out = tint::ReplaceAll(out, "$type", ExpandBuiltinType(ty));
@@ -1961,7 +1960,7 @@
 }
 
 TEST_P(RenamerBuiltinTypeTest, RenameShadowedByStruct) {
-    auto expand = [&](const char* source) {
+    auto expand = [&](std::string_view source) {
         std::string_view ty = GetParam();
         auto out = tint::ReplaceAll(source, "$name", ty);
         out = tint::ReplaceAll(out, "$type", ExpandBuiltinType(ty));
@@ -2003,31 +2002,33 @@
                          testing::ValuesIn(ConstructableTypes()));
 
 /// @return WGSL builtin identifier keywords
-std::vector<const char*> Identifiers() {
-    std::vector<const char*> out;
-    for (auto* ident : core::kBuiltinTypeStrings) {
+std::vector<std::string_view> Identifiers() {
+    std::vector<std::string_view> out;
+    for (auto ident : core::kBuiltinTypeStrings) {
         if (!tint::HasPrefix(ident, "__")) {
             out.push_back(ident);
         }
     }
-    for (auto* ident : core::kAddressSpaceStrings) {
+    for (auto ident : core::kAddressSpaceStrings) {
         if (!tint::HasPrefix(ident, "_")) {
             out.push_back(ident);
         }
     }
-    for (auto* ident : core::kTexelFormatStrings) {
+    for (auto ident : core::kTexelFormatStrings) {
         out.push_back(ident);
     }
-    for (auto* ident : core::kAccessStrings) {
+    for (auto ident : core::kAccessStrings) {
         out.push_back(ident);
     }
     return out;
 }
 
-using RenamerBuiltinIdentifierTest = TransformTestWithParam<const char*>;
+using RenamerBuiltinIdentifierTest = TransformTestWithParam<std::string_view>;
 
 TEST_P(RenamerBuiltinIdentifierTest, GlobalConstName) {
-    auto expand = [&](const char* source) { return tint::ReplaceAll(source, "$name", GetParam()); };
+    auto expand = [&](std::string_view source) {
+        return tint::ReplaceAll(source, "$name", GetParam());
+    };
 
     auto src = expand(R"(
 const $name = 42;
@@ -2051,7 +2052,9 @@
 }
 
 TEST_P(RenamerBuiltinIdentifierTest, LocalVarName) {
-    auto expand = [&](const char* source) { return tint::ReplaceAll(source, "$name", GetParam()); };
+    auto expand = [&](std::string_view source) {
+        return tint::ReplaceAll(source, "$name", GetParam());
+    };
 
     auto src = expand(R"(
 fn f() {
@@ -2071,7 +2074,9 @@
 }
 
 TEST_P(RenamerBuiltinIdentifierTest, FunctionName) {
-    auto expand = [&](const char* source) { return tint::ReplaceAll(source, "$name", GetParam()); };
+    auto expand = [&](std::string_view source) {
+        return tint::ReplaceAll(source, "$name", GetParam());
+    };
 
     auto src = expand(R"(
 fn $name() {
@@ -2097,7 +2102,7 @@
 }
 
 TEST_P(RenamerBuiltinIdentifierTest, StructName) {
-    auto expand = [&](const char* source) {
+    auto expand = [&](std::string_view source) {
         std::string_view name = GetParam();
         auto out = tint::ReplaceAll(source, "$name", name);
         return tint::ReplaceAll(out, "$other_type", name == "i32" ? "u32" : "i32");
diff --git a/src/tint/lang/wgsl/diagnostic_rule.h b/src/tint/lang/wgsl/diagnostic_rule.h
index db19783..cf2d8f0 100644
--- a/src/tint/lang/wgsl/diagnostic_rule.h
+++ b/src/tint/lang/wgsl/diagnostic_rule.h
@@ -68,7 +68,7 @@
 /// @returns the parsed enum, or CoreDiagnosticRule::kUndefined if the string could not be parsed.
 CoreDiagnosticRule ParseCoreDiagnosticRule(std::string_view str);
 
-constexpr const char* kCoreDiagnosticRuleStrings[] = {
+constexpr std::string_view kCoreDiagnosticRuleStrings[] = {
     "derivative_uniformity",
 };
 
@@ -96,7 +96,7 @@
 /// parsed.
 ChromiumDiagnosticRule ParseChromiumDiagnosticRule(std::string_view str);
 
-constexpr const char* kChromiumDiagnosticRuleStrings[] = {
+constexpr std::string_view kChromiumDiagnosticRuleStrings[] = {
     "unreachable_code",
 };
 
diff --git a/src/tint/lang/wgsl/diagnostic_severity.h b/src/tint/lang/wgsl/diagnostic_severity.h
index 026b421..dfccb5d 100644
--- a/src/tint/lang/wgsl/diagnostic_severity.h
+++ b/src/tint/lang/wgsl/diagnostic_severity.h
@@ -72,7 +72,7 @@
 /// @returns the parsed enum, or DiagnosticSeverity::kUndefined if the string could not be parsed.
 DiagnosticSeverity ParseDiagnosticSeverity(std::string_view str);
 
-constexpr const char* kDiagnosticSeverityStrings[] = {
+constexpr std::string_view kDiagnosticSeverityStrings[] = {
     "error",
     "info",
     "off",
diff --git a/src/tint/lang/wgsl/extension.h b/src/tint/lang/wgsl/extension.h
index b831c86..931c1ad 100644
--- a/src/tint/lang/wgsl/extension.h
+++ b/src/tint/lang/wgsl/extension.h
@@ -75,7 +75,7 @@
 /// @returns the parsed enum, or Extension::kUndefined if the string could not be parsed.
 Extension ParseExtension(std::string_view str);
 
-constexpr const char* kExtensionStrings[] = {
+constexpr std::string_view kExtensionStrings[] = {
     "chromium_disable_uniformity_analysis",      "chromium_experimental_dp4a",
     "chromium_experimental_full_ptr_parameters", "chromium_experimental_pixel_local",
     "chromium_experimental_push_constant",       "chromium_experimental_read_write_storage_texture",
diff --git a/src/tint/lang/wgsl/language_feature.h b/src/tint/lang/wgsl/language_feature.h
index 0f0b403..afdbdba 100644
--- a/src/tint/lang/wgsl/language_feature.h
+++ b/src/tint/lang/wgsl/language_feature.h
@@ -66,7 +66,7 @@
 /// @returns the parsed enum, or LanguageFeature::kUndefined if the string could not be parsed.
 LanguageFeature ParseLanguageFeature(std::string_view str);
 
-constexpr const char* kLanguageFeatureStrings[] = {
+constexpr std::string_view kLanguageFeatureStrings[] = {
     "readonly_and_readwrite_storage_textures",
 };
 
diff --git a/src/tint/lang/wgsl/reader/parser/lexer.cc b/src/tint/lang/wgsl/reader/parser/lexer.cc
index ebc5522..7b19d4c 100644
--- a/src/tint/lang/wgsl/reader/parser/lexer.cc
+++ b/src/tint/lang/wgsl/reader/parser/lexer.cc
@@ -132,7 +132,7 @@
     return tokens;
 }
 
-const std::string_view Lexer::line() const {
+std::string_view Lexer::line() const {
     if (file_->content.lines.size() == 0) {
         static const char* empty_string = "";
         return empty_string;
diff --git a/src/tint/lang/wgsl/reader/parser/lexer.h b/src/tint/lang/wgsl/reader/parser/lexer.h
index 23676c2..7d10dd9 100644
--- a/src/tint/lang/wgsl/reader/parser/lexer.h
+++ b/src/tint/lang/wgsl/reader/parser/lexer.h
@@ -87,7 +87,7 @@
     void end_source(Source&) const;
 
     /// @returns view of current line
-    const std::string_view line() const;
+    std::string_view line() const;
     /// @returns position in current line
     size_t pos() const;
     /// @returns length of current line
diff --git a/src/tint/lang/wgsl/reader/parser/parser.cc b/src/tint/lang/wgsl/reader/parser/parser.cc
index 54aeb27..6ac0847 100644
--- a/src/tint/lang/wgsl/reader/parser/parser.cc
+++ b/src/tint/lang/wgsl/reader/parser/parser.cc
@@ -907,10 +907,10 @@
     return builder_.ty(builder_.Ident(source.Source(), ident.to_str(), std::move(args.value)));
 }
 
-template <typename ENUM, size_t N>
+template <typename ENUM>
 Expect<ENUM> Parser::expect_enum(std::string_view name,
                                  ENUM (*parse)(std::string_view str),
-                                 const char* const (&strings)[N],
+                                 Slice<const std::string_view> strings,
                                  std::string_view use) {
     auto& t = peek();
     if (t.IsIdentifier()) {
diff --git a/src/tint/lang/wgsl/reader/parser/parser.h b/src/tint/lang/wgsl/reader/parser/parser.h
index c489700..0e6c152 100644
--- a/src/tint/lang/wgsl/reader/parser/parser.h
+++ b/src/tint/lang/wgsl/reader/parser/parser.h
@@ -868,10 +868,10 @@
     /// @param parse the optimized function used to parse the enum
     /// @param strings the list of possible strings in the enum
     /// @param use an optional description of what was being parsed if an error was raised.
-    template <typename ENUM, size_t N>
+    template <typename ENUM>
     Expect<ENUM> expect_enum(std::string_view name,
                              ENUM (*parse)(std::string_view str),
-                             const char* const (&strings)[N],
+                             Slice<const std::string_view> strings,
                              std::string_view use = "");
 
     Expect<ast::Type> expect_type(std::string_view use);
diff --git a/src/tint/lang/wgsl/reader/parser/token.cc b/src/tint/lang/wgsl/reader/parser/token.cc
index 89d58e8..9ed9460 100644
--- a/src/tint/lang/wgsl/reader/parser/token.cc
+++ b/src/tint/lang/wgsl/reader/parser/token.cc
@@ -213,7 +213,7 @@
 
 Token::Token() : type_(Type::kUninitialized) {}
 
-Token::Token(Type type, const Source& source, const std::string_view& view)
+Token::Token(Type type, const Source& source, std::string_view view)
     : type_(type), source_(source), value_(view) {}
 
 Token::Token(Type type, const Source& source, const std::string& str)
diff --git a/src/tint/lang/wgsl/reader/parser/token.h b/src/tint/lang/wgsl/reader/parser/token.h
index 8ea96f9..b3cdebe 100644
--- a/src/tint/lang/wgsl/reader/parser/token.h
+++ b/src/tint/lang/wgsl/reader/parser/token.h
@@ -238,7 +238,7 @@
     /// @param type the Token::Type of the token
     /// @param source the source of the token
     /// @param view the source string view for the token
-    Token(Type type, const Source& source, const std::string_view& view);
+    Token(Type type, const Source& source, std::string_view view);
     /// Create a string Token
     /// @param type the Token::Type of the token
     /// @param source the source of the token
diff --git a/src/tint/lang/wgsl/resolver/builtin_enum_test.cc b/src/tint/lang/wgsl/resolver/builtin_enum_test.cc
index 265cab8..cc7bbeb 100644
--- a/src/tint/lang/wgsl/resolver/builtin_enum_test.cc
+++ b/src/tint/lang/wgsl/resolver/builtin_enum_test.cc
@@ -46,7 +46,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // access
 ////////////////////////////////////////////////////////////////////////////////
-using ResolverAccessUsedWithTemplateArgs = ResolverTestWithParam<const char*>;
+using ResolverAccessUsedWithTemplateArgs = ResolverTestWithParam<std::string_view>;
 
 TEST_P(ResolverAccessUsedWithTemplateArgs, Test) {
     // @group(0) @binding(0) var t : texture_storage_2d<rgba8unorm, ACCESS<i32>>;
@@ -65,7 +65,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // address space
 ////////////////////////////////////////////////////////////////////////////////
-using ResolverAddressSpaceUsedWithTemplateArgs = ResolverTestWithParam<const char*>;
+using ResolverAddressSpaceUsedWithTemplateArgs = ResolverTestWithParam<std::string_view>;
 
 TEST_P(ResolverAddressSpaceUsedWithTemplateArgs, Test) {
     // fn f(p : ptr<ADDRESS_SPACE<T>, f32) {}
@@ -85,7 +85,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // builtin value
 ////////////////////////////////////////////////////////////////////////////////
-using ResolverBuiltinValueUsedWithTemplateArgs = ResolverTestWithParam<const char*>;
+using ResolverBuiltinValueUsedWithTemplateArgs = ResolverTestWithParam<std::string_view>;
 
 TEST_P(ResolverBuiltinValueUsedWithTemplateArgs, Test) {
     // fn f(@builtin(BUILTIN<T>) p : vec4<f32>) {}
@@ -104,7 +104,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // interpolation sampling
 ////////////////////////////////////////////////////////////////////////////////
-using ResolverInterpolationSamplingUsedWithTemplateArgs = ResolverTestWithParam<const char*>;
+using ResolverInterpolationSamplingUsedWithTemplateArgs = ResolverTestWithParam<std::string_view>;
 
 TEST_P(ResolverInterpolationSamplingUsedWithTemplateArgs, Test) {
     // @fragment
@@ -132,7 +132,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // interpolation type
 ////////////////////////////////////////////////////////////////////////////////
-using ResolverInterpolationTypeUsedWithTemplateArgs = ResolverTestWithParam<const char*>;
+using ResolverInterpolationTypeUsedWithTemplateArgs = ResolverTestWithParam<std::string_view>;
 
 TEST_P(ResolverInterpolationTypeUsedWithTemplateArgs, Test) {
     // @fragment
@@ -160,7 +160,7 @@
 ////////////////////////////////////////////////////////////////////////////////
 // texel format
 ////////////////////////////////////////////////////////////////////////////////
-using ResolverTexelFormatUsedWithTemplateArgs = ResolverTestWithParam<const char*>;
+using ResolverTexelFormatUsedWithTemplateArgs = ResolverTestWithParam<std::string_view>;
 
 TEST_P(ResolverTexelFormatUsedWithTemplateArgs, Test) {
     // @group(0) @binding(0) var t : texture_storage_2d<TEXEL_FORMAT<T>, write>
diff --git a/src/tint/lang/wgsl/resolver/dependency_graph_test.cc b/src/tint/lang/wgsl/resolver/dependency_graph_test.cc
index 8a1f136..05a83d4 100644
--- a/src/tint/lang/wgsl/resolver/dependency_graph_test.cc
+++ b/src/tint/lang/wgsl/resolver/dependency_graph_test.cc
@@ -1233,7 +1233,7 @@
 namespace resolve_to_builtin_type {
 
 using ResolverDependencyGraphResolveToBuiltinType =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToBuiltinType, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1272,7 +1272,7 @@
 namespace resolve_to_access {
 
 using ResolverDependencyGraphResolveToAccess =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToAccess, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1311,7 +1311,7 @@
 namespace resolve_to_address_space {
 
 using ResolverDependencyGraphResolveToAddressSpace =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToAddressSpace, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1350,7 +1350,7 @@
 namespace resolve_to_builtin_value {
 
 using ResolverDependencyGraphResolveToBuiltinValue =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToBuiltinValue, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1389,7 +1389,7 @@
 namespace resolve_to_interpolation_sampling {
 
 using ResolverDependencyGraphResolveToInterpolationSampling =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToInterpolationSampling, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1429,7 +1429,7 @@
 namespace resolve_to_interpolation_sampling {
 
 using ResolverDependencyGraphResolveToInterpolationType =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToInterpolationType, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1469,7 +1469,7 @@
 namespace resolve_to_texel_format {
 
 using ResolverDependencyGraphResolveToTexelFormat =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphResolveToTexelFormat, Resolve) {
     const auto use = std::get<0>(GetParam());
@@ -1546,7 +1546,7 @@
                                                           SymbolDeclKind::NestedLocalLet)));
 
 using ResolverDependencyGraphShadowKindTest =
-    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, const char*>>;
+    ResolverDependencyGraphTestWithParam<std::tuple<SymbolUseKind, std::string_view>>;
 
 TEST_P(ResolverDependencyGraphShadowKindTest, ShadowedByGlobalVar) {
     const auto use = std::get<0>(GetParam());
@@ -1667,10 +1667,10 @@
 
     Vector<SymbolUse, 64> symbol_uses;
 
-    auto add_use = [&](const ast::Node* decl, auto use, int line, const char* kind) {
-        symbol_uses.Push(
-            SymbolUse{decl, IdentifierOf(use),
-                      std::string(__FILE__) + ":" + std::to_string(line) + ": " + kind});
+    auto add_use = [&](const ast::Node* decl, auto use, int line, std::string_view kind) {
+        symbol_uses.Push(SymbolUse{
+            decl, IdentifierOf(use),
+            std::string(__FILE__) + ":" + std::to_string(line) + ": " + std::string(kind)});
         return use;
     };
 
diff --git a/src/tint/lang/wgsl/resolver/sem_helper.cc b/src/tint/lang/wgsl/resolver/sem_helper.cc
index adb384e..a68734a 100644
--- a/src/tint/lang/wgsl/resolver/sem_helper.cc
+++ b/src/tint/lang/wgsl/resolver/sem_helper.cc
@@ -136,21 +136,21 @@
 void SemHelper::ErrorUnexpectedExprKind(
     const sem::Expression* expr,
     std::string_view wanted,
-    tint::Slice<char const* const> suggestions /* = Empty */) const {
+    tint::Slice<const std::string_view> suggestions /* = Empty */) const {
     if (auto* ui = expr->As<UnresolvedIdentifier>()) {
         auto* ident = ui->Identifier();
         auto name = ident->identifier->symbol.Name();
         AddError("unresolved " + std::string(wanted) + " '" + name + "'", ident->source);
         if (!suggestions.IsEmpty()) {
             // Filter out suggestions that have a leading underscore.
-            Vector<const char*, 8> filtered;
-            for (auto* str : suggestions) {
+            Vector<std::string_view, 8> filtered;
+            for (auto str : suggestions) {
                 if (str[0] != '_') {
                     filtered.Push(str);
                 }
             }
             StringStream msg;
-            tint::SuggestAlternatives(name, filtered.Slice().Reinterpret<char const* const>(), msg);
+            tint::SuggestAlternatives(name, filtered.Slice(), msg);
             AddNote(msg.str(), ident->source);
         }
         return;
diff --git a/src/tint/lang/wgsl/resolver/sem_helper.h b/src/tint/lang/wgsl/resolver/sem_helper.h
index bda2b0d..ad63ac2 100644
--- a/src/tint/lang/wgsl/resolver/sem_helper.h
+++ b/src/tint/lang/wgsl/resolver/sem_helper.h
@@ -278,7 +278,7 @@
     /// @param suggestions suggested valid identifiers
     void ErrorUnexpectedExprKind(const sem::Expression* expr,
                                  std::string_view wanted,
-                                 tint::Slice<char const* const> suggestions = Empty) const;
+                                 tint::Slice<const std::string_view> suggestions = Empty) const;
 
     /// If @p node is a module-scope type, variable or function declaration, then appends a note
     /// diagnostic where this declaration was declared, otherwise the function does nothing.
diff --git a/src/tint/utils/templates/enums.tmpl.inc b/src/tint/utils/templates/enums.tmpl.inc
index 5bd899e..dd848a3 100644
--- a/src/tint/utils/templates/enums.tmpl.inc
+++ b/src/tint/utils/templates/enums.tmpl.inc
@@ -64,7 +64,7 @@
 /// @returns the parsed enum, or {{$enum}}::kUndefined if the string could not be parsed.
 {{$enum}} Parse{{$enum}}(std::string_view str);
 
-constexpr const char* k{{$enum}}Strings[] = {
+constexpr std::string_view k{{$enum}}Strings[] = {
 {{-   range $entry := $.Entries }}
 {{-     if not $entry.IsInternal}}
     "{{$entry.Name}}",
diff --git a/src/tint/utils/text/string.cc b/src/tint/utils/text/string.cc
index a84a5b1..0812dad 100644
--- a/src/tint/utils/text/string.cc
+++ b/src/tint/utils/text/string.cc
@@ -63,15 +63,7 @@
 }
 
 void SuggestAlternatives(std::string_view got,
-                         Slice<char const* const> strings,
-                         StringStream& ss,
-                         const SuggestAlternativeOptions& options /* = {} */) {
-    auto views = Transform<8>(strings, [](char const* const str) { return std::string_view(str); });
-    SuggestAlternatives(got, views.Slice(), ss, options);
-}
-
-void SuggestAlternatives(std::string_view got,
-                         Slice<std::string_view> strings,
+                         Slice<const std::string_view> strings,
                          StringStream& ss,
                          const SuggestAlternativeOptions& options /* = {} */) {
     // If the string typed was within kSuggestionDistance of one of the possible enum values,
diff --git a/src/tint/utils/text/string.h b/src/tint/utils/text/string.h
index f98f4b6..6ae2894 100644
--- a/src/tint/utils/text/string.h
+++ b/src/tint/utils/text/string.h
@@ -52,6 +52,20 @@
     return str;
 }
 
+/// @copydoc ReplaceAll(std::string, std::string_view, std::string_view)
+[[nodiscard]] inline std::string ReplaceAll(std::string_view str,
+                                            std::string_view substr,
+                                            std::string_view replacement) {
+    return ReplaceAll(std::string(str), substr, replacement);
+}
+
+/// @copydoc ReplaceAll(std::string, std::string_view, std::string_view)
+[[nodiscard]] inline std::string ReplaceAll(const char* str,
+                                            std::string_view substr,
+                                            std::string_view replacement) {
+    return ReplaceAll(std::string(str), substr, replacement);
+}
+
 /// @param value the boolean value to be printed as a string
 /// @returns value printed as a string via the stream `<<` operator
 inline std::string ToString(bool value) {
@@ -109,17 +123,7 @@
 /// @param ss the stream to write the suggest and list of possible values to
 /// @param options options for the suggestion
 void SuggestAlternatives(std::string_view got,
-                         Slice<char const* const> strings,
-                         StringStream& ss,
-                         const SuggestAlternativeOptions& options = {});
-
-/// Suggest alternatives for an unrecognized string from a list of possible values.
-/// @param got the unrecognized string
-/// @param strings the list of possible values
-/// @param ss the stream to write the suggest and list of possible values to
-/// @param options options for the suggestion
-void SuggestAlternatives(std::string_view got,
-                         Slice<std::string_view> strings,
+                         Slice<const std::string_view> strings,
                          StringStream& ss,
                          const SuggestAlternativeOptions& options = {});
 
diff --git a/src/tint/utils/text/string_test.cc b/src/tint/utils/text/string_test.cc
index fccf0fa..b14aaac 100644
--- a/src/tint/utils/text/string_test.cc
+++ b/src/tint/utils/text/string_test.cc
@@ -94,20 +94,20 @@
 
 TEST(StringTest, SuggestAlternatives) {
     {
-        const char* alternatives[] = {"hello world", "Hello World"};
+        std::string_view alternatives[] = {"hello world", "Hello World"};
         StringStream ss;
         SuggestAlternatives("hello wordl", alternatives, ss);
         EXPECT_EQ(ss.str(), R"(Did you mean 'hello world'?
 Possible values: 'hello world', 'Hello World')");
     }
     {
-        const char* alternatives[] = {"foobar", "something else"};
+        std::string_view alternatives[] = {"foobar", "something else"};
         StringStream ss;
         SuggestAlternatives("hello world", alternatives, ss);
         EXPECT_EQ(ss.str(), R"(Possible values: 'foobar', 'something else')");
     }
     {
-        const char* alternatives[] = {"hello world", "Hello World"};
+        std::string_view alternatives[] = {"hello world", "Hello World"};
         StringStream ss;
         SuggestAlternativeOptions opts;
         opts.prefix = "$";
@@ -116,7 +116,7 @@
 Possible values: '$hello world', '$Hello World')");
     }
     {
-        const char* alternatives[] = {"hello world", "Hello World"};
+        std::string_view alternatives[] = {"hello world", "Hello World"};
         StringStream ss;
         SuggestAlternativeOptions opts;
         opts.list_possible_values = false;