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