Update binding_array type for runtime arrays

Update the methods in the `binding_array` type class to support runtime
arrays. This mimics how an array works when set as a runtime array. Add
a `runtime_binding_array` helper method to the type manager.

Bug: 439626909
Change-Id: I1ba30aa19aadd279eeea6bcc919b5136ba1fd92e
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/257895
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: dan sinclair <dsinclair@chromium.org>
diff --git a/src/tint/lang/core/type/binding_array.cc b/src/tint/lang/core/type/binding_array.cc
index ece058d..7b834b8 100644
--- a/src/tint/lang/core/type/binding_array.cc
+++ b/src/tint/lang/core/type/binding_array.cc
@@ -55,21 +55,36 @@
 
 std::string BindingArray::FriendlyName() const {
     StringStream out;
-    out << "binding_array<" << element_->FriendlyName() << ", " << count_->FriendlyName() << ">";
+    out << "binding_array<" << element_->FriendlyName();
+
+    auto count_str = count_->FriendlyName();
+    if (!count_str.empty()) {
+        out << ", " << count_str;
+    }
+    out << ">";
+
     return out.str();
 }
 
-TypeAndCount BindingArray::Elements([[maybe_unused]] const Type*, [[maybe_unused]] uint32_t) const {
-    return {element_, count_->As<ConstantArrayCount>()->value};
+TypeAndCount BindingArray::Elements([[maybe_unused]] const Type*, uint32_t count_if_invalid) const {
+    uint32_t n = count_if_invalid;
+    if (auto* const_count = count_->As<ConstantArrayCount>()) {
+        n = const_count->value;
+    }
+    return {element_, n};
 }
 
 const Type* BindingArray::Element(uint32_t index) const {
-    return index < count_->As<ConstantArrayCount>()->value ? element_ : nullptr;
+    if (auto* count = count_->As<ConstantArrayCount>()) {
+        return index < count->value ? element_ : nullptr;
+    }
+    return element_;
 }
 
 BindingArray* BindingArray::Clone(CloneContext& ctx) const {
     auto* elem_ty = element_->Clone(ctx);
-    return ctx.dst.mgr->Get<BindingArray>(elem_ty, count_);
+    auto* count = count_->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_test.cc b/src/tint/lang/core/type/binding_array_test.cc
index e91e6a1..e6f8ff5 100644
--- a/src/tint/lang/core/type/binding_array_test.cc
+++ b/src/tint/lang/core/type/binding_array_test.cc
@@ -49,6 +49,15 @@
     EXPECT_EQ(a->Count()->As<ConstantArrayCount>()->value, 3u);
 }
 
+TEST_F(BindingArrayTest, RuntimeCreation) {
+    Manager ty;
+    auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
+    auto* a = ty.runtime_binding_array(t);
+
+    EXPECT_EQ(a->ElemType(), t);
+    EXPECT_TRUE(a->Count()->Is<RuntimeArrayCount>());
+}
+
 TEST_F(BindingArrayTest, Hash) {
     Manager ty;
     auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
@@ -67,10 +76,24 @@
     auto* a2 = ty.binding_array(t1, 3u);
     auto* a_count = ty.binding_array(t1, 4u);
     auto* a_type = ty.binding_array(t2, 3u);
+    auto* r = ty.runtime_binding_array(t1);
+    auto* r1 = ty.runtime_binding_array(t1);
+    auto* r2 = ty.runtime_binding_array(t2);
 
     EXPECT_EQ(a, a2);
     EXPECT_NE(a, a_count);
     EXPECT_NE(a, a_type);
+
+    EXPECT_NE(a, r);
+    EXPECT_EQ(r, r1);
+    EXPECT_NE(r, r2);
+}
+
+TEST_F(BindingArrayTest, RuntimeFriendlyName) {
+    Manager ty;
+    auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
+    auto* a = ty.runtime_binding_array(t);
+    EXPECT_EQ(a->FriendlyName(), "binding_array<texture_2d<f32>>");
 }
 
 TEST_F(BindingArrayTest, FriendlyName) {
@@ -88,6 +111,14 @@
     EXPECT_EQ(a->Element(3), nullptr);
 }
 
+TEST_F(BindingArrayTest, RuntimeElement) {
+    Manager ty;
+    auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
+    auto* a = ty.runtime_binding_array(t);
+    EXPECT_EQ(a->Element(2), t);
+    EXPECT_EQ(a->Element(3), t);
+}
+
 TEST_F(BindingArrayTest, Elements) {
     Manager ty;
     auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
@@ -96,6 +127,14 @@
     EXPECT_EQ(a->Elements().count, 3u);
 }
 
+TEST_F(BindingArrayTest, RuntimeElements) {
+    Manager ty;
+    auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
+    auto* a = ty.runtime_binding_array(t);
+    EXPECT_EQ(a->Elements().type, t);
+    EXPECT_EQ(a->Elements().count, 0u);
+}
+
 TEST_F(BindingArrayTest, Clone) {
     Manager ty;
     auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
@@ -111,5 +150,19 @@
     EXPECT_EQ(s->Count()->As<ConstantArrayCount>()->value, 3u);
 }
 
+TEST_F(BindingArrayTest, RuntimeClone) {
+    Manager ty;
+    auto* t = ty.sampled_texture(TextureDimension::k2d, ty.f32());
+    auto* a = ty.runtime_binding_array(t);
+
+    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_TRUE(s->Count()->Is<RuntimeArrayCount>());
+}
+
 }  // namespace
 }  // namespace tint::core::type
diff --git a/src/tint/lang/core/type/manager.cc b/src/tint/lang/core/type/manager.cc
index a3658d3..4969d1c 100644
--- a/src/tint/lang/core/type/manager.cc
+++ b/src/tint/lang/core/type/manager.cc
@@ -339,6 +339,10 @@
     return Get<core::type::BindingArray>(elem_ty, Get<ConstantArrayCount>(count));
 }
 
+const core::type::BindingArray* Manager::runtime_binding_array(const core::type::Type* elem_ty) {
+    return Get<core::type::BindingArray>(elem_ty, Get<RuntimeArrayCount>());
+}
+
 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 9036bcf..c2b3a01 100644
--- a/src/tint/lang/core/type/manager.h
+++ b/src/tint/lang/core/type/manager.h
@@ -565,6 +565,10 @@
     /// @returns the array type
     const core::type::BindingArray* binding_array(const core::type::Type* elem_ty, uint32_t count);
 
+    /// @param elem_ty the array element type
+    /// @returns the array type
+    const core::type::BindingArray* runtime_binding_array(const core::type::Type* elem_ty);
+
     /// @param address_space the address space
     /// @param subtype the pointer subtype
     /// @param access the access settings