[tint] Add string type

This will be used for the format string of msl.os_log(), and could
also be used by other overloads of the core `print()` builtin in the
future.

Bug: 433534277
Change-Id: Ia78dbf7e86327d8420d70741e29a1dd64054d2f6
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/254576
Commit-Queue: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Auto-Submit: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/type/BUILD.bazel b/src/tint/lang/core/type/BUILD.bazel
index 55273b8..36b09af 100644
--- a/src/tint/lang/core/type/BUILD.bazel
+++ b/src/tint/lang/core/type/BUILD.bazel
@@ -71,6 +71,7 @@
     "sampler_kind.cc",
     "scalar.cc",
     "storage_texture.cc",
+    "string.cc",
     "struct.cc",
     "subgroup_matrix.cc",
     "texel_buffer.cc",
@@ -118,6 +119,7 @@
     "sampler_kind.h",
     "scalar.h",
     "storage_texture.h",
+    "string.h",
     "struct.h",
     "subgroup_matrix.h",
     "texel_buffer.h",
@@ -176,6 +178,7 @@
     "sampled_texture_test.cc",
     "sampler_test.cc",
     "storage_texture_test.cc",
+    "string_test.cc",
     "struct_test.cc",
     "subgroup_matrix_test.cc",
     "texel_buffer_test.cc",
diff --git a/src/tint/lang/core/type/BUILD.cmake b/src/tint/lang/core/type/BUILD.cmake
index 0b7815e..97baad1 100644
--- a/src/tint/lang/core/type/BUILD.cmake
+++ b/src/tint/lang/core/type/BUILD.cmake
@@ -104,6 +104,8 @@
   lang/core/type/scalar.h
   lang/core/type/storage_texture.cc
   lang/core/type/storage_texture.h
+  lang/core/type/string.cc
+  lang/core/type/string.h
   lang/core/type/struct.cc
   lang/core/type/struct.h
   lang/core/type/subgroup_matrix.cc
@@ -177,6 +179,7 @@
   lang/core/type/sampled_texture_test.cc
   lang/core/type/sampler_test.cc
   lang/core/type/storage_texture_test.cc
+  lang/core/type/string_test.cc
   lang/core/type/struct_test.cc
   lang/core/type/subgroup_matrix_test.cc
   lang/core/type/texel_buffer_test.cc
diff --git a/src/tint/lang/core/type/BUILD.gn b/src/tint/lang/core/type/BUILD.gn
index 0342500..54427fc 100644
--- a/src/tint/lang/core/type/BUILD.gn
+++ b/src/tint/lang/core/type/BUILD.gn
@@ -110,6 +110,8 @@
     "scalar.h",
     "storage_texture.cc",
     "storage_texture.h",
+    "string.cc",
+    "string.h",
     "struct.cc",
     "struct.h",
     "subgroup_matrix.cc",
@@ -177,6 +179,7 @@
       "sampled_texture_test.cc",
       "sampler_test.cc",
       "storage_texture_test.cc",
+      "string_test.cc",
       "struct_test.cc",
       "subgroup_matrix_test.cc",
       "texel_buffer_test.cc",
diff --git a/src/tint/lang/core/type/manager.h b/src/tint/lang/core/type/manager.h
index 2e95633..9036bcf 100644
--- a/src/tint/lang/core/type/manager.h
+++ b/src/tint/lang/core/type/manager.h
@@ -40,6 +40,7 @@
 #include "src/tint/lang/core/type/input_attachment.h"
 #include "src/tint/lang/core/type/multisampled_texture.h"
 #include "src/tint/lang/core/type/sampler.h"
+#include "src/tint/lang/core/type/string.h"
 #include "src/tint/lang/core/type/struct.h"
 #include "src/tint/lang/core/type/subgroup_matrix.h"
 #include "src/tint/lang/core/type/texel_buffer.h"
@@ -631,6 +632,9 @@
         return Get<core::type::InputAttachment>(inner);
     }
 
