[tint] Allow for nested access eval (correctly)

There were some bugs in the original code [1] which simply just look
like minor untested oversights.


[1]
https://docs.google.com/document/d/14YnsZ3jUQ18PytRoh5PxIpJh4jo_1RmYHwboipAXmiY/edit?resourcekey=0-SskthuQTNPRt4BNUNJlShw&tab=t.0

Change-Id: Id94067530c3b3551751fb6392045bff70cac5a99
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/227634
Reviewed-by: James Price <jrprice@google.com>
Commit-Queue: Peter McNeeley <petermcneeley@google.com>
diff --git a/src/tint/lang/core/constant/eval.cc b/src/tint/lang/core/constant/eval.cc
index c6db4e9..ef47d50 100644
--- a/src/tint/lang/core/constant/eval.cc
+++ b/src/tint/lang/core/constant/eval.cc
@@ -1410,7 +1410,6 @@
     // on the type of the access op. An example is here is a failure generated by a compile time
     // negative index array access.
     auto el = obj_val ? obj_val->Type()->Elements() : obj_ty->UnwrapPtrOrRef()->Elements();
-
     AInt idx = idx_val->ValueAs<AInt>();
     if (idx < 0 || (el.count > 0 && idx >= el.count)) {
         auto& err = AddError(idx_source) << "index " << idx << " out of bounds";
diff --git a/src/tint/lang/core/ir/evaluator.cc b/src/tint/lang/core/ir/evaluator.cc
index 7a86a4b..a6bc3fe 100644
--- a/src/tint/lang/core/ir/evaluator.cc
+++ b/src/tint/lang/core/ir/evaluator.cc
@@ -31,6 +31,7 @@
 #include "src/tint/lang/core/ir/constexpr_if.h"
 #include "src/tint/lang/core/ir/function_param.h"
 #include "src/tint/lang/core/ir/override.h"
+#include "src/tint/lang/core/number.h"
 #include "src/tint/lang/core/type/bool.h"
 #include "src/tint/lang/core/type/matrix.h"
 #include "src/tint/utils/rtti/switch.h"
@@ -126,7 +127,7 @@
     }
 
     auto* obj = obj_res.Get();
-
+    auto* access_obj_type = a->Object()->Type()->UnwrapPtrOrRef();
     for (auto* idx : a->Indices()) {
         auto val = EvalValue(idx);
         if (val != Success) {
@@ -138,10 +139,15 @@
         }
         TINT_ASSERT(val.Get()->Is<core::constant::Value>());
 
-        auto res = const_eval_.Index(obj, a->Result(0)->Type(), val.Get(), SourceOf(a));
+        auto res = const_eval_.Index(obj, access_obj_type, val.Get(), SourceOf(a));
         if (res != Success) {
             return Failure();
         }
+
+        // Index element to type to support type nested access.
+        access_obj_type = access_obj_type->Element(val.Get()->ValueAs<u32>());
+        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 8ab95fa..93260a9 100644
--- a/src/tint/lang/core/ir/evaluator_test.cc
+++ b/src/tint/lang/core/ir/evaluator_test.cc
@@ -32,6 +32,7 @@
 #include "src/tint/lang/core/ir/evaluator.h"
 #include "src/tint/lang/core/ir/instruction.h"
 #include "src/tint/lang/core/ir/ir_helper_test.h"
+#include "src/tint/lang/core/type/u32.h"
 
 using namespace tint::core::number_suffixes;  // NOLINT
 using namespace tint::core::fluent_types;     // NOLINT
@@ -119,7 +120,39 @@
     EXPECT_EQ(2, c->Value()->ValueAs<int32_t>());
 }
 
-TEST_F(IR_EvaluatorTest, RuntimeArray_OutOfBoundsAccess) {
+TEST_F(IR_EvaluatorTest, NestedStruct_AccessSuccess) {
+    auto* S = ty.Struct(mod.symbols.New("S"), {{mod.symbols.New("data"), ty.array<u32, 4>()}});
+
+    auto* arr = b.Var("struct", ty.ptr(storage, S));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 0_i, 3_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_EvaluatorTest, NestedStruct_AccessFailArray) {
+    auto* S = ty.Struct(mod.symbols.New("S"), {{mod.symbols.New("data"), ty.array<u32, 4>()}});
+
+    auto* arr = b.Var("struct", ty.ptr(storage, S));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 0_i, 4_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(), R"(error: index 4 out of bounds [0..3])");
+}
+
+TEST_F(IR_EvaluatorTest, NestedStruct_AccessFailMember) {
+    auto* S = ty.Struct(mod.symbols.New("S"), {{mod.symbols.New("data"), ty.array<u32, 4>()}});
+
+    auto* arr = b.Var("struct", ty.ptr(storage, S));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 1_i, 3_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_NE(res, Success);
+    EXPECT_EQ(res.Failure().reason.Str(), R"(error: index 1 out of bounds [0..0])");
+}
+
+TEST_F(IR_EvaluatorTest, ArrayBounds_OutOfBoundsAccess) {
     auto* arr = b.Var("arr", ty.ptr(storage, ty.array<u32>()));
     auto* inst = b.Access(ty.ptr<storage, u32>(), arr, -1_i);
     auto res = Eval(b, inst);
@@ -129,6 +162,89 @@
     EXPECT_EQ(res.Failure().reason.Str(), R"(error: index -1 out of bounds)");
 }
 
+TEST_F(IR_EvaluatorTest, ArrayBounds_OverflowBoundsAccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<u32, 3>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 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_NestedOverflowBoundsAccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<u32, 3>, 5>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 3_i, 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_NestedoundsAccessSuccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<u32, 3>, 5>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 4_i, 2_i);
+    auto res = Eval(b, inst);
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_EvaluatorTest, ArrayBounds_DoubleNestedOverflowBoundsAccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<array<u32, 3>, 5>, 7>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 3_i, 3_i, 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_DoubleNestedBoundsAccessSuccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<array<u32, 3>, 5>, 7>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 6_i, 4_i, 2_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_EQ(res, Success);
+}
+
+TEST_F(IR_EvaluatorTest, ArrayBounds_DoubleNestedEarlyOverflowBoundsAccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<array<u32, 3>, 5>, 7>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 7_i, 3_i, 3_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_NE(res, Success);
+
+    EXPECT_EQ(res.Failure().reason.Str(), R"(error: index 7 out of bounds [0..6])");
+}
+
+TEST_F(IR_EvaluatorTest, ArrayBounds_DoubleNestedMidOverflowBoundsAccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<array<array<u32, 3>, 5>, 7>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 3_i, 5_i, 3_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_NE(res, Success);
+
+    EXPECT_EQ(res.Failure().reason.Str(), R"(error: index 5 out of bounds [0..4])");
+}
+
+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);
+    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_NestedVecBoundsAccessSuccess) {
+    auto* arr = b.Var("arr", ty.ptr(storage, ty.array<vec3<u32>, 7>()));
+    auto* inst = b.Access(ty.ptr<storage, u32>(), arr, 3_i, 2_i);
+    auto res = Eval(b, inst);
+
+    ASSERT_EQ(res, Success);
+}
+
 TEST_F(IR_EvaluatorTest, ConstructStruct_Access) {
     auto* S = ty.Struct(mod.symbols.New("S"),
                         {{mod.symbols.New("a"), ty.i32()}, {mod.symbols.New("b"), ty.f32()}});