// Copyright 2021 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/tint/resolver/resolver.h" #include "gmock/gmock.h" #include "src/tint/resolver/resolver_test_helper.h" #include "src/tint/sem/reference.h" using namespace tint::number_suffixes; // NOLINT namespace tint::resolver { namespace { using ResolverIndexAccessorTest = ResolverTest; TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_F32) { Global("my_var", ty.mat2x3(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 1_f)); WrapInFunction(acc); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'"); } TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_Ref) { Global("my_var", ty.mat2x3(), ast::StorageClass::kPrivate); auto* idx = Var("idx", ty.i32(), Construct(ty.i32())); auto* acc = IndexAccessor("my_var", idx); WrapInFunction(Decl(idx), acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions_Dynamic_Ref) { Global("my_var", ty.mat4x4(), ast::StorageClass::kPrivate); auto* idx = Var("idx", ty.u32(), Expr(3_u)); auto* idy = Var("idy", ty.u32(), Expr(2_u)); auto* acc = IndexAccessor(IndexAccessor("my_var", idx), idy); WrapInFunction(Decl(idx), Decl(idy), acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic) { GlobalConst("my_const", ty.mat2x3(), Construct(ty.mat2x3())); auto* idx = Var("idx", ty.i32(), Construct(ty.i32())); auto* acc = IndexAccessor("my_const", Expr(Source{{12, 34}}, idx)); WrapInFunction(Decl(idx), acc); EXPECT_TRUE(r()->Resolve()); EXPECT_EQ(r()->error(), ""); } TEST_F(ResolverIndexAccessorTest, Matrix_XDimension_Dynamic) { GlobalConst("my_var", ty.mat4x4(), Construct(ty.mat4x4())); auto* idx = Var("idx", ty.u32(), Expr(3_u)); auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)); WrapInFunction(Decl(idx), acc); EXPECT_TRUE(r()->Resolve()); EXPECT_EQ(r()->error(), ""); } TEST_F(ResolverIndexAccessorTest, Matrix_BothDimension_Dynamic) { GlobalConst("my_var", ty.mat4x4(), Construct(ty.mat4x4())); auto* idx = Var("idy", ty.u32(), Expr(2_u)); auto* acc = IndexAccessor(IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)), 1_i); WrapInFunction(Decl(idx), acc); EXPECT_TRUE(r()->Resolve()); EXPECT_EQ(r()->error(), ""); } TEST_F(ResolverIndexAccessorTest, Matrix) { Global("my_var", ty.mat2x3(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", 2_i); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); ASSERT_TRUE(TypeOf(acc)->Is()); auto* ref = TypeOf(acc)->As(); ASSERT_TRUE(ref->StoreType()->Is()); EXPECT_EQ(ref->StoreType()->As()->Width(), 3u); } TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions) { Global("my_var", ty.mat2x3(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor(IndexAccessor("my_var", 2_i), 1_i); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); ASSERT_TRUE(TypeOf(acc)->Is()); auto* ref = TypeOf(acc)->As(); EXPECT_TRUE(ref->StoreType()->Is()); } TEST_F(ResolverIndexAccessorTest, Vector_F32) { Global("my_var", ty.vec3(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 2_f)); WrapInFunction(acc); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'"); } TEST_F(ResolverIndexAccessorTest, Vector_Dynamic_Ref) { Global("my_var", ty.vec3(), ast::StorageClass::kPrivate); auto* idx = Var("idx", ty.i32(), Expr(2_i)); auto* acc = IndexAccessor("my_var", idx); WrapInFunction(Decl(idx), acc); EXPECT_TRUE(r()->Resolve()); } TEST_F(ResolverIndexAccessorTest, Vector_Dynamic) { GlobalConst("my_var", ty.vec3(), Construct(ty.vec3())); auto* idx = Var("idx", ty.i32(), Expr(2_i)); auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)); WrapInFunction(Decl(idx), acc); EXPECT_TRUE(r()->Resolve()); } TEST_F(ResolverIndexAccessorTest, Vector) { Global("my_var", ty.vec3(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", 2_i); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); ASSERT_TRUE(TypeOf(acc)->Is()); auto* ref = TypeOf(acc)->As(); EXPECT_TRUE(ref->StoreType()->Is()); } TEST_F(ResolverIndexAccessorTest, Array_Literal_i32) { Global("my_var", ty.array(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", 2_i); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); auto* ref = TypeOf(acc)->As(); ASSERT_NE(ref, nullptr); EXPECT_TRUE(ref->StoreType()->Is()); } TEST_F(ResolverIndexAccessorTest, Array_Literal_u32) { Global("my_var", ty.array(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", 2_u); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); auto* ref = TypeOf(acc)->As(); ASSERT_NE(ref, nullptr); EXPECT_TRUE(ref->StoreType()->Is()); } TEST_F(ResolverIndexAccessorTest, Array_Literal_AInt) { Global("my_var", ty.array(), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", 2_a); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); auto* ref = TypeOf(acc)->As(); ASSERT_NE(ref, nullptr); EXPECT_TRUE(ref->StoreType()->Is()); } TEST_F(ResolverIndexAccessorTest, Alias_Array) { auto* aary = Alias("myarrty", ty.array()); Global("my_var", ty.Of(aary), ast::StorageClass::kPrivate); auto* acc = IndexAccessor("my_var", 2_i); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); ASSERT_TRUE(TypeOf(acc)->Is()); auto* ref = TypeOf(acc)->As(); EXPECT_TRUE(ref->StoreType()->Is()); } TEST_F(ResolverIndexAccessorTest, Array_Constant) { GlobalConst("my_var", ty.array(), array()); auto* acc = IndexAccessor("my_var", 2_i); WrapInFunction(acc); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(acc), nullptr); EXPECT_TRUE(TypeOf(acc)->Is()); } TEST_F(ResolverIndexAccessorTest, Array_Dynamic_I32) { // let a : array = 0; // var idx : i32 = 0; // var f : f32 = a[idx]; auto* a = Let("a", ty.array(), array()); auto* idx = Var("idx", ty.i32(), Construct(ty.i32())); auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx))); Func("my_func", {}, ty.void_(), { Decl(a), Decl(idx), Decl(f), }); EXPECT_TRUE(r()->Resolve()); EXPECT_EQ(r()->error(), ""); } TEST_F(ResolverIndexAccessorTest, Array_Literal_F32) { // let a : array; // var f : f32 = a[2.0f]; auto* a = Let("a", ty.array(), array()); auto* f = Var("a_2", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, 2_f))); Func("my_func", {}, ty.void_(), { Decl(a), Decl(f), }); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'"); } TEST_F(ResolverIndexAccessorTest, Array_Literal_I32) { // let a : array; // var f : f32 = a[2i]; auto* a = Let("a", ty.array(), array()); auto* f = Var("a_2", ty.f32(), IndexAccessor("a", 2_i)); Func("my_func", {}, ty.void_(), { Decl(a), Decl(f), }); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverIndexAccessorTest, Expr_Deref_FuncGoodParent) { // fn func(p: ptr>) -> f32 { // let idx: u32 = u32(); // let x: f32 = (*p)[idx]; // return x; // } auto* p = Param("p", ty.pointer(ty.vec4(), ast::StorageClass::kFunction)); auto* idx = Let("idx", ty.u32(), Construct(ty.u32())); auto* star_p = Deref(p); auto* accessor_expr = IndexAccessor(Source{{12, 34}}, star_p, idx); auto* x = Var("x", ty.f32(), accessor_expr); Func("func", {p}, ty.f32(), {Decl(idx), Decl(x), Return(x)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverIndexAccessorTest, Expr_Deref_FuncBadParent) { // fn func(p: ptr>) -> f32 { // let idx: u32 = u32(); // let x: f32 = *p[idx]; // return x; // } auto* p = Param("p", ty.pointer(ty.vec4(), ast::StorageClass::kFunction)); auto* idx = Let("idx", ty.u32(), Construct(ty.u32())); auto* accessor_expr = IndexAccessor(Source{{12, 34}}, p, idx); auto* star_p = Deref(accessor_expr); auto* x = Var("x", ty.f32(), star_p); Func("func", {p}, ty.f32(), {Decl(idx), Decl(x), Return(x)}); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: cannot index type 'ptr, read_write>'"); } TEST_F(ResolverIndexAccessorTest, Exr_Deref_BadParent) { // var param: vec4 // let x: f32 = *(¶m)[0]; auto* param = Var("param", ty.vec4()); auto* idx = Var("idx", ty.u32(), Construct(ty.u32())); auto* addressOf_expr = AddressOf(param); auto* accessor_expr = IndexAccessor(Source{{12, 34}}, addressOf_expr, idx); auto* star_p = Deref(accessor_expr); auto* x = Var("x", ty.f32(), star_p); WrapInFunction(param, idx, x); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: cannot index type 'ptr, read_write>'"); } } // namespace } // namespace tint::resolver