[tint] Access evaluation for out of bounds

Even in the case of dynamic indexing type based out of bounds checking
is still done for const indexes. This change just extends this type
based indexing to apply at pipeline-override evaluation stage.

Bug: 415298444
Change-Id: I739f2e15b64e1dae82493a910c2e8ceedb74080f
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/240254
Reviewed-by: dan sinclair <dsinclair@chromium.org>
Commit-Queue: Peter McNeeley <petermcneeley@google.com>
Reviewed-by: James Price <jrprice@google.com>
diff --git a/src/tint/lang/core/ir/evaluator.cc b/src/tint/lang/core/ir/evaluator.cc
index 81e9fd0..1b073e0 100644
--- a/src/tint/lang/core/ir/evaluator.cc
+++ b/src/tint/lang/core/ir/evaluator.cc
@@ -133,21 +133,26 @@
             return val;
         }
         // Check if the value could be evaluated
-        if (!val.Get()) {
-            return nullptr;
-        }
-        TINT_ASSERT(val.Get()->Is<core::constant::Value>());
+        constexpr uint32_t kDefaultConstIndex = 0;
+        uint32_t index_const = kDefaultConstIndex;
+        if (val.Get()) {
+            TINT_ASSERT(val.Get()->Is<core::constant::Value>());
 
-        auto res = const_eval_.Index(obj, access_obj_type, val.Get(), SourceOf(a));
-        if (res != Success) {
-            return Failure();
+            auto res = const_eval_.Index(obj, access_obj_type, val.Get(), SourceOf(a));
+            if (res != Success) {
+                return Failure();
+            }
+            index_const = val.Get()->ValueAs<u32>();
+            obj = res.Get();
+        } else {
+            // No constant array evaluation possible for non-const (dynamic) indices. Only
+            // validation of bounds is possible at this stage.
+            obj = nullptr;
         }
 
         // Index element to type to support type nested access.
-        access_obj_type = access_obj_type->Element(val.Get()->ValueAs<u32>());
+        access_obj_type = access_obj_type->Element(index_const);
         TINT_ASSERT(access_obj_type);
-
-        obj = res.Get();
     }
     return obj;
 }
diff --git a/src/tint/lang/core/ir/evaluator_test.cc b/src/tint/lang/core/ir/evaluator_test.cc
index 88e9835..c3b0a0d 100644
--- a/src/tint/lang/core/ir/evaluator_test.cc
+++ b/src/tint/lang/core/ir/evaluator_test.cc
@@ -227,6 +227,25 @@
     EXPECT_EQ(res.Failure().reason.Str(), R"(error: index 5 out of bounds [0..4])");
 }
 
+TEST_F(IR_EvaluatorTest, ArrayBounds_NestedDynamicAndConstantInBounds) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<array<u32, 3>, 5>, 7>()));
+    auto* x = b.Var("x", 123_i);
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 6_i, x, 2_i);
+    auto res = Eval(b, inst);
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_EvaluatorTest, ArrayBounds_NestedDynamicAndConstantOutOfBoundsAccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<array<u32, 3>, 5>, 7>()));
+    auto* x = b.Var("x", 123_i);
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 6_i, x, 3_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_NE(res, Success);
+
+    EXPECT_EQ(res.Failure().reason.Str(), R"(error: index 3 out of bounds [0..2])");
+}
+
 TEST_F(IR_EvaluatorTest, ArrayBounds_NestedVecOverflowBoundsAccess) {
     auto* arr = b.Var("arr", ty.ptr(storage, ty.array<vec3<u32>, 7>()));
     auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 3_i, 3_i);