[tint] Add the BindingArray type

Bug: 393558555
Change-Id: Id57582101347d275a0249de0a284e45c1b2338f0
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/223877
Commit-Queue: Corentin Wallez <cwallez@chromium.org>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/type/BUILD.bazel b/src/tint/lang/core/type/BUILD.bazel
index 7846814..77d7dcf 100644
--- a/src/tint/lang/core/type/BUILD.bazel
+++ b/src/tint/lang/core/type/BUILD.bazel
@@ -45,6 +45,7 @@
     "array.cc",
     "array_count.cc",
     "atomic.cc",
+    "binding_array.cc",
     "bool.cc",
     "builtin_structs.cc",
     "depth_multisampled_texture.cc",
@@ -89,6 +90,7 @@
     "array.h",
     "array_count.h",
     "atomic.h",
+    "binding_array.h",
     "bool.h",
     "builtin_structs.h",
     "clone_context.h",
@@ -151,6 +153,7 @@
   srcs = [
     "array_test.cc",
     "atomic_test.cc",
+    "binding_array_test.cc",
     "bool_test.cc",
     "builtin_structs_test.cc",
     "depth_multisampled_texture_test.cc",
diff --git a/src/tint/lang/core/type/BUILD.cmake b/src/tint/lang/core/type/BUILD.cmake
index 5cb38228..56f6f53 100644
--- a/src/tint/lang/core/type/BUILD.cmake
+++ b/src/tint/lang/core/type/BUILD.cmake
@@ -51,6 +51,8 @@
   lang/core/type/array_count.h
   lang/core/type/atomic.cc
   lang/core/type/atomic.h
+  lang/core/type/binding_array.cc
+  lang/core/type/binding_array.h
   lang/core/type/bool.cc
   lang/core/type/bool.h
   lang/core/type/builtin_structs.cc
@@ -152,6 +154,7 @@
 tint_add_target(tint_lang_core_type_test test
   lang/core/type/array_test.cc
   lang/core/type/atomic_test.cc
+  lang/core/type/binding_array_test.cc
   lang/core/type/bool_test.cc
   lang/core/type/builtin_structs_test.cc
   lang/core/type/depth_multisampled_texture_test.cc
diff --git a/src/tint/lang/core/type/BUILD.gn b/src/tint/lang/core/type/BUILD.gn
index fa4c95e..b6e813c 100644
--- a/src/tint/lang/core/type/BUILD.gn
+++ b/src/tint/lang/core/type/BUILD.gn
@@ -57,6 +57,8 @@
     "array_count.h",
     "atomic.cc",
     "atomic.h",
+    "binding_array.cc",
+    "binding_array.h",
     "bool.cc",
     "bool.h",
     "builtin_structs.cc",
@@ -152,6 +154,7 @@
     sources = [
       "array_test.cc",
       "atomic_test.cc",
+      "binding_array_test.cc",
       "bool_test.cc",
       "builtin_structs_test.cc",
       "depth_multisampled_texture_test.cc",
diff --git a/src/tint/lang/core/type/binding_array.cc b/src/tint/lang/core/type/binding_array.cc
new file mode 100644
index 0000000..45d5bdf
--- /dev/null
+++ b/src/tint/lang/core/type/binding_array.cc
@@ -0,0 +1,75 @@
+// 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/binding_array.h"
+
+#include <string>
+
+#include "src/tint/lang/core/type/manager.h"
+#include "src/tint/utils/ice/ice.h"
+#include "src/tint/utils/math/hash.h"
+#include "src/tint/utils/symbol/symbol_table.h"
+#include "src/tint/utils/text/string_stream.h"
+
+TINT_INSTANTIATE_TYPEINFO(tint::core::type::BindingArray);
+
+namespace tint::core::type {
+
+BindingArray::BindingArray(const Type* element, uint32_t count)
+    : Base(Hash(tint::TypeCode::Of<BindingArray>().bits, count), core::type::Flags{}),
+      element_(element),
+      count_(count) {
+    TINT_ASSERT(element_);
+}
+
+bool BindingArray::Equals(const UniqueNode& other) const {
+    if (auto* o = other.As<BindingArray>()) {
+        return o->element_ == element_ && o->count_ == count_;
+    }
+    return false;
+}
+
+std::string BindingArray::FriendlyName() const {
+    StringStream out;
+    out << "binding_array<" << element_->FriendlyName() << ", " << count_ << ">";
+    return out.str();
+}
+
+TypeAndCount BindingArray::Elements([[maybe_unused]] const Type*, [[maybe_unused]] uint32_t) const {
+    return {element_, count_};
+}
+
+const Type* BindingArray::Element(uint32_t index) const {
+    return index < count_ ? element_ : nullptr;
+}
+
+BindingArray* BindingArray::Clone(CloneContext& ctx) const {
+    auto* elem_ty = element_->Clone(ctx);
+    return ctx.dst.mgr->Get<BindingArray>(elem_ty, count_);
+}
+
+}  // namespace tint::core::type
diff --git a/src/tint/lang/core/type/binding_array.h b/src/tint/lang/core/type/binding_array.h
new file mode 100644
index 0000000..abc8ce9
--- /dev/null
+++ b/src/tint/lang/core/type/binding_array.h
@@ -0,0 +1,80 @@
+// 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_BINDING_ARRAY_H_
+#define SRC_TINT_LANG_CORE_TYPE_BINDING_ARRAY_H_
+
+#include <stdint.h>
+#include <string>
+#include <variant>
+
+#include "src/tint/lang/core/type/type.h"
+#include "src/tint/utils/macros/compiler.h"
+
+namespace tint::core::type {
+
+/// Array holds the type information for BindingArray nodes.
+class BindingArray : public Castable<BindingArray, Type> {
+  public:
+    /// Constructor
+    /// @param element the array element type
+    /// @param count the number of elements in the array.
+    BindingArray(Type const* element, uint32_t count);
+
+    /// @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;
+
+    /// @return the array element type
+    Type const* ElemType() const { return element_; }
+
+    /// @returns the number of elements in the array.
+    uint32_t Count() const { return count_; }
+
+    /// @returns the name for this type that closely resembles how it would be
+    /// declared in WGSL.
+    std::string FriendlyName() const override;
+
+    /// @copydoc Type::Elements
+    TypeAndCount Elements(const Type* type_if_invalid = nullptr,
+                          uint32_t count_if_invalid = 0) const override;
+
+    /// @copydoc Type::Element
+    const Type* Element(uint32_t index) const override;
+
+    /// @param ctx the clone context
+    /// @returns a clone of this type
+    BindingArray* Clone(CloneContext& ctx) const override;
+
+  private:
+    Type const* const element_;
+    const uint32_t count_;
+};
+
+}  // namespace tint::core::type
+
+#endif  // SRC_TINT_LANG_CORE_TYPE_BINDING_ARRAY_H_
diff --git a/src/tint/lang/core/type/binding_array_test.cc b/src/tint/lang/core/type/binding_array_test.cc
new file mode 100644
index 0000000..e78fdce
--- /dev/null
+++ b/src/tint/lang/core/type/binding_array_test.cc
@@ -0,0 +1,112 @@
+// 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/helper_test.h"
+
+#include "src/tint/lang/core/type/binding_array.h"
+#include "src/tint/lang/core/type/f32.h"
+#include "src/tint/lang/core/type/sampled_texture.h"
+#include "src/tint/lang/core/type/u32.h"
+
+namespace tint::core::type {
+namespace {
+
+using BindingArrayTest = TestHelper;
+
+TEST_F(BindingArrayTest, Creation) {
+    auto* f32 = create<F32>();
+    auto* t = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* a = create<BindingArray>(t, 3u);
+
+    EXPECT_EQ(a->ElemType(), t);
+    EXPECT_EQ(a->Count(), 3u);
+}
+
+TEST_F(BindingArrayTest, Hash) {
+    auto* f32 = create<F32>();
+    auto* t = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* a = create<BindingArray>(t, 3u);
+    auto* a2 = create<BindingArray>(t, 3u);
+
+    EXPECT_EQ(a->unique_hash, a2->unique_hash);
+}
+
+TEST_F(BindingArrayTest, Equals) {
+    auto* f32 = create<F32>();
+    auto* t1 = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* u32 = create<U32>();
+    auto* t2 = create<SampledTexture>(TextureDimension::k2d, u32);
+
+    auto* a = create<BindingArray>(t1, 3u);
+    auto* a2 = create<BindingArray>(t1, 3u);
+    auto* a_count = create<BindingArray>(t1, 4u);
+    auto* a_type = create<BindingArray>(t2, 3u);
+
+    EXPECT_EQ(a, a2);
+    EXPECT_NE(a, a_count);
+    EXPECT_NE(a, a_type);
+}
+
+TEST_F(BindingArrayTest, FriendlyName) {
+    auto* f32 = create<F32>();
+    auto* t = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* a = create<BindingArray>(t, 3u);
+    EXPECT_EQ(a->FriendlyName(), "binding_array<texture_2d<f32>, 3>");
+}
+
+TEST_F(BindingArrayTest, Element) {
+    auto* f32 = create<F32>();
+    auto* t = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* a = create<BindingArray>(t, 3u);
+    EXPECT_EQ(a->Element(2), t);
+    EXPECT_EQ(a->Element(3), nullptr);
+}
+
+TEST_F(BindingArrayTest, Elements) {
+    auto* f32 = create<F32>();
+    auto* t = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* a = create<BindingArray>(t, 3u);
+    EXPECT_EQ(a->Elements().type, t);
+    EXPECT_EQ(a->Elements().count, 3u);
+}
+
+TEST_F(BindingArrayTest, Clone) {
+    auto* f32 = create<F32>();
+    auto* t = create<SampledTexture>(TextureDimension::k2d, f32);
+    auto* a = create<BindingArray>(t, 3u);
+
+    core::type::Manager mgr;
+    core::type::CloneContext ctx{{nullptr}, {nullptr, &mgr}};
+
+    auto* s = a->Clone(ctx);
+    EXPECT_TRUE(s->ElemType()->Is<SampledTexture>());
+    EXPECT_TRUE(s->ElemType()->As<SampledTexture>()->Type()->Is<F32>());
+    EXPECT_EQ(s->Count(), a->Count());
+}
+
+}  // namespace
+}  // namespace tint::core::type
diff --git a/src/tint/lang/core/type/manager.cc b/src/tint/lang/core/type/manager.cc
index 03eba32..be140d4 100644
--- a/src/tint/lang/core/type/manager.cc
+++ b/src/tint/lang/core/type/manager.cc
@@ -32,6 +32,7 @@
 #include "src/tint/lang/core/type/abstract_float.h"
 #include "src/tint/lang/core/type/abstract_int.h"
 #include "src/tint/lang/core/type/array.h"
+#include "src/tint/lang/core/type/binding_array.h"
 #include "src/tint/lang/core/type/bool.h"
 #include "src/tint/lang/core/type/f16.h"
 #include "src/tint/lang/core/type/f32.h"
@@ -246,6 +247,11 @@
         /* implicit stride */ implicit_stride);
 }
 
+const core::type::BindingArray* Manager::binding_array(const core::type::Type* elem_ty,
+                                                       uint32_t count) {
+    return Get<core::type::BindingArray>(elem_ty, count);
+}
+
 const core::type::Pointer* Manager::ptr(core::AddressSpace address_space,
                                         const core::type::Type* subtype,
                                         core::Access access /* = core::Access::kUndefined */) {
diff --git a/src/tint/lang/core/type/manager.h b/src/tint/lang/core/type/manager.h
index 37a1ccf..b1a5654 100644
--- a/src/tint/lang/core/type/manager.h
+++ b/src/tint/lang/core/type/manager.h
@@ -50,6 +50,7 @@
 class AbstractFloat;
 class AbstractInt;
 class Array;
+class BindingArray;
 class Bool;
 class F16;
 class F32;
@@ -529,6 +530,11 @@
         }
     }
 
+    /// @param elem_ty the array element type
+    /// @param count the array element count
+    /// @returns the array type
+    const core::type::BindingArray* binding_array(const core::type::Type* elem_ty, uint32_t count);
+
     /// @param address_space the address space
     /// @param subtype the pointer subtype
     /// @param access the access settings