[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()}});