+    /// @returns a string type
+    const core::type::String* String() { return Get<core::type::String>(); }
+
     /// A structure member descriptor.
     struct StructMemberDesc {
         /// The name of the struct member.
diff --git a/src/tint/lang/core/type/string.cc b/src/tint/lang/core/type/string.cc
new file mode 100644
index 0000000..25c372d
--- /dev/null
+++ b/src/tint/lang/core/type/string.cc
@@ -0,0 +1,56 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/core/type/string.h"
+
+#include <string>
+
+#include "src/tint/lang/core/type/clone_context.h"
+#include "src/tint/lang/core/type/manager.h"
+#include "src/tint/lang/core/type/unique_node.h"
+#include "src/tint/utils/math/hash.h"
+#include "src/tint/utils/rtti/castable.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::core::type::String);
+
+namespace tint::core::type {
+
+String::String() : Base(Hash(tint::TypeCode::Of<String>().bits), core::type::Flags{}) {}
+
+bool String::Equals(const UniqueNode& other) const {
+    return other.Is<String>();
+}
+
+std::string String::FriendlyName() const {
+    return "string";
+}
+
+String* String::Clone(core::type::CloneContext& ctx) const {
+    return ctx.dst.mgr->Get<String>();
+}
+
+}  // namespace tint::core::type
diff --git a/src/tint/lang/core/type/string.h b/src/tint/lang/core/type/string.h
new file mode 100644
index 0000000..afb5e76
--- /dev/null
+++ b/src/tint/lang/core/type/string.h
@@ -0,0 +1,57 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#ifndef SRC_TINT_LANG_CORE_TYPE_STRING_H_
+#define SRC_TINT_LANG_CORE_TYPE_STRING_H_
+
+#include <string>
+
+#include "src/tint/lang/core/type/type.h"
+
+namespace tint::core::type {
+
+/// String represents a C-style string, used for print format strings.
+class String final : public Castable<String, core::type::Type> {
+  public:
+    /// Constructor
+    String();
+
+    /// @param other the other node to compare against
+    /// @returns true if the this type is equal to @p other
+    bool Equals(const UniqueNode& other) const override;
+
+    /// @returns the friendly name for this type
+    std::string FriendlyName() const override;
+
+    /// @param ctx the clone context
+    /// @returns a clone of this type
+    String* Clone(core::type::CloneContext& ctx) const override;
+};
+
+}  // namespace tint::core::type
+
+#endif  // SRC_TINT_LANG_CORE_TYPE_STRING_H_
diff --git a/src/tint/lang/core/type/string_test.cc b/src/tint/lang/core/type/string_test.cc
new file mode 100644
index 0000000..30d44b6
--- /dev/null
+++ b/src/tint/lang/core/type/string_test.cc
@@ -0,0 +1,56 @@
+// Copyright 2025 The Dawn & Tint Authors
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+//
+// 1. Redistributions of source code must retain the above copyright notice, this
+//    list of conditions and the following disclaimer.
+//
+// 2. Redistributions in binary form must reproduce the above copyright notice,
+//    this list of conditions and the following disclaimer in the documentation
+//    and/or other materials provided with the distribution.
+//
+// 3. Neither the name of the copyright holder nor the names of its
+//    contributors may be used to endorse or promote products derived from
+//    this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "src/tint/lang/core/type/string.h"
+
+#include <gtest/gtest.h>
+
+#include "src/tint/lang/core/type/array.h"
+#include "src/tint/lang/core/type/manager.h"
+#include "src/tint/lang/core/type/u8.h"
+
+namespace tint::core::type {
+namespace {
+
+TEST(StringTypeTest, Equals) {
+    core::type::Manager ty;
+    auto* a = ty.String();
+    auto* b = ty.String();
+    auto* c = ty.array<core::u8, 8>();
+
+    EXPECT_TRUE(a->Equals(*b));
+    EXPECT_FALSE(a->Equals(*c));
+}
+
+TEST(StringTypeTest, FriendlyName) {
+    core::type::Manager ty;
+    auto* s = ty.String();
+    EXPECT_EQ(s->FriendlyName(), "string");
+}
+
+}  // namespace
+}  // namespace tint::core::type