Consistent formatting for Dawn/Tint.
This CL updates the clang format files to have a single shared format
between Dawn and Tint. The major changes are tabs are 4 spaces, lines
are 100 columns and namespaces are not indented.
Bug: dawn:1339
Change-Id: I4208742c95643998d9fd14e77a9cc558071ded39
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/87603
Commit-Queue: Dan Sinclair <dsinclair@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/src/tint/resolver/array_accessor_test.cc b/src/tint/resolver/array_accessor_test.cc
index 87e28c82..d227ce4 100644
--- a/src/tint/resolver/array_accessor_test.cc
+++ b/src/tint/resolver/array_accessor_test.cc
@@ -24,286 +24,277 @@
using ResolverIndexAccessorTest = ResolverTest;
TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic_F32) {
- Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 1.0f));
- WrapInFunction(acc);
+ Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+ auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 1.0f));
+ WrapInFunction(acc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
+ 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<f32>(), ast::StorageClass::kPrivate);
- auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
- auto* acc = IndexAccessor("my_var", idx);
- WrapInFunction(Decl(idx), acc);
+ Global("my_var", ty.mat2x3<f32>(), 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();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions_Dynamic_Ref) {
- Global("my_var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
- auto* idx = Var("idx", ty.u32(), Expr(3u));
- auto* idy = Var("idy", ty.u32(), Expr(2u));
- auto* acc = IndexAccessor(IndexAccessor("my_var", idx), idy);
- WrapInFunction(Decl(idx), Decl(idy), acc);
+ Global("my_var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
+ auto* idx = Var("idx", ty.u32(), Expr(3u));
+ auto* idy = Var("idy", ty.u32(), Expr(2u));
+ auto* acc = IndexAccessor(IndexAccessor("my_var", idx), idy);
+ WrapInFunction(Decl(idx), Decl(idy), acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIndexAccessorTest, Matrix_Dynamic) {
- GlobalConst("my_const", ty.mat2x3<f32>(), Construct(ty.mat2x3<f32>()));
- auto* idx = Var("idx", ty.i32(), Construct(ty.i32()));
- auto* acc = IndexAccessor("my_const", Expr(Source{{12, 34}}, idx));
- WrapInFunction(Decl(idx), acc);
+ GlobalConst("my_const", ty.mat2x3<f32>(), Construct(ty.mat2x3<f32>()));
+ 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(), "");
+ EXPECT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "");
}
TEST_F(ResolverIndexAccessorTest, Matrix_XDimension_Dynamic) {
- GlobalConst("my_var", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
- auto* idx = Var("idx", ty.u32(), Expr(3u));
- auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
- WrapInFunction(Decl(idx), acc);
+ GlobalConst("my_var", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
+ auto* idx = Var("idx", ty.u32(), Expr(3u));
+ auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
+ WrapInFunction(Decl(idx), acc);
- EXPECT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "");
+ EXPECT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "");
}
TEST_F(ResolverIndexAccessorTest, Matrix_BothDimension_Dynamic) {
- GlobalConst("my_var", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
- auto* idx = Var("idy", ty.u32(), Expr(2u));
- auto* acc =
- IndexAccessor(IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)), 1);
- WrapInFunction(Decl(idx), acc);
+ GlobalConst("my_var", ty.mat4x4<f32>(), Construct(ty.mat4x4<f32>()));
+ auto* idx = Var("idy", ty.u32(), Expr(2u));
+ auto* acc = IndexAccessor(IndexAccessor("my_var", Expr(Source{{12, 34}}, idx)), 1);
+ WrapInFunction(Decl(idx), acc);
- EXPECT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "");
+ EXPECT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "");
}
TEST_F(ResolverIndexAccessorTest, Matrix) {
- Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor("my_var", 2);
- WrapInFunction(acc);
+ auto* acc = IndexAccessor("my_var", 2);
+ WrapInFunction(acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(acc), nullptr);
- ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(acc), nullptr);
+ ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
- auto* ref = TypeOf(acc)->As<sem::Reference>();
- ASSERT_TRUE(ref->StoreType()->Is<sem::Vector>());
- EXPECT_EQ(ref->StoreType()->As<sem::Vector>()->Width(), 3u);
+ auto* ref = TypeOf(acc)->As<sem::Reference>();
+ ASSERT_TRUE(ref->StoreType()->Is<sem::Vector>());
+ EXPECT_EQ(ref->StoreType()->As<sem::Vector>()->Width(), 3u);
}
TEST_F(ResolverIndexAccessorTest, Matrix_BothDimensions) {
- Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor(IndexAccessor("my_var", 2), 1);
- WrapInFunction(acc);
+ auto* acc = IndexAccessor(IndexAccessor("my_var", 2), 1);
+ WrapInFunction(acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(acc), nullptr);
- ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(acc), nullptr);
+ ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
- auto* ref = TypeOf(acc)->As<sem::Reference>();
- EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+ auto* ref = TypeOf(acc)->As<sem::Reference>();
+ EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
}
TEST_F(ResolverIndexAccessorTest, Vector_F32) {
- Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 2.0f));
- WrapInFunction(acc);
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, 2.0f));
+ WrapInFunction(acc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
+ 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<f32>(), ast::StorageClass::kPrivate);
- auto* idx = Var("idx", ty.i32(), Expr(2));
- auto* acc = IndexAccessor("my_var", idx);
- WrapInFunction(Decl(idx), acc);
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ auto* idx = Var("idx", ty.i32(), Expr(2));
+ auto* acc = IndexAccessor("my_var", idx);
+ WrapInFunction(Decl(idx), acc);
- EXPECT_TRUE(r()->Resolve());
+ EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverIndexAccessorTest, Vector_Dynamic) {
- GlobalConst("my_var", ty.vec3<f32>(), Construct(ty.vec3<f32>()));
- auto* idx = Var("idx", ty.i32(), Expr(2));
- auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
- WrapInFunction(Decl(idx), acc);
+ GlobalConst("my_var", ty.vec3<f32>(), Construct(ty.vec3<f32>()));
+ auto* idx = Var("idx", ty.i32(), Expr(2));
+ auto* acc = IndexAccessor("my_var", Expr(Source{{12, 34}}, idx));
+ WrapInFunction(Decl(idx), acc);
- EXPECT_TRUE(r()->Resolve());
+ EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverIndexAccessorTest, Vector) {
- Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor("my_var", 2);
- WrapInFunction(acc);
+ auto* acc = IndexAccessor("my_var", 2);
+ WrapInFunction(acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(acc), nullptr);
- ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(acc), nullptr);
+ ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
- auto* ref = TypeOf(acc)->As<sem::Reference>();
- EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+ auto* ref = TypeOf(acc)->As<sem::Reference>();
+ EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
}
TEST_F(ResolverIndexAccessorTest, Array) {
- auto* idx = Expr(2);
- Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
+ auto* idx = Expr(2);
+ Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor("my_var", idx);
- WrapInFunction(acc);
+ auto* acc = IndexAccessor("my_var", idx);
+ WrapInFunction(acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(acc), nullptr);
- ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(acc), nullptr);
+ ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
- auto* ref = TypeOf(acc)->As<sem::Reference>();
- EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+ auto* ref = TypeOf(acc)->As<sem::Reference>();
+ EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
}
TEST_F(ResolverIndexAccessorTest, Alias_Array) {
- auto* aary = Alias("myarrty", ty.array<f32, 3>());
+ auto* aary = Alias("myarrty", ty.array<f32, 3>());
- Global("my_var", ty.Of(aary), ast::StorageClass::kPrivate);
+ Global("my_var", ty.Of(aary), ast::StorageClass::kPrivate);
- auto* acc = IndexAccessor("my_var", 2);
- WrapInFunction(acc);
+ auto* acc = IndexAccessor("my_var", 2);
+ WrapInFunction(acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(acc), nullptr);
- ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(acc), nullptr);
+ ASSERT_TRUE(TypeOf(acc)->Is<sem::Reference>());
- auto* ref = TypeOf(acc)->As<sem::Reference>();
- EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+ auto* ref = TypeOf(acc)->As<sem::Reference>();
+ EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
}
TEST_F(ResolverIndexAccessorTest, Array_Constant) {
- GlobalConst("my_var", ty.array<f32, 3>(), array<f32, 3>());
+ GlobalConst("my_var", ty.array<f32, 3>(), array<f32, 3>());
- auto* acc = IndexAccessor("my_var", 2);
- WrapInFunction(acc);
+ auto* acc = IndexAccessor("my_var", 2);
+ WrapInFunction(acc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(acc), nullptr);
- EXPECT_TRUE(TypeOf(acc)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(acc), nullptr);
+ EXPECT_TRUE(TypeOf(acc)->Is<sem::F32>());
}
TEST_F(ResolverIndexAccessorTest, Array_Dynamic_I32) {
- // let a : array<f32, 3> = 0;
- // var idx : i32 = 0;
- // var f : f32 = a[idx];
- auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
- 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", ast::VariableList{}, ty.void_(),
- {
- Decl(a),
- Decl(idx),
- Decl(f),
- },
- ast::AttributeList{});
+ // let a : array<f32, 3> = 0;
+ // var idx : i32 = 0;
+ // var f : f32 = a[idx];
+ auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
+ 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", ast::VariableList{}, ty.void_(),
+ {
+ Decl(a),
+ Decl(idx),
+ Decl(f),
+ },
+ ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "");
+ EXPECT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "");
}
TEST_F(ResolverIndexAccessorTest, Array_Literal_F32) {
- // let a : array<f32, 3>;
- // var f : f32 = a[2.0f];
- auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
- auto* f =
- Var("a_2", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, 2.0f)));
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(a),
- Decl(f),
- },
- ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
+ // let a : array<f32, 3>;
+ // var f : f32 = a[2.0f];
+ auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
+ auto* f = Var("a_2", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, 2.0f)));
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(a),
+ Decl(f),
+ },
+ ast::AttributeList{});
+ 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<f32, 3>;
- // var f : f32 = a[2];
- auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
- auto* f = Var("a_2", ty.f32(), IndexAccessor("a", 2));
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(a),
- Decl(f),
- },
- ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // let a : array<f32, 3>;
+ // var f : f32 = a[2];
+ auto* a = Let("a", ty.array<f32, 3>(), array<f32, 3>());
+ auto* f = Var("a_2", ty.f32(), IndexAccessor("a", 2));
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(a),
+ Decl(f),
+ },
+ ast::AttributeList{});
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIndexAccessorTest, EXpr_Deref_FuncGoodParent) {
- // fn func(p: ptr<function, vec4<f32>>) -> f32 {
- // let idx: u32 = u32();
- // let x: f32 = (*p)[idx];
- // return x;
- // }
- auto* p =
- Param("p", ty.pointer(ty.vec4<f32>(), 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)});
+ // fn func(p: ptr<function, vec4<f32>>) -> f32 {
+ // let idx: u32 = u32();
+ // let x: f32 = (*p)[idx];
+ // return x;
+ // }
+ auto* p = Param("p", ty.pointer(ty.vec4<f32>(), 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();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIndexAccessorTest, EXpr_Deref_FuncBadParent) {
- // fn func(p: ptr<function, vec4<f32>>) -> f32 {
- // let idx: u32 = u32();
- // let x: f32 = *p[idx];
- // return x;
- // }
- auto* p =
- Param("p", ty.pointer(ty.vec4<f32>(), 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)});
+ // fn func(p: ptr<function, vec4<f32>>) -> f32 {
+ // let idx: u32 = u32();
+ // let x: f32 = *p[idx];
+ // return x;
+ // }
+ auto* p = Param("p", ty.pointer(ty.vec4<f32>(), 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<function, vec4<f32>, read_write>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot index type 'ptr<function, vec4<f32>, read_write>'");
}
TEST_F(ResolverIndexAccessorTest, Exr_Deref_BadParent) {
- // var param: vec4<f32>
- // let x: f32 = *(¶m)[0];
- auto* param = Var("param", ty.vec4<f32>());
- 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);
+ // var param: vec4<f32>
+ // let x: f32 = *(¶m)[0];
+ auto* param = Var("param", ty.vec4<f32>());
+ 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<function, vec4<f32>, read_write>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot index type 'ptr<function, vec4<f32>, read_write>'");
}
} // namespace
diff --git a/src/tint/resolver/assignment_validation_test.cc b/src/tint/resolver/assignment_validation_test.cc
index bfb4361..36ea1ed 100644
--- a/src/tint/resolver/assignment_validation_test.cc
+++ b/src/tint/resolver/assignment_validation_test.cc
@@ -24,367 +24,346 @@
using ResolverAssignmentValidationTest = ResolverTest;
TEST_F(ResolverAssignmentValidationTest, ReadOnlyBuffer) {
- // struct S { m : i32 };
- // @group(0) @binding(0)
- // var<storage,read> a : S;
- auto* s = Structure("S", {Member("m", ty.i32())});
- Global(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S { m : i32 };
+ // @group(0) @binding(0)
+ // var<storage,read> a : S;
+ auto* s = Structure("S", {Member("m", ty.i32())});
+ Global(Source{{12, 34}}, "a", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("a", "m"), 1));
+ WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("a", "m"), 1));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: cannot store into a read-only type 'ref<storage, "
- "i32, read>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: cannot store into a read-only type 'ref<storage, "
+ "i32, read>'");
}
TEST_F(ResolverAssignmentValidationTest, AssignIncompatibleTypes) {
- // {
- // var a : i32 = 2;
- // a = 2.3;
- // }
+ // {
+ // var a : i32 = 2;
+ // a = 2.3;
+ // }
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* assign = Assign(Source{{12, 34}}, "a", 2.3f);
- WrapInFunction(var, assign);
+ auto* assign = Assign(Source{{12, 34}}, "a", 2.3f);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignArraysWithDifferentSizeExpressions_Pass) {
- // let len = 4u;
- // {
- // var a : array<f32, 4>;
- // var b : array<f32, len>;
- // a = b;
- // }
+TEST_F(ResolverAssignmentValidationTest, AssignArraysWithDifferentSizeExpressions_Pass) {
+ // let len = 4u;
+ // {
+ // var a : array<f32, 4>;
+ // var b : array<f32, len>;
+ // a = b;
+ // }
- GlobalConst("len", nullptr, Expr(4u));
+ GlobalConst("len", nullptr, Expr(4u));
- auto* a = Var("a", ty.array(ty.f32(), 4));
- auto* b = Var("b", ty.array(ty.f32(), "len"));
+ auto* a = Var("a", ty.array(ty.f32(), 4));
+ auto* b = Var("b", ty.array(ty.f32(), "len"));
- auto* assign = Assign(Source{{12, 34}}, "a", "b");
- WrapInFunction(a, b, assign);
+ auto* assign = Assign(Source{{12, 34}}, "a", "b");
+ WrapInFunction(a, b, assign);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignArraysWithDifferentSizeExpressions_Fail) {
- // let len = 5u;
- // {
- // var a : array<f32, 4>;
- // var b : array<f32, len>;
- // a = b;
- // }
+TEST_F(ResolverAssignmentValidationTest, AssignArraysWithDifferentSizeExpressions_Fail) {
+ // let len = 5u;
+ // {
+ // var a : array<f32, 4>;
+ // var b : array<f32, len>;
+ // a = b;
+ // }
- GlobalConst("len", nullptr, Expr(5u));
+ GlobalConst("len", nullptr, Expr(5u));
- auto* a = Var("a", ty.array(ty.f32(), 4));
- auto* b = Var("b", ty.array(ty.f32(), "len"));
+ auto* a = Var("a", ty.array(ty.f32(), 4));
+ auto* b = Var("b", ty.array(ty.f32(), "len"));
- auto* assign = Assign(Source{{12, 34}}, "a", "b");
- WrapInFunction(a, b, assign);
+ auto* assign = Assign(Source{{12, 34}}, "a", "b");
+ WrapInFunction(a, b, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot assign 'array<f32, 5>' to 'array<f32, 4>'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'array<f32, 5>' to 'array<f32, 4>'");
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignCompatibleTypesInBlockStatement_Pass) {
- // {
- // var a : i32 = 2;
- // a = 2
- // }
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Assign("a", 2));
+TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesInBlockStatement_Pass) {
+ // {
+ // var a : i32 = 2;
+ // a = 2
+ // }
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Assign("a", 2));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignIncompatibleTypesInBlockStatement_Fail) {
- // {
- // var a : i32 = 2;
- // a = 2.3;
- // }
+TEST_F(ResolverAssignmentValidationTest, AssignIncompatibleTypesInBlockStatement_Fail) {
+ // {
+ // var a : i32 = 2;
+ // a = 2.3;
+ // }
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2.3f));
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2.3f));
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignIncompatibleTypesInNestedBlockStatement_Fail) {
- // {
- // {
- // var a : i32 = 2;
- // a = 2.3;
- // }
- // }
+TEST_F(ResolverAssignmentValidationTest, AssignIncompatibleTypesInNestedBlockStatement_Fail) {
+ // {
+ // {
+ // var a : i32 = 2;
+ // a = 2.3;
+ // }
+ // }
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* inner_block = Block(Decl(var), Assign(Source{{12, 34}}, "a", 2.3f));
- auto* outer_block = Block(inner_block);
- WrapInFunction(outer_block);
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* inner_block = Block(Decl(var), Assign(Source{{12, 34}}, "a", 2.3f));
+ auto* outer_block = Block(inner_block);
+ WrapInFunction(outer_block);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'f32' to 'i32'");
}
TEST_F(ResolverAssignmentValidationTest, AssignToScalar_Fail) {
- // var my_var : i32 = 2;
- // 1 = my_var;
+ // var my_var : i32 = 2;
+ // 1 = my_var;
- auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Assign(Expr(Source{{12, 34}}, 1), "my_var"));
+ auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Assign(Expr(Source{{12, 34}}, 1), "my_var"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot assign to value of type 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign to value of type 'i32'");
}
TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypes_Pass) {
- // var a : i32 = 2;
- // a = 2
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2));
+ // var a : i32 = 2;
+ // a = 2
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignCompatibleTypesThroughAlias_Pass) {
- // alias myint = i32;
- // var a : myint = 2;
- // a = 2
- auto* myint = Alias("myint", ty.i32());
- auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2));
+TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesThroughAlias_Pass) {
+ // alias myint = i32;
+ // var a : myint = 2;
+ // a = 2
+ auto* myint = Alias("myint", ty.i32());
+ auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Assign(Source{{12, 34}}, "a", 2));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignCompatibleTypesInferRHSLoad_Pass) {
- // var a : i32 = 2;
- // var b : i32 = 3;
- // a = b;
- auto* var_a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* var_b = Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3));
- WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, "a", "b"));
+TEST_F(ResolverAssignmentValidationTest, AssignCompatibleTypesInferRHSLoad_Pass) {
+ // var a : i32 = 2;
+ // var b : i32 = 3;
+ // a = b;
+ auto* var_a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* var_b = Var("b", ty.i32(), ast::StorageClass::kNone, Expr(3));
+ WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, "a", "b"));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAssignmentValidationTest, AssignThroughPointer_Pass) {
- // var a : i32;
- // let b : ptr<function,i32> = &a;
- // *b = 2;
- const auto func = ast::StorageClass::kFunction;
- auto* var_a = Var("a", ty.i32(), func, Expr(2));
- auto* var_b = Let("b", ty.pointer<int>(func), AddressOf(Expr("a")));
- WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, Deref("b"), 2));
+ // var a : i32;
+ // let b : ptr<function,i32> = &a;
+ // *b = 2;
+ const auto func = ast::StorageClass::kFunction;
+ auto* var_a = Var("a", ty.i32(), func, Expr(2));
+ auto* var_b = Let("b", ty.pointer<int>(func), AddressOf(Expr("a")));
+ WrapInFunction(var_a, var_b, Assign(Source{{12, 34}}, Deref("b"), 2));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAssignmentValidationTest, AssignToConstant_Fail) {
- // {
- // let a : i32 = 2;
- // a = 2
- // }
- auto* var = Let("a", ty.i32(), Expr(2));
- WrapInFunction(var, Assign(Expr(Source{{12, 34}}, "a"), 2));
+ // {
+ // let a : i32 = 2;
+ // a = 2
+ // }
+ auto* var = Let("a", ty.i32(), Expr(2));
+ WrapInFunction(var, Assign(Expr(Source{{12, 34}}, "a"), 2));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot assign to const\nnote: 'a' is declared here:");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign to const\nnote: 'a' is declared here:");
}
TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Handle) {
- // var a : texture_storage_1d<rgba8unorm, write>;
- // var b : texture_storage_1d<rgba8unorm, write>;
- // a = b;
+ // var a : texture_storage_1d<rgba8unorm, write>;
+ // var b : texture_storage_1d<rgba8unorm, write>;
+ // a = b;
- auto make_type = [&] {
- return ty.storage_texture(ast::TextureDimension::k1d,
- ast::TexelFormat::kRgba8Unorm,
- ast::Access::kWrite);
- };
+ auto make_type = [&] {
+ return ty.storage_texture(ast::TextureDimension::k1d, ast::TexelFormat::kRgba8Unorm,
+ ast::Access::kWrite);
+ };
- Global("a", make_type(), ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- Global("b", make_type(), ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(0),
- });
+ Global("a", make_type(), ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ Global("b", make_type(), ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(0),
+ });
- WrapInFunction(Assign(Source{{56, 78}}, "a", "b"));
+ WrapInFunction(Assign(Source{{56, 78}}, "a", "b"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: storage type of assignment must be constructible");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: storage type of assignment must be constructible");
}
TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_Atomic) {
- // struct S { a : atomic<i32>; };
- // @group(0) @binding(0) var<storage, read_write> v : S;
- // v.a = v.a;
+ // struct S { a : atomic<i32>; };
+ // @group(0) @binding(0) var<storage, read_write> v : S;
+ // v.a = v.a;
- auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))});
- Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* s = Structure("S", {Member("a", ty.atomic(ty.i32()))});
+ Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"),
- MemberAccessor("v", "a")));
+ WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"), MemberAccessor("v", "a")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: storage type of assignment must be constructible");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: storage type of assignment must be constructible");
}
TEST_F(ResolverAssignmentValidationTest, AssignNonConstructible_RuntimeArray) {
- // struct S { a : array<f32>; };
- // @group(0) @binding(0) var<storage, read_write> v : S;
- // v.a = v.a;
+ // struct S { a : array<f32>; };
+ // @group(0) @binding(0) var<storage, read_write> v : S;
+ // v.a = v.a;
- auto* s = Structure("S", {Member("a", ty.array(ty.f32()))});
- Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* s = Structure("S", {Member("a", ty.array(ty.f32()))});
+ Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"),
- MemberAccessor("v", "a")));
+ WrapInFunction(Assign(Source{{56, 78}}, MemberAccessor("v", "a"), MemberAccessor("v", "a")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: storage type of assignment must be constructible");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: storage type of assignment must be constructible");
}
-TEST_F(ResolverAssignmentValidationTest,
- AssignToPhony_NonConstructibleStruct_Fail) {
- // struct S {
- // arr: array<i32>;
- // };
- // @group(0) @binding(0) var<storage, read_write> s : S;
- // fn f() {
- // _ = s;
- // }
- auto* s = Structure("S", {Member("arr", ty.array<i32>())});
- Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
+TEST_F(ResolverAssignmentValidationTest, AssignToPhony_NonConstructibleStruct_Fail) {
+ // struct S {
+ // arr: array<i32>;
+ // };
+ // @group(0) @binding(0) var<storage, read_write> s : S;
+ // fn f() {
+ // _ = s;
+ // }
+ auto* s = Structure("S", {Member("arr", ty.array<i32>())});
+ Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
- WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
+ WrapInFunction(Assign(Phony(), Expr(Source{{12, 34}}, "s")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot assign 'S' to '_'. "
- "'_' can only be assigned a constructible, pointer, texture or "
- "sampler type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot assign 'S' to '_'. "
+ "'_' can only be assigned a constructible, pointer, texture or "
+ "sampler type");
}
TEST_F(ResolverAssignmentValidationTest, AssignToPhony_DynamicArray_Fail) {
- // struct S {
- // arr: array<i32>;
- // };
- // @group(0) @binding(0) var<storage, read_write> s : S;
- // fn f() {
- // _ = s.arr;
- // }
- auto* s = Structure("S", {Member("arr", ty.array<i32>())});
- Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
+ // struct S {
+ // arr: array<i32>;
+ // };
+ // @group(0) @binding(0) var<storage, read_write> s : S;
+ // fn f() {
+ // _ = s.arr;
+ // }
+ auto* s = Structure("S", {Member("arr", ty.array<i32>())});
+ Global("s", ty.Of(s), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
- WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
+ WrapInFunction(Assign(Phony(), MemberAccessor(Source{{12, 34}}, "s", "arr")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: cannot assign 'array<i32>' to '_'. "
- "'_' can only be assigned a constructible, pointer, texture or sampler "
- "type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot assign 'array<i32>' to '_'. "
+ "'_' can only be assigned a constructible, pointer, texture or sampler "
+ "type");
}
TEST_F(ResolverAssignmentValidationTest, AssignToPhony_Pass) {
- // struct S {
- // i: i32;
- // arr: array<i32>;
- // };
- // struct U {
- // i: i32;
- // };
- // @group(0) @binding(0) var tex texture_2d;
- // @group(0) @binding(1) var smp sampler;
- // @group(0) @binding(2) var<uniform> u : U;
- // @group(0) @binding(3) var<storage, read_write> s : S;
- // var<workgroup> wg : array<f32, 10>
- // fn f() {
- // _ = 1;
- // _ = 2u;
- // _ = 3.0;
- // _ = vec2<bool>();
- // _ = tex;
- // _ = smp;
- // _ = &s;
- // _ = s.i;
- // _ = &s.arr;
- // _ = u;
- // _ = u.i;
- // _ = wg;
- // _ = wg[3];
- // }
- auto* S = Structure("S", {
- Member("i", ty.i32()),
- Member("arr", ty.array<i32>()),
- });
- auto* U = Structure("U", {Member("i", ty.i32())});
- Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(0, 0));
- Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
- Global("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
- Global("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
- Global("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
+ // struct S {
+ // i: i32;
+ // arr: array<i32>;
+ // };
+ // struct U {
+ // i: i32;
+ // };
+ // @group(0) @binding(0) var tex texture_2d;
+ // @group(0) @binding(1) var smp sampler;
+ // @group(0) @binding(2) var<uniform> u : U;
+ // @group(0) @binding(3) var<storage, read_write> s : S;
+ // var<workgroup> wg : array<f32, 10>
+ // fn f() {
+ // _ = 1;
+ // _ = 2u;
+ // _ = 3.0;
+ // _ = vec2<bool>();
+ // _ = tex;
+ // _ = smp;
+ // _ = &s;
+ // _ = s.i;
+ // _ = &s.arr;
+ // _ = u;
+ // _ = u.i;
+ // _ = wg;
+ // _ = wg[3];
+ // }
+ auto* S = Structure("S", {
+ Member("i", ty.i32()),
+ Member("arr", ty.array<i32>()),
+ });
+ auto* U = Structure("U", {Member("i", ty.i32())});
+ Global("tex", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(0, 0));
+ Global("smp", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 1));
+ Global("u", ty.Of(U), ast::StorageClass::kUniform, GroupAndBinding(0, 2));
+ Global("s", ty.Of(S), ast::StorageClass::kStorage, GroupAndBinding(0, 3));
+ Global("wg", ty.array<f32, 10>(), ast::StorageClass::kWorkgroup);
- WrapInFunction(Assign(Phony(), 1), //
- Assign(Phony(), 2), //
- Assign(Phony(), 3), //
- Assign(Phony(), vec2<bool>()), //
- Assign(Phony(), "tex"), //
- Assign(Phony(), "smp"), //
- Assign(Phony(), AddressOf("s")), //
- Assign(Phony(), MemberAccessor("s", "i")), //
- Assign(Phony(), AddressOf(MemberAccessor("s", "arr"))), //
- Assign(Phony(), "u"), //
- Assign(Phony(), MemberAccessor("u", "i")), //
- Assign(Phony(), "wg"), //
- Assign(Phony(), IndexAccessor("wg", 3)));
+ WrapInFunction(Assign(Phony(), 1), //
+ Assign(Phony(), 2), //
+ Assign(Phony(), 3), //
+ Assign(Phony(), vec2<bool>()), //
+ Assign(Phony(), "tex"), //
+ Assign(Phony(), "smp"), //
+ Assign(Phony(), AddressOf("s")), //
+ Assign(Phony(), MemberAccessor("s", "i")), //
+ Assign(Phony(), AddressOf(MemberAccessor("s", "arr"))), //
+ Assign(Phony(), "u"), //
+ Assign(Phony(), MemberAccessor("u", "i")), //
+ Assign(Phony(), "wg"), //
+ Assign(Phony(), IndexAccessor("wg", 3)));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/atomics_test.cc b/src/tint/resolver/atomics_test.cc
index 5c91066..c0fe5ce 100644
--- a/src/tint/resolver/atomics_test.cc
+++ b/src/tint/resolver/atomics_test.cc
@@ -22,49 +22,44 @@
namespace tint::resolver {
namespace {
-struct ResolverAtomicTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverAtomicTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverAtomicTest, GlobalWorkgroupI32) {
- auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.i32()),
- ast::StorageClass::kWorkgroup);
+ auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
- auto* atomic = TypeOf(g)->UnwrapRef()->As<sem::Atomic>();
- ASSERT_NE(atomic, nullptr);
- EXPECT_TRUE(atomic->Type()->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
+ auto* atomic = TypeOf(g)->UnwrapRef()->As<sem::Atomic>();
+ ASSERT_NE(atomic, nullptr);
+ EXPECT_TRUE(atomic->Type()->Is<sem::I32>());
}
TEST_F(ResolverAtomicTest, GlobalWorkgroupU32) {
- auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.u32()),
- ast::StorageClass::kWorkgroup);
+ auto* g = Global("a", ty.atomic(Source{{12, 34}}, ty.u32()), ast::StorageClass::kWorkgroup);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
- auto* atomic = TypeOf(g)->UnwrapRef()->As<sem::Atomic>();
- ASSERT_NE(atomic, nullptr);
- EXPECT_TRUE(atomic->Type()->Is<sem::U32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
+ auto* atomic = TypeOf(g)->UnwrapRef()->As<sem::Atomic>();
+ ASSERT_NE(atomic, nullptr);
+ EXPECT_TRUE(atomic->Type()->Is<sem::U32>());
}
TEST_F(ResolverAtomicTest, GlobalStorageStruct) {
- auto* s =
- Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
- auto* g = Global("g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ auto* g = Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
- auto* str = TypeOf(g)->UnwrapRef()->As<sem::Struct>();
- ASSERT_NE(str, nullptr);
- ASSERT_EQ(str->Members().size(), 1u);
- auto* atomic = str->Members()[0]->Type()->As<sem::Atomic>();
- ASSERT_NE(atomic, nullptr);
- ASSERT_TRUE(atomic->Type()->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(TypeOf(g)->Is<sem::Reference>());
+ auto* str = TypeOf(g)->UnwrapRef()->As<sem::Struct>();
+ ASSERT_NE(str, nullptr);
+ ASSERT_EQ(str->Members().size(), 1u);
+ auto* atomic = str->Members()[0]->Type()->As<sem::Atomic>();
+ ASSERT_NE(atomic, nullptr);
+ ASSERT_TRUE(atomic->Type()->Is<sem::I32>());
}
} // namespace
diff --git a/src/tint/resolver/atomics_validation_test.cc b/src/tint/resolver/atomics_validation_test.cc
index cb3dd96..de8d0ac 100644
--- a/src/tint/resolver/atomics_validation_test.cc
+++ b/src/tint/resolver/atomics_validation_test.cc
@@ -22,314 +22,292 @@
namespace tint::resolver {
namespace {
-struct ResolverAtomicValidationTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverAtomicValidationTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverAtomicValidationTest, StorageClass_WorkGroup) {
- Global("a", ty.atomic(Source{{12, 34}}, ty.i32()),
- ast::StorageClass::kWorkgroup);
+ Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kWorkgroup);
- EXPECT_TRUE(r()->Resolve());
+ EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverAtomicValidationTest, StorageClass_Storage) {
- Global("g", ty.atomic(Source{{12, 34}}, ty.i32()),
- ast::StorageClass::kStorage, ast::Access::kReadWrite,
- GroupAndBinding(0, 0));
+ Global("g", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kStorage,
+ ast::Access::kReadWrite, GroupAndBinding(0, 0));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAtomicValidationTest, StorageClass_Storage_Struct) {
- auto* s =
- Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
- Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
- GroupAndBinding(0, 0));
+ auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ Global("g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ GroupAndBinding(0, 0));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverAtomicValidationTest, InvalidType) {
- Global("a", ty.atomic(ty.f32(Source{{12, 34}})),
- ast::StorageClass::kWorkgroup);
+ Global("a", ty.atomic(ty.f32(Source{{12, 34}})), ast::StorageClass::kWorkgroup);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: atomic only supports i32 or u32 types");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Simple) {
- Global("a", ty.atomic(Source{{12, 34}}, ty.i32()),
- ast::StorageClass::kPrivate);
+ Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: atomic variables must have <storage> or <workgroup> "
- "storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: atomic variables must have <storage> or <workgroup> "
+ "storage class");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Array) {
- Global("a", ty.atomic(Source{{12, 34}}, ty.i32()),
- ast::StorageClass::kPrivate);
+ Global("a", ty.atomic(Source{{12, 34}}, ty.i32()), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: atomic variables must have <storage> or <workgroup> "
- "storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: atomic variables must have <storage> or <workgroup> "
+ "storage class");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Struct) {
- auto* s =
- Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
- Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+ auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ Global("g", ty.Of(s), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class\n"
- "note: atomic sub-type of 's' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class\n"
+ "note: atomic sub-type of 's' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_StructOfStruct) {
- // struct Inner { m : atomic<i32>; };
- // struct Outer { m : array<Inner, 4>; };
- // var<private> g : Outer;
+ // struct Inner { m : atomic<i32>; };
+ // struct Outer { m : array<Inner, 4>; };
+ // var<private> g : Outer;
- auto* Inner =
- Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
- auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
- Global("g", ty.Of(Outer), ast::StorageClass::kPrivate);
+ auto* Inner = Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+ Global("g", ty.Of(Outer), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class\n"
- "note: atomic sub-type of 'Outer' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class\n"
+ "note: atomic sub-type of 'Outer' is declared here");
}
-TEST_F(ResolverAtomicValidationTest,
- InvalidStorageClass_StructOfStructOfArray) {
- // struct Inner { m : array<atomic<i32>, 4>; };
- // struct Outer { m : array<Inner, 4>; };
- // var<private> g : Outer;
+TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_StructOfStructOfArray) {
+ // struct Inner { m : array<atomic<i32>, 4>; };
+ // struct Outer { m : array<Inner, 4>; };
+ // var<private> g : Outer;
- auto* Inner =
- Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
- auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
- Global("g", ty.Of(Outer), ast::StorageClass::kPrivate);
+ auto* Inner = Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
+ auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+ Global("g", ty.Of(Outer), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class\n"
- "12:34 note: atomic sub-type of 'Outer' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class\n"
+ "12:34 note: atomic sub-type of 'Outer' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfArray) {
- // type AtomicArray = array<atomic<i32>, 5>;
- // var<private> v: array<s, 5>;
+ // type AtomicArray = array<atomic<i32>, 5>;
+ // var<private> v: array<s, 5>;
- auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray",
- ty.atomic(Source{{12, 34}}, ty.i32()));
- Global(Source{{56, 78}}, "v", ty.Of(atomic_array),
- ast::StorageClass::kPrivate);
+ auto* atomic_array =
+ Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
+ Global(Source{{56, 78}}, "v", ty.Of(atomic_array), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfStruct) {
- // struct S{
- // m: atomic<u32>;
- // };
- // var<private> v: array<S, 5>;
+ // struct S{
+ // m: atomic<u32>;
+ // };
+ // var<private> v: array<S, 5>;
- auto* s = Structure("S", {Member("m", ty.atomic<u32>())});
- Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5),
- ast::StorageClass::kPrivate);
+ auto* s = Structure("S", {Member("m", ty.atomic<u32>())});
+ Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class\n"
- "note: atomic sub-type of 'array<S, 5>' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class\n"
+ "note: atomic sub-type of 'array<S, 5>' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_ArrayOfStructOfArray) {
- // type AtomicArray = array<atomic<i32>, 5>;
- // struct S{
- // m: AtomicArray;
- // };
- // var<private> v: array<S, 5>;
+ // type AtomicArray = array<atomic<i32>, 5>;
+ // struct S{
+ // m: AtomicArray;
+ // };
+ // var<private> v: array<S, 5>;
- auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray",
- ty.atomic(Source{{12, 34}}, ty.i32()));
- auto* s = Structure("S", {Member("m", ty.Of(atomic_array))});
- Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5),
- ast::StorageClass::kPrivate);
+ auto* atomic_array =
+ Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
+ auto* s = Structure("S", {Member("m", ty.Of(atomic_array))});
+ Global(Source{{56, 78}}, "v", ty.array(ty.Of(s), 5), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class\n"
- "note: atomic sub-type of 'array<S, 5>' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class\n"
+ "note: atomic sub-type of 'array<S, 5>' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidStorageClass_Complex) {
- // type AtomicArray = array<atomic<i32>, 5>;
- // struct S6 { x: array<i32, 4>; };
- // struct S5 { x: S6;
- // y: AtomicArray;
- // z: array<atomic<u32>, 8>; };
- // struct S4 { x: S6;
- // y: S5;
- // z: array<atomic<i32>, 4>; };
- // struct S3 { x: S4; };
- // struct S2 { x: S3; };
- // struct S1 { x: S2; };
- // struct S0 { x: S1; };
- // var<private> g : S0;
+ // type AtomicArray = array<atomic<i32>, 5>;
+ // struct S6 { x: array<i32, 4>; };
+ // struct S5 { x: S6;
+ // y: AtomicArray;
+ // z: array<atomic<u32>, 8>; };
+ // struct S4 { x: S6;
+ // y: S5;
+ // z: array<atomic<i32>, 4>; };
+ // struct S3 { x: S4; };
+ // struct S2 { x: S3; };
+ // struct S1 { x: S2; };
+ // struct S0 { x: S1; };
+ // var<private> g : S0;
- auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray",
- ty.atomic(Source{{12, 34}}, ty.i32()));
- auto* array_i32_4 = ty.array(ty.i32(), 4);
- auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8);
- auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4);
+ auto* atomic_array =
+ Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
+ auto* array_i32_4 = ty.array(ty.i32(), 4);
+ auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8);
+ auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4);
- auto* s6 = Structure("S6", {Member("x", array_i32_4)});
- auto* s5 = Structure("S5", {Member("x", ty.Of(s6)), //
- Member("y", ty.Of(atomic_array)), //
- Member("z", array_atomic_u32_8)}); //
- auto* s4 = Structure("S4", {Member("x", ty.Of(s6)), //
- Member("y", ty.Of(s5)), //
- Member("z", array_atomic_i32_4)}); //
- auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
- auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
- auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
- auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
- Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate);
+ auto* s6 = Structure("S6", {Member("x", array_i32_4)});
+ auto* s5 = Structure("S5", {Member("x", ty.Of(s6)), //
+ Member("y", ty.Of(atomic_array)), //
+ Member("z", array_atomic_u32_8)}); //
+ auto* s4 = Structure("S4", {Member("x", ty.Of(s6)), //
+ Member("y", ty.Of(s5)), //
+ Member("z", array_atomic_i32_4)}); //
+ auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
+ auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
+ auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
+ auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
+ Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables must have <storage> or <workgroup> "
- "storage class\n"
- "note: atomic sub-type of 'S0' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables must have <storage> or <workgroup> "
+ "storage class\n"
+ "note: atomic sub-type of 'S0' is declared here");
}
TEST_F(ResolverAtomicValidationTest, Struct_AccessMode_Read) {
- auto* s =
- Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
+ auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "error: atomic variables in <storage> storage class must have read_write "
- "access mode\n"
- "note: atomic sub-type of 's' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables in <storage> storage class must have read_write "
+ "access mode\n"
+ "note: atomic sub-type of 's' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Struct) {
- auto* s =
- Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
+ auto* s = Structure("s", {Member("a", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "error: atomic variables in <storage> storage class must have read_write "
- "access mode\n"
- "note: atomic sub-type of 's' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables in <storage> storage class must have read_write "
+ "access mode\n"
+ "note: atomic sub-type of 's' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStruct) {
- // struct Inner { m : atomic<i32>; };
- // struct Outer { m : array<Inner, 4>; };
- // var<storage, read> g : Outer;
+ // struct Inner { m : atomic<i32>; };
+ // struct Outer { m : array<Inner, 4>; };
+ // var<storage, read> g : Outer;
- auto* Inner =
- Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
- auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
- Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
+ auto* Inner = Structure("Inner", {Member("m", ty.atomic(Source{{12, 34}}, ty.i32()))});
+ auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+ Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "error: atomic variables in <storage> storage class must have read_write "
- "access mode\n"
- "note: atomic sub-type of 'Outer' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables in <storage> storage class must have read_write "
+ "access mode\n"
+ "note: atomic sub-type of 'Outer' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_StructOfStructOfArray) {
- // struct Inner { m : array<atomic<i32>, 4>; };
- // struct Outer { m : array<Inner, 4>; };
- // var<storage, read> g : Outer;
+ // struct Inner { m : array<atomic<i32>, 4>; };
+ // struct Outer { m : array<Inner, 4>; };
+ // var<storage, read> g : Outer;
- auto* Inner =
- Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
- auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
- Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
+ auto* Inner = Structure("Inner", {Member(Source{{12, 34}}, "m", ty.atomic(ty.i32()))});
+ auto* Outer = Structure("Outer", {Member("m", ty.Of(Inner))});
+ Global(Source{{56, 78}}, "g", ty.Of(Outer), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> storage class must have "
- "read_write access mode\n"
- "12:34 note: atomic sub-type of 'Outer' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables in <storage> storage class must have "
+ "read_write access mode\n"
+ "12:34 note: atomic sub-type of 'Outer' is declared here");
}
TEST_F(ResolverAtomicValidationTest, InvalidAccessMode_Complex) {
- // type AtomicArray = array<atomic<i32>, 5>;
- // struct S6 { x: array<i32, 4>; };
- // struct S5 { x: S6;
- // y: AtomicArray;
- // z: array<atomic<u32>, 8>; };
- // struct S4 { x: S6;
- // y: S5;
- // z: array<atomic<i32>, 4>; };
- // struct S3 { x: S4; };
- // struct S2 { x: S3; };
- // struct S1 { x: S2; };
- // struct S0 { x: S1; };
- // var<storage, read> g : S0;
+ // type AtomicArray = array<atomic<i32>, 5>;
+ // struct S6 { x: array<i32, 4>; };
+ // struct S5 { x: S6;
+ // y: AtomicArray;
+ // z: array<atomic<u32>, 8>; };
+ // struct S4 { x: S6;
+ // y: S5;
+ // z: array<atomic<i32>, 4>; };
+ // struct S3 { x: S4; };
+ // struct S2 { x: S3; };
+ // struct S1 { x: S2; };
+ // struct S0 { x: S1; };
+ // var<storage, read> g : S0;
- auto* atomic_array = Alias(Source{{12, 34}}, "AtomicArray",
- ty.atomic(Source{{12, 34}}, ty.i32()));
- auto* array_i32_4 = ty.array(ty.i32(), 4);
- auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8);
- auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4);
+ auto* atomic_array =
+ Alias(Source{{12, 34}}, "AtomicArray", ty.atomic(Source{{12, 34}}, ty.i32()));
+ auto* array_i32_4 = ty.array(ty.i32(), 4);
+ auto* array_atomic_u32_8 = ty.array(ty.atomic(ty.u32()), 8);
+ auto* array_atomic_i32_4 = ty.array(ty.atomic(ty.i32()), 4);
- auto* s6 = Structure("S6", {Member("x", array_i32_4)});
- auto* s5 = Structure("S5", {Member("x", ty.Of(s6)), //
- Member("y", ty.Of(atomic_array)), //
- Member("z", array_atomic_u32_8)}); //
- auto* s4 = Structure("S4", {Member("x", ty.Of(s6)), //
- Member("y", ty.Of(s5)), //
- Member("z", array_atomic_i32_4)}); //
- auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
- auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
- auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
- auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
- Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
+ auto* s6 = Structure("S6", {Member("x", array_i32_4)});
+ auto* s5 = Structure("S5", {Member("x", ty.Of(s6)), //
+ Member("y", ty.Of(atomic_array)), //
+ Member("z", array_atomic_u32_8)}); //
+ auto* s4 = Structure("S4", {Member("x", ty.Of(s6)), //
+ Member("y", ty.Of(s5)), //
+ Member("z", array_atomic_i32_4)}); //
+ auto* s3 = Structure("S3", {Member("x", ty.Of(s4))});
+ auto* s2 = Structure("S2", {Member("x", ty.Of(s3))});
+ auto* s1 = Structure("S1", {Member("x", ty.Of(s2))});
+ auto* s0 = Structure("S0", {Member("x", ty.Of(s1))});
+ Global(Source{{56, 78}}, "g", ty.Of(s0), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: atomic variables in <storage> storage class must have "
- "read_write access mode\n"
- "note: atomic sub-type of 'S0' is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: atomic variables in <storage> storage class must have "
+ "read_write access mode\n"
+ "note: atomic sub-type of 'S0' is declared here");
}
TEST_F(ResolverAtomicValidationTest, Local) {
- WrapInFunction(Var("a", ty.atomic(Source{{12, 34}}, ty.i32())));
+ WrapInFunction(Var("a", ty.atomic(Source{{12, 34}}, ty.i32())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function variable must have a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function variable must have a constructible type");
}
} // namespace
diff --git a/src/tint/resolver/attribute_validation_test.cc b/src/tint/resolver/attribute_validation_test.cc
index 3b8781b..f9f5ea9 100644
--- a/src/tint/resolver/attribute_validation_test.cc
+++ b/src/tint/resolver/attribute_validation_test.cc
@@ -51,758 +51,717 @@
namespace AttributeTests {
namespace {
enum class AttributeKind {
- kAlign,
- kBinding,
- kBuiltin,
- kGroup,
- kId,
- kInterpolate,
- kInvariant,
- kLocation,
- kOffset,
- kSize,
- kStage,
- kStride,
- kWorkgroup,
+ kAlign,
+ kBinding,
+ kBuiltin,
+ kGroup,
+ kId,
+ kInterpolate,
+ kInvariant,
+ kLocation,
+ kOffset,
+ kSize,
+ kStage,
+ kStride,
+ kWorkgroup,
- kBindingAndGroup,
+ kBindingAndGroup,
};
static bool IsBindingAttribute(AttributeKind kind) {
- switch (kind) {
- case AttributeKind::kBinding:
- case AttributeKind::kGroup:
- case AttributeKind::kBindingAndGroup:
- return true;
- default:
- return false;
- }
+ switch (kind) {
+ case AttributeKind::kBinding:
+ case AttributeKind::kGroup:
+ case AttributeKind::kBindingAndGroup:
+ return true;
+ default:
+ return false;
+ }
}
struct TestParams {
- AttributeKind kind;
- bool should_pass;
+ AttributeKind kind;
+ bool should_pass;
};
struct TestWithParams : ResolverTestWithParam<TestParams> {};
static ast::AttributeList createAttributes(const Source& source,
ProgramBuilder& builder,
AttributeKind kind) {
- switch (kind) {
- case AttributeKind::kAlign:
- return {builder.create<ast::StructMemberAlignAttribute>(source, 4u)};
- case AttributeKind::kBinding:
- return {builder.create<ast::BindingAttribute>(source, 1u)};
- case AttributeKind::kBuiltin:
- return {builder.Builtin(source, ast::Builtin::kPosition)};
- case AttributeKind::kGroup:
- return {builder.create<ast::GroupAttribute>(source, 1u)};
- case AttributeKind::kId:
- return {builder.create<ast::IdAttribute>(source, 0u)};
- case AttributeKind::kInterpolate:
- return {builder.Interpolate(source, ast::InterpolationType::kLinear,
- ast::InterpolationSampling::kCenter)};
- case AttributeKind::kInvariant:
- return {builder.Invariant(source)};
- case AttributeKind::kLocation:
- return {builder.Location(source, 1)};
- case AttributeKind::kOffset:
- return {builder.create<ast::StructMemberOffsetAttribute>(source, 4u)};
- case AttributeKind::kSize:
- return {builder.create<ast::StructMemberSizeAttribute>(source, 16u)};
- case AttributeKind::kStage:
- return {builder.Stage(source, ast::PipelineStage::kCompute)};
- case AttributeKind::kStride:
- return {builder.create<ast::StrideAttribute>(source, 4u)};
- case AttributeKind::kWorkgroup:
- return {builder.create<ast::WorkgroupAttribute>(source, builder.Expr(1))};
- case AttributeKind::kBindingAndGroup:
- return {builder.create<ast::BindingAttribute>(source, 1u),
- builder.create<ast::GroupAttribute>(source, 1u)};
- }
- return {};
+ switch (kind) {
+ case AttributeKind::kAlign:
+ return {builder.create<ast::StructMemberAlignAttribute>(source, 4u)};
+ case AttributeKind::kBinding:
+ return {builder.create<ast::BindingAttribute>(source, 1u)};
+ case AttributeKind::kBuiltin:
+ return {builder.Builtin(source, ast::Builtin::kPosition)};
+ case AttributeKind::kGroup:
+ return {builder.create<ast::GroupAttribute>(source, 1u)};
+ case AttributeKind::kId:
+ return {builder.create<ast::IdAttribute>(source, 0u)};
+ case AttributeKind::kInterpolate:
+ return {builder.Interpolate(source, ast::InterpolationType::kLinear,
+ ast::InterpolationSampling::kCenter)};
+ case AttributeKind::kInvariant:
+ return {builder.Invariant(source)};
+ case AttributeKind::kLocation:
+ return {builder.Location(source, 1)};
+ case AttributeKind::kOffset:
+ return {builder.create<ast::StructMemberOffsetAttribute>(source, 4u)};
+ case AttributeKind::kSize:
+ return {builder.create<ast::StructMemberSizeAttribute>(source, 16u)};
+ case AttributeKind::kStage:
+ return {builder.Stage(source, ast::PipelineStage::kCompute)};
+ case AttributeKind::kStride:
+ return {builder.create<ast::StrideAttribute>(source, 4u)};
+ case AttributeKind::kWorkgroup:
+ return {builder.create<ast::WorkgroupAttribute>(source, builder.Expr(1))};
+ case AttributeKind::kBindingAndGroup:
+ return {builder.create<ast::BindingAttribute>(source, 1u),
+ builder.create<ast::GroupAttribute>(source, 1u)};
+ }
+ return {};
}
namespace FunctionInputAndOutputTests {
using FunctionParameterAttributeTest = TestWithParams;
TEST_P(FunctionParameterAttributeTest, IsValid) {
- auto& params = GetParam();
+ auto& params = GetParam();
- Func("main",
- ast::VariableList{Param("a", ty.vec4<f32>(),
- createAttributes({}, *this, params.kind))},
- ty.void_(), {});
+ Func("main",
+ ast::VariableList{Param("a", ty.vec4<f32>(), createAttributes({}, *this, params.kind))},
+ ty.void_(), {});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: attribute is not valid for non-entry point function "
- "parameters");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: attribute is not valid for non-entry point function "
+ "parameters");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- FunctionParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ FunctionParameterAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using FunctionReturnTypeAttributeTest = TestWithParams;
TEST_P(FunctionReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
+ auto& params = GetParam();
- Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
- {}, createAttributes({}, *this, params.kind));
+ Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)}, {},
+ createAttributes({}, *this, params.kind));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: attribute is not valid for non-entry point function "
- "return types");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: attribute is not valid for non-entry point function "
+ "return types");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- FunctionReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ FunctionReturnTypeAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
} // namespace FunctionInputAndOutputTests
namespace EntryPointInputAndOutputTests {
using ComputeShaderParameterAttributeTest = TestWithParams;
TEST_P(ComputeShaderParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- auto* p = Param("a", ty.vec4<f32>(),
- createAttributes(Source{{12, 34}}, *this, params.kind));
- Func("main", ast::VariableList{p}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
+ auto& params = GetParam();
+ auto* p = Param("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind));
+ Func("main", ast::VariableList{p}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin(position) cannot be used in input of "
- "compute pipeline stage");
- } else if (params.kind == AttributeKind::kInterpolate ||
- params.kind == AttributeKind::kLocation ||
- params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attribute is not valid for compute shader inputs");
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for function parameters");
+ EXPECT_FALSE(r()->Resolve());
+ if (params.kind == AttributeKind::kBuiltin) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(position) cannot be used in input of "
+ "compute pipeline stage");
+ } else if (params.kind == AttributeKind::kInterpolate ||
+ params.kind == AttributeKind::kLocation ||
+ params.kind == AttributeKind::kInvariant) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for compute shader inputs");
+ } else {
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for function parameters");
+ }
}
- }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- ComputeShaderParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ ComputeShaderParameterAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using FragmentShaderParameterAttributeTest = TestWithParams;
TEST_P(FragmentShaderParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- if (params.kind != AttributeKind::kBuiltin &&
- params.kind != AttributeKind::kLocation) {
- attrs.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
- }
- auto* p = Param("a", ty.vec4<f32>(), attrs);
- Func("frag_main", {p}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ auto& params = GetParam();
+ auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
+ if (params.kind != AttributeKind::kBuiltin && params.kind != AttributeKind::kLocation) {
+ attrs.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
+ }
+ auto* p = Param("a", ty.vec4<f32>(), attrs);
+ Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for function parameters");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for function parameters");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- FragmentShaderParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- // kInterpolate tested separately (requires @location)
- TestParams{AttributeKind::kInvariant, true},
- TestParams{AttributeKind::kLocation, true},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ FragmentShaderParameterAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, true},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ // kInterpolate tested separately (requires @location)
+ TestParams{AttributeKind::kInvariant, true},
+ TestParams{AttributeKind::kLocation, true},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using VertexShaderParameterAttributeTest = TestWithParams;
TEST_P(VertexShaderParameterAttributeTest, IsValid) {
- auto& params = GetParam();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- if (params.kind != AttributeKind::kLocation) {
- attrs.push_back(Location(Source{{34, 56}}, 2));
- }
- auto* p = Param("a", ty.vec4<f32>(), attrs);
- Func("vertex_main", ast::VariableList{p}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kVertex)},
- {Builtin(ast::Builtin::kPosition)});
-
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin(position) cannot be used in input of "
- "vertex pipeline stage");
- } else if (params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
- } else {
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for function parameters");
+ auto& params = GetParam();
+ auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
+ if (params.kind != AttributeKind::kLocation) {
+ attrs.push_back(Location(Source{{34, 56}}, 2));
}
- }
+ auto* p = Param("a", ty.vec4<f32>(), attrs);
+ Func("vertex_main", ast::VariableList{p}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::Builtin::kPosition)});
+
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ if (params.kind == AttributeKind::kBuiltin) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(position) cannot be used in input of "
+ "vertex pipeline stage");
+ } else if (params.kind == AttributeKind::kInvariant) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: invariant attribute must only be applied to a "
+ "position builtin");
+ } else {
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for function parameters");
+ }
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- VertexShaderParameterAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, true},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, true},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ VertexShaderParameterAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, true},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, true},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using ComputeShaderReturnTypeAttributeTest = TestWithParams;
TEST_P(ComputeShaderReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- Func("main", ast::VariableList{}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>(), 1.f))},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)},
- createAttributes(Source{{12, 34}}, *this, params.kind));
+ auto& params = GetParam();
+ Func("main", ast::VariableList{}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>(), 1.f))},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)},
+ createAttributes(Source{{12, 34}}, *this, params.kind));
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin(position) cannot be used in output of "
- "compute pipeline stage");
- } else if (params.kind == AttributeKind::kInterpolate ||
- params.kind == AttributeKind::kLocation ||
- params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attribute is not valid for compute shader output");
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for entry point return "
- "types");
+ EXPECT_FALSE(r()->Resolve());
+ if (params.kind == AttributeKind::kBuiltin) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(position) cannot be used in output of "
+ "compute pipeline stage");
+ } else if (params.kind == AttributeKind::kInterpolate ||
+ params.kind == AttributeKind::kLocation ||
+ params.kind == AttributeKind::kInvariant) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for compute shader output");
+ } else {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for entry point return "
+ "types");
+ }
}
- }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- ComputeShaderReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ ComputeShaderReturnTypeAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using FragmentShaderReturnTypeAttributeTest = TestWithParams;
TEST_P(FragmentShaderReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- attrs.push_back(Location(Source{{34, 56}}, 2));
- Func("frag_main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kFragment)}, attrs);
+ auto& params = GetParam();
+ auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
+ attrs.push_back(Location(Source{{34, 56}}, 2));
+ Func("frag_main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kFragment)}, attrs);
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kBuiltin) {
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin(position) cannot be used in output of "
- "fragment pipeline stage");
- } else if (params.kind == AttributeKind::kInvariant) {
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
- } else if (params.kind == AttributeKind::kLocation) {
- EXPECT_EQ(r()->error(),
- "34:56 error: duplicate location attribute\n"
- "12:34 note: first attribute declared here");
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for entry point return "
- "types");
+ EXPECT_FALSE(r()->Resolve());
+ if (params.kind == AttributeKind::kBuiltin) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(position) cannot be used in output of "
+ "fragment pipeline stage");
+ } else if (params.kind == AttributeKind::kInvariant) {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: invariant attribute must only be applied to a "
+ "position builtin");
+ } else if (params.kind == AttributeKind::kLocation) {
+ EXPECT_EQ(r()->error(),
+ "34:56 error: duplicate location attribute\n"
+ "12:34 note: first attribute declared here");
+ } else {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for entry point return "
+ "types");
+ }
}
- }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- FragmentShaderReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, true},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ FragmentShaderReturnTypeAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, true},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using VertexShaderReturnTypeAttributeTest = TestWithParams;
TEST_P(VertexShaderReturnTypeAttributeTest, IsValid) {
- auto& params = GetParam();
- auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
- // a vertex shader must include the 'position' builtin in its return type
- if (params.kind != AttributeKind::kBuiltin) {
- attrs.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
- }
- Func("vertex_main", ast::VariableList{}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kVertex)}, attrs);
-
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (params.kind == AttributeKind::kLocation) {
- EXPECT_EQ(r()->error(),
- "34:56 error: multiple entry point IO attributes\n"
- "12:34 note: previously consumed location(1)");
- } else {
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for entry point return "
- "types");
+ auto& params = GetParam();
+ auto attrs = createAttributes(Source{{12, 34}}, *this, params.kind);
+ // a vertex shader must include the 'position' builtin in its return type
+ if (params.kind != AttributeKind::kBuiltin) {
+ attrs.push_back(Builtin(Source{{34, 56}}, ast::Builtin::kPosition));
}
- }
+ Func("vertex_main", ast::VariableList{}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kVertex)}, attrs);
+
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ if (params.kind == AttributeKind::kLocation) {
+ EXPECT_EQ(r()->error(),
+ "34:56 error: multiple entry point IO attributes\n"
+ "12:34 note: previously consumed location(1)");
+ } else {
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for entry point return "
+ "types");
+ }
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- VertexShaderReturnTypeAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- // kInterpolate tested separately (requires @location)
- TestParams{AttributeKind::kInvariant, true},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ VertexShaderReturnTypeAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, true},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ // kInterpolate tested separately (requires @location)
+ TestParams{AttributeKind::kInvariant, true},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using EntryPointParameterAttributeTest = TestWithParams;
TEST_F(EntryPointParameterAttributeTest, DuplicateAttribute) {
- Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
- {Stage(ast::PipelineStage::kFragment)},
- {
- Location(Source{{12, 34}}, 2),
- Location(Source{{56, 78}}, 3),
- });
+ Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
+ {Stage(ast::PipelineStage::kFragment)},
+ {
+ Location(Source{{12, 34}}, 2),
+ Location(Source{{56, 78}}, 3),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate location attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate location attribute
12:34 note: first attribute declared here)");
}
TEST_F(EntryPointParameterAttributeTest, DuplicateInternalAttribute) {
- auto* s = Param("s", ty.sampler(ast::SamplerKind::kSampler),
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- Disable(ast::DisabledValidation::kBindingPointCollision),
- Disable(ast::DisabledValidation::kEntryPointParameter),
- });
- Func("f", {s}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
+ auto* s = Param("s", ty.sampler(ast::SamplerKind::kSampler),
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ Disable(ast::DisabledValidation::kBindingPointCollision),
+ Disable(ast::DisabledValidation::kEntryPointParameter),
+ });
+ Func("f", {s}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
using EntryPointReturnTypeAttributeTest = ResolverTest;
TEST_F(EntryPointReturnTypeAttributeTest, DuplicateAttribute) {
- Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
- ast::AttributeList{
- Location(Source{{12, 34}}, 2),
- Location(Source{{56, 78}}, 3),
- });
+ Func("main", ast::VariableList{}, ty.f32(), ast::StatementList{Return(1.f)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
+ ast::AttributeList{
+ Location(Source{{12, 34}}, 2),
+ Location(Source{{56, 78}}, 3),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate location attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate location attribute
12:34 note: first attribute declared here)");
}
TEST_F(EntryPointReturnTypeAttributeTest, DuplicateInternalAttribute) {
- Func("f", {}, ty.i32(), {Return(1)}, {Stage(ast::PipelineStage::kFragment)},
- ast::AttributeList{
- Disable(ast::DisabledValidation::kBindingPointCollision),
- Disable(ast::DisabledValidation::kEntryPointParameter),
- });
+ Func("f", {}, ty.i32(), {Return(1)}, {Stage(ast::PipelineStage::kFragment)},
+ ast::AttributeList{
+ Disable(ast::DisabledValidation::kBindingPointCollision),
+ Disable(ast::DisabledValidation::kEntryPointParameter),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace EntryPointInputAndOutputTests
namespace StructAndStructMemberTests {
using StructAttributeTest = TestWithParams;
-using SpirvBlockAttribute =
- transform::AddSpirvBlockAttribute::SpirvBlockAttribute;
+using SpirvBlockAttribute = transform::AddSpirvBlockAttribute::SpirvBlockAttribute;
TEST_P(StructAttributeTest, IsValid) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* str = create<ast::Struct>(
- Sym("mystruct"), ast::StructMemberList{Member("a", ty.f32())},
- createAttributes(Source{{12, 34}}, *this, params.kind));
- AST().AddGlobalDeclaration(str);
+ auto* str = create<ast::Struct>(Sym("mystruct"), ast::StructMemberList{Member("a", ty.f32())},
+ createAttributes(Source{{12, 34}}, *this, params.kind));
+ AST().AddGlobalDeclaration(str);
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for struct declarations");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for struct declarations");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- StructAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ StructAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using StructMemberAttributeTest = TestWithParams;
TEST_P(StructMemberAttributeTest, IsValid) {
- auto& params = GetParam();
- ast::StructMemberList members;
- if (params.kind == AttributeKind::kBuiltin) {
- members.push_back(
- {Member("a", ty.vec4<f32>(),
- createAttributes(Source{{12, 34}}, *this, params.kind))});
- } else {
- members.push_back(
- {Member("a", ty.f32(),
- createAttributes(Source{{12, 34}}, *this, params.kind))});
- }
- Structure("mystruct", members);
- WrapInFunction();
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
+ auto& params = GetParam();
+ ast::StructMemberList members;
+ if (params.kind == AttributeKind::kBuiltin) {
+ members.push_back(
+ {Member("a", ty.vec4<f32>(), createAttributes(Source{{12, 34}}, *this, params.kind))});
+ } else {
+ members.push_back(
+ {Member("a", ty.f32(), createAttributes(Source{{12, 34}}, *this, params.kind))});
+ }
+ Structure("mystruct", members);
+ WrapInFunction();
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for structure members");
+ }
+}
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ StructMemberAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, true},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, true},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ // kInterpolate tested separately (requires @location)
+ // kInvariant tested separately (requires position builtin)
+ TestParams{AttributeKind::kLocation, true},
+ TestParams{AttributeKind::kOffset, true},
+ TestParams{AttributeKind::kSize, true},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
+TEST_F(StructMemberAttributeTest, DuplicateAttribute) {
+ Structure("mystruct",
+ {
+ Member("a", ty.i32(),
+ {
+ create<ast::StructMemberAlignAttribute>(Source{{12, 34}}, 4u),
+ create<ast::StructMemberAlignAttribute>(Source{{56, 78}}, 8u),
+ }),
+ });
+ WrapInFunction();
EXPECT_FALSE(r()->Resolve());
EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for structure members");
- }
-}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- StructMemberAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, true},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, true},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- // kInterpolate tested separately (requires @location)
- // kInvariant tested separately (requires position builtin)
- TestParams{AttributeKind::kLocation, true},
- TestParams{AttributeKind::kOffset, true},
- TestParams{AttributeKind::kSize, true},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
-TEST_F(StructMemberAttributeTest, DuplicateAttribute) {
- Structure(
- "mystruct",
- {
- Member(
- "a", ty.i32(),
- {
- create<ast::StructMemberAlignAttribute>(Source{{12, 34}}, 4u),
- create<ast::StructMemberAlignAttribute>(Source{{56, 78}}, 8u),
- }),
- });
- WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate align attribute
+ R"(56:78 error: duplicate align attribute
12:34 note: first attribute declared here)");
}
TEST_F(StructMemberAttributeTest, InvariantAttributeWithPosition) {
- Structure("mystruct", {
- Member("a", ty.vec4<f32>(),
- {
- Invariant(),
- Builtin(ast::Builtin::kPosition),
- }),
- });
- WrapInFunction();
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ Structure("mystruct", {
+ Member("a", ty.vec4<f32>(),
+ {
+ Invariant(),
+ Builtin(ast::Builtin::kPosition),
+ }),
+ });
+ WrapInFunction();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(StructMemberAttributeTest, InvariantAttributeWithoutPosition) {
- Structure("mystruct", {
- Member("a", ty.vec4<f32>(),
- {
- Invariant(Source{{12, 34}}),
- }),
- });
- WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
+ Structure("mystruct", {
+ Member("a", ty.vec4<f32>(),
+ {
+ Invariant(Source{{12, 34}}),
+ }),
+ });
+ WrapInFunction();
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: invariant attribute must only be applied to a "
+ "position builtin");
}
} // namespace StructAndStructMemberTests
using ArrayAttributeTest = TestWithParams;
TEST_P(ArrayAttributeTest, IsValid) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* arr = ty.array(ty.f32(), nullptr,
- createAttributes(Source{{12, 34}}, *this, params.kind));
- Structure("mystruct", {
- Member("a", arr),
- });
+ auto* arr = ty.array(ty.f32(), nullptr, createAttributes(Source{{12, 34}}, *this, params.kind));
+ Structure("mystruct", {
+ Member("a", arr),
+ });
- WrapInFunction();
+ WrapInFunction();
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for array types");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for array types");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- ArrayAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, true},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ ArrayAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, true},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
using VariableAttributeTest = TestWithParams;
TEST_P(VariableAttributeTest, IsValid) {
- auto& params = GetParam();
+ auto& params = GetParam();
- if (IsBindingAttribute(params.kind)) {
- Global("a", ty.sampler(ast::SamplerKind::kSampler),
- ast::StorageClass::kNone, nullptr,
- createAttributes(Source{{12, 34}}, *this, params.kind));
- } else {
- Global("a", ty.f32(), ast::StorageClass::kPrivate, nullptr,
- createAttributes(Source{{12, 34}}, *this, params.kind));
- }
-
- WrapInFunction();
-
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- if (!IsBindingAttribute(params.kind)) {
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for variables");
+ if (IsBindingAttribute(params.kind)) {
+ Global("a", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone, nullptr,
+ createAttributes(Source{{12, 34}}, *this, params.kind));
+ } else {
+ Global("a", ty.f32(), ast::StorageClass::kPrivate, nullptr,
+ createAttributes(Source{{12, 34}}, *this, params.kind));
}
- }
+
+ WrapInFunction();
+
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ if (!IsBindingAttribute(params.kind)) {
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for variables");
+ }
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- VariableAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, false},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, true}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ VariableAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, false},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, true}));
TEST_F(VariableAttributeTest, DuplicateAttribute) {
- Global("a", ty.sampler(ast::SamplerKind::kSampler),
- ast::AttributeList{
- create<ast::BindingAttribute>(Source{{12, 34}}, 2),
- create<ast::GroupAttribute>(2),
- create<ast::BindingAttribute>(Source{{56, 78}}, 3),
- });
+ Global("a", ty.sampler(ast::SamplerKind::kSampler),
+ ast::AttributeList{
+ create<ast::BindingAttribute>(Source{{12, 34}}, 2),
+ create<ast::GroupAttribute>(2),
+ create<ast::BindingAttribute>(Source{{56, 78}}, 3),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate binding attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate binding attribute
12:34 note: first attribute declared here)");
}
TEST_F(VariableAttributeTest, LocalVariable) {
- auto* v = Var("a", ty.f32(),
- ast::AttributeList{
- create<ast::BindingAttribute>(Source{{12, 34}}, 2),
- });
+ auto* v = Var("a", ty.f32(),
+ ast::AttributeList{
+ create<ast::BindingAttribute>(Source{{12, 34}}, 2),
+ });
- WrapInFunction(v);
+ WrapInFunction(v);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attributes are not valid on local variables");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attributes are not valid on local variables");
}
using ConstantAttributeTest = TestWithParams;
TEST_P(ConstantAttributeTest, IsValid) {
- auto& params = GetParam();
+ auto& params = GetParam();
- GlobalConst("a", ty.f32(), Expr(1.23f),
- createAttributes(Source{{12, 34}}, *this, params.kind));
+ GlobalConst("a", ty.f32(), Expr(1.23f), createAttributes(Source{{12, 34}}, *this, params.kind));
- WrapInFunction();
+ WrapInFunction();
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for constants");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for constants");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- ConstantAttributeTest,
- testing::Values(TestParams{AttributeKind::kAlign, false},
- TestParams{AttributeKind::kBinding, false},
- TestParams{AttributeKind::kBuiltin, false},
- TestParams{AttributeKind::kGroup, false},
- TestParams{AttributeKind::kId, true},
- TestParams{AttributeKind::kInterpolate, false},
- TestParams{AttributeKind::kInvariant, false},
- TestParams{AttributeKind::kLocation, false},
- TestParams{AttributeKind::kOffset, false},
- TestParams{AttributeKind::kSize, false},
- TestParams{AttributeKind::kStage, false},
- TestParams{AttributeKind::kStride, false},
- TestParams{AttributeKind::kWorkgroup, false},
- TestParams{AttributeKind::kBindingAndGroup, false}));
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ ConstantAttributeTest,
+ testing::Values(TestParams{AttributeKind::kAlign, false},
+ TestParams{AttributeKind::kBinding, false},
+ TestParams{AttributeKind::kBuiltin, false},
+ TestParams{AttributeKind::kGroup, false},
+ TestParams{AttributeKind::kId, true},
+ TestParams{AttributeKind::kInterpolate, false},
+ TestParams{AttributeKind::kInvariant, false},
+ TestParams{AttributeKind::kLocation, false},
+ TestParams{AttributeKind::kOffset, false},
+ TestParams{AttributeKind::kSize, false},
+ TestParams{AttributeKind::kStage, false},
+ TestParams{AttributeKind::kStride, false},
+ TestParams{AttributeKind::kWorkgroup, false},
+ TestParams{AttributeKind::kBindingAndGroup, false}));
TEST_F(ConstantAttributeTest, DuplicateAttribute) {
- GlobalConst("a", ty.f32(), Expr(1.23f),
- ast::AttributeList{
- create<ast::IdAttribute>(Source{{12, 34}}, 0),
- create<ast::IdAttribute>(Source{{56, 78}}, 1),
- });
+ GlobalConst("a", ty.f32(), Expr(1.23f),
+ ast::AttributeList{
+ create<ast::IdAttribute>(Source{{12, 34}}, 0),
+ create<ast::IdAttribute>(Source{{56, 78}}, 1),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate id attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate id attribute
12:34 note: first attribute declared here)");
}
@@ -813,46 +772,46 @@
namespace {
struct Params {
- builder::ast_type_func_ptr create_el_type;
- uint32_t stride;
- bool should_pass;
+ builder::ast_type_func_ptr create_el_type;
+ uint32_t stride;
+ bool should_pass;
};
template <typename T>
constexpr Params ParamsFor(uint32_t stride, bool should_pass) {
- return Params{DataType<T>::AST, stride, should_pass};
+ return Params{DataType<T>::AST, stride, should_pass};
}
struct TestWithParams : ResolverTestWithParam<Params> {};
using ArrayStrideTest = TestWithParams;
TEST_P(ArrayStrideTest, All) {
- auto& params = GetParam();
- auto* el_ty = params.create_el_type(*this);
+ auto& params = GetParam();
+ auto* el_ty = params.create_el_type(*this);
- std::stringstream ss;
- ss << "el_ty: " << FriendlyName(el_ty) << ", stride: " << params.stride
- << ", should_pass: " << params.should_pass;
- SCOPED_TRACE(ss.str());
+ std::stringstream ss;
+ ss << "el_ty: " << FriendlyName(el_ty) << ", stride: " << params.stride
+ << ", should_pass: " << params.should_pass;
+ SCOPED_TRACE(ss.str());
- auto* arr = ty.array(Source{{12, 34}}, el_ty, 4, params.stride);
+ auto* arr = ty.array(Source{{12, 34}}, el_ty, 4, params.stride);
- Global("myarray", arr, ast::StorageClass::kPrivate);
+ Global("myarray", arr, ast::StorageClass::kPrivate);
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: arrays decorated with the stride attribute must "
- "have a stride that is at least the size of the element type, "
- "and be a multiple of the element type's alignment value.");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: arrays decorated with the stride attribute must "
+ "have a stride that is at least the size of the element type, "
+ "and be a multiple of the element type's alignment value.");
+ }
}
struct SizeAndAlignment {
- uint32_t size;
- uint32_t align;
+ uint32_t size;
+ uint32_t align;
};
constexpr SizeAndAlignment default_u32 = {4, 4};
constexpr SizeAndAlignment default_i32 = {4, 4};
@@ -864,68 +823,67 @@
constexpr SizeAndAlignment default_mat3x3 = {48, 16};
constexpr SizeAndAlignment default_mat4x4 = {64, 16};
-INSTANTIATE_TEST_SUITE_P(
- ResolverAttributeValidationTest,
- ArrayStrideTest,
- testing::Values(
- // Succeed because stride >= element size (while being multiple of
- // element alignment)
- ParamsFor<u32>(default_u32.size, true),
- ParamsFor<i32>(default_i32.size, true),
- ParamsFor<f32>(default_f32.size, true),
- ParamsFor<vec2<f32>>(default_vec2.size, true),
- // vec3's default size is not a multiple of its alignment
- // ParamsFor<vec3<f32>, default_vec3.size, true},
- ParamsFor<vec4<f32>>(default_vec4.size, true),
- ParamsFor<mat2x2<f32>>(default_mat2x2.size, true),
- ParamsFor<mat3x3<f32>>(default_mat3x3.size, true),
- ParamsFor<mat4x4<f32>>(default_mat4x4.size, true),
+INSTANTIATE_TEST_SUITE_P(ResolverAttributeValidationTest,
+ ArrayStrideTest,
+ testing::Values(
+ // Succeed because stride >= element size (while being multiple of
+ // element alignment)
+ ParamsFor<u32>(default_u32.size, true),
+ ParamsFor<i32>(default_i32.size, true),
+ ParamsFor<f32>(default_f32.size, true),
+ ParamsFor<vec2<f32>>(default_vec2.size, true),
+ // vec3's default size is not a multiple of its alignment
+ // ParamsFor<vec3<f32>, default_vec3.size, true},
+ ParamsFor<vec4<f32>>(default_vec4.size, true),
+ ParamsFor<mat2x2<f32>>(default_mat2x2.size, true),
+ ParamsFor<mat3x3<f32>>(default_mat3x3.size, true),
+ ParamsFor<mat4x4<f32>>(default_mat4x4.size, true),
- // Fail because stride is < element size
- ParamsFor<u32>(default_u32.size - 1, false),
- ParamsFor<i32>(default_i32.size - 1, false),
- ParamsFor<f32>(default_f32.size - 1, false),
- ParamsFor<vec2<f32>>(default_vec2.size - 1, false),
- ParamsFor<vec3<f32>>(default_vec3.size - 1, false),
- ParamsFor<vec4<f32>>(default_vec4.size - 1, false),
- ParamsFor<mat2x2<f32>>(default_mat2x2.size - 1, false),
- ParamsFor<mat3x3<f32>>(default_mat3x3.size - 1, false),
- ParamsFor<mat4x4<f32>>(default_mat4x4.size - 1, false),
+ // Fail because stride is < element size
+ ParamsFor<u32>(default_u32.size - 1, false),
+ ParamsFor<i32>(default_i32.size - 1, false),
+ ParamsFor<f32>(default_f32.size - 1, false),
+ ParamsFor<vec2<f32>>(default_vec2.size - 1, false),
+ ParamsFor<vec3<f32>>(default_vec3.size - 1, false),
+ ParamsFor<vec4<f32>>(default_vec4.size - 1, false),
+ ParamsFor<mat2x2<f32>>(default_mat2x2.size - 1, false),
+ ParamsFor<mat3x3<f32>>(default_mat3x3.size - 1, false),
+ ParamsFor<mat4x4<f32>>(default_mat4x4.size - 1, false),
- // Succeed because stride equals multiple of element alignment
- ParamsFor<u32>(default_u32.align * 7, true),
- ParamsFor<i32>(default_i32.align * 7, true),
- ParamsFor<f32>(default_f32.align * 7, true),
- ParamsFor<vec2<f32>>(default_vec2.align * 7, true),
- ParamsFor<vec3<f32>>(default_vec3.align * 7, true),
- ParamsFor<vec4<f32>>(default_vec4.align * 7, true),
- ParamsFor<mat2x2<f32>>(default_mat2x2.align * 7, true),
- ParamsFor<mat3x3<f32>>(default_mat3x3.align * 7, true),
- ParamsFor<mat4x4<f32>>(default_mat4x4.align * 7, true),
+ // Succeed because stride equals multiple of element alignment
+ ParamsFor<u32>(default_u32.align * 7, true),
+ ParamsFor<i32>(default_i32.align * 7, true),
+ ParamsFor<f32>(default_f32.align * 7, true),
+ ParamsFor<vec2<f32>>(default_vec2.align * 7, true),
+ ParamsFor<vec3<f32>>(default_vec3.align * 7, true),
+ ParamsFor<vec4<f32>>(default_vec4.align * 7, true),
+ ParamsFor<mat2x2<f32>>(default_mat2x2.align * 7, true),
+ ParamsFor<mat3x3<f32>>(default_mat3x3.align * 7, true),
+ ParamsFor<mat4x4<f32>>(default_mat4x4.align * 7, true),
- // Fail because stride is not multiple of element alignment
- ParamsFor<u32>((default_u32.align - 1) * 7, false),
- ParamsFor<i32>((default_i32.align - 1) * 7, false),
- ParamsFor<f32>((default_f32.align - 1) * 7, false),
- ParamsFor<vec2<f32>>((default_vec2.align - 1) * 7, false),
- ParamsFor<vec3<f32>>((default_vec3.align - 1) * 7, false),
- ParamsFor<vec4<f32>>((default_vec4.align - 1) * 7, false),
- ParamsFor<mat2x2<f32>>((default_mat2x2.align - 1) * 7, false),
- ParamsFor<mat3x3<f32>>((default_mat3x3.align - 1) * 7, false),
- ParamsFor<mat4x4<f32>>((default_mat4x4.align - 1) * 7, false)));
+ // Fail because stride is not multiple of element alignment
+ ParamsFor<u32>((default_u32.align - 1) * 7, false),
+ ParamsFor<i32>((default_i32.align - 1) * 7, false),
+ ParamsFor<f32>((default_f32.align - 1) * 7, false),
+ ParamsFor<vec2<f32>>((default_vec2.align - 1) * 7, false),
+ ParamsFor<vec3<f32>>((default_vec3.align - 1) * 7, false),
+ ParamsFor<vec4<f32>>((default_vec4.align - 1) * 7, false),
+ ParamsFor<mat2x2<f32>>((default_mat2x2.align - 1) * 7, false),
+ ParamsFor<mat3x3<f32>>((default_mat3x3.align - 1) * 7, false),
+ ParamsFor<mat4x4<f32>>((default_mat4x4.align - 1) * 7, false)));
TEST_F(ArrayStrideTest, DuplicateAttribute) {
- auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4,
- {
- create<ast::StrideAttribute>(Source{{12, 34}}, 4),
- create<ast::StrideAttribute>(Source{{56, 78}}, 4),
- });
+ auto* arr = ty.array(Source{{12, 34}}, ty.i32(), 4,
+ {
+ create<ast::StrideAttribute>(Source{{12, 34}}, 4),
+ create<ast::StrideAttribute>(Source{{56, 78}}, 4),
+ });
- Global("myarray", arr, ast::StorageClass::kPrivate);
+ Global("myarray", arr, ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate stride attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate stride attribute
12:34 note: first attribute declared here)");
}
@@ -937,147 +895,132 @@
using ResourceAttributeTest = ResolverTest;
TEST_F(ResourceAttributeTest, UniformBufferMissingBinding) {
- auto* s = Structure("S", {Member("x", ty.i32())});
- Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
+ auto* s = Structure("S", {Member("x", ty.i32())});
+ Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kUniform);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: resource variables require @group and @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: resource variables require @group and @binding attributes)");
}
TEST_F(ResourceAttributeTest, StorageBufferMissingBinding) {
- auto* s = Structure("S", {Member("x", ty.i32())});
- Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead);
+ auto* s = Structure("S", {Member("x", ty.i32())});
+ Global(Source{{12, 34}}, "G", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: resource variables require @group and @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: resource variables require @group and @binding attributes)");
}
TEST_F(ResourceAttributeTest, TextureMissingBinding) {
- Global(Source{{12, 34}}, "G", ty.depth_texture(ast::TextureDimension::k2d),
- ast::StorageClass::kNone);
+ Global(Source{{12, 34}}, "G", ty.depth_texture(ast::TextureDimension::k2d),
+ ast::StorageClass::kNone);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: resource variables require @group and @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: resource variables require @group and @binding attributes)");
}
TEST_F(ResourceAttributeTest, SamplerMissingBinding) {
- Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
- ast::StorageClass::kNone);
+ Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: resource variables require @group and @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: resource variables require @group and @binding attributes)");
}
TEST_F(ResourceAttributeTest, BindingPairMissingBinding) {
- Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
- ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::GroupAttribute>(1),
- });
+ Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::GroupAttribute>(1),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: resource variables require @group and @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: resource variables require @group and @binding attributes)");
}
TEST_F(ResourceAttributeTest, BindingPairMissingGroup) {
- Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler),
- ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- });
+ Global(Source{{12, 34}}, "G", ty.sampler(ast::SamplerKind::kSampler), ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: resource variables require @group and @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: resource variables require @group and @binding attributes)");
}
TEST_F(ResourceAttributeTest, BindingPointUsedTwiceByEntryPoint) {
- Global(Source{{12, 34}}, "A",
- ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(2),
- });
- Global(Source{{56, 78}}, "B",
- ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(2),
- });
+ Global(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(2),
+ });
+ Global(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(2),
+ });
- Func("F", {}, ty.void_(),
- {
- Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
- Call("textureLoad", "A", vec2<i32>(1, 2), 0))),
- Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
- Call("textureLoad", "B", vec2<i32>(1, 2), 0))),
- },
- {Stage(ast::PipelineStage::kFragment)});
+ Func("F", {}, ty.void_(),
+ {
+ Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
+ Call("textureLoad", "A", vec2<i32>(1, 2), 0))),
+ Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
+ Call("textureLoad", "B", vec2<i32>(1, 2), 0))),
+ },
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: entry point 'F' references multiple variables that use the same resource binding @group(2), @binding(1)
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: entry point 'F' references multiple variables that use the same resource binding @group(2), @binding(1)
12:34 note: first resource binding usage declared here)");
}
TEST_F(ResourceAttributeTest, BindingPointUsedTwiceByDifferentEntryPoints) {
- Global(Source{{12, 34}}, "A",
- ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(2),
- });
- Global(Source{{56, 78}}, "B",
- ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- ast::StorageClass::kNone,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(2),
- });
+ Global(Source{{12, 34}}, "A", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(2),
+ });
+ Global(Source{{56, 78}}, "B", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ ast::StorageClass::kNone,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(2),
+ });
- Func("F_A", {}, ty.void_(),
- {
- Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
- Call("textureLoad", "A", vec2<i32>(1, 2), 0))),
- },
- {Stage(ast::PipelineStage::kFragment)});
- Func("F_B", {}, ty.void_(),
- {
- Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
- Call("textureLoad", "B", vec2<i32>(1, 2), 0))),
- },
- {Stage(ast::PipelineStage::kFragment)});
+ Func("F_A", {}, ty.void_(),
+ {
+ Decl(Var("a", ty.vec4<f32>(), ast::StorageClass::kNone,
+ Call("textureLoad", "A", vec2<i32>(1, 2), 0))),
+ },
+ {Stage(ast::PipelineStage::kFragment)});
+ Func("F_B", {}, ty.void_(),
+ {
+ Decl(Var("b", ty.vec4<f32>(), ast::StorageClass::kNone,
+ Call("textureLoad", "B", vec2<i32>(1, 2), 0))),
+ },
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResourceAttributeTest, BindingPointOnNonResource) {
- Global(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(2),
- });
+ Global(Source{{12, 34}}, "G", ty.f32(), ast::StorageClass::kPrivate,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(2),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: non-resource variables must not have @group or @binding attributes)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: non-resource variables must not have @group or @binding attributes)");
}
} // namespace
@@ -1087,31 +1030,30 @@
namespace {
using InvariantAttributeTests = ResolverTest;
TEST_F(InvariantAttributeTests, InvariantWithPosition) {
- auto* param = Param("p", ty.vec4<f32>(),
- {Invariant(Source{{12, 34}}),
- Builtin(Source{{56, 78}}, ast::Builtin::kPosition)});
- Func("main", ast::VariableList{param}, ty.vec4<f32>(),
- ast::StatementList{Return(Construct(ty.vec4<f32>()))},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
- ast::AttributeList{
- Location(0),
- });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* param =
+ Param("p", ty.vec4<f32>(),
+ {Invariant(Source{{12, 34}}), Builtin(Source{{56, 78}}, ast::Builtin::kPosition)});
+ Func("main", ast::VariableList{param}, ty.vec4<f32>(),
+ ast::StatementList{Return(Construct(ty.vec4<f32>()))},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
+ ast::AttributeList{
+ Location(0),
+ });
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(InvariantAttributeTests, InvariantWithoutPosition) {
- auto* param =
- Param("p", ty.vec4<f32>(), {Invariant(Source{{12, 34}}), Location(0)});
- Func("main", ast::VariableList{param}, ty.vec4<f32>(),
- ast::StatementList{Return(Construct(ty.vec4<f32>()))},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
- ast::AttributeList{
- Location(0),
- });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: invariant attribute must only be applied to a "
- "position builtin");
+ auto* param = Param("p", ty.vec4<f32>(), {Invariant(Source{{12, 34}}), Location(0)});
+ Func("main", ast::VariableList{param}, ty.vec4<f32>(),
+ ast::StatementList{Return(Construct(ty.vec4<f32>()))},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
+ ast::AttributeList{
+ Location(0),
+ });
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: invariant attribute must only be applied to a "
+ "position builtin");
}
} // namespace
} // namespace InvariantAttributeTests
@@ -1121,56 +1063,53 @@
using WorkgroupAttribute = ResolverTest;
TEST_F(WorkgroupAttribute, ComputeShaderPass) {
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(WorkgroupAttribute, Missing) {
- Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute)});
+ Func(Source{{12, 34}}, "main", {}, ty.void_(), {}, {Stage(ast::PipelineStage::kCompute)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: a compute shader must include 'workgroup_size' in its "
- "attributes");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: a compute shader must include 'workgroup_size' in its "
+ "attributes");
}
TEST_F(WorkgroupAttribute, NotAnEntryPoint) {
- Func("main", {}, ty.void_(), {},
- {create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
+ Func("main", {}, ty.void_(), {}, {create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: the workgroup_size attribute is only valid for "
- "compute stages");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: the workgroup_size attribute is only valid for "
+ "compute stages");
}
TEST_F(WorkgroupAttribute, NotAComputeShader) {
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment),
+ create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: the workgroup_size attribute is only valid for "
- "compute stages");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: the workgroup_size attribute is only valid for "
+ "compute stages");
}
TEST_F(WorkgroupAttribute, DuplicateAttribute) {
- Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
- {
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Source{{12, 34}}, 1, nullptr, nullptr),
- WorkgroupSize(Source{{56, 78}}, 2, nullptr, nullptr),
- });
+ Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
+ {
+ Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Source{{12, 34}}, 1, nullptr, nullptr),
+ WorkgroupSize(Source{{56, 78}}, 2, nullptr, nullptr),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate workgroup_size attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate workgroup_size attribute
12:34 note: first attribute declared here)");
}
@@ -1183,184 +1122,157 @@
using InterpolateTest = ResolverTest;
struct Params {
- ast::InterpolationType type;
- ast::InterpolationSampling sampling;
- bool should_pass;
+ ast::InterpolationType type;
+ ast::InterpolationSampling sampling;
+ bool should_pass;
};
struct TestWithParams : ResolverTestWithParam<Params> {};
using InterpolateParameterTest = TestWithParams;
TEST_P(InterpolateParameterTest, All) {
- auto& params = GetParam();
+ auto& params = GetParam();
- Func("main",
- ast::VariableList{Param(
- "a", ty.f32(),
- {Location(0),
- Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
- ty.void_(), {},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+ Func("main",
+ ast::VariableList{
+ Param("a", ty.f32(),
+ {Location(0), Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
+ ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
- if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: flat interpolation attribute must not have a "
- "sampling parameter");
- }
+ if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: flat interpolation attribute must not have a "
+ "sampling parameter");
+ }
}
TEST_P(InterpolateParameterTest, IntegerScalar) {
- auto& params = GetParam();
+ auto& params = GetParam();
- Func("main",
- ast::VariableList{Param(
- "a", ty.i32(),
- {Location(0),
- Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
- ty.void_(), {},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+ Func("main",
+ ast::VariableList{
+ Param("a", ty.i32(),
+ {Location(0), Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
+ ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
- if (params.type != ast::InterpolationType::kFlat) {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: interpolation type must be 'flat' for integral "
- "user-defined IO types");
- } else if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: flat interpolation attribute must not have a "
- "sampling parameter");
- }
+ if (params.type != ast::InterpolationType::kFlat) {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: interpolation type must be 'flat' for integral "
+ "user-defined IO types");
+ } else if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: flat interpolation attribute must not have a "
+ "sampling parameter");
+ }
}
TEST_P(InterpolateParameterTest, IntegerVector) {
- auto& params = GetParam();
+ auto& params = GetParam();
- Func("main",
- ast::VariableList{Param(
- "a", ty.vec4<u32>(),
- {Location(0),
- Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
- ty.void_(), {},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+ Func("main",
+ ast::VariableList{
+ Param("a", ty.vec4<u32>(),
+ {Location(0), Interpolate(Source{{12, 34}}, params.type, params.sampling)})},
+ ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
- if (params.type != ast::InterpolationType::kFlat) {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: interpolation type must be 'flat' for integral "
- "user-defined IO types");
- } else if (params.should_pass) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: flat interpolation attribute must not have a "
- "sampling parameter");
- }
+ if (params.type != ast::InterpolationType::kFlat) {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: interpolation type must be 'flat' for integral "
+ "user-defined IO types");
+ } else if (params.should_pass) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: flat interpolation attribute must not have a "
+ "sampling parameter");
+ }
}
INSTANTIATE_TEST_SUITE_P(
ResolverAttributeValidationTest,
InterpolateParameterTest,
- testing::Values(Params{ast::InterpolationType::kPerspective,
- ast::InterpolationSampling::kNone, true},
- Params{ast::InterpolationType::kPerspective,
- ast::InterpolationSampling::kCenter, true},
- Params{ast::InterpolationType::kPerspective,
- ast::InterpolationSampling::kCentroid, true},
- Params{ast::InterpolationType::kPerspective,
- ast::InterpolationSampling::kSample, true},
- Params{ast::InterpolationType::kLinear,
- ast::InterpolationSampling::kNone, true},
- Params{ast::InterpolationType::kLinear,
- ast::InterpolationSampling::kCenter, true},
- Params{ast::InterpolationType::kLinear,
- ast::InterpolationSampling::kCentroid, true},
- Params{ast::InterpolationType::kLinear,
- ast::InterpolationSampling::kSample, true},
- // flat interpolation must not have a sampling type
- Params{ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone, true},
- Params{ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kCenter, false},
- Params{ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kCentroid, false},
- Params{ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kSample, false}));
+ testing::Values(
+ Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kNone, true},
+ Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kCenter, true},
+ Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kCentroid, true},
+ Params{ast::InterpolationType::kPerspective, ast::InterpolationSampling::kSample, true},
+ Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kNone, true},
+ Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kCenter, true},
+ Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kCentroid, true},
+ Params{ast::InterpolationType::kLinear, ast::InterpolationSampling::kSample, true},
+ // flat interpolation must not have a sampling type
+ Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kNone, true},
+ Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kCenter, false},
+ Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kCentroid, false},
+ Params{ast::InterpolationType::kFlat, ast::InterpolationSampling::kSample, false}));
TEST_F(InterpolateTest, FragmentInput_Integer_MissingFlatInterpolation) {
- Func("main",
- ast::VariableList{Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)})},
- ty.void_(), {},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+ Func("main", ast::VariableList{Param(Source{{12, 34}}, "a", ty.i32(), {Location(0)})},
+ ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: integral user-defined fragment inputs must have a flat interpolation attribute)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: integral user-defined fragment inputs must have a flat interpolation attribute)");
}
TEST_F(InterpolateTest, VertexOutput_Integer_MissingFlatInterpolation) {
- auto* s = Structure(
- "S",
- {
- Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
- Member(Source{{12, 34}}, "u", ty.u32(), {Location(0)}),
- });
- Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
- ast::AttributeList{Stage(ast::PipelineStage::kVertex)});
+ auto* s = Structure("S", {
+ Member("pos", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)}),
+ Member(Source{{12, 34}}, "u", ty.u32(), {Location(0)}),
+ });
+ Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
+ ast::AttributeList{Stage(ast::PipelineStage::kVertex)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: integral user-defined vertex outputs must have a flat interpolation attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: integral user-defined vertex outputs must have a flat interpolation attribute
note: while analysing entry point 'main')");
}
TEST_F(InterpolateTest, MissingLocationAttribute_Parameter) {
- Func("main",
- ast::VariableList{
- Param("a", ty.vec4<f32>(),
- {Builtin(ast::Builtin::kPosition),
- Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone)})},
- ty.void_(), {},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+ Func("main",
+ ast::VariableList{Param("a", ty.vec4<f32>(),
+ {Builtin(ast::Builtin::kPosition),
+ Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+ ast::InterpolationSampling::kNone)})},
+ ty.void_(), {}, ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: interpolate attribute must only be used with @location)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: interpolate attribute must only be used with @location)");
}
TEST_F(InterpolateTest, MissingLocationAttribute_ReturnType) {
- Func("main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
- ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
- {Builtin(ast::Builtin::kPosition),
- Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone)});
+ Func("main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
+ {Builtin(ast::Builtin::kPosition),
+ Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+ ast::InterpolationSampling::kNone)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: interpolate attribute must only be used with @location)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: interpolate attribute must only be used with @location)");
}
TEST_F(InterpolateTest, MissingLocationAttribute_Struct) {
- Structure(
- "S", {Member("a", ty.f32(),
- {Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
- ast::InterpolationSampling::kNone)})});
+ Structure("S", {Member("a", ty.f32(),
+ {Interpolate(Source{{12, 34}}, ast::InterpolationType::kFlat,
+ ast::InterpolationSampling::kNone)})});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: interpolate attribute must only be used with @location)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: interpolate attribute must only be used with @location)");
}
} // namespace
diff --git a/src/tint/resolver/bitcast_validation_test.cc b/src/tint/resolver/bitcast_validation_test.cc
index 58dfa21..f7799b5 100644
--- a/src/tint/resolver/bitcast_validation_test.cc
+++ b/src/tint/resolver/bitcast_validation_test.cc
@@ -22,15 +22,15 @@
namespace {
struct Type {
- template <typename T>
- static constexpr Type Create() {
- return Type{builder::DataType<T>::AST, builder::DataType<T>::Sem,
- builder::DataType<T>::Expr};
- }
+ template <typename T>
+ static constexpr Type Create() {
+ return Type{builder::DataType<T>::AST, builder::DataType<T>::Sem,
+ builder::DataType<T>::Expr};
+ }
- builder::ast_type_func_ptr ast;
- builder::sem_type_func_ptr sem;
- builder::ast_expr_func_ptr expr;
+ builder::ast_type_func_ptr ast;
+ builder::sem_type_func_ptr sem;
+ builder::ast_expr_func_ptr expr;
};
static constexpr Type kNumericScalars[] = {
@@ -71,156 +71,140 @@
Type::Create<builder::ptr<builder::mat2x2<builder::f32>>>(),
};
-using ResolverBitcastValidationTest =
- ResolverTestWithParam<std::tuple<Type, Type>>;
+using ResolverBitcastValidationTest = ResolverTestWithParam<std::tuple<Type, Type>>;
////////////////////////////////////////////////////////////////////////////////
// Valid bitcasts
////////////////////////////////////////////////////////////////////////////////
using ResolverBitcastValidationTestPass = ResolverBitcastValidationTest;
TEST_P(ResolverBitcastValidationTestPass, Test) {
- auto src = std::get<0>(GetParam());
- auto dst = std::get<1>(GetParam());
+ auto src = std::get<0>(GetParam());
+ auto dst = std::get<1>(GetParam());
- auto* cast = Bitcast(dst.ast(*this), src.expr(*this, 0));
- WrapInFunction(cast);
+ auto* cast = Bitcast(dst.ast(*this), src.expr(*this, 0));
+ WrapInFunction(cast);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(TypeOf(cast), dst.sem(*this));
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(TypeOf(cast), dst.sem(*this));
}
INSTANTIATE_TEST_SUITE_P(Scalars,
ResolverBitcastValidationTestPass,
testing::Combine(testing::ValuesIn(kNumericScalars),
testing::ValuesIn(kNumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec2,
- ResolverBitcastValidationTestPass,
- testing::Combine(testing::ValuesIn(kVec2NumericScalars),
- testing::ValuesIn(kVec2NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec3,
- ResolverBitcastValidationTestPass,
- testing::Combine(testing::ValuesIn(kVec3NumericScalars),
- testing::ValuesIn(kVec3NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec4,
- ResolverBitcastValidationTestPass,
- testing::Combine(testing::ValuesIn(kVec4NumericScalars),
- testing::ValuesIn(kVec4NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec2,
+ ResolverBitcastValidationTestPass,
+ testing::Combine(testing::ValuesIn(kVec2NumericScalars),
+ testing::ValuesIn(kVec2NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec3,
+ ResolverBitcastValidationTestPass,
+ testing::Combine(testing::ValuesIn(kVec3NumericScalars),
+ testing::ValuesIn(kVec3NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec4,
+ ResolverBitcastValidationTestPass,
+ testing::Combine(testing::ValuesIn(kVec4NumericScalars),
+ testing::ValuesIn(kVec4NumericScalars)));
////////////////////////////////////////////////////////////////////////////////
// Invalid source type for bitcasts
////////////////////////////////////////////////////////////////////////////////
using ResolverBitcastValidationTestInvalidSrcTy = ResolverBitcastValidationTest;
TEST_P(ResolverBitcastValidationTestInvalidSrcTy, Test) {
- auto src = std::get<0>(GetParam());
- auto dst = std::get<1>(GetParam());
+ auto src = std::get<0>(GetParam());
+ auto dst = std::get<1>(GetParam());
- auto* cast = Bitcast(dst.ast(*this), Expr(Source{{12, 34}}, "src"));
- WrapInFunction(Let("src", nullptr, src.expr(*this, 0)), cast);
+ auto* cast = Bitcast(dst.ast(*this), Expr(Source{{12, 34}}, "src"));
+ WrapInFunction(Let("src", nullptr, src.expr(*this, 0)), cast);
- auto expected = "12:34 error: '" + src.sem(*this)->FriendlyName(Symbols()) +
- "' cannot be bitcast";
+ auto expected =
+ "12:34 error: '" + src.sem(*this)->FriendlyName(Symbols()) + "' cannot be bitcast";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), expected);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), expected);
}
INSTANTIATE_TEST_SUITE_P(Scalars,
ResolverBitcastValidationTestInvalidSrcTy,
testing::Combine(testing::ValuesIn(kInvalid),
testing::ValuesIn(kNumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec2,
- ResolverBitcastValidationTestInvalidSrcTy,
- testing::Combine(testing::ValuesIn(kInvalid),
- testing::ValuesIn(kVec2NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec3,
- ResolverBitcastValidationTestInvalidSrcTy,
- testing::Combine(testing::ValuesIn(kInvalid),
- testing::ValuesIn(kVec3NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec4,
- ResolverBitcastValidationTestInvalidSrcTy,
- testing::Combine(testing::ValuesIn(kInvalid),
- testing::ValuesIn(kVec4NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec2,
+ ResolverBitcastValidationTestInvalidSrcTy,
+ testing::Combine(testing::ValuesIn(kInvalid),
+ testing::ValuesIn(kVec2NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec3,
+ ResolverBitcastValidationTestInvalidSrcTy,
+ testing::Combine(testing::ValuesIn(kInvalid),
+ testing::ValuesIn(kVec3NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec4,
+ ResolverBitcastValidationTestInvalidSrcTy,
+ testing::Combine(testing::ValuesIn(kInvalid),
+ testing::ValuesIn(kVec4NumericScalars)));
////////////////////////////////////////////////////////////////////////////////
// Invalid target type for bitcasts
////////////////////////////////////////////////////////////////////////////////
using ResolverBitcastValidationTestInvalidDstTy = ResolverBitcastValidationTest;
TEST_P(ResolverBitcastValidationTestInvalidDstTy, Test) {
- auto src = std::get<0>(GetParam());
- auto dst = std::get<1>(GetParam());
+ auto src = std::get<0>(GetParam());
+ auto dst = std::get<1>(GetParam());
- // Use an alias so we can put a Source on the bitcast type
- Alias("T", dst.ast(*this));
- WrapInFunction(
- Bitcast(ty.type_name(Source{{12, 34}}, "T"), src.expr(*this, 0)));
+ // Use an alias so we can put a Source on the bitcast type
+ Alias("T", dst.ast(*this));
+ WrapInFunction(Bitcast(ty.type_name(Source{{12, 34}}, "T"), src.expr(*this, 0)));
- auto expected = "12:34 error: cannot bitcast to '" +
- dst.sem(*this)->FriendlyName(Symbols()) + "'";
+ auto expected =
+ "12:34 error: cannot bitcast to '" + dst.sem(*this)->FriendlyName(Symbols()) + "'";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), expected);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), expected);
}
INSTANTIATE_TEST_SUITE_P(Scalars,
ResolverBitcastValidationTestInvalidDstTy,
testing::Combine(testing::ValuesIn(kNumericScalars),
testing::ValuesIn(kInvalid)));
-INSTANTIATE_TEST_SUITE_P(
- Vec2,
- ResolverBitcastValidationTestInvalidDstTy,
- testing::Combine(testing::ValuesIn(kVec2NumericScalars),
- testing::ValuesIn(kInvalid)));
-INSTANTIATE_TEST_SUITE_P(
- Vec3,
- ResolverBitcastValidationTestInvalidDstTy,
- testing::Combine(testing::ValuesIn(kVec3NumericScalars),
- testing::ValuesIn(kInvalid)));
-INSTANTIATE_TEST_SUITE_P(
- Vec4,
- ResolverBitcastValidationTestInvalidDstTy,
- testing::Combine(testing::ValuesIn(kVec4NumericScalars),
- testing::ValuesIn(kInvalid)));
+INSTANTIATE_TEST_SUITE_P(Vec2,
+ ResolverBitcastValidationTestInvalidDstTy,
+ testing::Combine(testing::ValuesIn(kVec2NumericScalars),
+ testing::ValuesIn(kInvalid)));
+INSTANTIATE_TEST_SUITE_P(Vec3,
+ ResolverBitcastValidationTestInvalidDstTy,
+ testing::Combine(testing::ValuesIn(kVec3NumericScalars),
+ testing::ValuesIn(kInvalid)));
+INSTANTIATE_TEST_SUITE_P(Vec4,
+ ResolverBitcastValidationTestInvalidDstTy,
+ testing::Combine(testing::ValuesIn(kVec4NumericScalars),
+ testing::ValuesIn(kInvalid)));
////////////////////////////////////////////////////////////////////////////////
// Incompatible bitcast, but both src and dst types are valid
////////////////////////////////////////////////////////////////////////////////
using ResolverBitcastValidationTestIncompatible = ResolverBitcastValidationTest;
TEST_P(ResolverBitcastValidationTestIncompatible, Test) {
- auto src = std::get<0>(GetParam());
- auto dst = std::get<1>(GetParam());
+ auto src = std::get<0>(GetParam());
+ auto dst = std::get<1>(GetParam());
- WrapInFunction(Bitcast(Source{{12, 34}}, dst.ast(*this), src.expr(*this, 0)));
+ WrapInFunction(Bitcast(Source{{12, 34}}, dst.ast(*this), src.expr(*this, 0)));
- auto expected = "12:34 error: cannot bitcast from '" +
- src.sem(*this)->FriendlyName(Symbols()) + "' to '" +
- dst.sem(*this)->FriendlyName(Symbols()) + "'";
+ auto expected = "12:34 error: cannot bitcast from '" + src.sem(*this)->FriendlyName(Symbols()) +
+ "' to '" + dst.sem(*this)->FriendlyName(Symbols()) + "'";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), expected);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), expected);
}
-INSTANTIATE_TEST_SUITE_P(
- ScalarToVec2,
- ResolverBitcastValidationTestIncompatible,
- testing::Combine(testing::ValuesIn(kNumericScalars),
- testing::ValuesIn(kVec2NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec2ToVec3,
- ResolverBitcastValidationTestIncompatible,
- testing::Combine(testing::ValuesIn(kVec2NumericScalars),
- testing::ValuesIn(kVec3NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec3ToVec4,
- ResolverBitcastValidationTestIncompatible,
- testing::Combine(testing::ValuesIn(kVec3NumericScalars),
- testing::ValuesIn(kVec4NumericScalars)));
-INSTANTIATE_TEST_SUITE_P(
- Vec4ToScalar,
- ResolverBitcastValidationTestIncompatible,
- testing::Combine(testing::ValuesIn(kVec4NumericScalars),
- testing::ValuesIn(kNumericScalars)));
+INSTANTIATE_TEST_SUITE_P(ScalarToVec2,
+ ResolverBitcastValidationTestIncompatible,
+ testing::Combine(testing::ValuesIn(kNumericScalars),
+ testing::ValuesIn(kVec2NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec2ToVec3,
+ ResolverBitcastValidationTestIncompatible,
+ testing::Combine(testing::ValuesIn(kVec2NumericScalars),
+ testing::ValuesIn(kVec3NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec3ToVec4,
+ ResolverBitcastValidationTestIncompatible,
+ testing::Combine(testing::ValuesIn(kVec3NumericScalars),
+ testing::ValuesIn(kVec4NumericScalars)));
+INSTANTIATE_TEST_SUITE_P(Vec4ToScalar,
+ ResolverBitcastValidationTestIncompatible,
+ testing::Combine(testing::ValuesIn(kVec4NumericScalars),
+ testing::ValuesIn(kNumericScalars)));
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/resolver/builtin_test.cc b/src/tint/resolver/builtin_test.cc
index 64bb60a..3b5a03b 100644
--- a/src/tint/resolver/builtin_test.cc
+++ b/src/tint/resolver/builtin_test.cc
@@ -48,49 +48,48 @@
using ResolverBuiltinDerivativeTest = ResolverTestWithParam<std::string>;
TEST_P(ResolverBuiltinDerivativeTest, Scalar) {
- auto name = GetParam();
+ auto name = GetParam();
- Global("ident", ty.f32(), ast::StorageClass::kPrivate);
+ Global("ident", ty.f32(), ast::StorageClass::kPrivate);
- auto* expr = Call(name, "ident");
- Func("func", {}, ty.void_(), {Ignore(expr)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ auto* expr = Call(name, "ident");
+ Func("func", {}, ty.void_(), {Ignore(expr)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
}
TEST_P(ResolverBuiltinDerivativeTest, Vector) {
- auto name = GetParam();
- Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ auto name = GetParam();
+ Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* expr = Call(name, "ident");
- Func("func", {}, ty.void_(), {Ignore(expr)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ auto* expr = Call(name, "ident");
+ Func("func", {}, ty.void_(), {Ignore(expr)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(expr)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(expr)->As<sem::Vector>()->Width(), 4u);
}
TEST_P(ResolverBuiltinDerivativeTest, MissingParam) {
- auto name = GetParam();
+ auto name = GetParam();
- auto* expr = Call(name);
- WrapInFunction(expr);
+ auto* expr = Call(name);
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: no matching call to " + name +
- "()\n\n"
- "2 candidate functions:\n " +
- name + "(f32) -> f32\n " + name +
- "(vecN<f32>) -> vecN<f32>\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + name +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ name + "(f32) -> f32\n " + name + "(vecN<f32>) -> vecN<f32>\n");
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
@@ -107,30 +106,30 @@
using ResolverBuiltinTest_BoolMethod = ResolverTestWithParam<std::string>;
TEST_P(ResolverBuiltinTest_BoolMethod, Scalar) {
- auto name = GetParam();
+ auto name = GetParam();
- Global("my_var", ty.bool_(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.bool_(), ast::StorageClass::kPrivate);
- auto* expr = Call(name, "my_var");
- WrapInFunction(expr);
+ auto* expr = Call(name, "my_var");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::Bool>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::Bool>());
}
TEST_P(ResolverBuiltinTest_BoolMethod, Vector) {
- auto name = GetParam();
+ auto name = GetParam();
- Global("my_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
- auto* expr = Call(name, "my_var");
- WrapInFunction(expr);
+ auto* expr = Call(name, "my_var");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::Bool>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::Bool>());
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
ResolverBuiltinTest_BoolMethod,
@@ -138,167 +137,161 @@
enum class Texture { kF32, kI32, kU32 };
inline std::ostream& operator<<(std::ostream& out, Texture data) {
- if (data == Texture::kF32) {
- out << "f32";
- } else if (data == Texture::kI32) {
- out << "i32";
- } else {
- out << "u32";
- }
- return out;
+ if (data == Texture::kF32) {
+ out << "f32";
+ } else if (data == Texture::kI32) {
+ out << "i32";
+ } else {
+ out << "u32";
+ }
+ return out;
}
struct TextureTestParams {
- ast::TextureDimension dim;
- Texture type = Texture::kF32;
- ast::TexelFormat format = ast::TexelFormat::kR32Float;
+ ast::TextureDimension dim;
+ Texture type = Texture::kF32;
+ ast::TexelFormat format = ast::TexelFormat::kR32Float;
};
inline std::ostream& operator<<(std::ostream& out, TextureTestParams data) {
- out << data.dim << "_" << data.type;
- return out;
+ out << data.dim << "_" << data.type;
+ return out;
}
-class ResolverBuiltinTest_TextureOperation
- : public ResolverTestWithParam<TextureTestParams> {
- public:
- /// Gets an appropriate type for the coords parameter depending the the
- /// dimensionality of the texture being sampled.
- /// @param dim dimensionality of the texture being sampled
- /// @param scalar the scalar type
- /// @returns a pointer to a type appropriate for the coord param
- const ast::Type* GetCoordsType(ast::TextureDimension dim,
- const ast::Type* scalar) {
- switch (dim) {
- case ast::TextureDimension::k1d:
- return scalar;
- case ast::TextureDimension::k2d:
- case ast::TextureDimension::k2dArray:
- return ty.vec(scalar, 2);
- case ast::TextureDimension::k3d:
- case ast::TextureDimension::kCube:
- case ast::TextureDimension::kCubeArray:
- return ty.vec(scalar, 3);
- default:
- [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
- }
- return nullptr;
- }
-
- void add_call_param(std::string name,
- const ast::Type* type,
- ast::ExpressionList* call_params) {
- if (type->IsAnyOf<ast::Texture, ast::Sampler>()) {
- Global(name, type,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
-
- } else {
- Global(name, type, ast::StorageClass::kPrivate);
+class ResolverBuiltinTest_TextureOperation : public ResolverTestWithParam<TextureTestParams> {
+ public:
+ /// Gets an appropriate type for the coords parameter depending the the
+ /// dimensionality of the texture being sampled.
+ /// @param dim dimensionality of the texture being sampled
+ /// @param scalar the scalar type
+ /// @returns a pointer to a type appropriate for the coord param
+ const ast::Type* GetCoordsType(ast::TextureDimension dim, const ast::Type* scalar) {
+ switch (dim) {
+ case ast::TextureDimension::k1d:
+ return scalar;
+ case ast::TextureDimension::k2d:
+ case ast::TextureDimension::k2dArray:
+ return ty.vec(scalar, 2);
+ case ast::TextureDimension::k3d:
+ case ast::TextureDimension::kCube:
+ case ast::TextureDimension::kCubeArray:
+ return ty.vec(scalar, 3);
+ default:
+ [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
+ }
+ return nullptr;
}
- call_params->push_back(Expr(name));
- }
- const ast::Type* subtype(Texture type) {
- if (type == Texture::kF32) {
- return ty.f32();
+ void add_call_param(std::string name, const ast::Type* type, ast::ExpressionList* call_params) {
+ if (type->IsAnyOf<ast::Texture, ast::Sampler>()) {
+ Global(name, type,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+
+ } else {
+ Global(name, type, ast::StorageClass::kPrivate);
+ }
+
+ call_params->push_back(Expr(name));
}
- if (type == Texture::kI32) {
- return ty.i32();
+ const ast::Type* subtype(Texture type) {
+ if (type == Texture::kF32) {
+ return ty.f32();
+ }
+ if (type == Texture::kI32) {
+ return ty.i32();
+ }
+ return ty.u32();
}
- return ty.u32();
- }
};
-using ResolverBuiltinTest_SampledTextureOperation =
- ResolverBuiltinTest_TextureOperation;
+using ResolverBuiltinTest_SampledTextureOperation = ResolverBuiltinTest_TextureOperation;
TEST_P(ResolverBuiltinTest_SampledTextureOperation, TextureLoadSampled) {
- auto dim = GetParam().dim;
- auto type = GetParam().type;
+ auto dim = GetParam().dim;
+ auto type = GetParam().type;
- auto* s = subtype(type);
- auto* coords_type = GetCoordsType(dim, ty.i32());
- auto* texture_type = ty.sampled_texture(dim, s);
+ auto* s = subtype(type);
+ auto* coords_type = GetCoordsType(dim, ty.i32());
+ auto* texture_type = ty.sampled_texture(dim, s);
- ast::ExpressionList call_params;
+ ast::ExpressionList call_params;
- add_call_param("texture", texture_type, &call_params);
- add_call_param("coords", coords_type, &call_params);
- if (dim == ast::TextureDimension::k2dArray) {
- add_call_param("array_index", ty.i32(), &call_params);
- }
- add_call_param("level", ty.i32(), &call_params);
+ add_call_param("texture", texture_type, &call_params);
+ add_call_param("coords", coords_type, &call_params);
+ if (dim == ast::TextureDimension::k2dArray) {
+ add_call_param("array_index", ty.i32(), &call_params);
+ }
+ add_call_param("level", ty.i32(), &call_params);
- auto* expr = Call("textureLoad", call_params);
- WrapInFunction(expr);
+ auto* expr = Call("textureLoad", call_params);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::Vector>());
- if (type == Texture::kF32) {
- EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::F32>());
- } else if (type == Texture::kI32) {
- EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::I32>());
- } else {
- EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::U32>());
- }
- EXPECT_EQ(TypeOf(expr)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::Vector>());
+ if (type == Texture::kF32) {
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::F32>());
+ } else if (type == Texture::kI32) {
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::I32>());
+ } else {
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::U32>());
+ }
+ EXPECT_EQ(TypeOf(expr)->As<sem::Vector>()->Width(), 4u);
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_SampledTextureOperation,
- testing::Values(TextureTestParams{ast::TextureDimension::k1d},
- TextureTestParams{ast::TextureDimension::k2d},
- TextureTestParams{ast::TextureDimension::k2dArray},
- TextureTestParams{ast::TextureDimension::k3d}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_SampledTextureOperation,
+ testing::Values(TextureTestParams{ast::TextureDimension::k1d},
+ TextureTestParams{ast::TextureDimension::k2d},
+ TextureTestParams{ast::TextureDimension::k2dArray},
+ TextureTestParams{ast::TextureDimension::k3d}));
TEST_F(ResolverBuiltinTest, Dot_Vec2) {
- Global("my_var", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.vec2<f32>(), ast::StorageClass::kPrivate);
- auto* expr = Call("dot", "my_var", "my_var");
- WrapInFunction(expr);
+ auto* expr = Call("dot", "my_var", "my_var");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::F32>());
}
TEST_F(ResolverBuiltinTest, Dot_Vec3) {
- Global("my_var", ty.vec3<i32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.vec3<i32>(), ast::StorageClass::kPrivate);
- auto* expr = Call("dot", "my_var", "my_var");
- WrapInFunction(expr);
+ auto* expr = Call("dot", "my_var", "my_var");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::I32>());
}
TEST_F(ResolverBuiltinTest, Dot_Vec4) {
- Global("my_var", ty.vec4<u32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.vec4<u32>(), ast::StorageClass::kPrivate);
- auto* expr = Call("dot", "my_var", "my_var");
- WrapInFunction(expr);
+ auto* expr = Call("dot", "my_var", "my_var");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::U32>());
}
TEST_F(ResolverBuiltinTest, Dot_Error_Scalar) {
- auto* expr = Call("dot", 1.0f, 1.0f);
- WrapInFunction(expr);
+ auto* expr = Call("dot", 1.0f, 1.0f);
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to dot(f32, f32)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to dot(f32, f32)
1 candidate function:
dot(vecN<T>, vecN<T>) -> T where: T is f32, i32 or u32
@@ -306,29 +299,29 @@
}
TEST_F(ResolverBuiltinTest, Select) {
- Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kPrivate);
- Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
+ Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kPrivate);
- auto* expr = Call("select", "my_var", "my_var", "bool_var");
- WrapInFunction(expr);
+ auto* expr = Call("select", "my_var", "my_var", "bool_var");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::Vector>());
- EXPECT_EQ(TypeOf(expr)->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::Vector>());
+ EXPECT_EQ(TypeOf(expr)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Vector>()->type()->Is<sem::F32>());
}
TEST_F(ResolverBuiltinTest, Select_Error_NoParams) {
- auto* expr = Call("select");
- WrapInFunction(expr);
+ auto* expr = Call("select");
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select()
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select()
3 candidate functions:
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
@@ -338,13 +331,13 @@
}
TEST_F(ResolverBuiltinTest, Select_Error_SelectorInt) {
- auto* expr = Call("select", 1, 1, 1);
- WrapInFunction(expr);
+ auto* expr = Call("select", 1, 1, 1);
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(i32, i32, i32)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(i32, i32, i32)
3 candidate functions:
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
@@ -354,15 +347,14 @@
}
TEST_F(ResolverBuiltinTest, Select_Error_Matrix) {
- auto* expr = Call(
- "select", mat2x2<f32>(vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f)),
- mat2x2<f32>(vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f)), Expr(true));
- WrapInFunction(expr);
+ auto* expr = Call("select", mat2x2<f32>(vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f)),
+ mat2x2<f32>(vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f)), Expr(true));
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
3 candidate functions:
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
@@ -372,13 +364,13 @@
}
TEST_F(ResolverBuiltinTest, Select_Error_MismatchTypes) {
- auto* expr = Call("select", 1.0f, vec2<f32>(2.0f, 3.0f), Expr(true));
- WrapInFunction(expr);
+ auto* expr = Call("select", 1.0f, vec2<f32>(2.0f, 3.0f), Expr(true));
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(f32, vec2<f32>, bool)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(f32, vec2<f32>, bool)
3 candidate functions:
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
@@ -388,14 +380,13 @@
}
TEST_F(ResolverBuiltinTest, Select_Error_MismatchVectorSize) {
- auto* expr = Call("select", vec2<f32>(1.0f, 2.0f),
- vec3<f32>(3.0f, 4.0f, 5.0f), Expr(true));
- WrapInFunction(expr);
+ auto* expr = Call("select", vec2<f32>(1.0f, 2.0f), vec3<f32>(3.0f, 4.0f, 5.0f), Expr(true));
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
3 candidate functions:
select(T, T, bool) -> T where: T is f32, i32, u32 or bool
@@ -405,258 +396,248 @@
}
struct BuiltinData {
- const char* name;
- BuiltinType builtin;
+ const char* name;
+ BuiltinType builtin;
};
inline std::ostream& operator<<(std::ostream& out, BuiltinData data) {
- out << data.name;
- return out;
+ out << data.name;
+ return out;
}
using ResolverBuiltinTest_Barrier = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_Barrier, InferType) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(CallStmt(call));
+ auto* call = Call(param.name);
+ WrapInFunction(CallStmt(call));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::Void>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::Void>());
}
TEST_P(ResolverBuiltinTest_Barrier, Error_TooManyParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f);
- WrapInFunction(CallStmt(call));
+ auto* call = Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f);
+ WrapInFunction(CallStmt(call));
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name)));
+ EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name)));
}
INSTANTIATE_TEST_SUITE_P(
ResolverTest,
ResolverBuiltinTest_Barrier,
testing::Values(BuiltinData{"storageBarrier", BuiltinType::kStorageBarrier},
- BuiltinData{"workgroupBarrier",
- BuiltinType::kWorkgroupBarrier}));
+ BuiltinData{"workgroupBarrier", BuiltinType::kWorkgroupBarrier}));
using ResolverBuiltinTest_DataPacking = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_DataPacking, InferType) {
- auto param = GetParam();
+ auto param = GetParam();
- bool pack4 = param.builtin == BuiltinType::kPack4x8snorm ||
- param.builtin == BuiltinType::kPack4x8unorm;
+ bool pack4 =
+ param.builtin == BuiltinType::kPack4x8snorm || param.builtin == BuiltinType::kPack4x8unorm;
- auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f))
- : Call(param.name, vec2<f32>(1.f, 2.f));
- WrapInFunction(call);
+ auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f))
+ : Call(param.name, vec2<f32>(1.f, 2.f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
}
TEST_P(ResolverBuiltinTest_DataPacking, Error_IncorrectParamType) {
- auto param = GetParam();
+ auto param = GetParam();
- bool pack4 = param.builtin == BuiltinType::kPack4x8snorm ||
- param.builtin == BuiltinType::kPack4x8unorm;
+ bool pack4 =
+ param.builtin == BuiltinType::kPack4x8snorm || param.builtin == BuiltinType::kPack4x8unorm;
- auto* call = pack4 ? Call(param.name, vec4<i32>(1, 2, 3, 4))
- : Call(param.name, vec2<i32>(1, 2));
- WrapInFunction(call);
+ auto* call =
+ pack4 ? Call(param.name, vec4<i32>(1, 2, 3, 4)) : Call(param.name, vec2<i32>(1, 2));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name)));
+ EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name)));
}
TEST_P(ResolverBuiltinTest_DataPacking, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name)));
+ EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name)));
}
TEST_P(ResolverBuiltinTest_DataPacking, Error_TooManyParams) {
- auto param = GetParam();
+ auto param = GetParam();
- bool pack4 = param.builtin == BuiltinType::kPack4x8snorm ||
- param.builtin == BuiltinType::kPack4x8unorm;
+ bool pack4 =
+ param.builtin == BuiltinType::kPack4x8snorm || param.builtin == BuiltinType::kPack4x8unorm;
- auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f)
- : Call(param.name, vec2<f32>(1.f, 2.f), 1.0f);
- WrapInFunction(call);
+ auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f)
+ : Call(param.name, vec2<f32>(1.f, 2.f), 1.0f);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name)));
+ EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name)));
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_DataPacking,
- testing::Values(BuiltinData{"pack4x8snorm", BuiltinType::kPack4x8snorm},
- BuiltinData{"pack4x8unorm", BuiltinType::kPack4x8unorm},
- BuiltinData{"pack2x16snorm", BuiltinType::kPack2x16snorm},
- BuiltinData{"pack2x16unorm", BuiltinType::kPack2x16unorm},
- BuiltinData{"pack2x16float", BuiltinType::kPack2x16float}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_DataPacking,
+ testing::Values(BuiltinData{"pack4x8snorm", BuiltinType::kPack4x8snorm},
+ BuiltinData{"pack4x8unorm", BuiltinType::kPack4x8unorm},
+ BuiltinData{"pack2x16snorm", BuiltinType::kPack2x16snorm},
+ BuiltinData{"pack2x16unorm", BuiltinType::kPack2x16unorm},
+ BuiltinData{"pack2x16float",
+ BuiltinType::kPack2x16float}));
using ResolverBuiltinTest_DataUnpacking = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_DataUnpacking, InferType) {
- auto param = GetParam();
+ auto param = GetParam();
- bool pack4 = param.builtin == BuiltinType::kUnpack4x8snorm ||
- param.builtin == BuiltinType::kUnpack4x8unorm;
+ bool pack4 = param.builtin == BuiltinType::kUnpack4x8snorm ||
+ param.builtin == BuiltinType::kUnpack4x8unorm;
- auto* call = Call(param.name, 1u);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1u);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- if (pack4) {
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 4u);
- } else {
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 2u);
- }
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ if (pack4) {
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 4u);
+ } else {
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 2u);
+ }
}
INSTANTIATE_TEST_SUITE_P(
ResolverTest,
ResolverBuiltinTest_DataUnpacking,
- testing::Values(
- BuiltinData{"unpack4x8snorm", BuiltinType::kUnpack4x8snorm},
- BuiltinData{"unpack4x8unorm", BuiltinType::kUnpack4x8unorm},
- BuiltinData{"unpack2x16snorm", BuiltinType::kUnpack2x16snorm},
- BuiltinData{"unpack2x16unorm", BuiltinType::kUnpack2x16unorm},
- BuiltinData{"unpack2x16float", BuiltinType::kUnpack2x16float}));
+ testing::Values(BuiltinData{"unpack4x8snorm", BuiltinType::kUnpack4x8snorm},
+ BuiltinData{"unpack4x8unorm", BuiltinType::kUnpack4x8unorm},
+ BuiltinData{"unpack2x16snorm", BuiltinType::kUnpack2x16snorm},
+ BuiltinData{"unpack2x16unorm", BuiltinType::kUnpack2x16unorm},
+ BuiltinData{"unpack2x16float", BuiltinType::kUnpack2x16float}));
using ResolverBuiltinTest_SingleParam = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_SingleParam, Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1.f);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_P(ResolverBuiltinTest_SingleParam, Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_SingleParam, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32) -> f32\n " +
- std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32) -> f32\n " +
+ std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
}
TEST_P(ResolverBuiltinTest_SingleParam, Error_TooManyParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1, 2, 3);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1, 2, 3);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "(i32, i32, i32)\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32) -> f32\n " +
- std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "(i32, i32, i32)\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32) -> f32\n " +
+ std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_SingleParam,
- testing::Values(BuiltinData{"acos", BuiltinType::kAcos},
- BuiltinData{"asin", BuiltinType::kAsin},
- BuiltinData{"atan", BuiltinType::kAtan},
- BuiltinData{"ceil", BuiltinType::kCeil},
- BuiltinData{"cos", BuiltinType::kCos},
- BuiltinData{"cosh", BuiltinType::kCosh},
- BuiltinData{"exp", BuiltinType::kExp},
- BuiltinData{"exp2", BuiltinType::kExp2},
- BuiltinData{"floor", BuiltinType::kFloor},
- BuiltinData{"fract", BuiltinType::kFract},
- BuiltinData{"inverseSqrt", BuiltinType::kInverseSqrt},
- BuiltinData{"log", BuiltinType::kLog},
- BuiltinData{"log2", BuiltinType::kLog2},
- BuiltinData{"round", BuiltinType::kRound},
- BuiltinData{"sign", BuiltinType::kSign},
- BuiltinData{"sin", BuiltinType::kSin},
- BuiltinData{"sinh", BuiltinType::kSinh},
- BuiltinData{"sqrt", BuiltinType::kSqrt},
- BuiltinData{"tan", BuiltinType::kTan},
- BuiltinData{"tanh", BuiltinType::kTanh},
- BuiltinData{"trunc", BuiltinType::kTrunc}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_SingleParam,
+ testing::Values(BuiltinData{"acos", BuiltinType::kAcos},
+ BuiltinData{"asin", BuiltinType::kAsin},
+ BuiltinData{"atan", BuiltinType::kAtan},
+ BuiltinData{"ceil", BuiltinType::kCeil},
+ BuiltinData{"cos", BuiltinType::kCos},
+ BuiltinData{"cosh", BuiltinType::kCosh},
+ BuiltinData{"exp", BuiltinType::kExp},
+ BuiltinData{"exp2", BuiltinType::kExp2},
+ BuiltinData{"floor", BuiltinType::kFloor},
+ BuiltinData{"fract", BuiltinType::kFract},
+ BuiltinData{"inverseSqrt", BuiltinType::kInverseSqrt},
+ BuiltinData{"log", BuiltinType::kLog},
+ BuiltinData{"log2", BuiltinType::kLog2},
+ BuiltinData{"round", BuiltinType::kRound},
+ BuiltinData{"sign", BuiltinType::kSign},
+ BuiltinData{"sin", BuiltinType::kSin},
+ BuiltinData{"sinh", BuiltinType::kSinh},
+ BuiltinData{"sqrt", BuiltinType::kSqrt},
+ BuiltinData{"tan", BuiltinType::kTan},
+ BuiltinData{"tanh", BuiltinType::kTanh},
+ BuiltinData{"trunc", BuiltinType::kTrunc}));
using ResolverBuiltinDataTest = ResolverTest;
TEST_F(ResolverBuiltinDataTest, ArrayLength_Vector) {
- auto* ary = ty.array<i32>();
- auto* str = Structure("S", {Member("x", ary)});
- Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* ary = ty.array<i32>();
+ auto* str = Structure("S", {Member("x", ary)});
+ Global("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- auto* call = Call("arrayLength", AddressOf(MemberAccessor("a", "x")));
- WrapInFunction(call);
+ auto* call = Call("arrayLength", AddressOf(MemberAccessor("a", "x")));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
}
TEST_F(ResolverBuiltinDataTest, ArrayLength_Error_ArraySized) {
- Global("arr", ty.array<int, 4>(), ast::StorageClass::kPrivate);
- auto* call = Call("arrayLength", AddressOf("arr"));
- WrapInFunction(call);
+ Global("arr", ty.array<int, 4>(), ast::StorageClass::kPrivate);
+ auto* call = Call("arrayLength", AddressOf("arr"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to arrayLength(ptr<private, array<i32, 4>, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to arrayLength(ptr<private, array<i32, 4>, read_write>)
1 candidate function:
arrayLength(ptr<storage, array<T>, A>) -> u32
@@ -664,23 +645,23 @@
}
TEST_F(ResolverBuiltinDataTest, Normalize_Vector) {
- auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_F(ResolverBuiltinDataTest, Normalize_Error_NoParams) {
- auto* call = Call("normalize");
- WrapInFunction(call);
+ auto* call = Call("normalize");
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to normalize()
+ EXPECT_EQ(r()->error(), R"(error: no matching call to normalize()
1 candidate function:
normalize(vecN<f32>) -> vecN<f32>
@@ -688,77 +669,76 @@
}
TEST_F(ResolverBuiltinDataTest, FrexpScalar) {
- auto* call = Call("frexp", 1.0f);
- WrapInFunction(call);
+ auto* call = Call("frexp", 1.0f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- auto* ty = TypeOf(call)->As<sem::Struct>();
- ASSERT_NE(ty, nullptr);
- ASSERT_EQ(ty->Members().size(), 2u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ auto* ty = TypeOf(call)->As<sem::Struct>();
+ ASSERT_NE(ty, nullptr);
+ ASSERT_EQ(ty->Members().size(), 2u);
- auto* sig = ty->Members()[0];
- EXPECT_TRUE(sig->Type()->Is<sem::F32>());
- EXPECT_EQ(sig->Offset(), 0u);
- EXPECT_EQ(sig->Size(), 4u);
- EXPECT_EQ(sig->Align(), 4u);
- EXPECT_EQ(sig->Name(), Sym("sig"));
+ auto* sig = ty->Members()[0];
+ EXPECT_TRUE(sig->Type()->Is<sem::F32>());
+ EXPECT_EQ(sig->Offset(), 0u);
+ EXPECT_EQ(sig->Size(), 4u);
+ EXPECT_EQ(sig->Align(), 4u);
+ EXPECT_EQ(sig->Name(), Sym("sig"));
- auto* exp = ty->Members()[1];
- EXPECT_TRUE(exp->Type()->Is<sem::I32>());
- EXPECT_EQ(exp->Offset(), 4u);
- EXPECT_EQ(exp->Size(), 4u);
- EXPECT_EQ(exp->Align(), 4u);
- EXPECT_EQ(exp->Name(), Sym("exp"));
+ auto* exp = ty->Members()[1];
+ EXPECT_TRUE(exp->Type()->Is<sem::I32>());
+ EXPECT_EQ(exp->Offset(), 4u);
+ EXPECT_EQ(exp->Size(), 4u);
+ EXPECT_EQ(exp->Align(), 4u);
+ EXPECT_EQ(exp->Name(), Sym("exp"));
- EXPECT_EQ(ty->Size(), 8u);
- EXPECT_EQ(ty->SizeNoPadding(), 8u);
+ EXPECT_EQ(ty->Size(), 8u);
+ EXPECT_EQ(ty->SizeNoPadding(), 8u);
}
TEST_F(ResolverBuiltinDataTest, FrexpVector) {
- auto* call = Call("frexp", vec3<f32>());
- WrapInFunction(call);
+ auto* call = Call("frexp", vec3<f32>());
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- auto* ty = TypeOf(call)->As<sem::Struct>();
- ASSERT_NE(ty, nullptr);
- ASSERT_EQ(ty->Members().size(), 2u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ auto* ty = TypeOf(call)->As<sem::Struct>();
+ ASSERT_NE(ty, nullptr);
+ ASSERT_EQ(ty->Members().size(), 2u);
- auto* sig = ty->Members()[0];
- ASSERT_TRUE(sig->Type()->Is<sem::Vector>());
- EXPECT_EQ(sig->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(sig->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sig->Offset(), 0u);
- EXPECT_EQ(sig->Size(), 12u);
- EXPECT_EQ(sig->Align(), 16u);
- EXPECT_EQ(sig->Name(), Sym("sig"));
+ auto* sig = ty->Members()[0];
+ ASSERT_TRUE(sig->Type()->Is<sem::Vector>());
+ EXPECT_EQ(sig->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(sig->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sig->Offset(), 0u);
+ EXPECT_EQ(sig->Size(), 12u);
+ EXPECT_EQ(sig->Align(), 16u);
+ EXPECT_EQ(sig->Name(), Sym("sig"));
- auto* exp = ty->Members()[1];
- ASSERT_TRUE(exp->Type()->Is<sem::Vector>());
- EXPECT_EQ(exp->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(exp->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(exp->Offset(), 16u);
- EXPECT_EQ(exp->Size(), 12u);
- EXPECT_EQ(exp->Align(), 16u);
- EXPECT_EQ(exp->Name(), Sym("exp"));
+ auto* exp = ty->Members()[1];
+ ASSERT_TRUE(exp->Type()->Is<sem::Vector>());
+ EXPECT_EQ(exp->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(exp->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(exp->Offset(), 16u);
+ EXPECT_EQ(exp->Size(), 12u);
+ EXPECT_EQ(exp->Align(), 16u);
+ EXPECT_EQ(exp->Name(), Sym("exp"));
- EXPECT_EQ(ty->Size(), 32u);
- EXPECT_EQ(ty->SizeNoPadding(), 28u);
+ EXPECT_EQ(ty->Size(), 32u);
+ EXPECT_EQ(ty->SizeNoPadding(), 28u);
}
TEST_F(ResolverBuiltinDataTest, Frexp_Error_FirstParamInt) {
- Global("v", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", 1, AddressOf("v"));
- WrapInFunction(call);
+ Global("v", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", 1, AddressOf("v"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to frexp(i32, ptr<workgroup, i32, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to frexp(i32, ptr<workgroup, i32, read_write>)
2 candidate functions:
frexp(f32) -> __frexp_result
@@ -767,15 +747,14 @@
}
TEST_F(ResolverBuiltinDataTest, Frexp_Error_SecondParamFloatPtr) {
- Global("v", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", 1.0f, AddressOf("v"));
- WrapInFunction(call);
+ Global("v", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", 1.0f, AddressOf("v"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to frexp(f32, ptr<workgroup, f32, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to frexp(f32, ptr<workgroup, f32, read_write>)
2 candidate functions:
frexp(f32) -> __frexp_result
@@ -784,12 +763,12 @@
}
TEST_F(ResolverBuiltinDataTest, Frexp_Error_SecondParamNotAPointer) {
- auto* call = Call("frexp", 1.0f, 1);
- WrapInFunction(call);
+ auto* call = Call("frexp", 1.0f, 1);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(f32, i32)
+ EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(f32, i32)
2 candidate functions:
frexp(f32) -> __frexp_result
@@ -798,15 +777,14 @@
}
TEST_F(ResolverBuiltinDataTest, Frexp_Error_VectorSizesDontMatch) {
- Global("v", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", vec2<f32>(1.0f, 2.0f), AddressOf("v"));
- WrapInFunction(call);
+ Global("v", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", vec2<f32>(1.0f, 2.0f), AddressOf("v"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to frexp(vec2<f32>, ptr<workgroup, vec4<i32>, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to frexp(vec2<f32>, ptr<workgroup, vec4<i32>, read_write>)
2 candidate functions:
frexp(vecN<f32>) -> __frexp_result_vecN
@@ -815,77 +793,76 @@
}
TEST_F(ResolverBuiltinDataTest, ModfScalar) {
- auto* call = Call("modf", 1.0f);
- WrapInFunction(call);
+ auto* call = Call("modf", 1.0f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- auto* ty = TypeOf(call)->As<sem::Struct>();
- ASSERT_NE(ty, nullptr);
- ASSERT_EQ(ty->Members().size(), 2u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ auto* ty = TypeOf(call)->As<sem::Struct>();
+ ASSERT_NE(ty, nullptr);
+ ASSERT_EQ(ty->Members().size(), 2u);
- auto* fract = ty->Members()[0];
- EXPECT_TRUE(fract->Type()->Is<sem::F32>());
- EXPECT_EQ(fract->Offset(), 0u);
- EXPECT_EQ(fract->Size(), 4u);
- EXPECT_EQ(fract->Align(), 4u);
- EXPECT_EQ(fract->Name(), Sym("fract"));
+ auto* fract = ty->Members()[0];
+ EXPECT_TRUE(fract->Type()->Is<sem::F32>());
+ EXPECT_EQ(fract->Offset(), 0u);
+ EXPECT_EQ(fract->Size(), 4u);
+ EXPECT_EQ(fract->Align(), 4u);
+ EXPECT_EQ(fract->Name(), Sym("fract"));
- auto* whole = ty->Members()[1];
- EXPECT_TRUE(whole->Type()->Is<sem::F32>());
- EXPECT_EQ(whole->Offset(), 4u);
- EXPECT_EQ(whole->Size(), 4u);
- EXPECT_EQ(whole->Align(), 4u);
- EXPECT_EQ(whole->Name(), Sym("whole"));
+ auto* whole = ty->Members()[1];
+ EXPECT_TRUE(whole->Type()->Is<sem::F32>());
+ EXPECT_EQ(whole->Offset(), 4u);
+ EXPECT_EQ(whole->Size(), 4u);
+ EXPECT_EQ(whole->Align(), 4u);
+ EXPECT_EQ(whole->Name(), Sym("whole"));
- EXPECT_EQ(ty->Size(), 8u);
- EXPECT_EQ(ty->SizeNoPadding(), 8u);
+ EXPECT_EQ(ty->Size(), 8u);
+ EXPECT_EQ(ty->SizeNoPadding(), 8u);
}
TEST_F(ResolverBuiltinDataTest, ModfVector) {
- auto* call = Call("modf", vec3<f32>());
- WrapInFunction(call);
+ auto* call = Call("modf", vec3<f32>());
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- auto* ty = TypeOf(call)->As<sem::Struct>();
- ASSERT_NE(ty, nullptr);
- ASSERT_EQ(ty->Members().size(), 2u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ auto* ty = TypeOf(call)->As<sem::Struct>();
+ ASSERT_NE(ty, nullptr);
+ ASSERT_EQ(ty->Members().size(), 2u);
- auto* fract = ty->Members()[0];
- ASSERT_TRUE(fract->Type()->Is<sem::Vector>());
- EXPECT_EQ(fract->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(fract->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(fract->Offset(), 0u);
- EXPECT_EQ(fract->Size(), 12u);
- EXPECT_EQ(fract->Align(), 16u);
- EXPECT_EQ(fract->Name(), Sym("fract"));
+ auto* fract = ty->Members()[0];
+ ASSERT_TRUE(fract->Type()->Is<sem::Vector>());
+ EXPECT_EQ(fract->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(fract->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(fract->Offset(), 0u);
+ EXPECT_EQ(fract->Size(), 12u);
+ EXPECT_EQ(fract->Align(), 16u);
+ EXPECT_EQ(fract->Name(), Sym("fract"));
- auto* whole = ty->Members()[1];
- ASSERT_TRUE(whole->Type()->Is<sem::Vector>());
- EXPECT_EQ(whole->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(whole->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(whole->Offset(), 16u);
- EXPECT_EQ(whole->Size(), 12u);
- EXPECT_EQ(whole->Align(), 16u);
- EXPECT_EQ(whole->Name(), Sym("whole"));
+ auto* whole = ty->Members()[1];
+ ASSERT_TRUE(whole->Type()->Is<sem::Vector>());
+ EXPECT_EQ(whole->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(whole->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(whole->Offset(), 16u);
+ EXPECT_EQ(whole->Size(), 12u);
+ EXPECT_EQ(whole->Align(), 16u);
+ EXPECT_EQ(whole->Name(), Sym("whole"));
- EXPECT_EQ(ty->Size(), 32u);
- EXPECT_EQ(ty->SizeNoPadding(), 28u);
+ EXPECT_EQ(ty->Size(), 32u);
+ EXPECT_EQ(ty->SizeNoPadding(), 28u);
}
TEST_F(ResolverBuiltinDataTest, Modf_Error_FirstParamInt) {
- Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", 1, AddressOf("whole"));
- WrapInFunction(call);
+ Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", 1, AddressOf("whole"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to modf(i32, ptr<workgroup, f32, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to modf(i32, ptr<workgroup, f32, read_write>)
2 candidate functions:
modf(f32) -> __modf_result
@@ -894,15 +871,14 @@
}
TEST_F(ResolverBuiltinDataTest, Modf_Error_SecondParamIntPtr) {
- Global("whole", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", 1.0f, AddressOf("whole"));
- WrapInFunction(call);
+ Global("whole", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", 1.0f, AddressOf("whole"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to modf(f32, ptr<workgroup, i32, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to modf(f32, ptr<workgroup, i32, read_write>)
2 candidate functions:
modf(f32) -> __modf_result
@@ -911,12 +887,12 @@
}
TEST_F(ResolverBuiltinDataTest, Modf_Error_SecondParamNotAPointer) {
- auto* call = Call("modf", 1.0f, 1.0f);
- WrapInFunction(call);
+ auto* call = Call("modf", 1.0f, 1.0f);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to modf(f32, f32)
+ EXPECT_EQ(r()->error(), R"(error: no matching call to modf(f32, f32)
2 candidate functions:
modf(f32) -> __modf_result
@@ -925,15 +901,14 @@
}
TEST_F(ResolverBuiltinDataTest, Modf_Error_VectorSizesDontMatch) {
- Global("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", vec2<f32>(1.0f, 2.0f), AddressOf("whole"));
- WrapInFunction(call);
+ Global("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", vec2<f32>(1.0f, 2.0f), AddressOf("whole"));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: no matching call to modf(vec2<f32>, ptr<workgroup, vec4<f32>, read_write>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to modf(vec2<f32>, ptr<workgroup, vec4<f32>, read_write>)
2 candidate functions:
modf(vecN<f32>) -> __modf_result_vecN
@@ -941,234 +916,222 @@
)");
}
-using ResolverBuiltinTest_SingleParam_FloatOrInt =
- ResolverTestWithParam<BuiltinData>;
+using ResolverBuiltinTest_SingleParam_FloatOrInt = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Float_Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1.f);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Float_Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Sint_Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, -1);
- WrapInFunction(call);
+ auto* call = Call(param.name, -1);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
}
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Sint_Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<i32>(1, 1, 3));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Uint_Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1u);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1u);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
}
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Uint_Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_SingleParam_FloatOrInt, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T) -> T where: T is f32, i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(T) -> T where: T is f32, i32 or u32\n " +
+ std::string(param.name) + "(vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
ResolverBuiltinTest_SingleParam_FloatOrInt,
- testing::Values(BuiltinData{"abs",
- BuiltinType::kAbs}));
+ testing::Values(BuiltinData{"abs", BuiltinType::kAbs}));
TEST_F(ResolverBuiltinTest, Length_Scalar) {
- auto* call = Call("length", 1.f);
- WrapInFunction(call);
+ auto* call = Call("length", 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_F(ResolverBuiltinTest, Length_FloatVector) {
- auto* call = Call("length", vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call("length", vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
using ResolverBuiltinTest_TwoParam = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_TwoParam, Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1.f, 1.f);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1.f, 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_P(ResolverBuiltinTest_TwoParam, Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_TwoParam, Error_NoTooManyParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1, 2, 3);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1, 2, 3);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "(i32, i32, i32)\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32, f32) -> f32\n " +
- std::string(param.name) +
- "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "(i32, i32, i32)\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32, f32) -> f32\n " +
+ std::string(param.name) + "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
}
TEST_P(ResolverBuiltinTest_TwoParam, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32, f32) -> f32\n " +
- std::string(param.name) +
- "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32, f32) -> f32\n " +
+ std::string(param.name) + "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_TwoParam,
- testing::Values(BuiltinData{"atan2", BuiltinType::kAtan2},
- BuiltinData{"pow", BuiltinType::kPow},
- BuiltinData{"step", BuiltinType::kStep}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_TwoParam,
+ testing::Values(BuiltinData{"atan2", BuiltinType::kAtan2},
+ BuiltinData{"pow", BuiltinType::kPow},
+ BuiltinData{"step", BuiltinType::kStep}));
TEST_F(ResolverBuiltinTest, Distance_Scalar) {
- auto* call = Call("distance", 1.f, 1.f);
- WrapInFunction(call);
+ auto* call = Call("distance", 1.f, 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_F(ResolverBuiltinTest, Distance_Vector) {
- auto* call = Call("distance", vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call("distance", vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverBuiltinTest, Cross) {
- auto* call =
- Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_F(ResolverBuiltinTest, Cross_Error_NoArgs) {
- auto* call = Call("cross");
- WrapInFunction(call);
+ auto* call = Call("cross");
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to cross()
+ EXPECT_EQ(r()->error(), R"(error: no matching call to cross()
1 candidate function:
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
@@ -1176,12 +1139,12 @@
}
TEST_F(ResolverBuiltinTest, Cross_Error_Scalar) {
- auto* call = Call("cross", 1.0f, 1.0f);
- WrapInFunction(call);
+ auto* call = Call("cross", 1.0f, 1.0f);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to cross(f32, f32)
+ EXPECT_EQ(r()->error(), R"(error: no matching call to cross(f32, f32)
1 candidate function:
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
@@ -1189,13 +1152,13 @@
}
TEST_F(ResolverBuiltinTest, Cross_Error_Vec3Int) {
- auto* call = Call("cross", vec3<i32>(1, 2, 3), vec3<i32>(1, 2, 3));
- WrapInFunction(call);
+ auto* call = Call("cross", vec3<i32>(1, 2, 3), vec3<i32>(1, 2, 3));
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to cross(vec3<i32>, vec3<i32>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to cross(vec3<i32>, vec3<i32>)
1 candidate function:
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
@@ -1203,15 +1166,15 @@
}
TEST_F(ResolverBuiltinTest, Cross_Error_Vec4) {
- auto* call = Call("cross", vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f),
- vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
+ auto* call =
+ Call("cross", vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f), vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
- WrapInFunction(call);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to cross(vec4<f32>, vec4<f32>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to cross(vec4<f32>, vec4<f32>)
1 candidate function:
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
@@ -1219,38 +1182,38 @@
}
TEST_F(ResolverBuiltinTest, Cross_Error_TooManyParams) {
- auto* call = Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f),
- vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
+ auto* call = Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f),
+ vec3<f32>(1.0f, 2.0f, 3.0f));
- WrapInFunction(call);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
1 candidate function:
cross(vec3<f32>, vec3<f32>) -> vec3<f32>
)");
}
TEST_F(ResolverBuiltinTest, Normalize) {
- auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_F(ResolverBuiltinTest, Normalize_NoArgs) {
- auto* call = Call("normalize");
- WrapInFunction(call);
+ auto* call = Call("normalize");
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to normalize()
+ EXPECT_EQ(r()->error(), R"(error: no matching call to normalize()
1 candidate function:
normalize(vecN<f32>) -> vecN<f32>
@@ -1259,351 +1222,339 @@
using ResolverBuiltinTest_ThreeParam = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_ThreeParam, Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1.f, 1.f, 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_P(ResolverBuiltinTest_ThreeParam, Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f),
+ vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_ThreeParam, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name) + "()"));
+ EXPECT_THAT(r()->error(),
+ HasSubstr("error: no matching call to " + std::string(param.name) + "()"));
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_ThreeParam,
- testing::Values(BuiltinData{"mix", BuiltinType::kMix},
- BuiltinData{"smoothstep", BuiltinType::kSmoothstep},
- BuiltinData{"smoothStep", BuiltinType::kSmoothStep},
- BuiltinData{"fma", BuiltinType::kFma}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_ThreeParam,
+ testing::Values(BuiltinData{"mix", BuiltinType::kMix},
+ BuiltinData{"smoothstep", BuiltinType::kSmoothstep},
+ BuiltinData{"smoothStep", BuiltinType::kSmoothStep},
+ BuiltinData{"fma", BuiltinType::kFma}));
-using ResolverBuiltinTest_ThreeParam_FloatOrInt =
- ResolverTestWithParam<BuiltinData>;
+using ResolverBuiltinTest_ThreeParam_FloatOrInt = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Float_Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1.f, 1.f, 1.f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
}
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Float_Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f),
+ vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Sint_Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1, 1, 1);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1, 1, 1);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
}
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Sint_Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3),
- vec3<i32>(1, 1, 3));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Uint_Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1u, 1u, 1u);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1u, 1u, 1u);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
}
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Uint_Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u),
- vec3<u32>(1u, 1u, 3u));
- WrapInFunction(call);
+ auto* call =
+ Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_ThreeParam_FloatOrInt, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T, T, T) -> T where: T is f32, i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>, vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 "
- "or u32\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) +
+ "(T, T, T) -> T where: T is f32, i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>, vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 "
+ "or u32\n");
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
ResolverBuiltinTest_ThreeParam_FloatOrInt,
- testing::Values(BuiltinData{"clamp",
- BuiltinType::kClamp}));
+ testing::Values(BuiltinData{"clamp", BuiltinType::kClamp}));
using ResolverBuiltinTest_Int_SingleParam = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_Int_SingleParam, Scalar) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_integer_scalar());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_integer_scalar());
}
TEST_P(ResolverBuiltinTest_Int_SingleParam, Vector) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<i32>(1, 1, 3));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_Int_SingleParam, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: no matching call to " +
- std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T) -> T where: T is i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>) -> vecN<T> where: T is i32 or u32\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(T) -> T where: T is i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>) -> vecN<T> where: T is i32 or u32\n");
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_Int_SingleParam,
- testing::Values(BuiltinData{"countOneBits", BuiltinType::kCountOneBits},
- BuiltinData{"reverseBits", BuiltinType::kReverseBits}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_Int_SingleParam,
+ testing::Values(BuiltinData{"countOneBits", BuiltinType::kCountOneBits},
+ BuiltinData{"reverseBits", BuiltinType::kReverseBits}));
-using ResolverBuiltinTest_FloatOrInt_TwoParam =
- ResolverTestWithParam<BuiltinData>;
+using ResolverBuiltinTest_FloatOrInt_TwoParam = ResolverTestWithParam<BuiltinData>;
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Scalar_Signed) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1, 1);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1, 1);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
}
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Scalar_Unsigned) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1u, 1u);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1u, 1u);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::U32>());
}
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Scalar_Float) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, 1.0f, 1.0f);
- WrapInFunction(call);
+ auto* call = Call(param.name, 1.0f, 1.0f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Vector_Signed) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Vector_Unsigned) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Vector_Float) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call =
- Call(param.name, vec3<f32>(1.f, 1.f, 3.f), vec3<f32>(1.f, 1.f, 3.f));
- WrapInFunction(call);
+ auto* call = Call(param.name, vec3<f32>(1.f, 1.f, 3.f), vec3<f32>(1.f, 1.f, 3.f));
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<sem::Vector>()->Width(), 3u);
}
TEST_P(ResolverBuiltinTest_FloatOrInt_TwoParam, Error_NoParams) {
- auto param = GetParam();
+ auto param = GetParam();
- auto* call = Call(param.name);
- WrapInFunction(call);
+ auto* call = Call(param.name);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T, T) -> T where: T is f32, i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
+ EXPECT_EQ(r()->error(), "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) +
+ "(T, T) -> T where: T is f32, i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
ResolverBuiltinTest_FloatOrInt_TwoParam,
testing::Values(BuiltinData{"min", BuiltinType::kMin},
- BuiltinData{"max",
- BuiltinType::kMax}));
+ BuiltinData{"max", BuiltinType::kMax}));
TEST_F(ResolverBuiltinTest, Determinant_2x2) {
- Global("var", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
+ Global("var", ty.mat2x2<f32>(), ast::StorageClass::kPrivate);
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverBuiltinTest, Determinant_3x3) {
- Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
+ Global("var", ty.mat3x3<f32>(), ast::StorageClass::kPrivate);
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverBuiltinTest, Determinant_4x4) {
- Global("var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
+ Global("var", ty.mat4x4<f32>(), ast::StorageClass::kPrivate);
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverBuiltinTest, Determinant_NotSquare) {
- Global("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
+ Global("var", ty.mat2x3<f32>(), ast::StorageClass::kPrivate);
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(mat2x3<f32>)
+ EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(mat2x3<f32>)
1 candidate function:
determinant(matNxN<f32>) -> f32
@@ -1611,378 +1562,374 @@
}
TEST_F(ResolverBuiltinTest, Determinant_NotMatrix) {
- Global("var", ty.f32(), ast::StorageClass::kPrivate);
+ Global("var", ty.f32(), ast::StorageClass::kPrivate);
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(f32)
+ EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(f32)
1 candidate function:
determinant(matNxN<f32>) -> f32
)");
}
-using ResolverBuiltinTest_Texture =
- ResolverTestWithParam<ast::builtin::test::TextureOverloadCase>;
+using ResolverBuiltinTest_Texture = ResolverTestWithParam<ast::builtin::test::TextureOverloadCase>;
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverBuiltinTest_Texture,
- testing::ValuesIn(ast::builtin::test::TextureOverloadCase::ValidCases()));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverBuiltinTest_Texture,
+ testing::ValuesIn(ast::builtin::test::TextureOverloadCase::ValidCases()));
-std::string to_str(const std::string& function,
- const sem::ParameterList& params) {
- std::stringstream out;
- out << function << "(";
- bool first = true;
- for (auto* param : params) {
- if (!first) {
- out << ", ";
+std::string to_str(const std::string& function, const sem::ParameterList& params) {
+ std::stringstream out;
+ out << function << "(";
+ bool first = true;
+ for (auto* param : params) {
+ if (!first) {
+ out << ", ";
+ }
+ out << sem::str(param->Usage());
+ first = false;
}
- out << sem::str(param->Usage());
- first = false;
- }
- out << ")";
- return out.str();
+ out << ")";
+ return out.str();
}
-const char* expected_texture_overload(
- ast::builtin::test::ValidTextureOverload overload) {
- using ValidTextureOverload = ast::builtin::test::ValidTextureOverload;
- switch (overload) {
- case ValidTextureOverload::kDimensions1d:
- case ValidTextureOverload::kDimensions2d:
- case ValidTextureOverload::kDimensions2dArray:
- case ValidTextureOverload::kDimensions3d:
- case ValidTextureOverload::kDimensionsCube:
- case ValidTextureOverload::kDimensionsCubeArray:
- case ValidTextureOverload::kDimensionsMultisampled2d:
- case ValidTextureOverload::kDimensionsDepth2d:
- case ValidTextureOverload::kDimensionsDepth2dArray:
- case ValidTextureOverload::kDimensionsDepthCube:
- case ValidTextureOverload::kDimensionsDepthCubeArray:
- case ValidTextureOverload::kDimensionsDepthMultisampled2d:
- case ValidTextureOverload::kDimensionsStorageWO1d:
- case ValidTextureOverload::kDimensionsStorageWO2d:
- case ValidTextureOverload::kDimensionsStorageWO2dArray:
- case ValidTextureOverload::kDimensionsStorageWO3d:
- return R"(textureDimensions(texture))";
- case ValidTextureOverload::kGather2dF32:
- return R"(textureGather(component, texture, sampler, coords))";
- case ValidTextureOverload::kGather2dOffsetF32:
- return R"(textureGather(component, texture, sampler, coords, offset))";
- case ValidTextureOverload::kGather2dArrayF32:
- return R"(textureGather(component, texture, sampler, coords, array_index))";
- case ValidTextureOverload::kGather2dArrayOffsetF32:
- return R"(textureGather(component, texture, sampler, coords, array_index, offset))";
- case ValidTextureOverload::kGatherCubeF32:
- return R"(textureGather(component, texture, sampler, coords))";
- case ValidTextureOverload::kGatherCubeArrayF32:
- return R"(textureGather(component, texture, sampler, coords, array_index))";
- case ValidTextureOverload::kGatherDepth2dF32:
- return R"(textureGather(texture, sampler, coords))";
- case ValidTextureOverload::kGatherDepth2dOffsetF32:
- return R"(textureGather(texture, sampler, coords, offset))";
- case ValidTextureOverload::kGatherDepth2dArrayF32:
- return R"(textureGather(texture, sampler, coords, array_index))";
- case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
- return R"(textureGather(texture, sampler, coords, array_index, offset))";
- case ValidTextureOverload::kGatherDepthCubeF32:
- return R"(textureGather(texture, sampler, coords))";
- case ValidTextureOverload::kGatherDepthCubeArrayF32:
- return R"(textureGather(texture, sampler, coords, array_index))";
- case ValidTextureOverload::kGatherCompareDepth2dF32:
- return R"(textureGatherCompare(texture, sampler, coords, depth_ref))";
- case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
- return R"(textureGatherCompare(texture, sampler, coords, depth_ref, offset))";
- case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
- return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref))";
- case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
- return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref, offset))";
- case ValidTextureOverload::kGatherCompareDepthCubeF32:
- return R"(textureGatherCompare(texture, sampler, coords, depth_ref))";
- case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
- return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref))";
- case ValidTextureOverload::kNumLayers2dArray:
- case ValidTextureOverload::kNumLayersCubeArray:
- case ValidTextureOverload::kNumLayersDepth2dArray:
- case ValidTextureOverload::kNumLayersDepthCubeArray:
- case ValidTextureOverload::kNumLayersStorageWO2dArray:
- return R"(textureNumLayers(texture))";
- case ValidTextureOverload::kNumLevels2d:
- case ValidTextureOverload::kNumLevels2dArray:
- case ValidTextureOverload::kNumLevels3d:
- case ValidTextureOverload::kNumLevelsCube:
- case ValidTextureOverload::kNumLevelsCubeArray:
- case ValidTextureOverload::kNumLevelsDepth2d:
- case ValidTextureOverload::kNumLevelsDepth2dArray:
- case ValidTextureOverload::kNumLevelsDepthCube:
- case ValidTextureOverload::kNumLevelsDepthCubeArray:
- return R"(textureNumLevels(texture))";
- case ValidTextureOverload::kNumSamplesDepthMultisampled2d:
- case ValidTextureOverload::kNumSamplesMultisampled2d:
- return R"(textureNumSamples(texture))";
- case ValidTextureOverload::kDimensions2dLevel:
- case ValidTextureOverload::kDimensions2dArrayLevel:
- case ValidTextureOverload::kDimensions3dLevel:
- case ValidTextureOverload::kDimensionsCubeLevel:
- case ValidTextureOverload::kDimensionsCubeArrayLevel:
- case ValidTextureOverload::kDimensionsDepth2dLevel:
- case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
- case ValidTextureOverload::kDimensionsDepthCubeLevel:
- case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
- return R"(textureDimensions(texture, level))";
- case ValidTextureOverload::kSample1dF32:
- return R"(textureSample(texture, sampler, coords))";
- case ValidTextureOverload::kSample2dF32:
- return R"(textureSample(texture, sampler, coords))";
- case ValidTextureOverload::kSample2dOffsetF32:
- return R"(textureSample(texture, sampler, coords, offset))";
- case ValidTextureOverload::kSample2dArrayF32:
- return R"(textureSample(texture, sampler, coords, array_index))";
- case ValidTextureOverload::kSample2dArrayOffsetF32:
- return R"(textureSample(texture, sampler, coords, array_index, offset))";
- case ValidTextureOverload::kSample3dF32:
- return R"(textureSample(texture, sampler, coords))";
- case ValidTextureOverload::kSample3dOffsetF32:
- return R"(textureSample(texture, sampler, coords, offset))";
- case ValidTextureOverload::kSampleCubeF32:
- return R"(textureSample(texture, sampler, coords))";
- case ValidTextureOverload::kSampleCubeArrayF32:
- return R"(textureSample(texture, sampler, coords, array_index))";
- case ValidTextureOverload::kSampleDepth2dF32:
- return R"(textureSample(texture, sampler, coords))";
- case ValidTextureOverload::kSampleDepth2dOffsetF32:
- return R"(textureSample(texture, sampler, coords, offset))";
- case ValidTextureOverload::kSampleDepth2dArrayF32:
- return R"(textureSample(texture, sampler, coords, array_index))";
- case ValidTextureOverload::kSampleDepth2dArrayOffsetF32:
- return R"(textureSample(texture, sampler, coords, array_index, offset))";
- case ValidTextureOverload::kSampleDepthCubeF32:
- return R"(textureSample(texture, sampler, coords))";
- case ValidTextureOverload::kSampleDepthCubeArrayF32:
- return R"(textureSample(texture, sampler, coords, array_index))";
- case ValidTextureOverload::kSampleBias2dF32:
- return R"(textureSampleBias(texture, sampler, coords, bias))";
- case ValidTextureOverload::kSampleBias2dOffsetF32:
- return R"(textureSampleBias(texture, sampler, coords, bias, offset))";
- case ValidTextureOverload::kSampleBias2dArrayF32:
- return R"(textureSampleBias(texture, sampler, coords, array_index, bias))";
- case ValidTextureOverload::kSampleBias2dArrayOffsetF32:
- return R"(textureSampleBias(texture, sampler, coords, array_index, bias, offset))";
- case ValidTextureOverload::kSampleBias3dF32:
- return R"(textureSampleBias(texture, sampler, coords, bias))";
- case ValidTextureOverload::kSampleBias3dOffsetF32:
- return R"(textureSampleBias(texture, sampler, coords, bias, offset))";
- case ValidTextureOverload::kSampleBiasCubeF32:
- return R"(textureSampleBias(texture, sampler, coords, bias))";
- case ValidTextureOverload::kSampleBiasCubeArrayF32:
- return R"(textureSampleBias(texture, sampler, coords, array_index, bias))";
- case ValidTextureOverload::kSampleLevel2dF32:
- return R"(textureSampleLevel(texture, sampler, coords, level))";
- case ValidTextureOverload::kSampleLevel2dOffsetF32:
- return R"(textureSampleLevel(texture, sampler, coords, level, offset))";
- case ValidTextureOverload::kSampleLevel2dArrayF32:
- return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
- case ValidTextureOverload::kSampleLevel2dArrayOffsetF32:
- return R"(textureSampleLevel(texture, sampler, coords, array_index, level, offset))";
- case ValidTextureOverload::kSampleLevel3dF32:
- return R"(textureSampleLevel(texture, sampler, coords, level))";
- case ValidTextureOverload::kSampleLevel3dOffsetF32:
- return R"(textureSampleLevel(texture, sampler, coords, level, offset))";
- case ValidTextureOverload::kSampleLevelCubeF32:
- return R"(textureSampleLevel(texture, sampler, coords, level))";
- case ValidTextureOverload::kSampleLevelCubeArrayF32:
- return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
- case ValidTextureOverload::kSampleLevelDepth2dF32:
- return R"(textureSampleLevel(texture, sampler, coords, level))";
- case ValidTextureOverload::kSampleLevelDepth2dOffsetF32:
- return R"(textureSampleLevel(texture, sampler, coords, level, offset))";
- case ValidTextureOverload::kSampleLevelDepth2dArrayF32:
- return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
- case ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32:
- return R"(textureSampleLevel(texture, sampler, coords, array_index, level, offset))";
- case ValidTextureOverload::kSampleLevelDepthCubeF32:
- return R"(textureSampleLevel(texture, sampler, coords, level))";
- case ValidTextureOverload::kSampleLevelDepthCubeArrayF32:
- return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
- case ValidTextureOverload::kSampleGrad2dF32:
- return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy))";
- case ValidTextureOverload::kSampleGrad2dOffsetF32:
- return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy, offset))";
- case ValidTextureOverload::kSampleGrad2dArrayF32:
- return R"(textureSampleGrad(texture, sampler, coords, array_index, ddx, ddy))";
- case ValidTextureOverload::kSampleGrad2dArrayOffsetF32:
- return R"(textureSampleGrad(texture, sampler, coords, array_index, ddx, ddy, offset))";
- case ValidTextureOverload::kSampleGrad3dF32:
- return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy))";
- case ValidTextureOverload::kSampleGrad3dOffsetF32:
- return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy, offset))";
- case ValidTextureOverload::kSampleGradCubeF32:
- return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy))";
- case ValidTextureOverload::kSampleGradCubeArrayF32:
- return R"(textureSampleGrad(texture, sampler, coords, array_index, ddx, ddy))";
- case ValidTextureOverload::kSampleCompareDepth2dF32:
- return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
- case ValidTextureOverload::kSampleCompareDepth2dOffsetF32:
- return R"(textureSampleCompare(texture, sampler, coords, depth_ref, offset))";
- case ValidTextureOverload::kSampleCompareDepth2dArrayF32:
- return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
- case ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32:
- return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref, offset))";
- case ValidTextureOverload::kSampleCompareDepthCubeF32:
- return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
- case ValidTextureOverload::kSampleCompareDepthCubeArrayF32:
- return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
- case ValidTextureOverload::kSampleCompareLevelDepth2dF32:
- return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
- case ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32:
- return R"(textureSampleCompare(texture, sampler, coords, depth_ref, offset))";
- case ValidTextureOverload::kSampleCompareLevelDepth2dArrayF32:
- return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
- case ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32:
- return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref, offset))";
- case ValidTextureOverload::kSampleCompareLevelDepthCubeF32:
- return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
- case ValidTextureOverload::kSampleCompareLevelDepthCubeArrayF32:
- return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
- case ValidTextureOverload::kLoad1dLevelF32:
- case ValidTextureOverload::kLoad1dLevelU32:
- case ValidTextureOverload::kLoad1dLevelI32:
- case ValidTextureOverload::kLoad2dLevelF32:
- case ValidTextureOverload::kLoad2dLevelU32:
- case ValidTextureOverload::kLoad2dLevelI32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad2dArrayLevelF32:
- case ValidTextureOverload::kLoad2dArrayLevelU32:
- case ValidTextureOverload::kLoad2dArrayLevelI32:
- return R"(textureLoad(texture, coords, array_index, level))";
- case ValidTextureOverload::kLoad3dLevelF32:
- case ValidTextureOverload::kLoad3dLevelU32:
- case ValidTextureOverload::kLoad3dLevelI32:
- case ValidTextureOverload::kLoadDepth2dLevelF32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoadDepthMultisampled2dF32:
- case ValidTextureOverload::kLoadMultisampled2dF32:
- case ValidTextureOverload::kLoadMultisampled2dU32:
- case ValidTextureOverload::kLoadMultisampled2dI32:
- return R"(textureLoad(texture, coords, sample_index))";
- case ValidTextureOverload::kLoadDepth2dArrayLevelF32:
- return R"(textureLoad(texture, coords, array_index, level))";
- case ValidTextureOverload::kStoreWO1dRgba32float:
- case ValidTextureOverload::kStoreWO2dRgba32float:
- case ValidTextureOverload::kStoreWO3dRgba32float:
- return R"(textureStore(texture, coords, value))";
- case ValidTextureOverload::kStoreWO2dArrayRgba32float:
- return R"(textureStore(texture, coords, array_index, value))";
- }
- return "<unmatched texture overload>";
+const char* expected_texture_overload(ast::builtin::test::ValidTextureOverload overload) {
+ using ValidTextureOverload = ast::builtin::test::ValidTextureOverload;
+ switch (overload) {
+ case ValidTextureOverload::kDimensions1d:
+ case ValidTextureOverload::kDimensions2d:
+ case ValidTextureOverload::kDimensions2dArray:
+ case ValidTextureOverload::kDimensions3d:
+ case ValidTextureOverload::kDimensionsCube:
+ case ValidTextureOverload::kDimensionsCubeArray:
+ case ValidTextureOverload::kDimensionsMultisampled2d:
+ case ValidTextureOverload::kDimensionsDepth2d:
+ case ValidTextureOverload::kDimensionsDepth2dArray:
+ case ValidTextureOverload::kDimensionsDepthCube:
+ case ValidTextureOverload::kDimensionsDepthCubeArray:
+ case ValidTextureOverload::kDimensionsDepthMultisampled2d:
+ case ValidTextureOverload::kDimensionsStorageWO1d:
+ case ValidTextureOverload::kDimensionsStorageWO2d:
+ case ValidTextureOverload::kDimensionsStorageWO2dArray:
+ case ValidTextureOverload::kDimensionsStorageWO3d:
+ return R"(textureDimensions(texture))";
+ case ValidTextureOverload::kGather2dF32:
+ return R"(textureGather(component, texture, sampler, coords))";
+ case ValidTextureOverload::kGather2dOffsetF32:
+ return R"(textureGather(component, texture, sampler, coords, offset))";
+ case ValidTextureOverload::kGather2dArrayF32:
+ return R"(textureGather(component, texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kGather2dArrayOffsetF32:
+ return R"(textureGather(component, texture, sampler, coords, array_index, offset))";
+ case ValidTextureOverload::kGatherCubeF32:
+ return R"(textureGather(component, texture, sampler, coords))";
+ case ValidTextureOverload::kGatherCubeArrayF32:
+ return R"(textureGather(component, texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kGatherDepth2dF32:
+ return R"(textureGather(texture, sampler, coords))";
+ case ValidTextureOverload::kGatherDepth2dOffsetF32:
+ return R"(textureGather(texture, sampler, coords, offset))";
+ case ValidTextureOverload::kGatherDepth2dArrayF32:
+ return R"(textureGather(texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kGatherDepth2dArrayOffsetF32:
+ return R"(textureGather(texture, sampler, coords, array_index, offset))";
+ case ValidTextureOverload::kGatherDepthCubeF32:
+ return R"(textureGather(texture, sampler, coords))";
+ case ValidTextureOverload::kGatherDepthCubeArrayF32:
+ return R"(textureGather(texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kGatherCompareDepth2dF32:
+ return R"(textureGatherCompare(texture, sampler, coords, depth_ref))";
+ case ValidTextureOverload::kGatherCompareDepth2dOffsetF32:
+ return R"(textureGatherCompare(texture, sampler, coords, depth_ref, offset))";
+ case ValidTextureOverload::kGatherCompareDepth2dArrayF32:
+ return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref))";
+ case ValidTextureOverload::kGatherCompareDepth2dArrayOffsetF32:
+ return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref, offset))";
+ case ValidTextureOverload::kGatherCompareDepthCubeF32:
+ return R"(textureGatherCompare(texture, sampler, coords, depth_ref))";
+ case ValidTextureOverload::kGatherCompareDepthCubeArrayF32:
+ return R"(textureGatherCompare(texture, sampler, coords, array_index, depth_ref))";
+ case ValidTextureOverload::kNumLayers2dArray:
+ case ValidTextureOverload::kNumLayersCubeArray:
+ case ValidTextureOverload::kNumLayersDepth2dArray:
+ case ValidTextureOverload::kNumLayersDepthCubeArray:
+ case ValidTextureOverload::kNumLayersStorageWO2dArray:
+ return R"(textureNumLayers(texture))";
+ case ValidTextureOverload::kNumLevels2d:
+ case ValidTextureOverload::kNumLevels2dArray:
+ case ValidTextureOverload::kNumLevels3d:
+ case ValidTextureOverload::kNumLevelsCube:
+ case ValidTextureOverload::kNumLevelsCubeArray:
+ case ValidTextureOverload::kNumLevelsDepth2d:
+ case ValidTextureOverload::kNumLevelsDepth2dArray:
+ case ValidTextureOverload::kNumLevelsDepthCube:
+ case ValidTextureOverload::kNumLevelsDepthCubeArray:
+ return R"(textureNumLevels(texture))";
+ case ValidTextureOverload::kNumSamplesDepthMultisampled2d:
+ case ValidTextureOverload::kNumSamplesMultisampled2d:
+ return R"(textureNumSamples(texture))";
+ case ValidTextureOverload::kDimensions2dLevel:
+ case ValidTextureOverload::kDimensions2dArrayLevel:
+ case ValidTextureOverload::kDimensions3dLevel:
+ case ValidTextureOverload::kDimensionsCubeLevel:
+ case ValidTextureOverload::kDimensionsCubeArrayLevel:
+ case ValidTextureOverload::kDimensionsDepth2dLevel:
+ case ValidTextureOverload::kDimensionsDepth2dArrayLevel:
+ case ValidTextureOverload::kDimensionsDepthCubeLevel:
+ case ValidTextureOverload::kDimensionsDepthCubeArrayLevel:
+ return R"(textureDimensions(texture, level))";
+ case ValidTextureOverload::kSample1dF32:
+ return R"(textureSample(texture, sampler, coords))";
+ case ValidTextureOverload::kSample2dF32:
+ return R"(textureSample(texture, sampler, coords))";
+ case ValidTextureOverload::kSample2dOffsetF32:
+ return R"(textureSample(texture, sampler, coords, offset))";
+ case ValidTextureOverload::kSample2dArrayF32:
+ return R"(textureSample(texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kSample2dArrayOffsetF32:
+ return R"(textureSample(texture, sampler, coords, array_index, offset))";
+ case ValidTextureOverload::kSample3dF32:
+ return R"(textureSample(texture, sampler, coords))";
+ case ValidTextureOverload::kSample3dOffsetF32:
+ return R"(textureSample(texture, sampler, coords, offset))";
+ case ValidTextureOverload::kSampleCubeF32:
+ return R"(textureSample(texture, sampler, coords))";
+ case ValidTextureOverload::kSampleCubeArrayF32:
+ return R"(textureSample(texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kSampleDepth2dF32:
+ return R"(textureSample(texture, sampler, coords))";
+ case ValidTextureOverload::kSampleDepth2dOffsetF32:
+ return R"(textureSample(texture, sampler, coords, offset))";
+ case ValidTextureOverload::kSampleDepth2dArrayF32:
+ return R"(textureSample(texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kSampleDepth2dArrayOffsetF32:
+ return R"(textureSample(texture, sampler, coords, array_index, offset))";
+ case ValidTextureOverload::kSampleDepthCubeF32:
+ return R"(textureSample(texture, sampler, coords))";
+ case ValidTextureOverload::kSampleDepthCubeArrayF32:
+ return R"(textureSample(texture, sampler, coords, array_index))";
+ case ValidTextureOverload::kSampleBias2dF32:
+ return R"(textureSampleBias(texture, sampler, coords, bias))";
+ case ValidTextureOverload::kSampleBias2dOffsetF32:
+ return R"(textureSampleBias(texture, sampler, coords, bias, offset))";
+ case ValidTextureOverload::kSampleBias2dArrayF32:
+ return R"(textureSampleBias(texture, sampler, coords, array_index, bias))";
+ case ValidTextureOverload::kSampleBias2dArrayOffsetF32:
+ return R"(textureSampleBias(texture, sampler, coords, array_index, bias, offset))";
+ case ValidTextureOverload::kSampleBias3dF32:
+ return R"(textureSampleBias(texture, sampler, coords, bias))";
+ case ValidTextureOverload::kSampleBias3dOffsetF32:
+ return R"(textureSampleBias(texture, sampler, coords, bias, offset))";
+ case ValidTextureOverload::kSampleBiasCubeF32:
+ return R"(textureSampleBias(texture, sampler, coords, bias))";
+ case ValidTextureOverload::kSampleBiasCubeArrayF32:
+ return R"(textureSampleBias(texture, sampler, coords, array_index, bias))";
+ case ValidTextureOverload::kSampleLevel2dF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level))";
+ case ValidTextureOverload::kSampleLevel2dOffsetF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level, offset))";
+ case ValidTextureOverload::kSampleLevel2dArrayF32:
+ return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
+ case ValidTextureOverload::kSampleLevel2dArrayOffsetF32:
+ return R"(textureSampleLevel(texture, sampler, coords, array_index, level, offset))";
+ case ValidTextureOverload::kSampleLevel3dF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level))";
+ case ValidTextureOverload::kSampleLevel3dOffsetF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level, offset))";
+ case ValidTextureOverload::kSampleLevelCubeF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level))";
+ case ValidTextureOverload::kSampleLevelCubeArrayF32:
+ return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
+ case ValidTextureOverload::kSampleLevelDepth2dF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level))";
+ case ValidTextureOverload::kSampleLevelDepth2dOffsetF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level, offset))";
+ case ValidTextureOverload::kSampleLevelDepth2dArrayF32:
+ return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
+ case ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32:
+ return R"(textureSampleLevel(texture, sampler, coords, array_index, level, offset))";
+ case ValidTextureOverload::kSampleLevelDepthCubeF32:
+ return R"(textureSampleLevel(texture, sampler, coords, level))";
+ case ValidTextureOverload::kSampleLevelDepthCubeArrayF32:
+ return R"(textureSampleLevel(texture, sampler, coords, array_index, level))";
+ case ValidTextureOverload::kSampleGrad2dF32:
+ return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy))";
+ case ValidTextureOverload::kSampleGrad2dOffsetF32:
+ return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy, offset))";
+ case ValidTextureOverload::kSampleGrad2dArrayF32:
+ return R"(textureSampleGrad(texture, sampler, coords, array_index, ddx, ddy))";
+ case ValidTextureOverload::kSampleGrad2dArrayOffsetF32:
+ return R"(textureSampleGrad(texture, sampler, coords, array_index, ddx, ddy, offset))";
+ case ValidTextureOverload::kSampleGrad3dF32:
+ return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy))";
+ case ValidTextureOverload::kSampleGrad3dOffsetF32:
+ return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy, offset))";
+ case ValidTextureOverload::kSampleGradCubeF32:
+ return R"(textureSampleGrad(texture, sampler, coords, ddx, ddy))";
+ case ValidTextureOverload::kSampleGradCubeArrayF32:
+ return R"(textureSampleGrad(texture, sampler, coords, array_index, ddx, ddy))";
+ case ValidTextureOverload::kSampleCompareDepth2dF32:
+ return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
+ case ValidTextureOverload::kSampleCompareDepth2dOffsetF32:
+ return R"(textureSampleCompare(texture, sampler, coords, depth_ref, offset))";
+ case ValidTextureOverload::kSampleCompareDepth2dArrayF32:
+ return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
+ case ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32:
+ return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref, offset))";
+ case ValidTextureOverload::kSampleCompareDepthCubeF32:
+ return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
+ case ValidTextureOverload::kSampleCompareDepthCubeArrayF32:
+ return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
+ case ValidTextureOverload::kSampleCompareLevelDepth2dF32:
+ return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
+ case ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32:
+ return R"(textureSampleCompare(texture, sampler, coords, depth_ref, offset))";
+ case ValidTextureOverload::kSampleCompareLevelDepth2dArrayF32:
+ return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
+ case ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32:
+ return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref, offset))";
+ case ValidTextureOverload::kSampleCompareLevelDepthCubeF32:
+ return R"(textureSampleCompare(texture, sampler, coords, depth_ref))";
+ case ValidTextureOverload::kSampleCompareLevelDepthCubeArrayF32:
+ return R"(textureSampleCompare(texture, sampler, coords, array_index, depth_ref))";
+ case ValidTextureOverload::kLoad1dLevelF32:
+ case ValidTextureOverload::kLoad1dLevelU32:
+ case ValidTextureOverload::kLoad1dLevelI32:
+ case ValidTextureOverload::kLoad2dLevelF32:
+ case ValidTextureOverload::kLoad2dLevelU32:
+ case ValidTextureOverload::kLoad2dLevelI32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad2dArrayLevelF32:
+ case ValidTextureOverload::kLoad2dArrayLevelU32:
+ case ValidTextureOverload::kLoad2dArrayLevelI32:
+ return R"(textureLoad(texture, coords, array_index, level))";
+ case ValidTextureOverload::kLoad3dLevelF32:
+ case ValidTextureOverload::kLoad3dLevelU32:
+ case ValidTextureOverload::kLoad3dLevelI32:
+ case ValidTextureOverload::kLoadDepth2dLevelF32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoadDepthMultisampled2dF32:
+ case ValidTextureOverload::kLoadMultisampled2dF32:
+ case ValidTextureOverload::kLoadMultisampled2dU32:
+ case ValidTextureOverload::kLoadMultisampled2dI32:
+ return R"(textureLoad(texture, coords, sample_index))";
+ case ValidTextureOverload::kLoadDepth2dArrayLevelF32:
+ return R"(textureLoad(texture, coords, array_index, level))";
+ case ValidTextureOverload::kStoreWO1dRgba32float:
+ case ValidTextureOverload::kStoreWO2dRgba32float:
+ case ValidTextureOverload::kStoreWO3dRgba32float:
+ return R"(textureStore(texture, coords, value))";
+ case ValidTextureOverload::kStoreWO2dArrayRgba32float:
+ return R"(textureStore(texture, coords, array_index, value))";
+ }
+ return "<unmatched texture overload>";
}
TEST_P(ResolverBuiltinTest_Texture, Call) {
- auto param = GetParam();
+ auto param = GetParam();
- param.BuildTextureVariable(this);
- param.BuildSamplerVariable(this);
+ param.BuildTextureVariable(this);
+ param.BuildSamplerVariable(this);
- auto* call = Call(param.function, param.args(this));
- auto* stmt = CallStmt(call);
- Func("func", {}, ty.void_(), {stmt}, {Stage(ast::PipelineStage::kFragment)});
+ auto* call = Call(param.function, param.args(this));
+ auto* stmt = CallStmt(call);
+ Func("func", {}, ty.void_(), {stmt}, {Stage(ast::PipelineStage::kFragment)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- if (std::string(param.function) == "textureDimensions") {
- switch (param.texture_dimension) {
- default:
- FAIL() << "invalid texture dimensions: " << param.texture_dimension;
- case ast::TextureDimension::k1d:
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
- break;
- case ast::TextureDimension::k2d:
- case ast::TextureDimension::k2dArray:
- case ast::TextureDimension::kCube:
- case ast::TextureDimension::kCubeArray: {
- auto* vec = As<sem::Vector>(TypeOf(call));
- ASSERT_NE(vec, nullptr);
- EXPECT_EQ(vec->Width(), 2u);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- break;
- }
- case ast::TextureDimension::k3d: {
- auto* vec = As<sem::Vector>(TypeOf(call));
- ASSERT_NE(vec, nullptr);
- EXPECT_EQ(vec->Width(), 3u);
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- break;
- }
- }
- } else if (std::string(param.function) == "textureNumLayers") {
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
- } else if (std::string(param.function) == "textureNumLevels") {
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
- } else if (std::string(param.function) == "textureNumSamples") {
- EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
- } else if (std::string(param.function) == "textureStore") {
- EXPECT_TRUE(TypeOf(call)->Is<sem::Void>());
- } else if (std::string(param.function) == "textureGather") {
- auto* vec = As<sem::Vector>(TypeOf(call));
- ASSERT_NE(vec, nullptr);
- EXPECT_EQ(vec->Width(), 4u);
- switch (param.texture_data_type) {
- case ast::builtin::test::TextureDataType::kF32:
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- break;
- case ast::builtin::test::TextureDataType::kU32:
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- break;
- case ast::builtin::test::TextureDataType::kI32:
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- break;
- }
- } else if (std::string(param.function) == "textureGatherCompare") {
- auto* vec = As<sem::Vector>(TypeOf(call));
- ASSERT_NE(vec, nullptr);
- EXPECT_EQ(vec->Width(), 4u);
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- } else {
- switch (param.texture_kind) {
- case ast::builtin::test::TextureKind::kRegular:
- case ast::builtin::test::TextureKind::kMultisampled:
- case ast::builtin::test::TextureKind::kStorage: {
- auto* vec = TypeOf(call)->As<sem::Vector>();
- ASSERT_NE(vec, nullptr);
- switch (param.texture_data_type) {
- case ast::builtin::test::TextureDataType::kF32:
- EXPECT_TRUE(vec->type()->Is<sem::F32>());
- break;
- case ast::builtin::test::TextureDataType::kU32:
- EXPECT_TRUE(vec->type()->Is<sem::U32>());
- break;
- case ast::builtin::test::TextureDataType::kI32:
- EXPECT_TRUE(vec->type()->Is<sem::I32>());
- break;
+ if (std::string(param.function) == "textureDimensions") {
+ switch (param.texture_dimension) {
+ default:
+ FAIL() << "invalid texture dimensions: " << param.texture_dimension;
+ case ast::TextureDimension::k1d:
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ break;
+ case ast::TextureDimension::k2d:
+ case ast::TextureDimension::k2dArray:
+ case ast::TextureDimension::kCube:
+ case ast::TextureDimension::kCubeArray: {
+ auto* vec = As<sem::Vector>(TypeOf(call));
+ ASSERT_NE(vec, nullptr);
+ EXPECT_EQ(vec->Width(), 2u);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ break;
+ }
+ case ast::TextureDimension::k3d: {
+ auto* vec = As<sem::Vector>(TypeOf(call));
+ ASSERT_NE(vec, nullptr);
+ EXPECT_EQ(vec->Width(), 3u);
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ break;
+ }
}
- break;
- }
- case ast::builtin::test::TextureKind::kDepth:
- case ast::builtin::test::TextureKind::kDepthMultisampled: {
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
- break;
- }
+ } else if (std::string(param.function) == "textureNumLayers") {
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ } else if (std::string(param.function) == "textureNumLevels") {
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ } else if (std::string(param.function) == "textureNumSamples") {
+ EXPECT_TRUE(TypeOf(call)->Is<sem::I32>());
+ } else if (std::string(param.function) == "textureStore") {
+ EXPECT_TRUE(TypeOf(call)->Is<sem::Void>());
+ } else if (std::string(param.function) == "textureGather") {
+ auto* vec = As<sem::Vector>(TypeOf(call));
+ ASSERT_NE(vec, nullptr);
+ EXPECT_EQ(vec->Width(), 4u);
+ switch (param.texture_data_type) {
+ case ast::builtin::test::TextureDataType::kF32:
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ break;
+ case ast::builtin::test::TextureDataType::kU32:
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ break;
+ case ast::builtin::test::TextureDataType::kI32:
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ break;
+ }
+ } else if (std::string(param.function) == "textureGatherCompare") {
+ auto* vec = As<sem::Vector>(TypeOf(call));
+ ASSERT_NE(vec, nullptr);
+ EXPECT_EQ(vec->Width(), 4u);
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ } else {
+ switch (param.texture_kind) {
+ case ast::builtin::test::TextureKind::kRegular:
+ case ast::builtin::test::TextureKind::kMultisampled:
+ case ast::builtin::test::TextureKind::kStorage: {
+ auto* vec = TypeOf(call)->As<sem::Vector>();
+ ASSERT_NE(vec, nullptr);
+ switch (param.texture_data_type) {
+ case ast::builtin::test::TextureDataType::kF32:
+ EXPECT_TRUE(vec->type()->Is<sem::F32>());
+ break;
+ case ast::builtin::test::TextureDataType::kU32:
+ EXPECT_TRUE(vec->type()->Is<sem::U32>());
+ break;
+ case ast::builtin::test::TextureDataType::kI32:
+ EXPECT_TRUE(vec->type()->Is<sem::I32>());
+ break;
+ }
+ break;
+ }
+ case ast::builtin::test::TextureKind::kDepth:
+ case ast::builtin::test::TextureKind::kDepthMultisampled: {
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ break;
+ }
+ }
}
- }
- auto* call_sem = Sem().Get(call);
- ASSERT_NE(call_sem, nullptr);
- auto* target = call_sem->Target();
- ASSERT_NE(target, nullptr);
+ auto* call_sem = Sem().Get(call);
+ ASSERT_NE(call_sem, nullptr);
+ auto* target = call_sem->Target();
+ ASSERT_NE(target, nullptr);
- auto got = resolver::to_str(param.function, target->Parameters());
- auto* expected = expected_texture_overload(param.overload);
- EXPECT_EQ(got, expected);
+ auto got = resolver::to_str(param.function, target->Parameters());
+ auto* expected = expected_texture_overload(param.overload);
+ EXPECT_EQ(got, expected);
}
} // namespace
diff --git a/src/tint/resolver/builtin_validation_test.cc b/src/tint/resolver/builtin_validation_test.cc
index df6a0a9..f0bd8de 100644
--- a/src/tint/resolver/builtin_validation_test.cc
+++ b/src/tint/resolver/builtin_validation_test.cc
@@ -20,53 +20,49 @@
using ResolverBuiltinValidationTest = ResolverTest;
-TEST_F(ResolverBuiltinValidationTest,
- FunctionTypeMustMatchReturnStatementType_void_fail) {
- // fn func { return workgroupBarrier(); }
- Func("func", {}, ty.void_(),
- {
- Return(Call(Source{Source::Location{12, 34}}, "workgroupBarrier")),
- });
+TEST_F(ResolverBuiltinValidationTest, FunctionTypeMustMatchReturnStatementType_void_fail) {
+ // fn func { return workgroupBarrier(); }
+ Func("func", {}, ty.void_(),
+ {
+ Return(Call(Source{Source::Location{12, 34}}, "workgroupBarrier")),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin 'workgroupBarrier' does not return a value");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: builtin 'workgroupBarrier' does not return a value");
}
TEST_F(ResolverBuiltinValidationTest, InvalidPipelineStageDirect) {
- // @stage(compute) @workgroup_size(1) fn func { return dpdx(1.0); }
+ // @stage(compute) @workgroup_size(1) fn func { return dpdx(1.0); }
- auto* dpdx = create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"),
- ast::ExpressionList{Expr(1.0f)});
- Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(),
- {CallStmt(dpdx)},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
+ auto* dpdx =
+ create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1.0f)});
+ Func(Source{{1, 2}}, "func", ast::VariableList{}, ty.void_(), {CallStmt(dpdx)},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "3:4 error: built-in cannot be used by compute pipeline stage");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:4 error: built-in cannot be used by compute pipeline stage");
}
TEST_F(ResolverBuiltinValidationTest, InvalidPipelineStageIndirect) {
- // fn f0 { return dpdx(1.0); }
- // fn f1 { f0(); }
- // fn f2 { f1(); }
- // @stage(compute) @workgroup_size(1) fn main { return f2(); }
+ // fn f0 { return dpdx(1.0); }
+ // fn f1 { f0(); }
+ // fn f2 { f1(); }
+ // @stage(compute) @workgroup_size(1) fn main { return f2(); }
- auto* dpdx = create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"),
- ast::ExpressionList{Expr(1.0f)});
- Func(Source{{1, 2}}, "f0", {}, ty.void_(), {CallStmt(dpdx)});
+ auto* dpdx =
+ create<ast::CallExpression>(Source{{3, 4}}, Expr("dpdx"), ast::ExpressionList{Expr(1.0f)});
+ Func(Source{{1, 2}}, "f0", {}, ty.void_(), {CallStmt(dpdx)});
- Func(Source{{3, 4}}, "f1", {}, ty.void_(), {CallStmt(Call("f0"))});
+ Func(Source{{3, 4}}, "f1", {}, ty.void_(), {CallStmt(Call("f0"))});
- Func(Source{{5, 6}}, "f2", {}, ty.void_(), {CallStmt(Call("f1"))});
+ Func(Source{{5, 6}}, "f2", {}, ty.void_(), {CallStmt(Call("f1"))});
- Func(Source{{7, 8}}, "main", {}, ty.void_(), {CallStmt(Call("f2"))},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
+ Func(Source{{7, 8}}, "main", {}, ty.void_(), {CallStmt(Call("f2"))},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(3:4 error: built-in cannot be used by compute pipeline stage
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(3:4 error: built-in cannot be used by compute pipeline stage
1:2 note: called by function 'f0'
3:4 note: called by function 'f1'
5:6 note: called by function 'f2'
@@ -74,49 +70,43 @@
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsFunction) {
- Func(Source{{12, 34}}, "mix", {}, ty.i32(), {});
+ Func(Source{{12, 34}}, "mix", {}, ty.i32(), {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a function)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a function)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalLet) {
- GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1));
+ GlobalConst(Source{{12, 34}}, "mix", ty.i32(), Expr(1));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope let)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope let)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsGlobalVar) {
- Global(Source{{12, 34}}, "mix", ty.i32(), Expr(1),
- ast::StorageClass::kPrivate);
+ Global(Source{{12, 34}}, "mix", ty.i32(), Expr(1), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope var)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a module-scope var)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsAlias) {
- Alias(Source{{12, 34}}, "mix", ty.i32());
+ Alias(Source{{12, 34}}, "mix", ty.i32());
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: 'mix' is a builtin and cannot be redeclared as an alias)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: 'mix' is a builtin and cannot be redeclared as an alias)");
}
TEST_F(ResolverBuiltinValidationTest, BuiltinRedeclaredAsStruct) {
- Structure(Source{{12, 34}}, "mix", {Member("m", ty.i32())});
+ Structure(Source{{12, 34}}, "mix", {Member("m", ty.i32())});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a struct)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: 'mix' is a builtin and cannot be redeclared as a struct)");
}
namespace texture_constexpr_args {
@@ -131,268 +121,253 @@
static std::vector<TextureOverloadCase> TextureCases(
std::unordered_set<ValidTextureOverload> overloads) {
- std::vector<TextureOverloadCase> cases;
- for (auto c : TextureOverloadCase::ValidCases()) {
- if (overloads.count(c.overload)) {
- cases.push_back(c);
+ std::vector<TextureOverloadCase> cases;
+ for (auto c : TextureOverloadCase::ValidCases()) {
+ if (overloads.count(c.overload)) {
+ cases.push_back(c);
+ }
}
- }
- return cases;
+ return cases;
}
enum class Position {
- kFirst,
- kLast,
+ kFirst,
+ kLast,
};
struct Parameter {
- const char* const name;
- const Position position;
- int min;
- int max;
+ const char* const name;
+ const Position position;
+ int min;
+ int max;
};
class Constexpr {
- public:
- enum class Kind {
- kScalar,
- kVec2,
- kVec3,
- kVec3_Scalar_Vec2,
- kVec3_Vec2_Scalar,
- kEmptyVec2,
- kEmptyVec3,
- };
+ public:
+ enum class Kind {
+ kScalar,
+ kVec2,
+ kVec3,
+ kVec3_Scalar_Vec2,
+ kVec3_Vec2_Scalar,
+ kEmptyVec2,
+ kEmptyVec3,
+ };
- Constexpr(int32_t invalid_idx,
- Kind k,
- int32_t x = 0,
- int32_t y = 0,
- int32_t z = 0)
- : invalid_index(invalid_idx), kind(k), values{x, y, z} {}
+ Constexpr(int32_t invalid_idx, Kind k, int32_t x = 0, int32_t y = 0, int32_t z = 0)
+ : invalid_index(invalid_idx), kind(k), values{x, y, z} {}
- const ast::Expression* operator()(Source src, ProgramBuilder& b) {
- switch (kind) {
- case Kind::kScalar:
- return b.Expr(src, values[0]);
- case Kind::kVec2:
- return b.Construct(src, b.ty.vec2<i32>(), values[0], values[1]);
- case Kind::kVec3:
- return b.Construct(src, b.ty.vec3<i32>(), values[0], values[1],
- values[2]);
- case Kind::kVec3_Scalar_Vec2:
- return b.Construct(src, b.ty.vec3<i32>(), values[0],
- b.vec2<i32>(values[1], values[2]));
- case Kind::kVec3_Vec2_Scalar:
- return b.Construct(src, b.ty.vec3<i32>(),
- b.vec2<i32>(values[0], values[1]), values[2]);
- case Kind::kEmptyVec2:
- return b.Construct(src, b.ty.vec2<i32>());
- case Kind::kEmptyVec3:
- return b.Construct(src, b.ty.vec3<i32>());
+ const ast::Expression* operator()(Source src, ProgramBuilder& b) {
+ switch (kind) {
+ case Kind::kScalar:
+ return b.Expr(src, values[0]);
+ case Kind::kVec2:
+ return b.Construct(src, b.ty.vec2<i32>(), values[0], values[1]);
+ case Kind::kVec3:
+ return b.Construct(src, b.ty.vec3<i32>(), values[0], values[1], values[2]);
+ case Kind::kVec3_Scalar_Vec2:
+ return b.Construct(src, b.ty.vec3<i32>(), values[0],
+ b.vec2<i32>(values[1], values[2]));
+ case Kind::kVec3_Vec2_Scalar:
+ return b.Construct(src, b.ty.vec3<i32>(), b.vec2<i32>(values[0], values[1]),
+ values[2]);
+ case Kind::kEmptyVec2:
+ return b.Construct(src, b.ty.vec2<i32>());
+ case Kind::kEmptyVec3:
+ return b.Construct(src, b.ty.vec3<i32>());
+ }
+ return nullptr;
}
- return nullptr;
- }
- static const constexpr int32_t kValid = -1;
- const int32_t invalid_index; // Expected error value, or kValid
- const Kind kind;
- const std::array<int32_t, 3> values;
+ static const constexpr int32_t kValid = -1;
+ const int32_t invalid_index; // Expected error value, or kValid
+ const Kind kind;
+ const std::array<int32_t, 3> values;
};
static std::ostream& operator<<(std::ostream& out, Parameter param) {
- return out << param.name;
+ return out << param.name;
}
static std::ostream& operator<<(std::ostream& out, Constexpr expr) {
- switch (expr.kind) {
- case Constexpr::Kind::kScalar:
- return out << expr.values[0];
- case Constexpr::Kind::kVec2:
- return out << "vec2(" << expr.values[0] << ", " << expr.values[1] << ")";
- case Constexpr::Kind::kVec3:
- return out << "vec3(" << expr.values[0] << ", " << expr.values[1] << ", "
- << expr.values[2] << ")";
- case Constexpr::Kind::kVec3_Scalar_Vec2:
- return out << "vec3(" << expr.values[0] << ", vec2(" << expr.values[1]
- << ", " << expr.values[2] << "))";
- case Constexpr::Kind::kVec3_Vec2_Scalar:
- return out << "vec3(vec2(" << expr.values[0] << ", " << expr.values[1]
- << "), " << expr.values[2] << ")";
- case Constexpr::Kind::kEmptyVec2:
- return out << "vec2()";
- case Constexpr::Kind::kEmptyVec3:
- return out << "vec3()";
- }
- return out;
+ switch (expr.kind) {
+ case Constexpr::Kind::kScalar:
+ return out << expr.values[0];
+ case Constexpr::Kind::kVec2:
+ return out << "vec2(" << expr.values[0] << ", " << expr.values[1] << ")";
+ case Constexpr::Kind::kVec3:
+ return out << "vec3(" << expr.values[0] << ", " << expr.values[1] << ", "
+ << expr.values[2] << ")";
+ case Constexpr::Kind::kVec3_Scalar_Vec2:
+ return out << "vec3(" << expr.values[0] << ", vec2(" << expr.values[1] << ", "
+ << expr.values[2] << "))";
+ case Constexpr::Kind::kVec3_Vec2_Scalar:
+ return out << "vec3(vec2(" << expr.values[0] << ", " << expr.values[1] << "), "
+ << expr.values[2] << ")";
+ case Constexpr::Kind::kEmptyVec2:
+ return out << "vec2()";
+ case Constexpr::Kind::kEmptyVec3:
+ return out << "vec3()";
+ }
+ return out;
}
-using BuiltinTextureConstExprArgValidationTest = ResolverTestWithParam<
- std::tuple<TextureOverloadCase, Parameter, Constexpr>>;
+using BuiltinTextureConstExprArgValidationTest =
+ ResolverTestWithParam<std::tuple<TextureOverloadCase, Parameter, Constexpr>>;
TEST_P(BuiltinTextureConstExprArgValidationTest, Immediate) {
- auto& p = GetParam();
- auto overload = std::get<0>(p);
- auto param = std::get<1>(p);
- auto expr = std::get<2>(p);
+ auto& p = GetParam();
+ auto overload = std::get<0>(p);
+ auto param = std::get<1>(p);
+ auto expr = std::get<2>(p);
- overload.BuildTextureVariable(this);
- overload.BuildSamplerVariable(this);
+ overload.BuildTextureVariable(this);
+ overload.BuildSamplerVariable(this);
- auto args = overload.args(this);
- auto*& arg_to_replace =
- (param.position == Position::kFirst) ? args.front() : args.back();
+ auto args = overload.args(this);
+ auto*& arg_to_replace = (param.position == Position::kFirst) ? args.front() : args.back();
- // BuildTextureVariable() uses a Literal for scalars, and a CallExpression for
- // a vector constructor.
- bool is_vector = arg_to_replace->Is<ast::CallExpression>();
+ // BuildTextureVariable() uses a Literal for scalars, and a CallExpression for
+ // a vector constructor.
+ bool is_vector = arg_to_replace->Is<ast::CallExpression>();
- // Make the expression to be replaced, reachable. This keeps the resolver
- // happy.
- WrapInFunction(arg_to_replace);
+ // Make the expression to be replaced, reachable. This keeps the resolver
+ // happy.
+ WrapInFunction(arg_to_replace);
- arg_to_replace = expr(Source{{12, 34}}, *this);
+ arg_to_replace = expr(Source{{12, 34}}, *this);
- // Call the builtin with the constexpr argument replaced
- Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
- {Stage(ast::PipelineStage::kFragment)});
+ // Call the builtin with the constexpr argument replaced
+ Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
+ {Stage(ast::PipelineStage::kFragment)});
- if (expr.invalid_index == Constexpr::kValid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- std::stringstream err;
- if (is_vector) {
- err << "12:34 error: each component of the " << param.name
- << " argument must be at least " << param.min << " and at most "
- << param.max << ". " << param.name << " component "
- << expr.invalid_index << " is "
- << std::to_string(expr.values[expr.invalid_index]);
+ if (expr.invalid_index == Constexpr::kValid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
} else {
- err << "12:34 error: the " << param.name << " argument must be at least "
- << param.min << " and at most " << param.max << ". " << param.name
- << " is " << std::to_string(expr.values[expr.invalid_index]);
+ EXPECT_FALSE(r()->Resolve());
+ std::stringstream err;
+ if (is_vector) {
+ err << "12:34 error: each component of the " << param.name
+ << " argument must be at least " << param.min << " and at most " << param.max
+ << ". " << param.name << " component " << expr.invalid_index << " is "
+ << std::to_string(expr.values[expr.invalid_index]);
+ } else {
+ err << "12:34 error: the " << param.name << " argument must be at least " << param.min
+ << " and at most " << param.max << ". " << param.name << " is "
+ << std::to_string(expr.values[expr.invalid_index]);
+ }
+ EXPECT_EQ(r()->error(), err.str());
}
- EXPECT_EQ(r()->error(), err.str());
- }
}
TEST_P(BuiltinTextureConstExprArgValidationTest, GlobalConst) {
- auto& p = GetParam();
- auto overload = std::get<0>(p);
- auto param = std::get<1>(p);
- auto expr = std::get<2>(p);
+ auto& p = GetParam();
+ auto overload = std::get<0>(p);
+ auto param = std::get<1>(p);
+ auto expr = std::get<2>(p);
- // Build the global texture and sampler variables
- overload.BuildTextureVariable(this);
- overload.BuildSamplerVariable(this);
+ // Build the global texture and sampler variables
+ overload.BuildTextureVariable(this);
+ overload.BuildSamplerVariable(this);
- // Build the module-scope let 'G' with the offset value
- GlobalConst("G", nullptr, expr({}, *this));
+ // Build the module-scope let 'G' with the offset value
+ GlobalConst("G", nullptr, expr({}, *this));
- auto args = overload.args(this);
- auto*& arg_to_replace =
- (param.position == Position::kFirst) ? args.front() : args.back();
+ auto args = overload.args(this);
+ auto*& arg_to_replace = (param.position == Position::kFirst) ? args.front() : args.back();
- // Make the expression to be replaced, reachable. This keeps the resolver
- // happy.
- WrapInFunction(arg_to_replace);
+ // Make the expression to be replaced, reachable. This keeps the resolver
+ // happy.
+ WrapInFunction(arg_to_replace);
- arg_to_replace = Expr(Source{{12, 34}}, "G");
+ arg_to_replace = Expr(Source{{12, 34}}, "G");
- // Call the builtin with the constexpr argument replaced
- Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
- {Stage(ast::PipelineStage::kFragment)});
+ // Call the builtin with the constexpr argument replaced
+ Func("func", {}, ty.void_(), {CallStmt(Call(overload.function, args))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- std::stringstream err;
- err << "12:34 error: the " << param.name
- << " argument must be a const_expression";
- EXPECT_EQ(r()->error(), err.str());
+ EXPECT_FALSE(r()->Resolve());
+ std::stringstream err;
+ err << "12:34 error: the " << param.name << " argument must be a const_expression";
+ EXPECT_EQ(r()->error(), err.str());
}
INSTANTIATE_TEST_SUITE_P(
Offset2D,
BuiltinTextureConstExprArgValidationTest,
- testing::Combine(
- testing::ValuesIn(TextureCases({
- ValidTextureOverload::kSample2dOffsetF32,
- ValidTextureOverload::kSample2dArrayOffsetF32,
- ValidTextureOverload::kSampleDepth2dOffsetF32,
- ValidTextureOverload::kSampleDepth2dArrayOffsetF32,
- ValidTextureOverload::kSampleBias2dOffsetF32,
- ValidTextureOverload::kSampleBias2dArrayOffsetF32,
- ValidTextureOverload::kSampleLevel2dOffsetF32,
- ValidTextureOverload::kSampleLevel2dArrayOffsetF32,
- ValidTextureOverload::kSampleLevelDepth2dOffsetF32,
- ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32,
- ValidTextureOverload::kSampleGrad2dOffsetF32,
- ValidTextureOverload::kSampleGrad2dArrayOffsetF32,
- ValidTextureOverload::kSampleCompareDepth2dOffsetF32,
- ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32,
- ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32,
- ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32,
- })),
- testing::Values(Parameter{"offset", Position::kLast, -8, 7}),
- testing::Values(
- Constexpr{Constexpr::kValid, Constexpr::Kind::kEmptyVec2},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kVec2, -1, 1},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kVec2, 7, -8},
- Constexpr{0, Constexpr::Kind::kVec2, 8, 0},
- Constexpr{1, Constexpr::Kind::kVec2, 0, 8},
- Constexpr{0, Constexpr::Kind::kVec2, -9, 0},
- Constexpr{1, Constexpr::Kind::kVec2, 0, -9},
- Constexpr{0, Constexpr::Kind::kVec2, 8, 8},
- Constexpr{0, Constexpr::Kind::kVec2, -9, -9})));
+ testing::Combine(testing::ValuesIn(TextureCases({
+ ValidTextureOverload::kSample2dOffsetF32,
+ ValidTextureOverload::kSample2dArrayOffsetF32,
+ ValidTextureOverload::kSampleDepth2dOffsetF32,
+ ValidTextureOverload::kSampleDepth2dArrayOffsetF32,
+ ValidTextureOverload::kSampleBias2dOffsetF32,
+ ValidTextureOverload::kSampleBias2dArrayOffsetF32,
+ ValidTextureOverload::kSampleLevel2dOffsetF32,
+ ValidTextureOverload::kSampleLevel2dArrayOffsetF32,
+ ValidTextureOverload::kSampleLevelDepth2dOffsetF32,
+ ValidTextureOverload::kSampleLevelDepth2dArrayOffsetF32,
+ ValidTextureOverload::kSampleGrad2dOffsetF32,
+ ValidTextureOverload::kSampleGrad2dArrayOffsetF32,
+ ValidTextureOverload::kSampleCompareDepth2dOffsetF32,
+ ValidTextureOverload::kSampleCompareDepth2dArrayOffsetF32,
+ ValidTextureOverload::kSampleCompareLevelDepth2dOffsetF32,
+ ValidTextureOverload::kSampleCompareLevelDepth2dArrayOffsetF32,
+ })),
+ testing::Values(Parameter{"offset", Position::kLast, -8, 7}),
+ testing::Values(Constexpr{Constexpr::kValid, Constexpr::Kind::kEmptyVec2},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kVec2, -1, 1},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kVec2, 7, -8},
+ Constexpr{0, Constexpr::Kind::kVec2, 8, 0},
+ Constexpr{1, Constexpr::Kind::kVec2, 0, 8},
+ Constexpr{0, Constexpr::Kind::kVec2, -9, 0},
+ Constexpr{1, Constexpr::Kind::kVec2, 0, -9},
+ Constexpr{0, Constexpr::Kind::kVec2, 8, 8},
+ Constexpr{0, Constexpr::Kind::kVec2, -9, -9})));
INSTANTIATE_TEST_SUITE_P(
Offset3D,
BuiltinTextureConstExprArgValidationTest,
- testing::Combine(
- testing::ValuesIn(TextureCases({
- ValidTextureOverload::kSample3dOffsetF32,
- ValidTextureOverload::kSampleBias3dOffsetF32,
- ValidTextureOverload::kSampleLevel3dOffsetF32,
- ValidTextureOverload::kSampleGrad3dOffsetF32,
- })),
- testing::Values(Parameter{"offset", Position::kLast, -8, 7}),
- testing::Values(
- Constexpr{Constexpr::kValid, Constexpr::Kind::kEmptyVec3},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kVec3, 0, 0, 0},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kVec3, 7, -8, 7},
- Constexpr{0, Constexpr::Kind::kVec3, 10, 0, 0},
- Constexpr{1, Constexpr::Kind::kVec3, 0, 10, 0},
- Constexpr{2, Constexpr::Kind::kVec3, 0, 0, 10},
- Constexpr{0, Constexpr::Kind::kVec3, 10, 11, 12},
- Constexpr{0, Constexpr::Kind::kVec3_Scalar_Vec2, 10, 0, 0},
- Constexpr{1, Constexpr::Kind::kVec3_Scalar_Vec2, 0, 10, 0},
- Constexpr{2, Constexpr::Kind::kVec3_Scalar_Vec2, 0, 0, 10},
- Constexpr{0, Constexpr::Kind::kVec3_Scalar_Vec2, 10, 11, 12},
- Constexpr{0, Constexpr::Kind::kVec3_Vec2_Scalar, 10, 0, 0},
- Constexpr{1, Constexpr::Kind::kVec3_Vec2_Scalar, 0, 10, 0},
- Constexpr{2, Constexpr::Kind::kVec3_Vec2_Scalar, 0, 0, 10},
- Constexpr{0, Constexpr::Kind::kVec3_Vec2_Scalar, 10, 11, 12})));
+ testing::Combine(testing::ValuesIn(TextureCases({
+ ValidTextureOverload::kSample3dOffsetF32,
+ ValidTextureOverload::kSampleBias3dOffsetF32,
+ ValidTextureOverload::kSampleLevel3dOffsetF32,
+ ValidTextureOverload::kSampleGrad3dOffsetF32,
+ })),
+ testing::Values(Parameter{"offset", Position::kLast, -8, 7}),
+ testing::Values(Constexpr{Constexpr::kValid, Constexpr::Kind::kEmptyVec3},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kVec3, 0, 0, 0},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kVec3, 7, -8, 7},
+ Constexpr{0, Constexpr::Kind::kVec3, 10, 0, 0},
+ Constexpr{1, Constexpr::Kind::kVec3, 0, 10, 0},
+ Constexpr{2, Constexpr::Kind::kVec3, 0, 0, 10},
+ Constexpr{0, Constexpr::Kind::kVec3, 10, 11, 12},
+ Constexpr{0, Constexpr::Kind::kVec3_Scalar_Vec2, 10, 0, 0},
+ Constexpr{1, Constexpr::Kind::kVec3_Scalar_Vec2, 0, 10, 0},
+ Constexpr{2, Constexpr::Kind::kVec3_Scalar_Vec2, 0, 0, 10},
+ Constexpr{0, Constexpr::Kind::kVec3_Scalar_Vec2, 10, 11, 12},
+ Constexpr{0, Constexpr::Kind::kVec3_Vec2_Scalar, 10, 0, 0},
+ Constexpr{1, Constexpr::Kind::kVec3_Vec2_Scalar, 0, 10, 0},
+ Constexpr{2, Constexpr::Kind::kVec3_Vec2_Scalar, 0, 0, 10},
+ Constexpr{0, Constexpr::Kind::kVec3_Vec2_Scalar, 10, 11,
+ 12})));
INSTANTIATE_TEST_SUITE_P(
Component,
BuiltinTextureConstExprArgValidationTest,
- testing::Combine(
- testing::ValuesIn(
- TextureCases({ValidTextureOverload::kGather2dF32,
- ValidTextureOverload::kGather2dOffsetF32,
- ValidTextureOverload::kGather2dArrayF32,
- ValidTextureOverload::kGather2dArrayOffsetF32,
- ValidTextureOverload::kGatherCubeF32,
- ValidTextureOverload::kGatherCubeArrayF32})),
- testing::Values(Parameter{"component", Position::kFirst, 0, 3}),
- testing::Values(
- Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 0},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 1},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 2},
- Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 3},
- Constexpr{0, Constexpr::Kind::kScalar, 4},
- Constexpr{0, Constexpr::Kind::kScalar, 123},
- Constexpr{0, Constexpr::Kind::kScalar, -1})));
+ testing::Combine(testing::ValuesIn(TextureCases({ValidTextureOverload::kGather2dF32,
+ ValidTextureOverload::kGather2dOffsetF32,
+ ValidTextureOverload::kGather2dArrayF32,
+ ValidTextureOverload::kGather2dArrayOffsetF32,
+ ValidTextureOverload::kGatherCubeF32,
+ ValidTextureOverload::kGatherCubeArrayF32})),
+ testing::Values(Parameter{"component", Position::kFirst, 0, 3}),
+ testing::Values(Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 0},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 1},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 2},
+ Constexpr{Constexpr::kValid, Constexpr::Kind::kScalar, 3},
+ Constexpr{0, Constexpr::Kind::kScalar, 4},
+ Constexpr{0, Constexpr::Kind::kScalar, 123},
+ Constexpr{0, Constexpr::Kind::kScalar, -1})));
} // namespace texture_constexpr_args
diff --git a/src/tint/resolver/builtins_validation_test.cc b/src/tint/resolver/builtins_validation_test.cc
index 4bad973..804ccb4 100644
--- a/src/tint/resolver/builtins_validation_test.cc
+++ b/src/tint/resolver/builtins_validation_test.cc
@@ -30,1007 +30,872 @@
using i32 = builder::i32;
using u32 = builder::u32;
-class ResolverBuiltinsValidationTest : public resolver::TestHelper,
- public testing::Test {};
+class ResolverBuiltinsValidationTest : public resolver::TestHelper, public testing::Test {};
namespace StageTest {
struct Params {
- builder::ast_type_func_ptr type;
- ast::Builtin builtin;
- ast::PipelineStage stage;
- bool is_valid;
+ builder::ast_type_func_ptr type;
+ ast::Builtin builtin;
+ ast::PipelineStage stage;
+ bool is_valid;
};
template <typename T>
-constexpr Params ParamsFor(ast::Builtin builtin,
- ast::PipelineStage stage,
- bool is_valid) {
- return Params{DataType<T>::AST, builtin, stage, is_valid};
+constexpr Params ParamsFor(ast::Builtin builtin, ast::PipelineStage stage, bool is_valid) {
+ return Params{DataType<T>::AST, builtin, stage, is_valid};
}
static constexpr Params cases[] = {
- ParamsFor<vec4<f32>>(ast::Builtin::kPosition,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<vec4<f32>>(ast::Builtin::kPosition,
- ast::PipelineStage::kFragment,
- true),
- ParamsFor<vec4<f32>>(ast::Builtin::kPosition,
- ast::PipelineStage::kCompute,
- false),
+ ParamsFor<vec4<f32>>(ast::Builtin::kPosition, ast::PipelineStage::kVertex, false),
+ ParamsFor<vec4<f32>>(ast::Builtin::kPosition, ast::PipelineStage::kFragment, true),
+ ParamsFor<vec4<f32>>(ast::Builtin::kPosition, ast::PipelineStage::kCompute, false),
- ParamsFor<u32>(ast::Builtin::kVertexIndex,
- ast::PipelineStage::kVertex,
- true),
- ParamsFor<u32>(ast::Builtin::kVertexIndex,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<u32>(ast::Builtin::kVertexIndex,
- ast::PipelineStage::kCompute,
- false),
+ ParamsFor<u32>(ast::Builtin::kVertexIndex, ast::PipelineStage::kVertex, true),
+ ParamsFor<u32>(ast::Builtin::kVertexIndex, ast::PipelineStage::kFragment, false),
+ ParamsFor<u32>(ast::Builtin::kVertexIndex, ast::PipelineStage::kCompute, false),
- ParamsFor<u32>(ast::Builtin::kInstanceIndex,
- ast::PipelineStage::kVertex,
- true),
- ParamsFor<u32>(ast::Builtin::kInstanceIndex,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<u32>(ast::Builtin::kInstanceIndex,
- ast::PipelineStage::kCompute,
- false),
+ ParamsFor<u32>(ast::Builtin::kInstanceIndex, ast::PipelineStage::kVertex, true),
+ ParamsFor<u32>(ast::Builtin::kInstanceIndex, ast::PipelineStage::kFragment, false),
+ ParamsFor<u32>(ast::Builtin::kInstanceIndex, ast::PipelineStage::kCompute, false),
- ParamsFor<bool>(ast::Builtin::kFrontFacing,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<bool>(ast::Builtin::kFrontFacing,
- ast::PipelineStage::kFragment,
- true),
- ParamsFor<bool>(ast::Builtin::kFrontFacing,
- ast::PipelineStage::kCompute,
- false),
+ ParamsFor<bool>(ast::Builtin::kFrontFacing, ast::PipelineStage::kVertex, false),
+ ParamsFor<bool>(ast::Builtin::kFrontFacing, ast::PipelineStage::kFragment, true),
+ ParamsFor<bool>(ast::Builtin::kFrontFacing, ast::PipelineStage::kCompute, false),
- ParamsFor<vec3<u32>>(ast::Builtin::kLocalInvocationId,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kLocalInvocationId,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kLocalInvocationId,
- ast::PipelineStage::kCompute,
- true),
+ ParamsFor<vec3<u32>>(ast::Builtin::kLocalInvocationId, ast::PipelineStage::kVertex, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kLocalInvocationId, ast::PipelineStage::kFragment, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kLocalInvocationId, ast::PipelineStage::kCompute, true),
- ParamsFor<u32>(ast::Builtin::kLocalInvocationIndex,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<u32>(ast::Builtin::kLocalInvocationIndex,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<u32>(ast::Builtin::kLocalInvocationIndex,
- ast::PipelineStage::kCompute,
- true),
+ ParamsFor<u32>(ast::Builtin::kLocalInvocationIndex, ast::PipelineStage::kVertex, false),
+ ParamsFor<u32>(ast::Builtin::kLocalInvocationIndex, ast::PipelineStage::kFragment, false),
+ ParamsFor<u32>(ast::Builtin::kLocalInvocationIndex, ast::PipelineStage::kCompute, true),
- ParamsFor<vec3<u32>>(ast::Builtin::kGlobalInvocationId,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kGlobalInvocationId,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kGlobalInvocationId,
- ast::PipelineStage::kCompute,
- true),
+ ParamsFor<vec3<u32>>(ast::Builtin::kGlobalInvocationId, ast::PipelineStage::kVertex, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kGlobalInvocationId, ast::PipelineStage::kFragment, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kGlobalInvocationId, ast::PipelineStage::kCompute, true),
- ParamsFor<vec3<u32>>(ast::Builtin::kWorkgroupId,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kWorkgroupId,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kWorkgroupId,
- ast::PipelineStage::kCompute,
- true),
+ ParamsFor<vec3<u32>>(ast::Builtin::kWorkgroupId, ast::PipelineStage::kVertex, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kWorkgroupId, ast::PipelineStage::kFragment, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kWorkgroupId, ast::PipelineStage::kCompute, true),
- ParamsFor<vec3<u32>>(ast::Builtin::kNumWorkgroups,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kNumWorkgroups,
- ast::PipelineStage::kFragment,
- false),
- ParamsFor<vec3<u32>>(ast::Builtin::kNumWorkgroups,
- ast::PipelineStage::kCompute,
- true),
+ ParamsFor<vec3<u32>>(ast::Builtin::kNumWorkgroups, ast::PipelineStage::kVertex, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kNumWorkgroups, ast::PipelineStage::kFragment, false),
+ ParamsFor<vec3<u32>>(ast::Builtin::kNumWorkgroups, ast::PipelineStage::kCompute, true),
- ParamsFor<u32>(ast::Builtin::kSampleIndex,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<u32>(ast::Builtin::kSampleIndex,
- ast::PipelineStage::kFragment,
- true),
- ParamsFor<u32>(ast::Builtin::kSampleIndex,
- ast::PipelineStage::kCompute,
- false),
+ ParamsFor<u32>(ast::Builtin::kSampleIndex, ast::PipelineStage::kVertex, false),
+ ParamsFor<u32>(ast::Builtin::kSampleIndex, ast::PipelineStage::kFragment, true),
+ ParamsFor<u32>(ast::Builtin::kSampleIndex, ast::PipelineStage::kCompute, false),
- ParamsFor<u32>(ast::Builtin::kSampleMask,
- ast::PipelineStage::kVertex,
- false),
- ParamsFor<u32>(ast::Builtin::kSampleMask,
- ast::PipelineStage::kFragment,
- true),
- ParamsFor<u32>(ast::Builtin::kSampleMask,
- ast::PipelineStage::kCompute,
- false),
+ ParamsFor<u32>(ast::Builtin::kSampleMask, ast::PipelineStage::kVertex, false),
+ ParamsFor<u32>(ast::Builtin::kSampleMask, ast::PipelineStage::kFragment, true),
+ ParamsFor<u32>(ast::Builtin::kSampleMask, ast::PipelineStage::kCompute, false),
};
using ResolverBuiltinsStageTest = ResolverTestWithParam<Params>;
TEST_P(ResolverBuiltinsStageTest, All_input) {
- const Params& params = GetParam();
+ const Params& params = GetParam();
- auto* p = Global("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* input =
- Param("input", params.type(*this),
- ast::AttributeList{Builtin(Source{{12, 34}}, params.builtin)});
- switch (params.stage) {
- case ast::PipelineStage::kVertex:
- Func("main", {input}, ty.vec4<f32>(), {Return(p)},
- {Stage(ast::PipelineStage::kVertex)},
- {Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
- break;
- case ast::PipelineStage::kFragment:
- Func("main", {input}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)}, {});
- break;
- case ast::PipelineStage::kCompute:
- Func("main", {input}, ty.void_(), {},
- ast::AttributeList{Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(1)});
- break;
- default:
- break;
- }
+ auto* p = Global("p", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ auto* input = Param("input", params.type(*this),
+ ast::AttributeList{Builtin(Source{{12, 34}}, params.builtin)});
+ switch (params.stage) {
+ case ast::PipelineStage::kVertex:
+ Func("main", {input}, ty.vec4<f32>(), {Return(p)}, {Stage(ast::PipelineStage::kVertex)},
+ {Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
+ break;
+ case ast::PipelineStage::kFragment:
+ Func("main", {input}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)}, {});
+ break;
+ case ast::PipelineStage::kCompute:
+ Func("main", {input}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
+ break;
+ default:
+ break;
+ }
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- std::stringstream err;
- err << "12:34 error: builtin(" << params.builtin << ")";
- err << " cannot be used in input of " << params.stage << " pipeline stage";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), err.str());
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ std::stringstream err;
+ err << "12:34 error: builtin(" << params.builtin << ")";
+ err << " cannot be used in input of " << params.stage << " pipeline stage";
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), err.str());
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest,
ResolverBuiltinsStageTest,
testing::ValuesIn(cases));
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInput_Fail) {
- // @stage(fragment)
- // fn fs_main(
- // @builtin(frag_depth) fd: f32,
- // ) -> @location(0) f32 { return 1.0; }
- auto* fd = Param(
- "fd", ty.f32(),
- ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
- Func("fs_main", ast::VariableList{fd}, ty.f32(), {Return(1.0f)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin(frag_depth) cannot be used in input of "
- "fragment pipeline stage");
+ // @stage(fragment)
+ // fn fs_main(
+ // @builtin(frag_depth) fd: f32,
+ // ) -> @location(0) f32 { return 1.0; }
+ auto* fd = Param("fd", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
+ Func("fs_main", ast::VariableList{fd}, ty.f32(), {Return(1.0f)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(frag_depth) cannot be used in input of "
+ "fragment pipeline stage");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsInputStruct_Fail) {
- // struct MyInputs {
- // @builtin(frag_depth) ff: f32;
- // };
- // @stage(fragment)
- // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
+ // struct MyInputs {
+ // @builtin(frag_depth) ff: f32;
+ // };
+ // @stage(fragment)
+ // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* s = Structure(
- "MyInputs", {Member("frag_depth", ty.f32(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kFragDepth)})});
+ auto* s = Structure(
+ "MyInputs",
+ {Member("frag_depth", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)})});
- Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: builtin(frag_depth) cannot be used in input of "
- "fragment pipeline stage\n"
- "note: while analysing entry point 'fragShader'");
+ Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(frag_depth) cannot be used in input of "
+ "fragment pipeline stage\n"
+ "note: while analysing entry point 'fragShader'");
}
TEST_F(ResolverBuiltinsValidationTest, StructBuiltinInsideEntryPoint_Ignored) {
- // struct S {
- // @builtin(vertex_index) idx: u32;
- // };
- // @stage(fragment)
- // fn fragShader() { var s : S; }
+ // struct S {
+ // @builtin(vertex_index) idx: u32;
+ // };
+ // @stage(fragment)
+ // fn fragShader() { var s : S; }
- Structure("S",
- {Member("idx", ty.u32(), {Builtin(ast::Builtin::kVertexIndex)})});
+ Structure("S", {Member("idx", ty.u32(), {Builtin(ast::Builtin::kVertexIndex)})});
- Func("fragShader", {}, ty.void_(), {Decl(Var("s", ty.type_name("S")))},
- {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve());
+ Func("fragShader", {}, ty.void_(), {Decl(Var("s", ty.type_name("S")))},
+ {Stage(ast::PipelineStage::kFragment)});
+ EXPECT_TRUE(r()->Resolve());
}
} // namespace StageTest
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) {
- // struct MyInputs {
- // @builtin(kPosition) p: vec4<u32>;
- // };
- // @stage(fragment)
- // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
+ // struct MyInputs {
+ // @builtin(kPosition) p: vec4<u32>;
+ // };
+ // @stage(fragment)
+ // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* m = Member(
- "position", ty.vec4<u32>(),
- ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
- auto* s = Structure("MyInputs", {m});
- Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ auto* m = Member("position", ty.vec4<u32>(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
+ auto* s = Structure("MyInputs", {m});
+ Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_ReturnType_Fail) {
- // @stage(vertex)
- // fn main() -> @builtin(position) f32 { return 1.0; }
- Func("main", {}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kVertex)},
- {Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
+ // @stage(vertex)
+ // fn main() -> @builtin(position) f32 { return 1.0; }
+ Func("main", {}, ty.f32(), {Return(1.0f)}, {Stage(ast::PipelineStage::kVertex)},
+ {Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthNotF32_Struct_Fail) {
- // struct MyInputs {
- // @builtin(kFragDepth) p: i32;
- // };
- // @stage(fragment)
- // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
+ // struct MyInputs {
+ // @builtin(kFragDepth) p: i32;
+ // };
+ // @stage(fragment)
+ // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* m = Member(
- "frag_depth", ty.i32(),
- ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
- auto* s = Structure("MyInputs", {m});
- Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ auto* m = Member("frag_depth", ty.i32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
+ auto* s = Structure("MyInputs", {m});
+ Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(frag_depth) must be 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_Struct_Fail) {
- // struct MyInputs {
- // @builtin(sample_mask) m: f32;
- // };
- // @stage(fragment)
- // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
+ // struct MyInputs {
+ // @builtin(sample_mask) m: f32;
+ // };
+ // @stage(fragment)
+ // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* s = Structure(
- "MyInputs", {Member("m", ty.f32(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kSampleMask)})});
- Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ auto* s = Structure(
+ "MyInputs",
+ {Member("m", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)})});
+ Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(sample_mask) must be 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskNotU32_ReturnType_Fail) {
- // @stage(fragment)
- // fn main() -> @builtin(sample_mask) i32 { return 1; }
- Func("main", {}, ty.i32(), {Return(1)},
- {Stage(ast::PipelineStage::kFragment)},
- {Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
+ // @stage(fragment)
+ // fn main() -> @builtin(sample_mask) i32 { return 1; }
+ Func("main", {}, ty.i32(), {Return(1)}, {Stage(ast::PipelineStage::kFragment)},
+ {Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(sample_mask) must be 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleMaskIsNotU32_Fail) {
- // @stage(fragment)
- // fn fs_main(
- // @builtin(sample_mask) arg: bool
- // ) -> @location(0) f32 { return 1.0; }
- auto* arg = Param(
- "arg", ty.bool_(),
- ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
- Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(sample_mask) must be 'u32'");
+ // @stage(fragment)
+ // fn fs_main(
+ // @builtin(sample_mask) arg: bool
+ // ) -> @location(0) f32 { return 1.0; }
+ auto* arg = Param("arg", ty.bool_(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)});
+ Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_mask) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Struct_Fail) {
- // struct MyInputs {
- // @builtin(sample_index) m: f32;
- // };
- // @stage(fragment)
- // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
+ // struct MyInputs {
+ // @builtin(sample_index) m: f32;
+ // };
+ // @stage(fragment)
+ // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* s = Structure(
- "MyInputs", {Member("m", ty.f32(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kSampleIndex)})});
- Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ auto* s = Structure(
+ "MyInputs",
+ {Member("m", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)})});
+ Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(sample_index) must be 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, SampleIndexIsNotU32_Fail) {
- // @stage(fragment)
- // fn fs_main(
- // @builtin(sample_index) arg: bool
- // ) -> @location(0) f32 { return 1.0; }
- auto* arg = Param("arg", ty.bool_(),
- ast::AttributeList{
- Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)});
- Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(sample_index) must be 'u32'");
+ // @stage(fragment)
+ // fn fs_main(
+ // @builtin(sample_index) arg: bool
+ // ) -> @location(0) f32 { return 1.0; }
+ auto* arg = Param("arg", ty.bool_(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)});
+ Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(sample_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, PositionIsNotF32_Fail) {
- // @stage(fragment)
- // fn fs_main(
- // @builtin(kPosition) p: vec3<f32>,
- // ) -> @location(0) f32 { return 1.0; }
- auto* p = Param(
- "p", ty.vec3<f32>(),
- ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
- Func("fs_main", ast::VariableList{p}, ty.f32(), {Return(1.0f)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
+ // @stage(fragment)
+ // fn fs_main(
+ // @builtin(kPosition) p: vec3<f32>,
+ // ) -> @location(0) f32 { return 1.0; }
+ auto* p = Param("p", ty.vec3<f32>(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)});
+ Func("fs_main", ast::VariableList{p}, ty.f32(), {Return(1.0f)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4<f32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) {
- // @stage(fragment)
- // fn fs_main() -> @builtin(kFragDepth) f32 { var fd: i32; return fd; }
- auto* fd = Var("fd", ty.i32());
- Func("fs_main", {}, ty.i32(), {Decl(fd), Return(fd)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
- ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(frag_depth) must be 'f32'");
+ // @stage(fragment)
+ // fn fs_main() -> @builtin(kFragDepth) f32 { var fd: i32; return fd; }
+ auto* fd = Var("fd", ty.i32());
+ Func("fs_main", {}, ty.i32(), {Decl(fd), Return(fd)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(frag_depth) must be 'f32'");
}
TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) {
- // @stage(vertex)
- // fn main(
- // @builtin(kVertexIndex) vi : f32,
- // @builtin(kPosition) p :vec4<f32>
- // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
- auto* p = Param("p", ty.vec4<f32>(),
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- auto* vi = Param("vi", ty.f32(),
- ast::AttributeList{
- Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
- Func("main", ast::VariableList{vi, p}, ty.vec4<f32>(), {Return(Expr("p"))},
- ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(vertex_index) must be 'u32'");
+ // @stage(vertex)
+ // fn main(
+ // @builtin(kVertexIndex) vi : f32,
+ // @builtin(kPosition) p :vec4<f32>
+ // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
+ auto* p = Param("p", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ auto* vi = Param("vi", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
+ Func("main", ast::VariableList{vi, p}, ty.vec4<f32>(), {Return(Expr("p"))},
+ ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
+ ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(vertex_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, InstanceIndexIsNotU32) {
- // @stage(vertex)
- // fn main(
- // @builtin(kInstanceIndex) ii : f32,
- // @builtin(kPosition) p :vec4<f32>
- // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
- auto* p = Param("p", ty.vec4<f32>(),
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- auto* ii = Param("ii", ty.f32(),
- ast::AttributeList{Builtin(Source{{12, 34}},
- ast::Builtin::kInstanceIndex)});
- Func("main", ast::VariableList{ii, p}, ty.vec4<f32>(), {Return(Expr("p"))},
- ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(instance_index) must be 'u32'");
+ // @stage(vertex)
+ // fn main(
+ // @builtin(kInstanceIndex) ii : f32,
+ // @builtin(kPosition) p :vec4<f32>
+ // ) -> @builtin(kPosition) vec4<f32> { return vec4<f32>(); }
+ auto* p = Param("p", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ auto* ii = Param("ii", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)});
+ Func("main", ast::VariableList{ii, p}, ty.vec4<f32>(), {Return(Expr("p"))},
+ ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
+ ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(instance_index) must be 'u32'");
}
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltin_Pass) {
- // @stage(fragment)
- // fn fs_main(
- // @builtin(kPosition) p: vec4<f32>,
- // @builtin(front_facing) ff: bool,
- // @builtin(sample_index) si: u32,
- // @builtin(sample_mask) sm : u32
- // ) -> @builtin(frag_depth) f32 { var fd: f32; return fd; }
- auto* p = Param("p", ty.vec4<f32>(),
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- auto* ff = Param("ff", ty.bool_(),
- ast::AttributeList{Builtin(ast::Builtin::kFrontFacing)});
- auto* si = Param("si", ty.u32(),
- ast::AttributeList{Builtin(ast::Builtin::kSampleIndex)});
- auto* sm = Param("sm", ty.u32(),
- ast::AttributeList{Builtin(ast::Builtin::kSampleMask)});
- auto* var_fd = Var("fd", ty.f32());
- Func("fs_main", ast::VariableList{p, ff, si, sm}, ty.f32(),
- {Decl(var_fd), Return(var_fd)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
- ast::AttributeList{Builtin(ast::Builtin::kFragDepth)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // @stage(fragment)
+ // fn fs_main(
+ // @builtin(kPosition) p: vec4<f32>,
+ // @builtin(front_facing) ff: bool,
+ // @builtin(sample_index) si: u32,
+ // @builtin(sample_mask) sm : u32
+ // ) -> @builtin(frag_depth) f32 { var fd: f32; return fd; }
+ auto* p = Param("p", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ auto* ff = Param("ff", ty.bool_(), ast::AttributeList{Builtin(ast::Builtin::kFrontFacing)});
+ auto* si = Param("si", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleIndex)});
+ auto* sm = Param("sm", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleMask)});
+ auto* var_fd = Var("fd", ty.f32());
+ Func("fs_main", ast::VariableList{p, ff, si, sm}, ty.f32(), {Decl(var_fd), Return(var_fd)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)},
+ ast::AttributeList{Builtin(ast::Builtin::kFragDepth)});
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, VertexBuiltin_Pass) {
- // @stage(vertex)
- // fn main(
- // @builtin(vertex_index) vi : u32,
- // @builtin(instance_index) ii : u32,
- // ) -> @builtin(position) vec4<f32> { var p :vec4<f32>; return p; }
- auto* vi = Param("vi", ty.u32(),
- ast::AttributeList{
- Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
+ // @stage(vertex)
+ // fn main(
+ // @builtin(vertex_index) vi : u32,
+ // @builtin(instance_index) ii : u32,
+ // ) -> @builtin(position) vec4<f32> { var p :vec4<f32>; return p; }
+ auto* vi = Param("vi", ty.u32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)});
- auto* ii = Param("ii", ty.u32(),
- ast::AttributeList{Builtin(Source{{12, 34}},
- ast::Builtin::kInstanceIndex)});
- auto* p = Var("p", ty.vec4<f32>());
- Func("main", ast::VariableList{vi, ii}, ty.vec4<f32>(),
- {
- Decl(p),
- Return(p),
- },
- ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ auto* ii = Param("ii", ty.u32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)});
+ auto* p = Var("p", ty.vec4<f32>());
+ Func("main", ast::VariableList{vi, ii}, ty.vec4<f32>(),
+ {
+ Decl(p),
+ Return(p),
+ },
+ ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
+ ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_Pass) {
- // @stage(compute) @workgroup_size(1)
- // fn main(
- // @builtin(local_invocationId) li_id: vec3<u32>,
- // @builtin(local_invocationIndex) li_index: u32,
- // @builtin(global_invocationId) gi: vec3<u32>,
- // @builtin(workgroup_id) wi: vec3<u32>,
- // @builtin(num_workgroups) nwgs: vec3<u32>,
- // ) {}
+ // @stage(compute) @workgroup_size(1)
+ // fn main(
+ // @builtin(local_invocationId) li_id: vec3<u32>,
+ // @builtin(local_invocationIndex) li_index: u32,
+ // @builtin(global_invocationId) gi: vec3<u32>,
+ // @builtin(workgroup_id) wi: vec3<u32>,
+ // @builtin(num_workgroups) nwgs: vec3<u32>,
+ // ) {}
- auto* li_id =
- Param("li_id", ty.vec3<u32>(),
- ast::AttributeList{Builtin(ast::Builtin::kLocalInvocationId)});
- auto* li_index =
- Param("li_index", ty.u32(),
- ast::AttributeList{Builtin(ast::Builtin::kLocalInvocationIndex)});
- auto* gi =
- Param("gi", ty.vec3<u32>(),
- ast::AttributeList{Builtin(ast::Builtin::kGlobalInvocationId)});
- auto* wi = Param("wi", ty.vec3<u32>(),
- ast::AttributeList{Builtin(ast::Builtin::kWorkgroupId)});
- auto* nwgs = Param("nwgs", ty.vec3<u32>(),
- ast::AttributeList{Builtin(ast::Builtin::kNumWorkgroups)});
+ auto* li_id = Param("li_id", ty.vec3<u32>(),
+ ast::AttributeList{Builtin(ast::Builtin::kLocalInvocationId)});
+ auto* li_index = Param("li_index", ty.u32(),
+ ast::AttributeList{Builtin(ast::Builtin::kLocalInvocationIndex)});
+ auto* gi =
+ Param("gi", ty.vec3<u32>(), ast::AttributeList{Builtin(ast::Builtin::kGlobalInvocationId)});
+ auto* wi = Param("wi", ty.vec3<u32>(), ast::AttributeList{Builtin(ast::Builtin::kWorkgroupId)});
+ auto* nwgs =
+ Param("nwgs", ty.vec3<u32>(), ast::AttributeList{Builtin(ast::Builtin::kNumWorkgroups)});
- Func("main", ast::VariableList{li_id, li_index, gi, wi, nwgs}, ty.void_(), {},
- ast::AttributeList{
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
+ Func("main", ast::VariableList{li_id, li_index, gi, wi, nwgs}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) {
- auto* wi = Param("wi", ty.f32(),
- ast::AttributeList{
- Builtin(Source{{12, 34}}, ast::Builtin::kWorkgroupId)});
- Func("main", ast::VariableList{wi}, ty.void_(), {},
- ast::AttributeList{
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
+ auto* wi = Param("wi", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kWorkgroupId)});
+ Func("main", ast::VariableList{wi}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(workgroup_id) must be "
- "'vec3<u32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: store type of builtin(workgroup_id) must be "
+ "'vec3<u32>'");
}
TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_NumWorkgroupsNotVec3U32) {
- auto* nwgs = Param("nwgs", ty.f32(),
- ast::AttributeList{Builtin(Source{{12, 34}},
- ast::Builtin::kNumWorkgroups)});
- Func("main", ast::VariableList{nwgs}, ty.void_(), {},
- ast::AttributeList{
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
+ auto* nwgs = Param("nwgs", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kNumWorkgroups)});
+ Func("main", ast::VariableList{nwgs}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(num_workgroups) must be "
- "'vec3<u32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: store type of builtin(num_workgroups) must be "
+ "'vec3<u32>'");
}
-TEST_F(ResolverBuiltinsValidationTest,
- ComputeBuiltin_GlobalInvocationNotVec3U32) {
- auto* gi = Param("gi", ty.vec3<i32>(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kGlobalInvocationId)});
- Func("main", ast::VariableList{gi}, ty.void_(), {},
- ast::AttributeList{
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
+TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32) {
+ auto* gi =
+ Param("gi", ty.vec3<i32>(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kGlobalInvocationId)});
+ Func("main", ast::VariableList{gi}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(global_invocation_id) must be "
- "'vec3<u32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: store type of builtin(global_invocation_id) must be "
+ "'vec3<u32>'");
}
-TEST_F(ResolverBuiltinsValidationTest,
- ComputeBuiltin_LocalInvocationIndexNotU32) {
- auto* li_index =
- Param("li_index", ty.vec3<u32>(),
- ast::AttributeList{Builtin(Source{{12, 34}},
- ast::Builtin::kLocalInvocationIndex)});
- Func("main", ast::VariableList{li_index}, ty.void_(), {},
- ast::AttributeList{
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
+TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32) {
+ auto* li_index =
+ Param("li_index", ty.vec3<u32>(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationIndex)});
+ Func("main", ast::VariableList{li_index}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: store type of builtin(local_invocation_index) must be "
- "'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: store type of builtin(local_invocation_index) must be "
+ "'u32'");
}
-TEST_F(ResolverBuiltinsValidationTest,
- ComputeBuiltin_LocalInvocationNotVec3U32) {
- auto* li_id = Param("li_id", ty.vec2<u32>(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kLocalInvocationId)});
- Func("main", ast::VariableList{li_id}, ty.void_(), {},
- ast::AttributeList{
- Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
+TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32) {
+ auto* li_id =
+ Param("li_id", ty.vec2<u32>(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationId)});
+ Func("main", ast::VariableList{li_id}, ty.void_(), {},
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(local_invocation_id) must be "
- "'vec3<u32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: store type of builtin(local_invocation_id) must be "
+ "'vec3<u32>'");
}
TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltinStruct_Pass) {
- // Struct MyInputs {
- // @builtin(kPosition) p: vec4<f32>;
- // @builtin(frag_depth) fd: f32;
- // @builtin(sample_index) si: u32;
- // @builtin(sample_mask) sm : u32;;
- // };
- // @stage(fragment)
- // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
+ // Struct MyInputs {
+ // @builtin(kPosition) p: vec4<f32>;
+ // @builtin(frag_depth) fd: f32;
+ // @builtin(sample_index) si: u32;
+ // @builtin(sample_mask) sm : u32;;
+ // };
+ // @stage(fragment)
+ // fn fragShader(arg: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* s = Structure(
- "MyInputs",
- {Member("position", ty.vec4<f32>(),
- ast::AttributeList{Builtin(ast::Builtin::kPosition)}),
- Member("front_facing", ty.bool_(),
- ast::AttributeList{Builtin(ast::Builtin::kFrontFacing)}),
- Member("sample_index", ty.u32(),
- ast::AttributeList{Builtin(ast::Builtin::kSampleIndex)}),
- Member("sample_mask", ty.u32(),
- ast::AttributeList{Builtin(ast::Builtin::kSampleMask)})});
- Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* s = Structure(
+ "MyInputs",
+ {Member("position", ty.vec4<f32>(), ast::AttributeList{Builtin(ast::Builtin::kPosition)}),
+ Member("front_facing", ty.bool_(),
+ ast::AttributeList{Builtin(ast::Builtin::kFrontFacing)}),
+ Member("sample_index", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleIndex)}),
+ Member("sample_mask", ty.u32(), ast::AttributeList{Builtin(ast::Builtin::kSampleMask)})});
+ Func("fragShader", {Param("arg", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, FrontFacingParamIsNotBool_Fail) {
- // @stage(fragment)
- // fn fs_main(
- // @builtin(front_facing) is_front: i32;
- // ) -> @location(0) f32 { return 1.0; }
+ // @stage(fragment)
+ // fn fs_main(
+ // @builtin(front_facing) is_front: i32;
+ // ) -> @location(0) f32 { return 1.0; }
- auto* is_front = Param("is_front", ty.i32(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kFrontFacing)});
- Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1.0f)},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ auto* is_front =
+ Param("is_front", ty.i32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFrontFacing)});
+ Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1.0f)},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(front_facing) must be 'bool'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
}
TEST_F(ResolverBuiltinsValidationTest, FrontFacingMemberIsNotBool_Fail) {
- // struct MyInputs {
- // @builtin(front_facing) pos: f32;
- // };
- // @stage(fragment)
- // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
+ // struct MyInputs {
+ // @builtin(front_facing) pos: f32;
+ // };
+ // @stage(fragment)
+ // fn fragShader(is_front: MyInputs) -> @location(0) f32 { return 1.0; }
- auto* s = Structure(
- "MyInputs", {Member("pos", ty.f32(),
- ast::AttributeList{Builtin(
- Source{{12, 34}}, ast::Builtin::kFrontFacing)})});
- Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ auto* s = Structure(
+ "MyInputs",
+ {Member("pos", ty.f32(),
+ ast::AttributeList{Builtin(Source{{12, 34}}, ast::Builtin::kFrontFacing)})});
+ Func("fragShader", {Param("is_front", ty.Of(s))}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of builtin(front_facing) must be 'bool'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(front_facing) must be 'bool'");
}
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) {
- auto* builtin = Call("length", 1.0f);
- WrapInFunction(builtin);
+ auto* builtin = Call("length", 1.0f);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Vec2) {
- auto* builtin = Call("length", vec2<f32>(1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("length", vec2<f32>(1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Vec3) {
- auto* builtin = Call("length", vec3<f32>(1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("length", vec3<f32>(1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Length_Float_Vec4) {
- auto* builtin = Call("length", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("length", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Scalar) {
- auto* builtin = Call("distance", 1.0f, 1.0f);
- WrapInFunction(builtin);
+ auto* builtin = Call("distance", 1.0f, 1.0f);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Vec2) {
- auto* builtin =
- Call("distance", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("distance", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Vec3) {
- auto* builtin = Call("distance", vec3<f32>(1.0f, 1.0f, 1.0f),
- vec3<f32>(1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("distance", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Vec4) {
- auto* builtin = Call("distance", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f),
- vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin =
+ Call("distance", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f), vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Determinant_Mat2x2) {
- auto* builtin = Call(
- "determinant", mat2x2<f32>(vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f)));
- WrapInFunction(builtin);
+ auto* builtin = Call("determinant", mat2x2<f32>(vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f)));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Determinant_Mat3x3) {
- auto* builtin = Call("determinant", mat3x3<f32>(vec3<f32>(1.0f, 1.0f, 1.0f),
- vec3<f32>(1.0f, 1.0f, 1.0f),
- vec3<f32>(1.0f, 1.0f, 1.0f)));
- WrapInFunction(builtin);
+ auto* builtin =
+ Call("determinant", mat3x3<f32>(vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f),
+ vec3<f32>(1.0f, 1.0f, 1.0f)));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Determinant_Mat4x4) {
- auto* builtin =
- Call("determinant", mat4x4<f32>(vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f),
- vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f),
- vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f),
- vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f)));
- WrapInFunction(builtin);
+ auto* builtin =
+ Call("determinant",
+ mat4x4<f32>(vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f), vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f), vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f)));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Frexp_Scalar) {
- auto* builtin = Call("frexp", 1.0f);
- WrapInFunction(builtin);
+ auto* builtin = Call("frexp", 1.0f);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- EXPECT_TRUE(members[0]->Type()->Is<sem::F32>());
- EXPECT_TRUE(members[1]->Type()->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ EXPECT_TRUE(members[0]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(members[1]->Type()->Is<sem::I32>());
}
TEST_F(ResolverBuiltinsValidationTest, Frexp_Vec2) {
- auto* builtin = Call("frexp", vec2<f32>(1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("frexp", vec2<f32>(1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
- ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
- EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 2u);
- EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 2u);
- EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
+ ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
+ EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 2u);
+ EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 2u);
+ EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
}
TEST_F(ResolverBuiltinsValidationTest, Frexp_Vec3) {
- auto* builtin = Call("frexp", vec3<f32>(1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("frexp", vec3<f32>(1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
- ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
- EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
+ ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
+ EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
}
TEST_F(ResolverBuiltinsValidationTest, Frexp_Vec4) {
- auto* builtin = Call("frexp", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("frexp", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
- ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
- EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 4u);
- EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 4u);
- EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
+ ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
+ EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 4u);
+ EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 4u);
+ EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
}
TEST_F(ResolverBuiltinsValidationTest, Modf_Scalar) {
- auto* builtin = Call("modf", 1.0f);
- WrapInFunction(builtin);
+ auto* builtin = Call("modf", 1.0f);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- EXPECT_TRUE(members[0]->Type()->Is<sem::F32>());
- EXPECT_TRUE(members[1]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ EXPECT_TRUE(members[0]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(members[1]->Type()->Is<sem::F32>());
}
TEST_F(ResolverBuiltinsValidationTest, Modf_Vec2) {
- auto* builtin = Call("modf", vec2<f32>(1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("modf", vec2<f32>(1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
- ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
- EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 2u);
- EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 2u);
- EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
+ ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
+ EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 2u);
+ EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 2u);
+ EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
}
TEST_F(ResolverBuiltinsValidationTest, Modf_Vec3) {
- auto* builtin = Call("modf", vec3<f32>(1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("modf", vec3<f32>(1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
- ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
- EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
+ ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
+ EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
}
TEST_F(ResolverBuiltinsValidationTest, Modf_Vec4) {
- auto* builtin = Call("modf", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("modf", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
- ASSERT_TRUE(res_ty != nullptr);
- auto& members = res_ty->Members();
- ASSERT_EQ(members.size(), 2u);
- ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
- ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
- EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 4u);
- EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 4u);
- EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* res_ty = TypeOf(builtin)->As<sem::Struct>();
+ ASSERT_TRUE(res_ty != nullptr);
+ auto& members = res_ty->Members();
+ ASSERT_EQ(members.size(), 2u);
+ ASSERT_TRUE(members[0]->Type()->Is<sem::Vector>());
+ ASSERT_TRUE(members[1]->Type()->Is<sem::Vector>());
+ EXPECT_EQ(members[0]->Type()->As<sem::Vector>()->Width(), 4u);
+ EXPECT_TRUE(members[0]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(members[1]->Type()->As<sem::Vector>()->Width(), 4u);
+ EXPECT_TRUE(members[1]->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
}
TEST_F(ResolverBuiltinsValidationTest, Cross_Float_Vec3) {
- auto* builtin =
- Call("cross", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("cross", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Dot_Float_Vec2) {
- auto* builtin = Call("dot", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("dot", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Dot_Float_Vec3) {
- auto* builtin =
- Call("dot", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin = Call("dot", vec3<f32>(1.0f, 1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Dot_Float_Vec4) {
- auto* builtin = Call("dot", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f),
- vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
+ auto* builtin =
+ Call("dot", vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f), vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Select_Float_Scalar) {
- auto* builtin = Call("select", Expr(1.0f), Expr(1.0f), Expr(true));
- WrapInFunction(builtin);
+ auto* builtin = Call("select", Expr(1.0f), Expr(1.0f), Expr(true));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Select_Integer_Scalar) {
- auto* builtin = Call("select", Expr(1), Expr(1), Expr(true));
- WrapInFunction(builtin);
+ auto* builtin = Call("select", Expr(1), Expr(1), Expr(true));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Select_Boolean_Scalar) {
- auto* builtin = Call("select", Expr(true), Expr(true), Expr(true));
- WrapInFunction(builtin);
+ auto* builtin = Call("select", Expr(true), Expr(true), Expr(true));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Select_Float_Vec2) {
- auto* builtin = Call("select", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f),
- vec2<bool>(true, true));
- WrapInFunction(builtin);
+ auto* builtin =
+ Call("select", vec2<f32>(1.0f, 1.0f), vec2<f32>(1.0f, 1.0f), vec2<bool>(true, true));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Select_Integer_Vec2) {
- auto* builtin =
- Call("select", vec2<int>(1, 1), vec2<int>(1, 1), vec2<bool>(true, true));
- WrapInFunction(builtin);
+ auto* builtin = Call("select", vec2<int>(1, 1), vec2<int>(1, 1), vec2<bool>(true, true));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverBuiltinsValidationTest, Select_Boolean_Vec2) {
- auto* builtin = Call("select", vec2<bool>(true, true), vec2<bool>(true, true),
- vec2<bool>(true, true));
- WrapInFunction(builtin);
+ auto* builtin =
+ Call("select", vec2<bool>(true, true), vec2<bool>(true, true), vec2<bool>(true, true));
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
template <typename T>
-class ResolverBuiltinsValidationTestWithParams
- : public resolver::TestHelper,
- public testing::TestWithParam<T> {};
+class ResolverBuiltinsValidationTestWithParams : public resolver::TestHelper,
+ public testing::TestWithParam<T> {};
using FloatAllMatching =
ResolverBuiltinsValidationTestWithParams<std::tuple<std::string, uint32_t>>;
TEST_P(FloatAllMatching, Scalar) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(Expr(1.0f));
- }
- auto* builtin = Call(name, params);
- Func("func", {}, ty.void_(), {CallStmt(builtin)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ auto* builtin = Call(name, params);
+ Func("func", {}, ty.void_(), {CallStmt(builtin)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->Is<sem::F32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<sem::F32>());
}
TEST_P(FloatAllMatching, Vec2) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec2<f32>(1.0f, 1.0f));
- }
- auto* builtin = Call(name, params);
- Func("func", {}, ty.void_(), {CallStmt(builtin)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<f32>(1.0f, 1.0f));
+ }
+ auto* builtin = Call(name, params);
+ Func("func", {}, ty.void_(), {CallStmt(builtin)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
}
TEST_P(FloatAllMatching, Vec3) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec3<f32>(1.0f, 1.0f, 1.0f));
- }
- auto* builtin = Call(name, params);
- Func("func", {}, ty.void_(), {CallStmt(builtin)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<f32>(1.0f, 1.0f, 1.0f));
+ }
+ auto* builtin = Call(name, params);
+ Func("func", {}, ty.void_(), {CallStmt(builtin)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
}
TEST_P(FloatAllMatching, Vec4) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- }
- auto* builtin = Call(name, params);
- Func("func", {}, ty.void_(), {CallStmt(builtin)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ }
+ auto* builtin = Call(name, params);
+ Func("func", {}, ty.void_(), {CallStmt(builtin)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
}
INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest,
@@ -1080,123 +945,123 @@
ResolverBuiltinsValidationTestWithParams<std::tuple<std::string, uint32_t>>;
TEST_P(IntegerAllMatching, ScalarUnsigned) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(Construct<uint32_t>(1));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->Is<sem::U32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<sem::U32>());
}
TEST_P(IntegerAllMatching, Vec2Unsigned) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec2<uint32_t>(1u, 1u));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<uint32_t>(1u, 1u));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
}
TEST_P(IntegerAllMatching, Vec3Unsigned) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec3<uint32_t>(1u, 1u, 1u));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<uint32_t>(1u, 1u, 1u));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
}
TEST_P(IntegerAllMatching, Vec4Unsigned) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec4<uint32_t>(1u, 1u, 1u, 1u));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<uint32_t>(1u, 1u, 1u, 1u));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
}
TEST_P(IntegerAllMatching, ScalarSigned) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(Construct<int32_t>(1));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Construct<int32_t>(1));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->Is<sem::I32>());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<sem::I32>());
}
TEST_P(IntegerAllMatching, Vec2Signed) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec2<int32_t>(1, 1));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<int32_t>(1, 1));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
}
TEST_P(IntegerAllMatching, Vec3Signed) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec3<int32_t>(1, 1, 1));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<int32_t>(1, 1, 1));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
}
TEST_P(IntegerAllMatching, Vec4Signed) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec4<int32_t>(1, 1, 1, 1));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<int32_t>(1, 1, 1, 1));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
}
INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest,
@@ -1212,59 +1077,58 @@
ResolverBuiltinsValidationTestWithParams<std::tuple<std::string, uint32_t>>;
TEST_P(BooleanVectorInput, Vec2) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec2<bool>(true, true));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<bool>(true, true));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(BooleanVectorInput, Vec3) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec3<bool>(true, true, true));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<bool>(true, true, true));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(BooleanVectorInput, Vec4) {
- std::string name = std::get<0>(GetParam());
- uint32_t num_params = std::get<1>(GetParam());
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
- ast::ExpressionList params;
- for (uint32_t i = 0; i < num_params; ++i) {
- params.push_back(vec4<bool>(true, true, true, true));
- }
- auto* builtin = Call(name, params);
- WrapInFunction(builtin);
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<bool>(true, true, true, true));
+ }
+ auto* builtin = Call(name, params);
+ WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest,
BooleanVectorInput,
- ::testing::Values(std::make_tuple("all", 1),
- std::make_tuple("any", 1)));
+ ::testing::Values(std::make_tuple("all", 1), std::make_tuple("any", 1)));
using DataPacking4x8 = ResolverBuiltinsValidationTestWithParams<std::string>;
TEST_P(DataPacking4x8, Float_Vec4) {
- auto name = GetParam();
- auto* builtin = Call(name, vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
- WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto name = GetParam();
+ auto* builtin = Call(name, vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f));
+ WrapInFunction(builtin);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest,
@@ -1274,17 +1138,15 @@
using DataPacking2x16 = ResolverBuiltinsValidationTestWithParams<std::string>;
TEST_P(DataPacking2x16, Float_Vec2) {
- auto name = GetParam();
- auto* builtin = Call(name, vec2<f32>(1.0f, 1.0f));
- WrapInFunction(builtin);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto name = GetParam();
+ auto* builtin = Call(name, vec2<f32>(1.0f, 1.0f));
+ WrapInFunction(builtin);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest,
DataPacking2x16,
- ::testing::Values("pack2x16snorm",
- "pack2x16unorm",
- "pack2x16float"));
+ ::testing::Values("pack2x16snorm", "pack2x16unorm", "pack2x16float"));
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/resolver/call_test.cc b/src/tint/resolver/call_test.cc
index 20c80bc..0b622f1 100644
--- a/src/tint/resolver/call_test.cc
+++ b/src/tint/resolver/call_test.cc
@@ -58,13 +58,13 @@
using ResolverCallTest = ResolverTest;
struct Params {
- builder::ast_expr_func_ptr create_value;
- builder::ast_type_func_ptr create_type;
+ builder::ast_expr_func_ptr create_value;
+ builder::ast_type_func_ptr create_type;
};
template <typename T>
constexpr Params ParamsFor() {
- return Params{DataType<T>::Expr, DataType<T>::AST};
+ return Params{DataType<T>::Expr, DataType<T>::AST};
}
static constexpr Params all_param_types[] = {
@@ -82,34 +82,34 @@
};
TEST_F(ResolverCallTest, Valid) {
- ast::VariableList params;
- ast::ExpressionList args;
- for (auto& p : all_param_types) {
- params.push_back(Param(Sym(), p.create_type(*this)));
- args.push_back(p.create_value(*this, 0));
- }
+ ast::VariableList params;
+ ast::ExpressionList args;
+ for (auto& p : all_param_types) {
+ params.push_back(Param(Sym(), p.create_type(*this)));
+ args.push_back(p.create_value(*this, 0));
+ }
- auto* func = Func("foo", std::move(params), ty.f32(), {Return(1.23f)});
- auto* call_expr = Call("foo", std::move(args));
- WrapInFunction(call_expr);
+ auto* func = Func("foo", std::move(params), ty.f32(), {Return(1.23f)});
+ auto* call_expr = Call("foo", std::move(args));
+ WrapInFunction(call_expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* call = Sem().Get(call_expr);
- EXPECT_NE(call, nullptr);
- EXPECT_EQ(call->Target(), Sem().Get(func));
+ auto* call = Sem().Get(call_expr);
+ EXPECT_NE(call, nullptr);
+ EXPECT_EQ(call->Target(), Sem().Get(func));
}
TEST_F(ResolverCallTest, OutOfOrder) {
- auto* call_expr = Call("b");
- Func("a", {}, ty.void_(), {CallStmt(call_expr)});
- auto* b = Func("b", {}, ty.void_(), {});
+ auto* call_expr = Call("b");
+ Func("a", {}, ty.void_(), {CallStmt(call_expr)});
+ auto* b = Func("b", {}, ty.void_(), {});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* call = Sem().Get(call_expr);
- EXPECT_NE(call, nullptr);
- EXPECT_EQ(call->Target(), Sem().Get(b));
+ auto* call = Sem().Get(call_expr);
+ EXPECT_NE(call, nullptr);
+ EXPECT_EQ(call->Target(), Sem().Get(b));
}
} // namespace
diff --git a/src/tint/resolver/call_validation_test.cc b/src/tint/resolver/call_validation_test.cc
index 25b23c0..c470bf5 100644
--- a/src/tint/resolver/call_validation_test.cc
+++ b/src/tint/resolver/call_validation_test.cc
@@ -24,261 +24,244 @@
using ResolverCallValidationTest = ResolverTest;
TEST_F(ResolverCallValidationTest, TooFewArgs) {
- Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(),
- {Return()});
- auto* call = Call(Source{{12, 34}}, "foo", 1);
- WrapInFunction(call);
+ Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), {Return()});
+ auto* call = Call(Source{{12, 34}}, "foo", 1);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: too few arguments in call to 'foo', expected 2, got 1");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: too few arguments in call to 'foo', expected 2, got 1");
}
TEST_F(ResolverCallValidationTest, TooManyArgs) {
- Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(),
- {Return()});
- auto* call = Call(Source{{12, 34}}, "foo", 1, 1.0f, 1.0f);
- WrapInFunction(call);
+ Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), {Return()});
+ auto* call = Call(Source{{12, 34}}, "foo", 1, 1.0f, 1.0f);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: too many arguments in call to 'foo', expected 2, got 3");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: too many arguments in call to 'foo', expected 2, got 3");
}
TEST_F(ResolverCallValidationTest, MismatchedArgs) {
- Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(),
- {Return()});
- auto* call = Call("foo", Expr(Source{{12, 34}}, true), 1.0f);
- WrapInFunction(call);
+ Func("foo", {Param(Sym(), ty.i32()), Param(Sym(), ty.f32())}, ty.void_(), {Return()});
+ auto* call = Call("foo", Expr(Source{{12, 34}}, true), 1.0f);
+ WrapInFunction(call);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type mismatch for argument 1 in call to 'foo', "
- "expected 'i32', got 'bool'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type mismatch for argument 1 in call to 'foo', "
+ "expected 'i32', got 'bool'");
}
TEST_F(ResolverCallValidationTest, UnusedRetval) {
- // fn func() -> f32 { return 1.0; }
- // fn main() {func(); return; }
+ // fn func() -> f32 { return 1.0; }
+ // fn main() {func(); return; }
- Func("func", {}, ty.f32(), {Return(Expr(1.0f))}, {});
+ Func("func", {}, ty.f32(), {Return(Expr(1.0f))}, {});
- Func("main", {}, ty.void_(),
- {
- CallStmt(Source{{12, 34}}, Call("func")),
- Return(),
- });
+ Func("main", {}, ty.void_(),
+ {
+ CallStmt(Source{{12, 34}}, Call("func")),
+ Return(),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCallValidationTest, PointerArgument_VariableIdentExpr) {
- // fn foo(p: ptr<function, i32>) {}
- // fn main() {
- // var z: i32 = 1;
- // foo(&z);
- // }
- auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
- Func("foo", {param}, ty.void_(), {});
- Func("main", {}, ty.void_(),
- {
- Decl(Var("z", ty.i32(), Expr(1))),
- CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Expr("z")))),
- });
+ // fn foo(p: ptr<function, i32>) {}
+ // fn main() {
+ // var z: i32 = 1;
+ // foo(&z);
+ // }
+ auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+ Func("foo", {param}, ty.void_(), {});
+ Func("main", {}, ty.void_(),
+ {
+ Decl(Var("z", ty.i32(), Expr(1))),
+ CallStmt(Call("foo", AddressOf(Source{{12, 34}}, Expr("z")))),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCallValidationTest, PointerArgument_ConstIdentExpr) {
- // fn foo(p: ptr<function, i32>) {}
- // fn main() {
- // let z: i32 = 1;
- // foo(&z);
- // }
- auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
- Func("foo", {param}, ty.void_(), {});
- Func("main", {}, ty.void_(),
- {
- Decl(Let("z", ty.i32(), Expr(1))),
- CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
- });
+ // fn foo(p: ptr<function, i32>) {}
+ // fn main() {
+ // let z: i32 = 1;
+ // foo(&z);
+ // }
+ auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+ Func("foo", {param}, ty.void_(), {});
+ Func("main", {}, ty.void_(),
+ {
+ Decl(Let("z", ty.i32(), Expr(1))),
+ CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, "z")))),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
}
TEST_F(ResolverCallValidationTest, PointerArgument_NotIdentExprVar) {
- // struct S { m: i32; };
- // fn foo(p: ptr<function, i32>) {}
- // fn main() {
- // var v: S;
- // foo(&v.m);
- // }
- auto* S = Structure("S", {Member("m", ty.i32())});
- auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
- Func("foo", {param}, ty.void_(), {});
- Func("main", {}, ty.void_(),
- {
- Decl(Var("v", ty.Of(S))),
- CallStmt(Call(
- "foo", AddressOf(Source{{12, 34}}, MemberAccessor("v", "m")))),
- });
+ // struct S { m: i32; };
+ // fn foo(p: ptr<function, i32>) {}
+ // fn main() {
+ // var v: S;
+ // foo(&v.m);
+ // }
+ auto* S = Structure("S", {Member("m", ty.i32())});
+ auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+ Func("foo", {param}, ty.void_(), {});
+ Func("main", {}, ty.void_(),
+ {
+ Decl(Var("v", ty.Of(S))),
+ CallStmt(Call("foo", AddressOf(Source{{12, 34}}, MemberAccessor("v", "m")))),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: expected an address-of expression of a variable "
- "identifier expression or a function parameter");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: expected an address-of expression of a variable "
+ "identifier expression or a function parameter");
}
TEST_F(ResolverCallValidationTest, PointerArgument_AddressOfMemberAccessor) {
- // struct S { m: i32; };
- // fn foo(p: ptr<function, i32>) {}
- // fn main() {
- // let v: S = S();
- // foo(&v.m);
- // }
- auto* S = Structure("S", {Member("m", ty.i32())});
- auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
- Func("foo", {param}, ty.void_(), {});
- Func("main", {}, ty.void_(),
- {
- Decl(Let("v", ty.Of(S), Construct(ty.Of(S)))),
- CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}},
- MemberAccessor("v", "m"))))),
- });
+ // struct S { m: i32; };
+ // fn foo(p: ptr<function, i32>) {}
+ // fn main() {
+ // let v: S = S();
+ // foo(&v.m);
+ // }
+ auto* S = Structure("S", {Member("m", ty.i32())});
+ auto* param = Param("p", ty.pointer<i32>(ast::StorageClass::kFunction));
+ Func("foo", {param}, ty.void_(), {});
+ Func("main", {}, ty.void_(),
+ {
+ Decl(Let("v", ty.Of(S), Construct(ty.Of(S)))),
+ CallStmt(Call("foo", AddressOf(Expr(Source{{12, 34}}, MemberAccessor("v", "m"))))),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
}
TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParam) {
- // fn foo(p: ptr<function, i32>) {}
- // fn bar(p: ptr<function, i32>) {
- // foo(p);
- // }
- Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
- ty.void_(), {});
- Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
- ty.void_(), ast::StatementList{CallStmt(Call("foo", Expr("p")))});
+ // fn foo(p: ptr<function, i32>) {}
+ // fn bar(p: ptr<function, i32>) {
+ // foo(p);
+ // }
+ Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
+ Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(),
+ ast::StatementList{CallStmt(Call("foo", Expr("p")))});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCallValidationTest, PointerArgument_FunctionParamWithMain) {
- // fn foo(p: ptr<function, i32>) {}
- // fn bar(p: ptr<function, i32>) {
- // foo(p);
- // }
- // @stage(fragment)
- // fn main() {
- // var v: i32;
- // bar(&v);
- // }
- Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
- ty.void_(), {});
- Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
- ty.void_(), ast::StatementList{CallStmt(Call("foo", Expr("p")))});
- Func("main", ast::VariableList{}, ty.void_(),
- {
- Decl(Var("v", ty.i32(), Expr(1))),
- CallStmt(Call("foo", AddressOf(Expr("v")))),
- },
- {
- Stage(ast::PipelineStage::kFragment),
- });
+ // fn foo(p: ptr<function, i32>) {}
+ // fn bar(p: ptr<function, i32>) {
+ // foo(p);
+ // }
+ // @stage(fragment)
+ // fn main() {
+ // var v: i32;
+ // bar(&v);
+ // }
+ Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
+ Func("bar", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(),
+ ast::StatementList{CallStmt(Call("foo", Expr("p")))});
+ Func("main", ast::VariableList{}, ty.void_(),
+ {
+ Decl(Var("v", ty.i32(), Expr(1))),
+ CallStmt(Call("foo", AddressOf(Expr("v")))),
+ },
+ {
+ Stage(ast::PipelineStage::kFragment),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCallValidationTest, LetPointer) {
- // fn x(p : ptr<function, i32>) -> i32 {}
- // @stage(fragment)
- // fn main() {
- // var v: i32;
- // let p: ptr<function, i32> = &v;
- // var c: i32 = x(p);
- // }
- Func("x", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))},
- ty.void_(), {});
- auto* v = Var("v", ty.i32());
- auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kFunction),
- AddressOf(v));
- auto* c = Var("c", ty.i32(), ast::StorageClass::kNone,
- Call("x", Expr(Source{{12, 34}}, p)));
- Func("main", ast::VariableList{}, ty.void_(),
- {
- Decl(v),
- Decl(p),
- Decl(c),
- },
- {
- Stage(ast::PipelineStage::kFragment),
- });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: expected an address-of expression of a variable "
- "identifier expression or a function parameter");
+ // fn x(p : ptr<function, i32>) -> i32 {}
+ // @stage(fragment)
+ // fn main() {
+ // var v: i32;
+ // let p: ptr<function, i32> = &v;
+ // var c: i32 = x(p);
+ // }
+ Func("x", {Param("p", ty.pointer<i32>(ast::StorageClass::kFunction))}, ty.void_(), {});
+ auto* v = Var("v", ty.i32());
+ auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(v));
+ auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("x", Expr(Source{{12, 34}}, p)));
+ Func("main", ast::VariableList{}, ty.void_(),
+ {
+ Decl(v),
+ Decl(p),
+ Decl(c),
+ },
+ {
+ Stage(ast::PipelineStage::kFragment),
+ });
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: expected an address-of expression of a variable "
+ "identifier expression or a function parameter");
}
TEST_F(ResolverCallValidationTest, LetPointerPrivate) {
- // let p: ptr<private, i32> = &v;
- // fn foo(p : ptr<private, i32>) -> i32 {}
- // var v: i32;
- // @stage(fragment)
- // fn main() {
- // var c: i32 = foo(p);
- // }
- Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kPrivate))},
- ty.void_(), {});
- auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
- auto* p =
- Let("p", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(v));
- auto* c = Var("c", ty.i32(), ast::StorageClass::kNone,
- Call("foo", Expr(Source{{12, 34}}, p)));
- Func("main", ast::VariableList{}, ty.void_(),
- {
- Decl(p),
- Decl(c),
- },
- {
- Stage(ast::PipelineStage::kFragment),
- });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: expected an address-of expression of a variable "
- "identifier expression or a function parameter");
+ // let p: ptr<private, i32> = &v;
+ // fn foo(p : ptr<private, i32>) -> i32 {}
+ // var v: i32;
+ // @stage(fragment)
+ // fn main() {
+ // var c: i32 = foo(p);
+ // }
+ Func("foo", {Param("p", ty.pointer<i32>(ast::StorageClass::kPrivate))}, ty.void_(), {});
+ auto* v = Global("v", ty.i32(), ast::StorageClass::kPrivate);
+ auto* p = Let("p", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(v));
+ auto* c = Var("c", ty.i32(), ast::StorageClass::kNone, Call("foo", Expr(Source{{12, 34}}, p)));
+ Func("main", ast::VariableList{}, ty.void_(),
+ {
+ Decl(p),
+ Decl(c),
+ },
+ {
+ Stage(ast::PipelineStage::kFragment),
+ });
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: expected an address-of expression of a variable "
+ "identifier expression or a function parameter");
}
TEST_F(ResolverCallValidationTest, CallVariable) {
- // var v : i32;
- // fn f() {
- // v();
- // }
- Global("v", ty.i32(), ast::StorageClass::kPrivate);
- Func("f", {}, ty.void_(), {CallStmt(Call(Source{{12, 34}}, "v"))});
+ // var v : i32;
+ // fn f() {
+ // v();
+ // }
+ Global("v", ty.i32(), ast::StorageClass::kPrivate);
+ Func("f", {}, ty.void_(), {CallStmt(Call(Source{{12, 34}}, "v"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: cannot call variable 'v'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(error: cannot call variable 'v'
note: 'v' declared here)");
}
TEST_F(ResolverCallValidationTest, CallVariableShadowsFunction) {
- // fn x() {}
- // fn f() {
- // var x : i32;
- // x();
- // }
- Func("x", {}, ty.void_(), {});
- Func("f", {}, ty.void_(),
- {
- Decl(Var(Source{{56, 78}}, "x", ty.i32())),
- CallStmt(Call(Source{{12, 34}}, "x")),
- });
+ // fn x() {}
+ // fn f() {
+ // var x : i32;
+ // x();
+ // }
+ Func("x", {}, ty.void_(), {});
+ Func("f", {}, ty.void_(),
+ {
+ Decl(Var(Source{{56, 78}}, "x", ty.i32())),
+ CallStmt(Call(Source{{12, 34}}, "x")),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(error: cannot call variable 'x'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(error: cannot call variable 'x'
56:78 note: 'x' declared here)");
}
diff --git a/src/tint/resolver/compound_assignment_validation_test.cc b/src/tint/resolver/compound_assignment_validation_test.cc
index c28a489..bf1ed07 100644
--- a/src/tint/resolver/compound_assignment_validation_test.cc
+++ b/src/tint/resolver/compound_assignment_validation_test.cc
@@ -24,274 +24,252 @@
using ResolverCompoundAssignmentValidationTest = ResolverTest;
TEST_F(ResolverCompoundAssignmentValidationTest, CompatibleTypes) {
- // var a : i32 = 2;
- // a += 2
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var,
- CompoundAssign(Source{{12, 34}}, "a", 2, ast::BinaryOp::kAdd));
+ // var a : i32 = 2;
+ // a += 2
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, CompoundAssign(Source{{12, 34}}, "a", 2, ast::BinaryOp::kAdd));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCompoundAssignmentValidationTest, CompatibleTypesThroughAlias) {
- // alias myint = i32;
- // var a : myint = 2;
- // a += 2
- auto* myint = Alias("myint", ty.i32());
- auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var,
- CompoundAssign(Source{{12, 34}}, "a", 2, ast::BinaryOp::kAdd));
+ // alias myint = i32;
+ // var a : myint = 2;
+ // a += 2
+ auto* myint = Alias("myint", ty.i32());
+ auto* var = Var("a", ty.Of(myint), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, CompoundAssign(Source{{12, 34}}, "a", 2, ast::BinaryOp::kAdd));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverCompoundAssignmentValidationTest,
- CompatibleTypesAssignThroughPointer) {
- // var a : i32;
- // let b : ptr<function,i32> = &a;
- // *b += 2;
- const auto func = ast::StorageClass::kFunction;
- auto* var_a = Var("a", ty.i32(), func, Expr(2));
- auto* var_b = Let("b", ty.pointer<int>(func), AddressOf(Expr("a")));
- WrapInFunction(
- var_a, var_b,
- CompoundAssign(Source{{12, 34}}, Deref("b"), 2, ast::BinaryOp::kAdd));
+TEST_F(ResolverCompoundAssignmentValidationTest, CompatibleTypesAssignThroughPointer) {
+ // var a : i32;
+ // let b : ptr<function,i32> = &a;
+ // *b += 2;
+ const auto func = ast::StorageClass::kFunction;
+ auto* var_a = Var("a", ty.i32(), func, Expr(2));
+ auto* var_b = Let("b", ty.pointer<int>(func), AddressOf(Expr("a")));
+ WrapInFunction(var_a, var_b,
+ CompoundAssign(Source{{12, 34}}, Deref("b"), 2, ast::BinaryOp::kAdd));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCompoundAssignmentValidationTest, IncompatibleTypes) {
- // {
- // var a : i32 = 2;
- // a += 2.3;
- // }
+ // {
+ // var a : i32 = 2;
+ // a += 2.3;
+ // }
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* assign =
- CompoundAssign(Source{{12, 34}}, "a", 2.3f, ast::BinaryOp::kAdd);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2.3f, ast::BinaryOp::kAdd);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: compound assignment operand types are invalid: i32 "
- "add f32");
+ EXPECT_EQ(r()->error(),
+ "12:34 error: compound assignment operand types are invalid: i32 "
+ "add f32");
}
TEST_F(ResolverCompoundAssignmentValidationTest, IncompatibleOp) {
- // {
- // var a : f32 = 1.0;
- // a |= 2.0;
- // }
+ // {
+ // var a : f32 = 1.0;
+ // a |= 2.0;
+ // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(1.f));
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(1.f));
- auto* assign =
- CompoundAssign(Source{{12, 34}}, "a", 2.0f, ast::BinaryOp::kOr);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2.0f, ast::BinaryOp::kOr);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: compound assignment operand types are invalid: f32 or f32");
+ EXPECT_EQ(r()->error(),
+ "12:34 error: compound assignment operand types are invalid: f32 or f32");
}
TEST_F(ResolverCompoundAssignmentValidationTest, VectorScalar_Pass) {
- // {
- // var a : vec4<f32>;
- // a += 1.0;
- // }
+ // {
+ // var a : vec4<f32>;
+ // a += 1.0;
+ // }
- auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
- auto* assign =
- CompoundAssign(Source{{12, 34}}, "a", 1.f, ast::BinaryOp::kAdd);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", 1.f, ast::BinaryOp::kAdd);
+ WrapInFunction(var, assign);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCompoundAssignmentValidationTest, ScalarVector_Fail) {
- // {
- // var a : f32;
- // a += vec4<f32>();
- // }
+ // {
+ // var a : f32;
+ // a += vec4<f32>();
+ // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* assign =
- CompoundAssign(Source{{12, 34}}, "a", vec4<f32>(), ast::BinaryOp::kAdd);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", vec4<f32>(), ast::BinaryOp::kAdd);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'vec4<f32>' to 'f32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'vec4<f32>' to 'f32'");
}
TEST_F(ResolverCompoundAssignmentValidationTest, MatrixScalar_Pass) {
- // {
- // var a : mat4x4<f32>;
- // a *= 2.0;
- // }
+ // {
+ // var a : mat4x4<f32>;
+ // a *= 2.0;
+ // }
- auto* var = Var("a", ty.mat4x4<f32>(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.mat4x4<f32>(), ast::StorageClass::kNone);
- auto* assign =
- CompoundAssign(Source{{12, 34}}, "a", 2.f, ast::BinaryOp::kMultiply);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", 2.f, ast::BinaryOp::kMultiply);
+ WrapInFunction(var, assign);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCompoundAssignmentValidationTest, ScalarMatrix_Fail) {
- // {
- // var a : f32;
- // a *= mat4x4();
- // }
+ // {
+ // var a : f32;
+ // a *= mat4x4();
+ // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat4x4<f32>(),
- ast::BinaryOp::kMultiply);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat4x4<f32>(), ast::BinaryOp::kMultiply);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'mat4x4<f32>' to 'f32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'mat4x4<f32>' to 'f32'");
}
TEST_F(ResolverCompoundAssignmentValidationTest, VectorMatrix_Pass) {
- // {
- // var a : vec4<f32>;
- // a *= mat4x4();
- // }
+ // {
+ // var a : vec4<f32>;
+ // a *= mat4x4();
+ // }
- auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
- auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat4x4<f32>(),
- ast::BinaryOp::kMultiply);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat4x4<f32>(), ast::BinaryOp::kMultiply);
+ WrapInFunction(var, assign);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverCompoundAssignmentValidationTest, VectorMatrix_ColumnMismatch) {
- // {
- // var a : vec4<f32>;
- // a *= mat4x2();
- // }
+ // {
+ // var a : vec4<f32>;
+ // a *= mat4x2();
+ // }
- auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
- auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat4x2<f32>(),
- ast::BinaryOp::kMultiply);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat4x2<f32>(), ast::BinaryOp::kMultiply);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: compound assignment operand types are invalid: "
- "vec4<f32> multiply mat4x2<f32>");
+ EXPECT_EQ(r()->error(),
+ "12:34 error: compound assignment operand types are invalid: "
+ "vec4<f32> multiply mat4x2<f32>");
}
TEST_F(ResolverCompoundAssignmentValidationTest, VectorMatrix_ResultMismatch) {
- // {
- // var a : vec4<f32>;
- // a *= mat2x4();
- // }
+ // {
+ // var a : vec4<f32>;
+ // a *= mat2x4();
+ // }
- auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.vec4<f32>(), ast::StorageClass::kNone);
- auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat2x4<f32>(),
- ast::BinaryOp::kMultiply);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", mat2x4<f32>(), ast::BinaryOp::kMultiply);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot assign 'vec2<f32>' to 'vec4<f32>'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'vec2<f32>' to 'vec4<f32>'");
}
TEST_F(ResolverCompoundAssignmentValidationTest, MatrixVector_Fail) {
- // {
- // var a : mat4x4<f32>;
- // a *= vec4();
- // }
+ // {
+ // var a : mat4x4<f32>;
+ // a *= vec4();
+ // }
- auto* var = Var("a", ty.mat4x4<f32>(), ast::StorageClass::kNone);
+ auto* var = Var("a", ty.mat4x4<f32>(), ast::StorageClass::kNone);
- auto* assign = CompoundAssign(Source{{12, 34}}, "a", vec4<f32>(),
- ast::BinaryOp::kMultiply);
- WrapInFunction(var, assign);
+ auto* assign = CompoundAssign(Source{{12, 34}}, "a", vec4<f32>(), ast::BinaryOp::kMultiply);
+ WrapInFunction(var, assign);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot assign 'vec4<f32>' to 'mat4x4<f32>'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot assign 'vec4<f32>' to 'mat4x4<f32>'");
}
TEST_F(ResolverCompoundAssignmentValidationTest, Phony) {
- // {
- // _ += 1;
- // }
- WrapInFunction(
- CompoundAssign(Source{{56, 78}}, Phony(), 1, ast::BinaryOp::kAdd));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: compound assignment operand types are invalid: void "
- "add i32");
+ // {
+ // _ += 1;
+ // }
+ WrapInFunction(CompoundAssign(Source{{56, 78}}, Phony(), 1, ast::BinaryOp::kAdd));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: compound assignment operand types are invalid: void "
+ "add i32");
}
TEST_F(ResolverCompoundAssignmentValidationTest, ReadOnlyBuffer) {
- // @group(0) @binding(0) var<storage,read> a : i32;
- // {
- // a += 1;
- // }
- Global(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
- WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", 1, ast::BinaryOp::kAdd));
+ // @group(0) @binding(0) var<storage,read> a : i32;
+ // {
+ // a += 1;
+ // }
+ Global(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
+ WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", 1, ast::BinaryOp::kAdd));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: cannot store into a read-only type 'ref<storage, "
- "i32, read>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: cannot store into a read-only type 'ref<storage, "
+ "i32, read>'");
}
TEST_F(ResolverCompoundAssignmentValidationTest, LhsConstant) {
- // let a = 1;
- // a += 1;
- auto* a = Let(Source{{12, 34}}, "a", nullptr, Expr(1));
- WrapInFunction(
- a, CompoundAssign(Expr(Source{{56, 78}}, "a"), 1, ast::BinaryOp::kAdd));
+ // let a = 1;
+ // a += 1;
+ auto* a = Let(Source{{12, 34}}, "a", nullptr, Expr(1));
+ WrapInFunction(a, CompoundAssign(Expr(Source{{56, 78}}, "a"), 1, ast::BinaryOp::kAdd));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot assign to const
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(56:78 error: cannot assign to const
12:34 note: 'a' is declared here:)");
}
TEST_F(ResolverCompoundAssignmentValidationTest, LhsLiteral) {
- // 1 += 1;
- WrapInFunction(
- CompoundAssign(Expr(Source{{56, 78}}, 1), 1, ast::BinaryOp::kAdd));
+ // 1 += 1;
+ WrapInFunction(CompoundAssign(Expr(Source{{56, 78}}, 1), 1, ast::BinaryOp::kAdd));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "56:78 error: cannot assign to value of type 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: cannot assign to value of type 'i32'");
}
TEST_F(ResolverCompoundAssignmentValidationTest, LhsAtomic) {
- // var<workgroup> a : atomic<i32>;
- // a += a;
- Global(Source{{12, 34}}, "a", ty.atomic(ty.i32()),
- ast::StorageClass::kWorkgroup);
- WrapInFunction(
- CompoundAssign(Source{{56, 78}}, "a", "a", ast::BinaryOp::kAdd));
+ // var<workgroup> a : atomic<i32>;
+ // a += a;
+ Global(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+ WrapInFunction(CompoundAssign(Source{{56, 78}}, "a", "a", ast::BinaryOp::kAdd));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: compound assignment operand types are invalid: "
- "atomic<i32> add atomic<i32>");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: compound assignment operand types are invalid: "
+ "atomic<i32> add atomic<i32>");
}
} // namespace
diff --git a/src/tint/resolver/compound_statement_test.cc b/src/tint/resolver/compound_statement_test.cc
index f15bf28..e3aec74 100644
--- a/src/tint/resolver/compound_statement_test.cc
+++ b/src/tint/resolver/compound_statement_test.cc
@@ -28,352 +28,339 @@
using ResolverCompoundStatementTest = ResolverTest;
TEST_F(ResolverCompoundStatementTest, FunctionBlock) {
- // fn F() {
- // var x : 32;
- // }
- auto* stmt = Decl(Var("x", ty.i32()));
- auto* f = Func("F", {}, ty.void_(), {stmt});
+ // fn F() {
+ // var x : 32;
+ // }
+ auto* stmt = Decl(Var("x", ty.i32()));
+ auto* f = Func("F", {}, ty.void_(), {stmt});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* s = Sem().Get(stmt);
- ASSERT_NE(s, nullptr);
- ASSERT_NE(s->Block(), nullptr);
- ASSERT_TRUE(s->Block()->Is<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Function()->Declaration(), f);
- EXPECT_EQ(s->Block()->Parent(), nullptr);
+ auto* s = Sem().Get(stmt);
+ ASSERT_NE(s, nullptr);
+ ASSERT_NE(s->Block(), nullptr);
+ ASSERT_TRUE(s->Block()->Is<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Function()->Declaration(), f);
+ EXPECT_EQ(s->Block()->Parent(), nullptr);
}
TEST_F(ResolverCompoundStatementTest, Block) {
- // fn F() {
- // {
- // var x : 32;
- // }
- // }
- auto* stmt = Decl(Var("x", ty.i32()));
- auto* block = Block(stmt);
- auto* f = Func("F", {}, ty.void_(), {block});
+ // fn F() {
+ // {
+ // var x : 32;
+ // }
+ // }
+ auto* stmt = Decl(Var("x", ty.i32()));
+ auto* block = Block(stmt);
+ auto* f = Func("F", {}, ty.void_(), {block});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- {
- auto* s = Sem().Get(block);
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::BlockStatement>());
- EXPECT_EQ(s, s->Block());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- }
- {
- auto* s = Sem().Get(stmt);
- ASSERT_NE(s, nullptr);
- ASSERT_NE(s->Block(), nullptr);
- EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Block()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- ASSERT_TRUE(s->Block()->Parent()->Is<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Function()->Declaration(), f);
- EXPECT_EQ(s->Block()->Parent()->Parent(), nullptr);
- }
+ {
+ auto* s = Sem().Get(block);
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::BlockStatement>());
+ EXPECT_EQ(s, s->Block());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
+ {
+ auto* s = Sem().Get(stmt);
+ ASSERT_NE(s, nullptr);
+ ASSERT_NE(s->Block(), nullptr);
+ EXPECT_EQ(s->Block(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Block()->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ ASSERT_TRUE(s->Block()->Parent()->Is<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Function()->Declaration(), f);
+ EXPECT_EQ(s->Block()->Parent()->Parent(), nullptr);
+ }
}
TEST_F(ResolverCompoundStatementTest, Loop) {
- // fn F() {
- // loop {
- // break;
- // continuing {
- // stmt;
- // }
- // }
- // }
- auto* brk = Break();
- auto* stmt = Ignore(1);
- auto* loop = Loop(Block(brk), Block(stmt));
- auto* f = Func("F", {}, ty.void_(), {loop});
+ // fn F() {
+ // loop {
+ // break;
+ // continuing {
+ // stmt;
+ // }
+ // }
+ // }
+ auto* brk = Break();
+ auto* stmt = Ignore(1);
+ auto* loop = Loop(Block(brk), Block(stmt));
+ auto* f = Func("F", {}, ty.void_(), {loop});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- {
- auto* s = Sem().Get(loop);
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::LoopStatement>());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- }
- {
- auto* s = Sem().Get(brk);
- ASSERT_NE(s, nullptr);
- ASSERT_NE(s->Block(), nullptr);
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::LoopBlockStatement>());
+ {
+ auto* s = Sem().Get(loop);
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::LoopStatement>());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ }
+ {
+ auto* s = Sem().Get(brk);
+ ASSERT_NE(s, nullptr);
+ ASSERT_NE(s->Block(), nullptr);
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::LoopBlockStatement>());
- EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::LoopStatement>());
- EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()));
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::LoopStatement>());
+ EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()));
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_TRUE(
- Is<sem::FunctionBlockStatement>(s->Parent()->Parent()->Parent()));
+ EXPECT_EQ(s->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()->Parent()));
- EXPECT_EQ(s->Function()->Declaration(), f);
+ EXPECT_EQ(s->Function()->Declaration(), f);
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(), nullptr);
- }
- {
- auto* s = Sem().Get(stmt);
- ASSERT_NE(s, nullptr);
- ASSERT_NE(s->Block(), nullptr);
- EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(), nullptr);
+ }
+ {
+ auto* s = Sem().Get(stmt);
+ ASSERT_NE(s, nullptr);
+ ASSERT_NE(s->Block(), nullptr);
+ EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent(),
- s->FindFirstParent<sem::LoopContinuingBlockStatement>());
- EXPECT_TRUE(Is<sem::LoopContinuingBlockStatement>(s->Parent()));
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::LoopContinuingBlockStatement>());
+ EXPECT_TRUE(Is<sem::LoopContinuingBlockStatement>(s->Parent()));
- EXPECT_EQ(s->Parent()->Parent(),
- s->FindFirstParent<sem::LoopBlockStatement>());
- EXPECT_TRUE(Is<sem::LoopBlockStatement>(s->Parent()->Parent()));
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::LoopBlockStatement>());
+ EXPECT_TRUE(Is<sem::LoopBlockStatement>(s->Parent()->Parent()));
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::LoopStatement>());
- EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()->Parent()));
+ EXPECT_EQ(s->Parent()->Parent()->Parent(), s->FindFirstParent<sem::LoopStatement>());
+ EXPECT_TRUE(Is<sem::LoopStatement>(s->Parent()->Parent()->Parent()));
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_TRUE(Is<sem::FunctionBlockStatement>(
- s->Parent()->Parent()->Parent()->Parent()));
- EXPECT_EQ(s->Function()->Declaration(), f);
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()->Parent()->Parent()));
+ EXPECT_EQ(s->Function()->Declaration(), f);
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent()->Parent(), nullptr);
- }
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent()->Parent(), nullptr);
+ }
}
TEST_F(ResolverCompoundStatementTest, ForLoop) {
- // fn F() {
- // for (var i : u32; true; i = i + 1u) {
- // return;
- // }
- // }
- auto* init = Decl(Var("i", ty.u32()));
- auto* cond = Expr(true);
- auto* cont = Assign("i", Add("i", 1u));
- auto* stmt = Return();
- auto* body = Block(stmt);
- auto* for_ = For(init, cond, cont, body);
- auto* f = Func("F", {}, ty.void_(), {for_});
+ // fn F() {
+ // for (var i : u32; true; i = i + 1u) {
+ // return;
+ // }
+ // }
+ auto* init = Decl(Var("i", ty.u32()));
+ auto* cond = Expr(true);
+ auto* cont = Assign("i", Add("i", 1u));
+ auto* stmt = Return();
+ auto* body = Block(stmt);
+ auto* for_ = For(init, cond, cont, body);
+ auto* f = Func("F", {}, ty.void_(), {for_});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- {
- auto* s = Sem().Get(for_);
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::ForLoopStatement>());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- }
- {
- auto* s = Sem().Get(init);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
- EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()));
- EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()));
- }
- { // Condition expression's statement is the for-loop itself
- auto* e = Sem().Get(cond);
- ASSERT_NE(e, nullptr);
- auto* s = e->Stmt();
- ASSERT_NE(s, nullptr);
- ASSERT_TRUE(Is<sem::ForLoopStatement>(s));
- ASSERT_NE(s->Parent(), nullptr);
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Block()));
- }
- {
- auto* s = Sem().Get(cont);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
- EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()));
- EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()));
- }
- {
- auto* s = Sem().Get(stmt);
- ASSERT_NE(s, nullptr);
- ASSERT_NE(s->Block(), nullptr);
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Block(), s->FindFirstParent<sem::LoopBlockStatement>());
- EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()->Parent()));
- EXPECT_EQ(s->Block()->Parent(),
- s->FindFirstParent<sem::ForLoopStatement>());
- ASSERT_TRUE(
- Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
- EXPECT_EQ(s->Block()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Function()->Declaration(), f);
- EXPECT_EQ(s->Block()->Parent()->Parent()->Parent(), nullptr);
- }
+ {
+ auto* s = Sem().Get(for_);
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::ForLoopStatement>());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ }
+ {
+ auto* s = Sem().Get(init);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
+ EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()));
+ EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()));
+ }
+ { // Condition expression's statement is the for-loop itself
+ auto* e = Sem().Get(cond);
+ ASSERT_NE(e, nullptr);
+ auto* s = e->Stmt();
+ ASSERT_NE(s, nullptr);
+ ASSERT_TRUE(Is<sem::ForLoopStatement>(s));
+ ASSERT_NE(s->Parent(), nullptr);
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Block()));
+ }
+ {
+ auto* s = Sem().Get(cont);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
+ EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()));
+ EXPECT_EQ(s->Block(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_TRUE(Is<sem::FunctionBlockStatement>(s->Parent()->Parent()));
+ }
+ {
+ auto* s = Sem().Get(stmt);
+ ASSERT_NE(s, nullptr);
+ ASSERT_NE(s->Block(), nullptr);
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Block(), s->FindFirstParent<sem::LoopBlockStatement>());
+ EXPECT_TRUE(Is<sem::ForLoopStatement>(s->Parent()->Parent()));
+ EXPECT_EQ(s->Block()->Parent(), s->FindFirstParent<sem::ForLoopStatement>());
+ ASSERT_TRUE(Is<sem::FunctionBlockStatement>(s->Block()->Parent()->Parent()));
+ EXPECT_EQ(s->Block()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Function()->Declaration(), f);
+ EXPECT_EQ(s->Block()->Parent()->Parent()->Parent(), nullptr);
+ }
}
TEST_F(ResolverCompoundStatementTest, If) {
- // fn F() {
- // if (cond_a) {
- // stat_a;
- // } else if (cond_b) {
- // stat_b;
- // } else {
- // stat_c;
- // }
- // }
+ // fn F() {
+ // if (cond_a) {
+ // stat_a;
+ // } else if (cond_b) {
+ // stat_b;
+ // } else {
+ // stat_c;
+ // }
+ // }
- auto* cond_a = Expr(true);
- auto* stmt_a = Ignore(1);
- auto* cond_b = Expr(true);
- auto* stmt_b = Ignore(1);
- auto* stmt_c = Ignore(1);
- auto* if_stmt =
- If(cond_a, Block(stmt_a), If(cond_b, Block(stmt_b), Block(stmt_c)));
- WrapInFunction(if_stmt);
+ auto* cond_a = Expr(true);
+ auto* stmt_a = Ignore(1);
+ auto* cond_b = Expr(true);
+ auto* stmt_b = Ignore(1);
+ auto* stmt_c = Ignore(1);
+ auto* if_stmt = If(cond_a, Block(stmt_a), If(cond_b, Block(stmt_b), Block(stmt_c)));
+ WrapInFunction(if_stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- {
- auto* s = Sem().Get(if_stmt);
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::IfStatement>());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- }
- {
- auto* e = Sem().Get(cond_a);
- ASSERT_NE(e, nullptr);
- auto* s = e->Stmt();
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::IfStatement>());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- }
- {
- auto* s = Sem().Get(stmt_a);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::IfStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- }
- {
- auto* e = Sem().Get(cond_b);
- ASSERT_NE(e, nullptr);
- auto* s = e->Stmt();
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::IfStatement>());
- EXPECT_EQ(s->Parent(), s->Parent()->FindFirstParent<sem::IfStatement>());
- EXPECT_EQ(s->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent()->Parent(), s->Block());
- }
- {
- auto* s = Sem().Get(stmt_b);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- auto* elseif = s->FindFirstParent<sem::IfStatement>();
- EXPECT_EQ(s->Parent()->Parent(), elseif);
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- elseif->Parent()->FindFirstParent<sem::IfStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- }
- {
- auto* s = Sem().Get(stmt_c);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- auto* elseif = s->FindFirstParent<sem::IfStatement>();
- EXPECT_EQ(s->Parent()->Parent(), elseif);
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- elseif->Parent()->FindFirstParent<sem::IfStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- }
+ {
+ auto* s = Sem().Get(if_stmt);
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::IfStatement>());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ }
+ {
+ auto* e = Sem().Get(cond_a);
+ ASSERT_NE(e, nullptr);
+ auto* s = e->Stmt();
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::IfStatement>());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ }
+ {
+ auto* s = Sem().Get(stmt_a);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::IfStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
+ {
+ auto* e = Sem().Get(cond_b);
+ ASSERT_NE(e, nullptr);
+ auto* s = e->Stmt();
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::IfStatement>());
+ EXPECT_EQ(s->Parent(), s->Parent()->FindFirstParent<sem::IfStatement>());
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent()->Parent(), s->Block());
+ }
+ {
+ auto* s = Sem().Get(stmt_b);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ auto* elseif = s->FindFirstParent<sem::IfStatement>();
+ EXPECT_EQ(s->Parent()->Parent(), elseif);
+ EXPECT_EQ(s->Parent()->Parent()->Parent(),
+ elseif->Parent()->FindFirstParent<sem::IfStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
+ {
+ auto* s = Sem().Get(stmt_c);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ auto* elseif = s->FindFirstParent<sem::IfStatement>();
+ EXPECT_EQ(s->Parent()->Parent(), elseif);
+ EXPECT_EQ(s->Parent()->Parent()->Parent(),
+ elseif->Parent()->FindFirstParent<sem::IfStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
}
TEST_F(ResolverCompoundStatementTest, Switch) {
- // fn F() {
- // switch (expr) {
- // case 1: {
- // stmt_a;
- // }
- // case 2: {
- // stmt_b;
- // }
- // default: {
- // stmt_c;
- // }
- // }
- // }
+ // fn F() {
+ // switch (expr) {
+ // case 1: {
+ // stmt_a;
+ // }
+ // case 2: {
+ // stmt_b;
+ // }
+ // default: {
+ // stmt_c;
+ // }
+ // }
+ // }
- auto* expr = Expr(5);
- auto* stmt_a = Ignore(1);
- auto* stmt_b = Ignore(1);
- auto* stmt_c = Ignore(1);
- auto* swi = Switch(expr, Case(Expr(1), Block(stmt_a)),
- Case(Expr(2), Block(stmt_b)), DefaultCase(Block(stmt_c)));
- WrapInFunction(swi);
+ auto* expr = Expr(5);
+ auto* stmt_a = Ignore(1);
+ auto* stmt_b = Ignore(1);
+ auto* stmt_c = Ignore(1);
+ auto* swi = Switch(expr, Case(Expr(1), Block(stmt_a)), Case(Expr(2), Block(stmt_b)),
+ DefaultCase(Block(stmt_c)));
+ WrapInFunction(swi);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- {
- auto* s = Sem().Get(swi);
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::SwitchStatement>());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- }
- {
- auto* e = Sem().Get(expr);
- ASSERT_NE(e, nullptr);
- auto* s = e->Stmt();
- ASSERT_NE(s, nullptr);
- EXPECT_TRUE(s->Is<sem::SwitchStatement>());
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- }
- {
- auto* s = Sem().Get(stmt_a);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::CaseStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::SwitchStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- }
- {
- auto* s = Sem().Get(stmt_b);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::CaseStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::SwitchStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- }
- {
- auto* s = Sem().Get(stmt_c);
- ASSERT_NE(s, nullptr);
- EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
- EXPECT_EQ(s->Parent(), s->Block());
- EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::CaseStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::SwitchStatement>());
- EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
- s->FindFirstParent<sem::FunctionBlockStatement>());
- }
+ {
+ auto* s = Sem().Get(swi);
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::SwitchStatement>());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ }
+ {
+ auto* e = Sem().Get(expr);
+ ASSERT_NE(e, nullptr);
+ auto* s = e->Stmt();
+ ASSERT_NE(s, nullptr);
+ EXPECT_TRUE(s->Is<sem::SwitchStatement>());
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::FunctionBlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ }
+ {
+ auto* s = Sem().Get(stmt_a);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::CaseStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent(), s->FindFirstParent<sem::SwitchStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
+ {
+ auto* s = Sem().Get(stmt_b);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::CaseStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent(), s->FindFirstParent<sem::SwitchStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
+ {
+ auto* s = Sem().Get(stmt_c);
+ ASSERT_NE(s, nullptr);
+ EXPECT_EQ(s->Parent(), s->FindFirstParent<sem::BlockStatement>());
+ EXPECT_EQ(s->Parent(), s->Block());
+ EXPECT_EQ(s->Parent()->Parent(), s->FindFirstParent<sem::CaseStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent(), s->FindFirstParent<sem::SwitchStatement>());
+ EXPECT_EQ(s->Parent()->Parent()->Parent()->Parent(),
+ s->FindFirstParent<sem::FunctionBlockStatement>());
+ }
}
} // namespace
diff --git a/src/tint/resolver/control_block_validation_test.cc b/src/tint/resolver/control_block_validation_test.cc
index fdb2059..3630d5c 100644
--- a/src/tint/resolver/control_block_validation_test.cc
+++ b/src/tint/resolver/control_block_validation_test.cc
@@ -21,343 +21,330 @@
namespace tint::resolver {
namespace {
-class ResolverControlBlockValidationTest : public TestHelper,
- public testing::Test {};
+class ResolverControlBlockValidationTest : public TestHelper, public testing::Test {};
-TEST_F(ResolverControlBlockValidationTest,
- SwitchSelectorExpressionNoneIntegerType_Fail) {
- // var a : f32 = 3.14;
- // switch (a) {
- // default: {}
- // }
- auto* var = Var("a", ty.f32(), Expr(3.14f));
+TEST_F(ResolverControlBlockValidationTest, SwitchSelectorExpressionNoneIntegerType_Fail) {
+ // var a : f32 = 3.14;
+ // switch (a) {
+ // default: {}
+ // }
+ auto* var = Var("a", ty.f32(), Expr(3.14f));
- auto* block = Block(Decl(var), Switch(Expr(Source{{12, 34}}, "a"), //
- DefaultCase()));
+ auto* block = Block(Decl(var), Switch(Expr(Source{{12, 34}}, "a"), //
+ DefaultCase()));
- WrapInFunction(block);
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: switch statement selector expression must be of a "
- "scalar integer type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: switch statement selector expression must be of a "
+ "scalar integer type");
}
TEST_F(ResolverControlBlockValidationTest, SwitchWithoutDefault_Fail) {
- // var a : i32 = 2;
- // switch (a) {
- // case 1: {}
- // }
- auto* var = Var("a", ty.i32(), Expr(2));
+ // var a : i32 = 2;
+ // switch (a) {
+ // case 1: {}
+ // }
+ auto* var = Var("a", ty.i32(), Expr(2));
- auto* block = Block(Decl(var), //
- Switch(Source{{12, 34}}, "a", //
- Case(Expr(1))));
+ auto* block = Block(Decl(var), //
+ Switch(Source{{12, 34}}, "a", //
+ Case(Expr(1))));
- WrapInFunction(block);
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: switch statement must have a default clause");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: switch statement must have a default clause");
}
TEST_F(ResolverControlBlockValidationTest, SwitchWithTwoDefault_Fail) {
- // var a : i32 = 2;
- // switch (a) {
- // default: {}
- // case 1: {}
- // default: {}
- // }
- auto* var = Var("a", ty.i32(), Expr(2));
+ // var a : i32 = 2;
+ // switch (a) {
+ // default: {}
+ // case 1: {}
+ // default: {}
+ // }
+ auto* var = Var("a", ty.i32(), Expr(2));
- auto* block = Block(Decl(var), //
- Switch("a", //
- DefaultCase(), //
- Case(Expr(1)), //
- DefaultCase(Source{{12, 34}})));
+ auto* block = Block(Decl(var), //
+ Switch("a", //
+ DefaultCase(), //
+ Case(Expr(1)), //
+ DefaultCase(Source{{12, 34}})));
- WrapInFunction(block);
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: switch statement must have exactly one default clause");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: switch statement must have exactly one default clause");
}
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_Loop_continue) {
- // loop {
- // if (false) { break; }
- // var z: i32;
- // continue;
- // z = 1;
- // }
- auto* decl_z = Decl(Var("z", ty.i32()));
- auto* cont = Continue();
- auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
- WrapInFunction(
- Loop(Block(If(false, Block(Break())), decl_z, cont, assign_z)));
+ // loop {
+ // if (false) { break; }
+ // var z: i32;
+ // continue;
+ // z = 1;
+ // }
+ auto* decl_z = Decl(Var("z", ty.i32()));
+ auto* cont = Continue();
+ auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
+ WrapInFunction(Loop(Block(If(false, Block(Break())), decl_z, cont, assign_z)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
- EXPECT_TRUE(Sem().Get(cont)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
+ EXPECT_TRUE(Sem().Get(cont)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
}
-TEST_F(ResolverControlBlockValidationTest,
- UnreachableCode_Loop_continue_InBlocks) {
- // loop {
- // if (false) { break; }
- // var z: i32;
- // {{{continue;}}}
- // z = 1;
- // }
- auto* decl_z = Decl(Var("z", ty.i32()));
- auto* cont = Continue();
- auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
- WrapInFunction(Loop(Block(If(false, Block(Break())), decl_z,
- Block(Block(Block(cont))), assign_z)));
+TEST_F(ResolverControlBlockValidationTest, UnreachableCode_Loop_continue_InBlocks) {
+ // loop {
+ // if (false) { break; }
+ // var z: i32;
+ // {{{continue;}}}
+ // z = 1;
+ // }
+ auto* decl_z = Decl(Var("z", ty.i32()));
+ auto* cont = Continue();
+ auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
+ WrapInFunction(
+ Loop(Block(If(false, Block(Break())), decl_z, Block(Block(Block(cont))), assign_z)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
- EXPECT_TRUE(Sem().Get(cont)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
+ EXPECT_TRUE(Sem().Get(cont)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
}
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_ForLoop_continue) {
- // for (;false;) {
- // var z: i32;
- // continue;
- // z = 1;
- // }
- auto* decl_z = Decl(Var("z", ty.i32()));
- auto* cont = Continue();
- auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
- WrapInFunction(For(nullptr, false, nullptr, //
- Block(decl_z, cont, assign_z)));
+ // for (;false;) {
+ // var z: i32;
+ // continue;
+ // z = 1;
+ // }
+ auto* decl_z = Decl(Var("z", ty.i32()));
+ auto* cont = Continue();
+ auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
+ WrapInFunction(For(nullptr, false, nullptr, //
+ Block(decl_z, cont, assign_z)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
- EXPECT_TRUE(Sem().Get(cont)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
+ EXPECT_TRUE(Sem().Get(cont)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
}
-TEST_F(ResolverControlBlockValidationTest,
- UnreachableCode_ForLoop_continue_InBlocks) {
- // for (;false;) {
- // var z: i32;
- // {{{continue;}}}
- // z = 1;
- // }
- auto* decl_z = Decl(Var("z", ty.i32()));
- auto* cont = Continue();
- auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
- WrapInFunction(For(nullptr, false, nullptr,
- Block(decl_z, Block(Block(Block(cont))), assign_z)));
+TEST_F(ResolverControlBlockValidationTest, UnreachableCode_ForLoop_continue_InBlocks) {
+ // for (;false;) {
+ // var z: i32;
+ // {{{continue;}}}
+ // z = 1;
+ // }
+ auto* decl_z = Decl(Var("z", ty.i32()));
+ auto* cont = Continue();
+ auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
+ WrapInFunction(
+ For(nullptr, false, nullptr, Block(decl_z, Block(Block(Block(cont))), assign_z)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
- EXPECT_TRUE(Sem().Get(cont)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
+ EXPECT_TRUE(Sem().Get(cont)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
}
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break) {
- // switch (1) {
- // case 1: {
- // var z: i32;
- // break;
- // z = 1;
- // default: {}
- // }
- auto* decl_z = Decl(Var("z", ty.i32()));
- auto* brk = Break();
- auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
- WrapInFunction( //
- Block(Switch(1, //
- Case(Expr(1), Block(decl_z, brk, assign_z)), //
- DefaultCase())));
+ // switch (1) {
+ // case 1: {
+ // var z: i32;
+ // break;
+ // z = 1;
+ // default: {}
+ // }
+ auto* decl_z = Decl(Var("z", ty.i32()));
+ auto* brk = Break();
+ auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
+ WrapInFunction( //
+ Block(Switch(1, //
+ Case(Expr(1), Block(decl_z, brk, assign_z)), //
+ DefaultCase())));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
- EXPECT_TRUE(Sem().Get(brk)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
+ EXPECT_TRUE(Sem().Get(brk)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
}
TEST_F(ResolverControlBlockValidationTest, UnreachableCode_break_InBlocks) {
- // loop {
- // switch (1) {
- // case 1: { {{{break;}}} var a : u32 = 2;}
- // default: {}
- // }
- // break;
- // }
- auto* decl_z = Decl(Var("z", ty.i32()));
- auto* brk = Break();
- auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
- WrapInFunction(Loop(Block(
- Switch(1, //
- Case(Expr(1), Block(decl_z, Block(Block(Block(brk))), assign_z)),
- DefaultCase()), //
- Break())));
+ // loop {
+ // switch (1) {
+ // case 1: { {{{break;}}} var a : u32 = 2;}
+ // default: {}
+ // }
+ // break;
+ // }
+ auto* decl_z = Decl(Var("z", ty.i32()));
+ auto* brk = Break();
+ auto* assign_z = Assign(Source{{12, 34}}, "z", 1);
+ WrapInFunction(
+ Loop(Block(Switch(1, //
+ Case(Expr(1), Block(decl_z, Block(Block(Block(brk))), assign_z)),
+ DefaultCase()), //
+ Break())));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
- EXPECT_TRUE(Sem().Get(brk)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_z)->IsReachable());
+ EXPECT_TRUE(Sem().Get(brk)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_z)->IsReachable());
}
-TEST_F(ResolverControlBlockValidationTest,
- SwitchConditionTypeMustMatchSelectorType2_Fail) {
- // var a : u32 = 2;
- // switch (a) {
- // case 1: {}
- // default: {}
- // }
- auto* var = Var("a", ty.i32(), Expr(2));
+TEST_F(ResolverControlBlockValidationTest, SwitchConditionTypeMustMatchSelectorType2_Fail) {
+ // var a : u32 = 2;
+ // switch (a) {
+ // case 1: {}
+ // default: {}
+ // }
+ auto* var = Var("a", ty.i32(), Expr(2));
- auto* block = Block(Decl(var), Switch("a", //
- Case(Source{{12, 34}}, {Expr(1u)}), //
- DefaultCase()));
- WrapInFunction(block);
+ auto* block = Block(Decl(var), Switch("a", //
+ Case(Source{{12, 34}}, {Expr(1u)}), //
+ DefaultCase()));
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: the case selector values must have the same type as "
- "the selector expression.");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: the case selector values must have the same type as "
+ "the selector expression.");
}
-TEST_F(ResolverControlBlockValidationTest,
- SwitchConditionTypeMustMatchSelectorType_Fail) {
- // var a : u32 = 2;
- // switch (a) {
- // case -1: {}
- // default: {}
- // }
- auto* var = Var("a", ty.u32(), Expr(2u));
+TEST_F(ResolverControlBlockValidationTest, SwitchConditionTypeMustMatchSelectorType_Fail) {
+ // var a : u32 = 2;
+ // switch (a) {
+ // case -1: {}
+ // default: {}
+ // }
+ auto* var = Var("a", ty.u32(), Expr(2u));
- auto* block = Block(Decl(var), //
- Switch("a", //
- Case(Source{{12, 34}}, {Expr(-1)}), //
- DefaultCase()));
- WrapInFunction(block);
+ auto* block = Block(Decl(var), //
+ Switch("a", //
+ Case(Source{{12, 34}}, {Expr(-1)}), //
+ DefaultCase()));
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: the case selector values must have the same type as "
- "the selector expression.");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: the case selector values must have the same type as "
+ "the selector expression.");
}
-TEST_F(ResolverControlBlockValidationTest,
- NonUniqueCaseSelectorValueUint_Fail) {
- // var a : u32 = 3;
- // switch (a) {
- // case 0u: {}
- // case 2u, 3u, 2u: {}
- // default: {}
- // }
- auto* var = Var("a", ty.u32(), Expr(3u));
+TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorValueUint_Fail) {
+ // var a : u32 = 3;
+ // switch (a) {
+ // case 0u: {}
+ // case 2u, 3u, 2u: {}
+ // default: {}
+ // }
+ auto* var = Var("a", ty.u32(), Expr(3u));
- auto* block = Block(Decl(var), //
- Switch("a", //
- Case(Expr(0u)),
- Case({
- Expr(Source{{12, 34}}, 2u),
- Expr(3u),
- Expr(Source{{56, 78}}, 2u),
- }),
- DefaultCase()));
- WrapInFunction(block);
+ auto* block = Block(Decl(var), //
+ Switch("a", //
+ Case(Expr(0u)),
+ Case({
+ Expr(Source{{12, 34}}, 2u),
+ Expr(3u),
+ Expr(Source{{56, 78}}, 2u),
+ }),
+ DefaultCase()));
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: duplicate switch case '2'\n"
- "12:34 note: previous case declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: duplicate switch case '2'\n"
+ "12:34 note: previous case declared here");
}
-TEST_F(ResolverControlBlockValidationTest,
- NonUniqueCaseSelectorValueSint_Fail) {
- // var a : i32 = 2;
- // switch (a) {
- // case -10: {}
- // case 0,1,2,-10: {}
- // default: {}
- // }
- auto* var = Var("a", ty.i32(), Expr(2));
+TEST_F(ResolverControlBlockValidationTest, NonUniqueCaseSelectorValueSint_Fail) {
+ // var a : i32 = 2;
+ // switch (a) {
+ // case -10: {}
+ // case 0,1,2,-10: {}
+ // default: {}
+ // }
+ auto* var = Var("a", ty.i32(), Expr(2));
- auto* block = Block(Decl(var), //
- Switch("a", //
- Case(Expr(Source{{12, 34}}, -10)),
- Case({
- Expr(0),
- Expr(1),
- Expr(2),
- Expr(Source{{56, 78}}, -10),
- }),
- DefaultCase()));
- WrapInFunction(block);
+ auto* block = Block(Decl(var), //
+ Switch("a", //
+ Case(Expr(Source{{12, 34}}, -10)),
+ Case({
+ Expr(0),
+ Expr(1),
+ Expr(2),
+ Expr(Source{{56, 78}}, -10),
+ }),
+ DefaultCase()));
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: duplicate switch case '-10'\n"
- "12:34 note: previous case declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: duplicate switch case '-10'\n"
+ "12:34 note: previous case declared here");
}
-TEST_F(ResolverControlBlockValidationTest,
- LastClauseLastStatementIsFallthrough_Fail) {
- // var a : i32 = 2;
- // switch (a) {
- // default: { fallthrough; }
- // }
- auto* var = Var("a", ty.i32(), Expr(2));
- auto* fallthrough = create<ast::FallthroughStatement>(Source{{12, 34}});
- auto* block = Block(Decl(var), //
- Switch("a", //
- DefaultCase(Block(fallthrough))));
- WrapInFunction(block);
+TEST_F(ResolverControlBlockValidationTest, LastClauseLastStatementIsFallthrough_Fail) {
+ // var a : i32 = 2;
+ // switch (a) {
+ // default: { fallthrough; }
+ // }
+ auto* var = Var("a", ty.i32(), Expr(2));
+ auto* fallthrough = create<ast::FallthroughStatement>(Source{{12, 34}});
+ auto* block = Block(Decl(var), //
+ Switch("a", //
+ DefaultCase(Block(fallthrough))));
+ WrapInFunction(block);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: a fallthrough statement must not be used in the last "
- "switch case");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: a fallthrough statement must not be used in the last "
+ "switch case");
}
TEST_F(ResolverControlBlockValidationTest, SwitchCase_Pass) {
- // var a : i32 = 2;
- // switch (a) {
- // default: {}
- // case 5: {}
- // }
- auto* var = Var("a", ty.i32(), Expr(2));
+ // var a : i32 = 2;
+ // switch (a) {
+ // default: {}
+ // case 5: {}
+ // }
+ auto* var = Var("a", ty.i32(), Expr(2));
- auto* block = Block(Decl(var), //
- Switch("a", //
- DefaultCase(Source{{12, 34}}), //
- Case(Expr(5))));
- WrapInFunction(block);
+ auto* block = Block(Decl(var), //
+ Switch("a", //
+ DefaultCase(Source{{12, 34}}), //
+ Case(Expr(5))));
+ WrapInFunction(block);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverControlBlockValidationTest, SwitchCaseAlias_Pass) {
- // type MyInt = u32;
- // var v: MyInt;
- // switch(v){
- // default: {}
- // }
+ // type MyInt = u32;
+ // var v: MyInt;
+ // switch(v){
+ // default: {}
+ // }
- auto* my_int = Alias("MyInt", ty.u32());
- auto* var = Var("a", ty.Of(my_int), Expr(2u));
- auto* block = Block(Decl(var), //
- Switch("a", DefaultCase(Source{{12, 34}})));
+ auto* my_int = Alias("MyInt", ty.u32());
+ auto* var = Var("a", ty.Of(my_int), Expr(2u));
+ auto* block = Block(Decl(var), //
+ Switch("a", DefaultCase(Source{{12, 34}})));
- WrapInFunction(block);
+ WrapInFunction(block);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/dependency_graph.cc b/src/tint/resolver/dependency_graph.cc
index 3d69472..0a9bdc0 100644
--- a/src/tint/resolver/dependency_graph.cc
+++ b/src/tint/resolver/dependency_graph.cc
@@ -40,48 +40,46 @@
/// Dependency describes how one global depends on another global
struct DependencyInfo {
- /// The source of the symbol that forms the dependency
- Source source;
- /// A string describing how the dependency is referenced. e.g. 'calls'
- const char* action = nullptr;
+ /// The source of the symbol that forms the dependency
+ Source source;
+ /// A string describing how the dependency is referenced. e.g. 'calls'
+ const char* action = nullptr;
};
/// DependencyEdge describes the two Globals used to define a dependency
/// relationship.
struct DependencyEdge {
- /// The Global that depends on #to
- const Global* from;
- /// The Global that is depended on by #from
- const Global* to;
+ /// The Global that depends on #to
+ const Global* from;
+ /// The Global that is depended on by #from
+ const Global* to;
};
/// DependencyEdgeCmp implements the contracts of std::equal_to<DependencyEdge>
/// and std::hash<DependencyEdge>.
struct DependencyEdgeCmp {
- /// Equality operator
- bool operator()(const DependencyEdge& lhs, const DependencyEdge& rhs) const {
- return lhs.from == rhs.from && lhs.to == rhs.to;
- }
- /// Hashing operator
- inline std::size_t operator()(const DependencyEdge& d) const {
- return utils::Hash(d.from, d.to);
- }
+ /// Equality operator
+ bool operator()(const DependencyEdge& lhs, const DependencyEdge& rhs) const {
+ return lhs.from == rhs.from && lhs.to == rhs.to;
+ }
+ /// Hashing operator
+ inline std::size_t operator()(const DependencyEdge& d) const {
+ return utils::Hash(d.from, d.to);
+ }
};
/// A map of DependencyEdge to DependencyInfo
-using DependencyEdges = std::unordered_map<DependencyEdge,
- DependencyInfo,
- DependencyEdgeCmp,
- DependencyEdgeCmp>;
+using DependencyEdges =
+ std::unordered_map<DependencyEdge, DependencyInfo, DependencyEdgeCmp, DependencyEdgeCmp>;
/// Global describes a module-scope variable, type or function.
struct Global {
- explicit Global(const ast::Node* n) : node(n) {}
+ explicit Global(const ast::Node* n) : node(n) {}
- /// The declaration ast::Node
- const ast::Node* node;
- /// A list of dependencies that this global depends on
- std::vector<Global*> deps;
+ /// The declaration ast::Node
+ const ast::Node* node;
+ /// A list of dependencies that this global depends on
+ std::vector<Global*> deps;
};
/// A map of global name to Global
@@ -89,643 +87,614 @@
/// Raises an ICE that a global ast::Node type was not handled by this system.
void UnhandledNode(diag::List& diagnostics, const ast::Node* node) {
- TINT_ICE(Resolver, diagnostics)
- << "unhandled node type: " << node->TypeInfo().name;
+ TINT_ICE(Resolver, diagnostics) << "unhandled node type: " << node->TypeInfo().name;
}
/// Raises an error diagnostic with the given message and source.
-void AddError(diag::List& diagnostics,
- const std::string& msg,
- const Source& source) {
- diagnostics.add_error(diag::System::Resolver, msg, source);
+void AddError(diag::List& diagnostics, const std::string& msg, const Source& source) {
+ diagnostics.add_error(diag::System::Resolver, msg, source);
}
/// Raises a note diagnostic with the given message and source.
-void AddNote(diag::List& diagnostics,
- const std::string& msg,
- const Source& source) {
- diagnostics.add_note(diag::System::Resolver, msg, source);
+void AddNote(diag::List& diagnostics, const std::string& msg, const Source& source) {
+ diagnostics.add_note(diag::System::Resolver, msg, source);
}
/// DependencyScanner is used to traverse a module to build the list of
/// global-to-global dependencies.
class DependencyScanner {
- public:
- /// Constructor
- /// @param syms the program symbol table
- /// @param globals_by_name map of global symbol to Global pointer
- /// @param diagnostics diagnostic messages, appended with any errors found
- /// @param graph the dependency graph to populate with resolved symbols
- /// @param edges the map of globals-to-global dependency edges, which will
- /// be populated by calls to Scan()
- DependencyScanner(const SymbolTable& syms,
- const GlobalMap& globals_by_name,
- diag::List& diagnostics,
- DependencyGraph& graph,
- DependencyEdges& edges)
- : symbols_(syms),
- globals_(globals_by_name),
- diagnostics_(diagnostics),
- graph_(graph),
- dependency_edges_(edges) {
- // Register all the globals at global-scope
- for (auto it : globals_by_name) {
- scope_stack_.Set(it.first, it.second->node);
+ public:
+ /// Constructor
+ /// @param syms the program symbol table
+ /// @param globals_by_name map of global symbol to Global pointer
+ /// @param diagnostics diagnostic messages, appended with any errors found
+ /// @param graph the dependency graph to populate with resolved symbols
+ /// @param edges the map of globals-to-global dependency edges, which will
+ /// be populated by calls to Scan()
+ DependencyScanner(const SymbolTable& syms,
+ const GlobalMap& globals_by_name,
+ diag::List& diagnostics,
+ DependencyGraph& graph,
+ DependencyEdges& edges)
+ : symbols_(syms),
+ globals_(globals_by_name),
+ diagnostics_(diagnostics),
+ graph_(graph),
+ dependency_edges_(edges) {
+ // Register all the globals at global-scope
+ for (auto it : globals_by_name) {
+ scope_stack_.Set(it.first, it.second->node);
+ }
}
- }
- /// Walks the global declarations, resolving symbols, and determining the
- /// dependencies of each global.
- void Scan(Global* global) {
- TINT_SCOPED_ASSIGNMENT(current_global_, global);
- Switch(
- global->node,
- [&](const ast::Struct* str) {
- Declare(str->name, str);
- for (auto* member : str->members) {
- TraverseType(member->type);
- }
- },
- [&](const ast::Alias* alias) {
- Declare(alias->name, alias);
- TraverseType(alias->type);
- },
- [&](const ast::Function* func) {
- Declare(func->symbol, func);
- TraverseAttributes(func->attributes);
- TraverseFunction(func);
- },
- [&](const ast::Variable* var) {
- Declare(var->symbol, var);
- TraverseType(var->type);
- if (var->constructor) {
- TraverseExpression(var->constructor);
- }
- },
- [&](const ast::Enable*) {
- // Enable directives do not effect the dependency graph.
- },
- [&](Default) { UnhandledNode(diagnostics_, global->node); });
- }
-
- private:
- /// Traverses the function, performing symbol resolution and determining
- /// global dependencies.
- void TraverseFunction(const ast::Function* func) {
- // Perform symbol resolution on all the parameter types before registering
- // the parameters themselves. This allows the case of declaring a parameter
- // with the same identifier as its type.
- for (auto* param : func->params) {
- TraverseType(param->type);
+ /// Walks the global declarations, resolving symbols, and determining the
+ /// dependencies of each global.
+ void Scan(Global* global) {
+ TINT_SCOPED_ASSIGNMENT(current_global_, global);
+ Switch(
+ global->node,
+ [&](const ast::Struct* str) {
+ Declare(str->name, str);
+ for (auto* member : str->members) {
+ TraverseType(member->type);
+ }
+ },
+ [&](const ast::Alias* alias) {
+ Declare(alias->name, alias);
+ TraverseType(alias->type);
+ },
+ [&](const ast::Function* func) {
+ Declare(func->symbol, func);
+ TraverseAttributes(func->attributes);
+ TraverseFunction(func);
+ },
+ [&](const ast::Variable* var) {
+ Declare(var->symbol, var);
+ TraverseType(var->type);
+ if (var->constructor) {
+ TraverseExpression(var->constructor);
+ }
+ },
+ [&](const ast::Enable*) {
+ // Enable directives do not effect the dependency graph.
+ },
+ [&](Default) { UnhandledNode(diagnostics_, global->node); });
}
- // Resolve the return type
- TraverseType(func->return_type);
- // Push the scope stack for the parameters and function body.
- scope_stack_.Push();
- TINT_DEFER(scope_stack_.Pop());
+ private:
+ /// Traverses the function, performing symbol resolution and determining
+ /// global dependencies.
+ void TraverseFunction(const ast::Function* func) {
+ // Perform symbol resolution on all the parameter types before registering
+ // the parameters themselves. This allows the case of declaring a parameter
+ // with the same identifier as its type.
+ for (auto* param : func->params) {
+ TraverseType(param->type);
+ }
+ // Resolve the return type
+ TraverseType(func->return_type);
- for (auto* param : func->params) {
- if (auto* shadows = scope_stack_.Get(param->symbol)) {
- graph_.shadows.emplace(param, shadows);
- }
- Declare(param->symbol, param);
- }
- if (func->body) {
- TraverseStatements(func->body->statements);
- }
- }
+ // Push the scope stack for the parameters and function body.
+ scope_stack_.Push();
+ TINT_DEFER(scope_stack_.Pop());
- /// Traverses the statements, performing symbol resolution and determining
- /// global dependencies.
- void TraverseStatements(const ast::StatementList& stmts) {
- for (auto* s : stmts) {
- TraverseStatement(s);
- }
- }
-
- /// Traverses the statement, performing symbol resolution and determining
- /// global dependencies.
- void TraverseStatement(const ast::Statement* stmt) {
- if (!stmt) {
- return;
- }
- Switch(
- stmt, //
- [&](const ast::AssignmentStatement* a) {
- TraverseExpression(a->lhs);
- TraverseExpression(a->rhs);
- },
- [&](const ast::BlockStatement* b) {
- scope_stack_.Push();
- TINT_DEFER(scope_stack_.Pop());
- TraverseStatements(b->statements);
- },
- [&](const ast::CallStatement* r) { //
- TraverseExpression(r->expr);
- },
- [&](const ast::CompoundAssignmentStatement* a) {
- TraverseExpression(a->lhs);
- TraverseExpression(a->rhs);
- },
- [&](const ast::ForLoopStatement* l) {
- scope_stack_.Push();
- TINT_DEFER(scope_stack_.Pop());
- TraverseStatement(l->initializer);
- TraverseExpression(l->condition);
- TraverseStatement(l->continuing);
- TraverseStatement(l->body);
- },
- [&](const ast::IncrementDecrementStatement* i) {
- TraverseExpression(i->lhs);
- },
- [&](const ast::LoopStatement* l) {
- scope_stack_.Push();
- TINT_DEFER(scope_stack_.Pop());
- TraverseStatements(l->body->statements);
- TraverseStatement(l->continuing);
- },
- [&](const ast::IfStatement* i) {
- TraverseExpression(i->condition);
- TraverseStatement(i->body);
- if (i->else_statement) {
- TraverseStatement(i->else_statement);
- }
- },
- [&](const ast::ReturnStatement* r) { //
- TraverseExpression(r->value);
- },
- [&](const ast::SwitchStatement* s) {
- TraverseExpression(s->condition);
- for (auto* c : s->body) {
- for (auto* sel : c->selectors) {
- TraverseExpression(sel);
+ for (auto* param : func->params) {
+ if (auto* shadows = scope_stack_.Get(param->symbol)) {
+ graph_.shadows.emplace(param, shadows);
}
- TraverseStatement(c->body);
- }
- },
- [&](const ast::VariableDeclStatement* v) {
- if (auto* shadows = scope_stack_.Get(v->variable->symbol)) {
- graph_.shadows.emplace(v->variable, shadows);
- }
- TraverseType(v->variable->type);
- TraverseExpression(v->variable->constructor);
- Declare(v->variable->symbol, v->variable);
- },
- [&](Default) {
- if (!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
- ast::DiscardStatement,
- ast::FallthroughStatement>()) {
- UnhandledNode(diagnostics_, stmt);
- }
- });
- }
-
- /// Adds the symbol definition to the current scope, raising an error if two
- /// symbols collide within the same scope.
- void Declare(Symbol symbol, const ast::Node* node) {
- auto* old = scope_stack_.Set(symbol, node);
- if (old != nullptr && node != old) {
- auto name = symbols_.NameFor(symbol);
- AddError(diagnostics_, "redeclaration of '" + name + "'", node->source);
- AddNote(diagnostics_, "'" + name + "' previously declared here",
- old->source);
+ Declare(param->symbol, param);
+ }
+ if (func->body) {
+ TraverseStatements(func->body->statements);
+ }
}
- }
- /// Traverses the expression, performing symbol resolution and determining
- /// global dependencies.
- void TraverseExpression(const ast::Expression* root) {
- if (!root) {
- return;
+ /// Traverses the statements, performing symbol resolution and determining
+ /// global dependencies.
+ void TraverseStatements(const ast::StatementList& stmts) {
+ for (auto* s : stmts) {
+ TraverseStatement(s);
+ }
}
- ast::TraverseExpressions(
- root, diagnostics_, [&](const ast::Expression* expr) {
- Switch(
- expr,
- [&](const ast::IdentifierExpression* ident) {
- AddDependency(ident, ident->symbol, "identifier", "references");
- },
- [&](const ast::CallExpression* call) {
- if (call->target.name) {
- AddDependency(call->target.name, call->target.name->symbol,
- "function", "calls");
+
+ /// Traverses the statement, performing symbol resolution and determining
+ /// global dependencies.
+ void TraverseStatement(const ast::Statement* stmt) {
+ if (!stmt) {
+ return;
+ }
+ Switch(
+ stmt, //
+ [&](const ast::AssignmentStatement* a) {
+ TraverseExpression(a->lhs);
+ TraverseExpression(a->rhs);
+ },
+ [&](const ast::BlockStatement* b) {
+ scope_stack_.Push();
+ TINT_DEFER(scope_stack_.Pop());
+ TraverseStatements(b->statements);
+ },
+ [&](const ast::CallStatement* r) { //
+ TraverseExpression(r->expr);
+ },
+ [&](const ast::CompoundAssignmentStatement* a) {
+ TraverseExpression(a->lhs);
+ TraverseExpression(a->rhs);
+ },
+ [&](const ast::ForLoopStatement* l) {
+ scope_stack_.Push();
+ TINT_DEFER(scope_stack_.Pop());
+ TraverseStatement(l->initializer);
+ TraverseExpression(l->condition);
+ TraverseStatement(l->continuing);
+ TraverseStatement(l->body);
+ },
+ [&](const ast::IncrementDecrementStatement* i) { TraverseExpression(i->lhs); },
+ [&](const ast::LoopStatement* l) {
+ scope_stack_.Push();
+ TINT_DEFER(scope_stack_.Pop());
+ TraverseStatements(l->body->statements);
+ TraverseStatement(l->continuing);
+ },
+ [&](const ast::IfStatement* i) {
+ TraverseExpression(i->condition);
+ TraverseStatement(i->body);
+ if (i->else_statement) {
+ TraverseStatement(i->else_statement);
}
- if (call->target.type) {
- TraverseType(call->target.type);
+ },
+ [&](const ast::ReturnStatement* r) { //
+ TraverseExpression(r->value);
+ },
+ [&](const ast::SwitchStatement* s) {
+ TraverseExpression(s->condition);
+ for (auto* c : s->body) {
+ for (auto* sel : c->selectors) {
+ TraverseExpression(sel);
+ }
+ TraverseStatement(c->body);
}
- },
- [&](const ast::BitcastExpression* cast) {
- TraverseType(cast->type);
- });
- return ast::TraverseAction::Descend;
+ },
+ [&](const ast::VariableDeclStatement* v) {
+ if (auto* shadows = scope_stack_.Get(v->variable->symbol)) {
+ graph_.shadows.emplace(v->variable, shadows);
+ }
+ TraverseType(v->variable->type);
+ TraverseExpression(v->variable->constructor);
+ Declare(v->variable->symbol, v->variable);
+ },
+ [&](Default) {
+ if (!stmt->IsAnyOf<ast::BreakStatement, ast::ContinueStatement,
+ ast::DiscardStatement, ast::FallthroughStatement>()) {
+ UnhandledNode(diagnostics_, stmt);
+ }
+ });
+ }
+
+ /// Adds the symbol definition to the current scope, raising an error if two
+ /// symbols collide within the same scope.
+ void Declare(Symbol symbol, const ast::Node* node) {
+ auto* old = scope_stack_.Set(symbol, node);
+ if (old != nullptr && node != old) {
+ auto name = symbols_.NameFor(symbol);
+ AddError(diagnostics_, "redeclaration of '" + name + "'", node->source);
+ AddNote(diagnostics_, "'" + name + "' previously declared here", old->source);
+ }
+ }
+
+ /// Traverses the expression, performing symbol resolution and determining
+ /// global dependencies.
+ void TraverseExpression(const ast::Expression* root) {
+ if (!root) {
+ return;
+ }
+ ast::TraverseExpressions(root, diagnostics_, [&](const ast::Expression* expr) {
+ Switch(
+ expr,
+ [&](const ast::IdentifierExpression* ident) {
+ AddDependency(ident, ident->symbol, "identifier", "references");
+ },
+ [&](const ast::CallExpression* call) {
+ if (call->target.name) {
+ AddDependency(call->target.name, call->target.name->symbol, "function",
+ "calls");
+ }
+ if (call->target.type) {
+ TraverseType(call->target.type);
+ }
+ },
+ [&](const ast::BitcastExpression* cast) { TraverseType(cast->type); });
+ return ast::TraverseAction::Descend;
});
- }
-
- /// Traverses the type node, performing symbol resolution and determining
- /// global dependencies.
- void TraverseType(const ast::Type* ty) {
- if (!ty) {
- return;
- }
- Switch(
- ty, //
- [&](const ast::Array* arr) {
- TraverseType(arr->type); //
- TraverseExpression(arr->count);
- },
- [&](const ast::Atomic* atomic) { //
- TraverseType(atomic->type);
- },
- [&](const ast::Matrix* mat) { //
- TraverseType(mat->type);
- },
- [&](const ast::Pointer* ptr) { //
- TraverseType(ptr->type);
- },
- [&](const ast::TypeName* tn) { //
- AddDependency(tn, tn->name, "type", "references");
- },
- [&](const ast::Vector* vec) { //
- TraverseType(vec->type);
- },
- [&](const ast::SampledTexture* tex) { //
- TraverseType(tex->type);
- },
- [&](const ast::MultisampledTexture* tex) { //
- TraverseType(tex->type);
- },
- [&](Default) {
- if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
- ast::DepthTexture, ast::DepthMultisampledTexture,
- ast::StorageTexture, ast::ExternalTexture,
- ast::Sampler>()) {
- UnhandledNode(diagnostics_, ty);
- }
- });
- }
-
- /// Traverses the attribute list, performing symbol resolution and
- /// determining global dependencies.
- void TraverseAttributes(const ast::AttributeList& attrs) {
- for (auto* attr : attrs) {
- TraverseAttribute(attr);
- }
- }
-
- /// Traverses the attribute, performing symbol resolution and determining
- /// global dependencies.
- void TraverseAttribute(const ast::Attribute* attr) {
- if (auto* wg = attr->As<ast::WorkgroupAttribute>()) {
- TraverseExpression(wg->x);
- TraverseExpression(wg->y);
- TraverseExpression(wg->z);
- return;
- }
- if (attr->IsAnyOf<
- ast::BindingAttribute, ast::BuiltinAttribute, ast::GroupAttribute,
- ast::IdAttribute, ast::InternalAttribute, ast::InterpolateAttribute,
- ast::InvariantAttribute, ast::LocationAttribute,
- ast::StageAttribute, ast::StrideAttribute,
- ast::StructMemberAlignAttribute, ast::StructMemberOffsetAttribute,
- ast::StructMemberSizeAttribute>()) {
- return;
}
- UnhandledNode(diagnostics_, attr);
- }
-
- /// Adds the dependency from `from` to `to`, erroring if `to` cannot be
- /// resolved.
- void AddDependency(const ast::Node* from,
- Symbol to,
- const char* use,
- const char* action) {
- auto* resolved = scope_stack_.Get(to);
- if (!resolved) {
- if (!IsBuiltin(to)) {
- UnknownSymbol(to, from->source, use);
- return;
- }
+ /// Traverses the type node, performing symbol resolution and determining
+ /// global dependencies.
+ void TraverseType(const ast::Type* ty) {
+ if (!ty) {
+ return;
+ }
+ Switch(
+ ty, //
+ [&](const ast::Array* arr) {
+ TraverseType(arr->type); //
+ TraverseExpression(arr->count);
+ },
+ [&](const ast::Atomic* atomic) { //
+ TraverseType(atomic->type);
+ },
+ [&](const ast::Matrix* mat) { //
+ TraverseType(mat->type);
+ },
+ [&](const ast::Pointer* ptr) { //
+ TraverseType(ptr->type);
+ },
+ [&](const ast::TypeName* tn) { //
+ AddDependency(tn, tn->name, "type", "references");
+ },
+ [&](const ast::Vector* vec) { //
+ TraverseType(vec->type);
+ },
+ [&](const ast::SampledTexture* tex) { //
+ TraverseType(tex->type);
+ },
+ [&](const ast::MultisampledTexture* tex) { //
+ TraverseType(tex->type);
+ },
+ [&](Default) {
+ if (!ty->IsAnyOf<ast::Void, ast::Bool, ast::I32, ast::U32, ast::F32,
+ ast::DepthTexture, ast::DepthMultisampledTexture,
+ ast::StorageTexture, ast::ExternalTexture, ast::Sampler>()) {
+ UnhandledNode(diagnostics_, ty);
+ }
+ });
}
- if (auto* global = utils::Lookup(globals_, to);
- global && global->node == resolved) {
- if (dependency_edges_
- .emplace(DependencyEdge{current_global_, global},
- DependencyInfo{from->source, action})
- .second) {
- current_global_->deps.emplace_back(global);
- }
+ /// Traverses the attribute list, performing symbol resolution and
+ /// determining global dependencies.
+ void TraverseAttributes(const ast::AttributeList& attrs) {
+ for (auto* attr : attrs) {
+ TraverseAttribute(attr);
+ }
}
- graph_.resolved_symbols.emplace(from, resolved);
- }
+ /// Traverses the attribute, performing symbol resolution and determining
+ /// global dependencies.
+ void TraverseAttribute(const ast::Attribute* attr) {
+ if (auto* wg = attr->As<ast::WorkgroupAttribute>()) {
+ TraverseExpression(wg->x);
+ TraverseExpression(wg->y);
+ TraverseExpression(wg->z);
+ return;
+ }
+ if (attr->IsAnyOf<ast::BindingAttribute, ast::BuiltinAttribute, ast::GroupAttribute,
+ ast::IdAttribute, ast::InternalAttribute, ast::InterpolateAttribute,
+ ast::InvariantAttribute, ast::LocationAttribute, ast::StageAttribute,
+ ast::StrideAttribute, ast::StructMemberAlignAttribute,
+ ast::StructMemberOffsetAttribute, ast::StructMemberSizeAttribute>()) {
+ return;
+ }
- /// @returns true if `name` is the name of a builtin function
- bool IsBuiltin(Symbol name) const {
- return sem::ParseBuiltinType(symbols_.NameFor(name)) !=
- sem::BuiltinType::kNone;
- }
+ UnhandledNode(diagnostics_, attr);
+ }
- /// Appends an error to the diagnostics that the given symbol cannot be
- /// resolved.
- void UnknownSymbol(Symbol name, Source source, const char* use) {
- AddError(
- diagnostics_,
- "unknown " + std::string(use) + ": '" + symbols_.NameFor(name) + "'",
- source);
- }
+ /// Adds the dependency from `from` to `to`, erroring if `to` cannot be
+ /// resolved.
+ void AddDependency(const ast::Node* from, Symbol to, const char* use, const char* action) {
+ auto* resolved = scope_stack_.Get(to);
+ if (!resolved) {
+ if (!IsBuiltin(to)) {
+ UnknownSymbol(to, from->source, use);
+ return;
+ }
+ }
- using VariableMap = std::unordered_map<Symbol, const ast::Variable*>;
- const SymbolTable& symbols_;
- const GlobalMap& globals_;
- diag::List& diagnostics_;
- DependencyGraph& graph_;
- DependencyEdges& dependency_edges_;
+ if (auto* global = utils::Lookup(globals_, to); global && global->node == resolved) {
+ if (dependency_edges_
+ .emplace(DependencyEdge{current_global_, global},
+ DependencyInfo{from->source, action})
+ .second) {
+ current_global_->deps.emplace_back(global);
+ }
+ }
- ScopeStack<const ast::Node*> scope_stack_;
- Global* current_global_ = nullptr;
+ graph_.resolved_symbols.emplace(from, resolved);
+ }
+
+ /// @returns true if `name` is the name of a builtin function
+ bool IsBuiltin(Symbol name) const {
+ return sem::ParseBuiltinType(symbols_.NameFor(name)) != sem::BuiltinType::kNone;
+ }
+
+ /// Appends an error to the diagnostics that the given symbol cannot be
+ /// resolved.
+ void UnknownSymbol(Symbol name, Source source, const char* use) {
+ AddError(diagnostics_, "unknown " + std::string(use) + ": '" + symbols_.NameFor(name) + "'",
+ source);
+ }
+
+ using VariableMap = std::unordered_map<Symbol, const ast::Variable*>;
+ const SymbolTable& symbols_;
+ const GlobalMap& globals_;
+ diag::List& diagnostics_;
+ DependencyGraph& graph_;
+ DependencyEdges& dependency_edges_;
+
+ ScopeStack<const ast::Node*> scope_stack_;
+ Global* current_global_ = nullptr;
};
/// The global dependency analysis system
struct DependencyAnalysis {
- public:
- /// Constructor
- DependencyAnalysis(const SymbolTable& symbols,
- diag::List& diagnostics,
- DependencyGraph& graph)
- : symbols_(symbols), diagnostics_(diagnostics), graph_(graph) {}
+ public:
+ /// Constructor
+ DependencyAnalysis(const SymbolTable& symbols, diag::List& diagnostics, DependencyGraph& graph)
+ : symbols_(symbols), diagnostics_(diagnostics), graph_(graph) {}
- /// Performs global dependency analysis on the module, emitting any errors to
- /// #diagnostics.
- /// @returns true if analysis found no errors, otherwise false.
- bool Run(const ast::Module& module) {
- // Collect all the named globals from the AST module
- GatherGlobals(module);
+ /// Performs global dependency analysis on the module, emitting any errors to
+ /// #diagnostics.
+ /// @returns true if analysis found no errors, otherwise false.
+ bool Run(const ast::Module& module) {
+ // Collect all the named globals from the AST module
+ GatherGlobals(module);
- // Traverse the named globals to build the dependency graph
- DetermineDependencies();
+ // Traverse the named globals to build the dependency graph
+ DetermineDependencies();
- // Sort the globals into dependency order
- SortGlobals();
+ // Sort the globals into dependency order
+ SortGlobals();
- // Dump the dependency graph if TINT_DUMP_DEPENDENCY_GRAPH is non-zero
- DumpDependencyGraph();
+ // Dump the dependency graph if TINT_DUMP_DEPENDENCY_GRAPH is non-zero
+ DumpDependencyGraph();
- graph_.ordered_globals = std::move(sorted_);
+ graph_.ordered_globals = std::move(sorted_);
- return !diagnostics_.contains_errors();
- }
-
- private:
- /// @param node the ast::Node of the global declaration
- /// @returns the symbol of the global declaration node
- /// @note will raise an ICE if the node is not a type, function or variable
- /// declaration
- Symbol SymbolOf(const ast::Node* node) const {
- return Switch(
- node, //
- [&](const ast::TypeDecl* td) { return td->name; },
- [&](const ast::Function* func) { return func->symbol; },
- [&](const ast::Variable* var) { return var->symbol; },
- [&](Default) {
- UnhandledNode(diagnostics_, node);
- return Symbol{};
- });
- }
-
- /// @param node the ast::Node of the global declaration
- /// @returns the name of the global declaration node
- /// @note will raise an ICE if the node is not a type, function or variable
- /// declaration
- std::string NameOf(const ast::Node* node) const {
- return symbols_.NameFor(SymbolOf(node));
- }
-
- /// @param node the ast::Node of the global declaration
- /// @returns a string representation of the global declaration kind
- /// @note will raise an ICE if the node is not a type, function or variable
- /// declaration
- std::string KindOf(const ast::Node* node) {
- return Switch(
- node, //
- [&](const ast::Struct*) { return "struct"; },
- [&](const ast::Alias*) { return "alias"; },
- [&](const ast::Function*) { return "function"; },
- [&](const ast::Variable* var) { return var->is_const ? "let" : "var"; },
- [&](Default) {
- UnhandledNode(diagnostics_, node);
- return "<error>";
- });
- }
-
- /// Traverses `module`, collecting all the global declarations and populating
- /// the #globals and #declaration_order fields.
- void GatherGlobals(const ast::Module& module) {
- for (auto* node : module.GlobalDeclarations()) {
- auto* global = allocator_.Create(node);
- // Enable directives do not form a symbol. Skip them.
- if (!node->Is<ast::Enable>()) {
- globals_.emplace(SymbolOf(node), global);
- }
- declaration_order_.emplace_back(global);
- }
- }
-
- /// Walks the global declarations, determining the dependencies of each global
- /// and adding these to each global's Global::deps field.
- void DetermineDependencies() {
- DependencyScanner scanner(symbols_, globals_, diagnostics_, graph_,
- dependency_edges_);
- for (auto* global : declaration_order_) {
- scanner.Scan(global);
- }
- }
-
- /// Performs a depth-first traversal of `root`'s dependencies, calling `enter`
- /// as the function decends into each dependency and `exit` when bubbling back
- /// up towards the root.
- /// @param enter is a function with the signature: `bool(Global*)`. The
- /// `enter` function returns true if TraverseDependencies() should traverse
- /// the dependency, otherwise it will be skipped.
- /// @param exit is a function with the signature: `void(Global*)`. The `exit`
- /// function is only called if the corresponding `enter` call returned true.
- template <typename ENTER, typename EXIT>
- void TraverseDependencies(const Global* root, ENTER&& enter, EXIT&& exit) {
- // Entry is a single entry in the traversal stack. Entry points to a
- // dep_idx'th dependency of Entry::global.
- struct Entry {
- const Global* global; // The parent global
- size_t dep_idx; // The dependency index in `global->deps`
- };
-
- if (!enter(root)) {
- return;
+ return !diagnostics_.contains_errors();
}
- std::vector<Entry> stack{Entry{root, 0}};
- while (true) {
- auto& entry = stack.back();
- // Have we exhausted the dependencies of entry.global?
- if (entry.dep_idx < entry.global->deps.size()) {
- // No, there's more dependencies to traverse.
- auto& dep = entry.global->deps[entry.dep_idx];
- // Does the caller want to enter this dependency?
- if (enter(dep)) { // Yes.
- stack.push_back(Entry{dep, 0}); // Enter the dependency.
- } else {
- entry.dep_idx++; // No. Skip this node.
- }
- } else {
- // Yes. Time to back up.
- // Exit this global, pop the stack, and if there's another parent node,
- // increment its dependency index, and loop again.
- exit(entry.global);
- stack.pop_back();
- if (stack.empty()) {
- return; // All done.
- }
- stack.back().dep_idx++;
- }
- }
- }
-
- /// SortGlobals sorts the globals into dependency order, erroring if cyclic
- /// dependencies are found. The sorted dependencies are assigned to #sorted.
- void SortGlobals() {
- if (diagnostics_.contains_errors()) {
- return; // This code assumes there are no undeclared identifiers.
+ private:
+ /// @param node the ast::Node of the global declaration
+ /// @returns the symbol of the global declaration node
+ /// @note will raise an ICE if the node is not a type, function or variable
+ /// declaration
+ Symbol SymbolOf(const ast::Node* node) const {
+ return Switch(
+ node, //
+ [&](const ast::TypeDecl* td) { return td->name; },
+ [&](const ast::Function* func) { return func->symbol; },
+ [&](const ast::Variable* var) { return var->symbol; },
+ [&](Default) {
+ UnhandledNode(diagnostics_, node);
+ return Symbol{};
+ });
}
- std::unordered_set<const Global*> visited;
- for (auto* global : declaration_order_) {
- utils::UniqueVector<const Global*> stack;
- TraverseDependencies(
- global,
- [&](const Global* g) { // Enter
- if (!stack.add(g)) {
- CyclicDependencyFound(g, stack);
- return false;
+ /// @param node the ast::Node of the global declaration
+ /// @returns the name of the global declaration node
+ /// @note will raise an ICE if the node is not a type, function or variable
+ /// declaration
+ std::string NameOf(const ast::Node* node) const { return symbols_.NameFor(SymbolOf(node)); }
+
+ /// @param node the ast::Node of the global declaration
+ /// @returns a string representation of the global declaration kind
+ /// @note will raise an ICE if the node is not a type, function or variable
+ /// declaration
+ std::string KindOf(const ast::Node* node) {
+ return Switch(
+ node, //
+ [&](const ast::Struct*) { return "struct"; },
+ [&](const ast::Alias*) { return "alias"; },
+ [&](const ast::Function*) { return "function"; },
+ [&](const ast::Variable* var) { return var->is_const ? "let" : "var"; },
+ [&](Default) {
+ UnhandledNode(diagnostics_, node);
+ return "<error>";
+ });
+ }
+
+ /// Traverses `module`, collecting all the global declarations and populating
+ /// the #globals and #declaration_order fields.
+ void GatherGlobals(const ast::Module& module) {
+ for (auto* node : module.GlobalDeclarations()) {
+ auto* global = allocator_.Create(node);
+ // Enable directives do not form a symbol. Skip them.
+ if (!node->Is<ast::Enable>()) {
+ globals_.emplace(SymbolOf(node), global);
}
- if (sorted_.contains(g->node)) {
- // Visited this global already.
- // stack was pushed, but exit() will not be called when we return
- // false, so pop here.
- stack.pop_back();
- return false;
+ declaration_order_.emplace_back(global);
+ }
+ }
+
+ /// Walks the global declarations, determining the dependencies of each global
+ /// and adding these to each global's Global::deps field.
+ void DetermineDependencies() {
+ DependencyScanner scanner(symbols_, globals_, diagnostics_, graph_, dependency_edges_);
+ for (auto* global : declaration_order_) {
+ scanner.Scan(global);
+ }
+ }
+
+ /// Performs a depth-first traversal of `root`'s dependencies, calling `enter`
+ /// as the function decends into each dependency and `exit` when bubbling back
+ /// up towards the root.
+ /// @param enter is a function with the signature: `bool(Global*)`. The
+ /// `enter` function returns true if TraverseDependencies() should traverse
+ /// the dependency, otherwise it will be skipped.
+ /// @param exit is a function with the signature: `void(Global*)`. The `exit`
+ /// function is only called if the corresponding `enter` call returned true.
+ template <typename ENTER, typename EXIT>
+ void TraverseDependencies(const Global* root, ENTER&& enter, EXIT&& exit) {
+ // Entry is a single entry in the traversal stack. Entry points to a
+ // dep_idx'th dependency of Entry::global.
+ struct Entry {
+ const Global* global; // The parent global
+ size_t dep_idx; // The dependency index in `global->deps`
+ };
+
+ if (!enter(root)) {
+ return;
+ }
+
+ std::vector<Entry> stack{Entry{root, 0}};
+ while (true) {
+ auto& entry = stack.back();
+ // Have we exhausted the dependencies of entry.global?
+ if (entry.dep_idx < entry.global->deps.size()) {
+ // No, there's more dependencies to traverse.
+ auto& dep = entry.global->deps[entry.dep_idx];
+ // Does the caller want to enter this dependency?
+ if (enter(dep)) { // Yes.
+ stack.push_back(Entry{dep, 0}); // Enter the dependency.
+ } else {
+ entry.dep_idx++; // No. Skip this node.
+ }
+ } else {
+ // Yes. Time to back up.
+ // Exit this global, pop the stack, and if there's another parent node,
+ // increment its dependency index, and loop again.
+ exit(entry.global);
+ stack.pop_back();
+ if (stack.empty()) {
+ return; // All done.
+ }
+ stack.back().dep_idx++;
}
- return true;
- },
- [&](const Global* g) { // Exit. Only called if Enter returned true.
- sorted_.add(g->node);
- stack.pop_back();
- });
+ }
+ }
- sorted_.add(global->node);
+ /// SortGlobals sorts the globals into dependency order, erroring if cyclic
+ /// dependencies are found. The sorted dependencies are assigned to #sorted.
+ void SortGlobals() {
+ if (diagnostics_.contains_errors()) {
+ return; // This code assumes there are no undeclared identifiers.
+ }
- if (!stack.empty()) {
- // Each stack.push() must have a corresponding stack.pop_back().
+ std::unordered_set<const Global*> visited;
+ for (auto* global : declaration_order_) {
+ utils::UniqueVector<const Global*> stack;
+ TraverseDependencies(
+ global,
+ [&](const Global* g) { // Enter
+ if (!stack.add(g)) {
+ CyclicDependencyFound(g, stack);
+ return false;
+ }
+ if (sorted_.contains(g->node)) {
+ // Visited this global already.
+ // stack was pushed, but exit() will not be called when we return
+ // false, so pop here.
+ stack.pop_back();
+ return false;
+ }
+ return true;
+ },
+ [&](const Global* g) { // Exit. Only called if Enter returned true.
+ sorted_.add(g->node);
+ stack.pop_back();
+ });
+
+ sorted_.add(global->node);
+
+ if (!stack.empty()) {
+ // Each stack.push() must have a corresponding stack.pop_back().
+ TINT_ICE(Resolver, diagnostics_)
+ << "stack not empty after returning from TraverseDependencies()";
+ }
+ }
+ }
+
+ /// DepInfoFor() looks up the global dependency information for the dependency
+ /// of global `from` depending on `to`.
+ /// @note will raise an ICE if the edge is not found.
+ DependencyInfo DepInfoFor(const Global* from, const Global* to) const {
+ auto it = dependency_edges_.find(DependencyEdge{from, to});
+ if (it != dependency_edges_.end()) {
+ return it->second;
+ }
TINT_ICE(Resolver, diagnostics_)
- << "stack not empty after returning from TraverseDependencies()";
- }
+ << "failed to find dependency info for edge: '" << NameOf(from->node) << "' -> '"
+ << NameOf(to->node) << "'";
+ return {};
}
- }
- /// DepInfoFor() looks up the global dependency information for the dependency
- /// of global `from` depending on `to`.
- /// @note will raise an ICE if the edge is not found.
- DependencyInfo DepInfoFor(const Global* from, const Global* to) const {
- auto it = dependency_edges_.find(DependencyEdge{from, to});
- if (it != dependency_edges_.end()) {
- return it->second;
+ /// CyclicDependencyFound() emits an error diagnostic for a cyclic dependency.
+ /// @param root is the global that starts the cyclic dependency, which must be
+ /// found in `stack`.
+ /// @param stack is the global dependency stack that contains a loop.
+ void CyclicDependencyFound(const Global* root, const std::vector<const Global*>& stack) {
+ std::stringstream msg;
+ msg << "cyclic dependency found: ";
+ constexpr size_t kLoopNotStarted = ~0u;
+ size_t loop_start = kLoopNotStarted;
+ for (size_t i = 0; i < stack.size(); i++) {
+ auto* e = stack[i];
+ if (loop_start == kLoopNotStarted && e == root) {
+ loop_start = i;
+ }
+ if (loop_start != kLoopNotStarted) {
+ msg << "'" << NameOf(e->node) << "' -> ";
+ }
+ }
+ msg << "'" << NameOf(root->node) << "'";
+ AddError(diagnostics_, msg.str(), root->node->source);
+ for (size_t i = loop_start; i < stack.size(); i++) {
+ auto* from = stack[i];
+ auto* to = (i + 1 < stack.size()) ? stack[i + 1] : stack[loop_start];
+ auto info = DepInfoFor(from, to);
+ AddNote(diagnostics_,
+ KindOf(from->node) + " '" + NameOf(from->node) + "' " + info.action + " " +
+ KindOf(to->node) + " '" + NameOf(to->node) + "' here",
+ info.source);
+ }
}
- TINT_ICE(Resolver, diagnostics_)
- << "failed to find dependency info for edge: '" << NameOf(from->node)
- << "' -> '" << NameOf(to->node) << "'";
- return {};
- }
- /// CyclicDependencyFound() emits an error diagnostic for a cyclic dependency.
- /// @param root is the global that starts the cyclic dependency, which must be
- /// found in `stack`.
- /// @param stack is the global dependency stack that contains a loop.
- void CyclicDependencyFound(const Global* root,
- const std::vector<const Global*>& stack) {
- std::stringstream msg;
- msg << "cyclic dependency found: ";
- constexpr size_t kLoopNotStarted = ~0u;
- size_t loop_start = kLoopNotStarted;
- for (size_t i = 0; i < stack.size(); i++) {
- auto* e = stack[i];
- if (loop_start == kLoopNotStarted && e == root) {
- loop_start = i;
- }
- if (loop_start != kLoopNotStarted) {
- msg << "'" << NameOf(e->node) << "' -> ";
- }
- }
- msg << "'" << NameOf(root->node) << "'";
- AddError(diagnostics_, msg.str(), root->node->source);
- for (size_t i = loop_start; i < stack.size(); i++) {
- auto* from = stack[i];
- auto* to = (i + 1 < stack.size()) ? stack[i + 1] : stack[loop_start];
- auto info = DepInfoFor(from, to);
- AddNote(diagnostics_,
- KindOf(from->node) + " '" + NameOf(from->node) + "' " +
- info.action + " " + KindOf(to->node) + " '" +
- NameOf(to->node) + "' here",
- info.source);
- }
- }
-
- void DumpDependencyGraph() {
+ void DumpDependencyGraph() {
#if TINT_DUMP_DEPENDENCY_GRAPH == 0
- if ((true)) {
- return;
- }
+ if ((true)) {
+ return;
+ }
#endif // TINT_DUMP_DEPENDENCY_GRAPH
- printf("=========================\n");
- printf("------ declaration ------ \n");
- for (auto* global : declaration_order_) {
- printf("%s\n", NameOf(global->node).c_str());
+ printf("=========================\n");
+ printf("------ declaration ------ \n");
+ for (auto* global : declaration_order_) {
+ printf("%s\n", NameOf(global->node).c_str());
+ }
+ printf("------ dependencies ------ \n");
+ for (auto* node : sorted_) {
+ auto symbol = SymbolOf(node);
+ auto* global = globals_.at(symbol);
+ printf("%s depends on:\n", symbols_.NameFor(symbol).c_str());
+ for (auto* dep : global->deps) {
+ printf(" %s\n", NameOf(dep->node).c_str());
+ }
+ }
+ printf("=========================\n");
}
- printf("------ dependencies ------ \n");
- for (auto* node : sorted_) {
- auto symbol = SymbolOf(node);
- auto* global = globals_.at(symbol);
- printf("%s depends on:\n", symbols_.NameFor(symbol).c_str());
- for (auto* dep : global->deps) {
- printf(" %s\n", NameOf(dep->node).c_str());
- }
- }
- printf("=========================\n");
- }
- /// Program symbols
- const SymbolTable& symbols_;
+ /// Program symbols
+ const SymbolTable& symbols_;
- /// Program diagnostics
- diag::List& diagnostics_;
+ /// Program diagnostics
+ diag::List& diagnostics_;
- /// The resulting dependency graph
- DependencyGraph& graph_;
+ /// The resulting dependency graph
+ DependencyGraph& graph_;
- /// Allocator of Globals
- utils::BlockAllocator<Global> allocator_;
+ /// Allocator of Globals
+ utils::BlockAllocator<Global> allocator_;
- /// Global map, keyed by name. Populated by GatherGlobals().
- GlobalMap globals_;
+ /// Global map, keyed by name. Populated by GatherGlobals().
+ GlobalMap globals_;
- /// Map of DependencyEdge to DependencyInfo. Populated by
- /// DetermineDependencies().
- DependencyEdges dependency_edges_;
+ /// Map of DependencyEdge to DependencyInfo. Populated by
+ /// DetermineDependencies().
+ DependencyEdges dependency_edges_;
- /// Globals in declaration order. Populated by GatherGlobals().
- std::vector<Global*> declaration_order_;
+ /// Globals in declaration order. Populated by GatherGlobals().
+ std::vector<Global*> declaration_order_;
- /// Globals in sorted dependency order. Populated by SortGlobals().
- utils::UniqueVector<const ast::Node*> sorted_;
+ /// Globals in sorted dependency order. Populated by SortGlobals().
+ utils::UniqueVector<const ast::Node*> sorted_;
};
} // namespace
@@ -738,8 +707,8 @@
const SymbolTable& symbols,
diag::List& diagnostics,
DependencyGraph& output) {
- DependencyAnalysis da{symbols, diagnostics, output};
- return da.Run(module);
+ DependencyAnalysis da{symbols, diagnostics, output};
+ return da.Run(module);
}
} // namespace tint::resolver
diff --git a/src/tint/resolver/dependency_graph.h b/src/tint/resolver/dependency_graph.h
index e8042f1..0554817 100644
--- a/src/tint/resolver/dependency_graph.h
+++ b/src/tint/resolver/dependency_graph.h
@@ -26,37 +26,37 @@
/// DependencyGraph holds information about module-scope declaration dependency
/// analysis and symbol resolutions.
struct DependencyGraph {
- /// Constructor
- DependencyGraph();
- /// Move-constructor
- DependencyGraph(DependencyGraph&&);
- /// Destructor
- ~DependencyGraph();
+ /// Constructor
+ DependencyGraph();
+ /// Move-constructor
+ DependencyGraph(DependencyGraph&&);
+ /// Destructor
+ ~DependencyGraph();
- /// Build() performs symbol resolution and dependency analysis on `module`,
- /// populating `output` with the resulting dependency graph.
- /// @param module the AST module to analyse
- /// @param symbols the symbol table
- /// @param diagnostics the diagnostic list to populate with errors / warnings
- /// @param output the resulting DependencyGraph
- /// @returns true on success, false on error
- static bool Build(const ast::Module& module,
- const SymbolTable& symbols,
- diag::List& diagnostics,
- DependencyGraph& output);
+ /// Build() performs symbol resolution and dependency analysis on `module`,
+ /// populating `output` with the resulting dependency graph.
+ /// @param module the AST module to analyse
+ /// @param symbols the symbol table
+ /// @param diagnostics the diagnostic list to populate with errors / warnings
+ /// @param output the resulting DependencyGraph
+ /// @returns true on success, false on error
+ static bool Build(const ast::Module& module,
+ const SymbolTable& symbols,
+ diag::List& diagnostics,
+ DependencyGraph& output);
- /// All globals in dependency-sorted order.
- std::vector<const ast::Node*> ordered_globals;
+ /// All globals in dependency-sorted order.
+ std::vector<const ast::Node*> ordered_globals;
- /// Map of ast::IdentifierExpression or ast::TypeName to a type, function, or
- /// variable that declares the symbol.
- std::unordered_map<const ast::Node*, const ast::Node*> resolved_symbols;
+ /// Map of ast::IdentifierExpression or ast::TypeName to a type, function, or
+ /// variable that declares the symbol.
+ std::unordered_map<const ast::Node*, const ast::Node*> resolved_symbols;
- /// Map of ast::Variable to a type, function, or variable that is shadowed by
- /// the variable key. A declaration (X) shadows another (Y) if X and Y use
- /// the same symbol, and X is declared in a sub-scope of the scope that
- /// declares Y.
- std::unordered_map<const ast::Variable*, const ast::Node*> shadows;
+ /// Map of ast::Variable to a type, function, or variable that is shadowed by
+ /// the variable key. A declaration (X) shadows another (Y) if X and Y use
+ /// the same symbol, and X is declared in a sub-scope of the scope that
+ /// declares Y.
+ std::unordered_map<const ast::Variable*, const ast::Node*> shadows;
};
} // namespace tint::resolver
diff --git a/src/tint/resolver/dependency_graph_test.cc b/src/tint/resolver/dependency_graph_test.cc
index 639991a..a02d08c 100644
--- a/src/tint/resolver/dependency_graph_test.cc
+++ b/src/tint/resolver/dependency_graph_test.cc
@@ -27,23 +27,22 @@
template <typename T>
class ResolverDependencyGraphTestWithParam : public ResolverTestWithParam<T> {
- public:
- DependencyGraph Build(std::string expected_error = "") {
- DependencyGraph graph;
- auto result = DependencyGraph::Build(this->AST(), this->Symbols(),
- this->Diagnostics(), graph);
- if (expected_error.empty()) {
- EXPECT_TRUE(result) << this->Diagnostics().str();
- } else {
- EXPECT_FALSE(result);
- EXPECT_EQ(expected_error, this->Diagnostics().str());
+ public:
+ DependencyGraph Build(std::string expected_error = "") {
+ DependencyGraph graph;
+ auto result =
+ DependencyGraph::Build(this->AST(), this->Symbols(), this->Diagnostics(), graph);
+ if (expected_error.empty()) {
+ EXPECT_TRUE(result) << this->Diagnostics().str();
+ } else {
+ EXPECT_FALSE(result);
+ EXPECT_EQ(expected_error, this->Diagnostics().str());
+ }
+ return graph;
}
- return graph;
- }
};
-using ResolverDependencyGraphTest =
- ResolverDependencyGraphTestWithParam<::testing::Test>;
+using ResolverDependencyGraphTest = ResolverDependencyGraphTestWithParam<::testing::Test>;
////////////////////////////////////////////////////////////////////////////////
// Parameterized test helpers
@@ -52,24 +51,23 @@
/// SymbolDeclKind is used by parameterized tests to enumerate the different
/// kinds of symbol declarations.
enum class SymbolDeclKind {
- GlobalVar,
- GlobalConst,
- Alias,
- Struct,
- Function,
- Parameter,
- LocalVar,
- LocalLet,
- NestedLocalVar,
- NestedLocalLet,
+ GlobalVar,
+ GlobalConst,
+ Alias,
+ Struct,
+ Function,
+ Parameter,
+ LocalVar,
+ LocalLet,
+ NestedLocalVar,
+ NestedLocalLet,
};
static constexpr SymbolDeclKind kAllSymbolDeclKinds[] = {
- SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst,
- SymbolDeclKind::Alias, SymbolDeclKind::Struct,
- SymbolDeclKind::Function, SymbolDeclKind::Parameter,
- SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet,
- SymbolDeclKind::NestedLocalVar, SymbolDeclKind::NestedLocalLet,
+ SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst, SymbolDeclKind::Alias,
+ SymbolDeclKind::Struct, SymbolDeclKind::Function, SymbolDeclKind::Parameter,
+ SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar,
+ SymbolDeclKind::NestedLocalLet,
};
static constexpr SymbolDeclKind kTypeDeclKinds[] = {
@@ -78,22 +76,19 @@
};
static constexpr SymbolDeclKind kValueDeclKinds[] = {
- SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst,
- SymbolDeclKind::Parameter, SymbolDeclKind::LocalVar,
- SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar,
+ SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst, SymbolDeclKind::Parameter,
+ SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar,
SymbolDeclKind::NestedLocalLet,
};
static constexpr SymbolDeclKind kGlobalDeclKinds[] = {
- SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst,
- SymbolDeclKind::Alias, SymbolDeclKind::Struct,
- SymbolDeclKind::Function,
+ SymbolDeclKind::GlobalVar, SymbolDeclKind::GlobalConst, SymbolDeclKind::Alias,
+ SymbolDeclKind::Struct, SymbolDeclKind::Function,
};
static constexpr SymbolDeclKind kLocalDeclKinds[] = {
- SymbolDeclKind::Parameter, SymbolDeclKind::LocalVar,
- SymbolDeclKind::LocalLet, SymbolDeclKind::NestedLocalVar,
- SymbolDeclKind::NestedLocalLet,
+ SymbolDeclKind::Parameter, SymbolDeclKind::LocalVar, SymbolDeclKind::LocalLet,
+ SymbolDeclKind::NestedLocalVar, SymbolDeclKind::NestedLocalLet,
};
static constexpr SymbolDeclKind kGlobalValueDeclKinds[] = {
@@ -108,37 +103,37 @@
/// SymbolUseKind is used by parameterized tests to enumerate the different
/// kinds of symbol uses.
enum class SymbolUseKind {
- GlobalVarType,
- GlobalVarArrayElemType,
- GlobalVarArraySizeValue,
- GlobalVarVectorElemType,
- GlobalVarMatrixElemType,
- GlobalVarSampledTexElemType,
- GlobalVarMultisampledTexElemType,
- GlobalVarValue,
- GlobalLetType,
- GlobalLetArrayElemType,
- GlobalLetArraySizeValue,
- GlobalLetVectorElemType,
- GlobalLetMatrixElemType,
- GlobalLetValue,
- AliasType,
- StructMemberType,
- CallFunction,
- ParameterType,
- LocalVarType,
- LocalVarArrayElemType,
- LocalVarArraySizeValue,
- LocalVarVectorElemType,
- LocalVarMatrixElemType,
- LocalVarValue,
- LocalLetType,
- LocalLetValue,
- NestedLocalVarType,
- NestedLocalVarValue,
- NestedLocalLetType,
- NestedLocalLetValue,
- WorkgroupSizeValue,
+ GlobalVarType,
+ GlobalVarArrayElemType,
+ GlobalVarArraySizeValue,
+ GlobalVarVectorElemType,
+ GlobalVarMatrixElemType,
+ GlobalVarSampledTexElemType,
+ GlobalVarMultisampledTexElemType,
+ GlobalVarValue,
+ GlobalLetType,
+ GlobalLetArrayElemType,
+ GlobalLetArraySizeValue,
+ GlobalLetVectorElemType,
+ GlobalLetMatrixElemType,
+ GlobalLetValue,
+ AliasType,
+ StructMemberType,
+ CallFunction,
+ ParameterType,
+ LocalVarType,
+ LocalVarArrayElemType,
+ LocalVarArraySizeValue,
+ LocalVarVectorElemType,
+ LocalVarMatrixElemType,
+ LocalVarValue,
+ LocalLetType,
+ LocalLetValue,
+ NestedLocalVarType,
+ NestedLocalVarValue,
+ NestedLocalLetType,
+ NestedLocalLetValue,
+ WorkgroupSizeValue,
};
static constexpr SymbolUseKind kTypeUseKinds[] = {
@@ -181,473 +176,466 @@
/// @returns the description of the symbol declaration kind.
/// @note: This differs from the strings used in diagnostic messages.
std::ostream& operator<<(std::ostream& out, SymbolDeclKind kind) {
- switch (kind) {
- case SymbolDeclKind::GlobalVar:
- return out << "global var";
- case SymbolDeclKind::GlobalConst:
- return out << "global let";
- case SymbolDeclKind::Alias:
- return out << "alias";
- case SymbolDeclKind::Struct:
- return out << "struct";
- case SymbolDeclKind::Function:
- return out << "function";
- case SymbolDeclKind::Parameter:
- return out << "parameter";
- case SymbolDeclKind::LocalVar:
- return out << "local var";
- case SymbolDeclKind::LocalLet:
- return out << "local let";
- case SymbolDeclKind::NestedLocalVar:
- return out << "nested local var";
- case SymbolDeclKind::NestedLocalLet:
- return out << "nested local let";
- }
- return out << "<unknown>";
+ switch (kind) {
+ case SymbolDeclKind::GlobalVar:
+ return out << "global var";
+ case SymbolDeclKind::GlobalConst:
+ return out << "global let";
+ case SymbolDeclKind::Alias:
+ return out << "alias";
+ case SymbolDeclKind::Struct:
+ return out << "struct";
+ case SymbolDeclKind::Function:
+ return out << "function";
+ case SymbolDeclKind::Parameter:
+ return out << "parameter";
+ case SymbolDeclKind::LocalVar:
+ return out << "local var";
+ case SymbolDeclKind::LocalLet:
+ return out << "local let";
+ case SymbolDeclKind::NestedLocalVar:
+ return out << "nested local var";
+ case SymbolDeclKind::NestedLocalLet:
+ return out << "nested local let";
+ }
+ return out << "<unknown>";
}
/// @returns the description of the symbol use kind.
/// @note: This differs from the strings used in diagnostic messages.
std::ostream& operator<<(std::ostream& out, SymbolUseKind kind) {
- switch (kind) {
- case SymbolUseKind::GlobalVarType:
- return out << "global var type";
- case SymbolUseKind::GlobalVarValue:
- return out << "global var value";
- case SymbolUseKind::GlobalVarArrayElemType:
- return out << "global var array element type";
- case SymbolUseKind::GlobalVarArraySizeValue:
- return out << "global var array size value";
- case SymbolUseKind::GlobalVarVectorElemType:
- return out << "global var vector element type";
- case SymbolUseKind::GlobalVarMatrixElemType:
- return out << "global var matrix element type";
- case SymbolUseKind::GlobalVarSampledTexElemType:
- return out << "global var sampled_texture element type";
- case SymbolUseKind::GlobalVarMultisampledTexElemType:
- return out << "global var multisampled_texture element type";
- case SymbolUseKind::GlobalLetType:
- return out << "global let type";
- case SymbolUseKind::GlobalLetValue:
- return out << "global let value";
- case SymbolUseKind::GlobalLetArrayElemType:
- return out << "global let array element type";
- case SymbolUseKind::GlobalLetArraySizeValue:
- return out << "global let array size value";
- case SymbolUseKind::GlobalLetVectorElemType:
- return out << "global let vector element type";
- case SymbolUseKind::GlobalLetMatrixElemType:
- return out << "global let matrix element type";
- case SymbolUseKind::AliasType:
- return out << "alias type";
- case SymbolUseKind::StructMemberType:
- return out << "struct member type";
- case SymbolUseKind::CallFunction:
- return out << "call function";
- case SymbolUseKind::ParameterType:
- return out << "parameter type";
- case SymbolUseKind::LocalVarType:
- return out << "local var type";
- case SymbolUseKind::LocalVarArrayElemType:
- return out << "local var array element type";
- case SymbolUseKind::LocalVarArraySizeValue:
- return out << "local var array size value";
- case SymbolUseKind::LocalVarVectorElemType:
- return out << "local var vector element type";
- case SymbolUseKind::LocalVarMatrixElemType:
- return out << "local var matrix element type";
- case SymbolUseKind::LocalVarValue:
- return out << "local var value";
- case SymbolUseKind::LocalLetType:
- return out << "local let type";
- case SymbolUseKind::LocalLetValue:
- return out << "local let value";
- case SymbolUseKind::NestedLocalVarType:
- return out << "nested local var type";
- case SymbolUseKind::NestedLocalVarValue:
- return out << "nested local var value";
- case SymbolUseKind::NestedLocalLetType:
- return out << "nested local let type";
- case SymbolUseKind::NestedLocalLetValue:
- return out << "nested local let value";
- case SymbolUseKind::WorkgroupSizeValue:
- return out << "workgroup size value";
- }
- return out << "<unknown>";
+ switch (kind) {
+ case SymbolUseKind::GlobalVarType:
+ return out << "global var type";
+ case SymbolUseKind::GlobalVarValue:
+ return out << "global var value";
+ case SymbolUseKind::GlobalVarArrayElemType:
+ return out << "global var array element type";
+ case SymbolUseKind::GlobalVarArraySizeValue:
+ return out << "global var array size value";
+ case SymbolUseKind::GlobalVarVectorElemType:
+ return out << "global var vector element type";
+ case SymbolUseKind::GlobalVarMatrixElemType:
+ return out << "global var matrix element type";
+ case SymbolUseKind::GlobalVarSampledTexElemType:
+ return out << "global var sampled_texture element type";
+ case SymbolUseKind::GlobalVarMultisampledTexElemType:
+ return out << "global var multisampled_texture element type";
+ case SymbolUseKind::GlobalLetType:
+ return out << "global let type";
+ case SymbolUseKind::GlobalLetValue:
+ return out << "global let value";
+ case SymbolUseKind::GlobalLetArrayElemType:
+ return out << "global let array element type";
+ case SymbolUseKind::GlobalLetArraySizeValue:
+ return out << "global let array size value";
+ case SymbolUseKind::GlobalLetVectorElemType:
+ return out << "global let vector element type";
+ case SymbolUseKind::GlobalLetMatrixElemType:
+ return out << "global let matrix element type";
+ case SymbolUseKind::AliasType:
+ return out << "alias type";
+ case SymbolUseKind::StructMemberType:
+ return out << "struct member type";
+ case SymbolUseKind::CallFunction:
+ return out << "call function";
+ case SymbolUseKind::ParameterType:
+ return out << "parameter type";
+ case SymbolUseKind::LocalVarType:
+ return out << "local var type";
+ case SymbolUseKind::LocalVarArrayElemType:
+ return out << "local var array element type";
+ case SymbolUseKind::LocalVarArraySizeValue:
+ return out << "local var array size value";
+ case SymbolUseKind::LocalVarVectorElemType:
+ return out << "local var vector element type";
+ case SymbolUseKind::LocalVarMatrixElemType:
+ return out << "local var matrix element type";
+ case SymbolUseKind::LocalVarValue:
+ return out << "local var value";
+ case SymbolUseKind::LocalLetType:
+ return out << "local let type";
+ case SymbolUseKind::LocalLetValue:
+ return out << "local let value";
+ case SymbolUseKind::NestedLocalVarType:
+ return out << "nested local var type";
+ case SymbolUseKind::NestedLocalVarValue:
+ return out << "nested local var value";
+ case SymbolUseKind::NestedLocalLetType:
+ return out << "nested local let type";
+ case SymbolUseKind::NestedLocalLetValue:
+ return out << "nested local let value";
+ case SymbolUseKind::WorkgroupSizeValue:
+ return out << "workgroup size value";
+ }
+ return out << "<unknown>";
}
/// @returns the the diagnostic message name used for the given use
std::string DiagString(SymbolUseKind kind) {
- switch (kind) {
- case SymbolUseKind::GlobalVarType:
- case SymbolUseKind::GlobalVarArrayElemType:
- case SymbolUseKind::GlobalVarVectorElemType:
- case SymbolUseKind::GlobalVarMatrixElemType:
- case SymbolUseKind::GlobalVarSampledTexElemType:
- case SymbolUseKind::GlobalVarMultisampledTexElemType:
- case SymbolUseKind::GlobalLetType:
- case SymbolUseKind::GlobalLetArrayElemType:
- case SymbolUseKind::GlobalLetVectorElemType:
- case SymbolUseKind::GlobalLetMatrixElemType:
- case SymbolUseKind::AliasType:
- case SymbolUseKind::StructMemberType:
- case SymbolUseKind::ParameterType:
- case SymbolUseKind::LocalVarType:
- case SymbolUseKind::LocalVarArrayElemType:
- case SymbolUseKind::LocalVarVectorElemType:
- case SymbolUseKind::LocalVarMatrixElemType:
- case SymbolUseKind::LocalLetType:
- case SymbolUseKind::NestedLocalVarType:
- case SymbolUseKind::NestedLocalLetType:
- return "type";
- case SymbolUseKind::GlobalVarValue:
- case SymbolUseKind::GlobalVarArraySizeValue:
- case SymbolUseKind::GlobalLetValue:
- case SymbolUseKind::GlobalLetArraySizeValue:
- case SymbolUseKind::LocalVarValue:
- case SymbolUseKind::LocalVarArraySizeValue:
- case SymbolUseKind::LocalLetValue:
- case SymbolUseKind::NestedLocalVarValue:
- case SymbolUseKind::NestedLocalLetValue:
- case SymbolUseKind::WorkgroupSizeValue:
- return "identifier";
- case SymbolUseKind::CallFunction:
- return "function";
- }
- return "<unknown>";
+ switch (kind) {
+ case SymbolUseKind::GlobalVarType:
+ case SymbolUseKind::GlobalVarArrayElemType:
+ case SymbolUseKind::GlobalVarVectorElemType:
+ case SymbolUseKind::GlobalVarMatrixElemType:
+ case SymbolUseKind::GlobalVarSampledTexElemType:
+ case SymbolUseKind::GlobalVarMultisampledTexElemType:
+ case SymbolUseKind::GlobalLetType:
+ case SymbolUseKind::GlobalLetArrayElemType:
+ case SymbolUseKind::GlobalLetVectorElemType:
+ case SymbolUseKind::GlobalLetMatrixElemType:
+ case SymbolUseKind::AliasType:
+ case SymbolUseKind::StructMemberType:
+ case SymbolUseKind::ParameterType:
+ case SymbolUseKind::LocalVarType:
+ case SymbolUseKind::LocalVarArrayElemType:
+ case SymbolUseKind::LocalVarVectorElemType:
+ case SymbolUseKind::LocalVarMatrixElemType:
+ case SymbolUseKind::LocalLetType:
+ case SymbolUseKind::NestedLocalVarType:
+ case SymbolUseKind::NestedLocalLetType:
+ return "type";
+ case SymbolUseKind::GlobalVarValue:
+ case SymbolUseKind::GlobalVarArraySizeValue:
+ case SymbolUseKind::GlobalLetValue:
+ case SymbolUseKind::GlobalLetArraySizeValue:
+ case SymbolUseKind::LocalVarValue:
+ case SymbolUseKind::LocalVarArraySizeValue:
+ case SymbolUseKind::LocalLetValue:
+ case SymbolUseKind::NestedLocalVarValue:
+ case SymbolUseKind::NestedLocalLetValue:
+ case SymbolUseKind::WorkgroupSizeValue:
+ return "identifier";
+ case SymbolUseKind::CallFunction:
+ return "function";
+ }
+ return "<unknown>";
}
/// @returns the declaration scope depth for the symbol declaration kind.
/// Globals are at depth 0, parameters and locals are at depth 1,
/// nested locals are at depth 2.
int ScopeDepth(SymbolDeclKind kind) {
- switch (kind) {
- case SymbolDeclKind::GlobalVar:
- case SymbolDeclKind::GlobalConst:
- case SymbolDeclKind::Alias:
- case SymbolDeclKind::Struct:
- case SymbolDeclKind::Function:
- return 0;
- case SymbolDeclKind::Parameter:
- case SymbolDeclKind::LocalVar:
- case SymbolDeclKind::LocalLet:
- return 1;
- case SymbolDeclKind::NestedLocalVar:
- case SymbolDeclKind::NestedLocalLet:
- return 2;
- }
- return -1;
+ switch (kind) {
+ case SymbolDeclKind::GlobalVar:
+ case SymbolDeclKind::GlobalConst:
+ case SymbolDeclKind::Alias:
+ case SymbolDeclKind::Struct:
+ case SymbolDeclKind::Function:
+ return 0;
+ case SymbolDeclKind::Parameter:
+ case SymbolDeclKind::LocalVar:
+ case SymbolDeclKind::LocalLet:
+ return 1;
+ case SymbolDeclKind::NestedLocalVar:
+ case SymbolDeclKind::NestedLocalLet:
+ return 2;
+ }
+ return -1;
}
/// @returns the use depth for the symbol use kind.
/// Globals are at depth 0, parameters and locals are at depth 1,
/// nested locals are at depth 2.
int ScopeDepth(SymbolUseKind kind) {
- switch (kind) {
- case SymbolUseKind::GlobalVarType:
- case SymbolUseKind::GlobalVarValue:
- case SymbolUseKind::GlobalVarArrayElemType:
- case SymbolUseKind::GlobalVarArraySizeValue:
- case SymbolUseKind::GlobalVarVectorElemType:
- case SymbolUseKind::GlobalVarMatrixElemType:
- case SymbolUseKind::GlobalVarSampledTexElemType:
- case SymbolUseKind::GlobalVarMultisampledTexElemType:
- case SymbolUseKind::GlobalLetType:
- case SymbolUseKind::GlobalLetValue:
- case SymbolUseKind::GlobalLetArrayElemType:
- case SymbolUseKind::GlobalLetArraySizeValue:
- case SymbolUseKind::GlobalLetVectorElemType:
- case SymbolUseKind::GlobalLetMatrixElemType:
- case SymbolUseKind::AliasType:
- case SymbolUseKind::StructMemberType:
- case SymbolUseKind::WorkgroupSizeValue:
- return 0;
- case SymbolUseKind::CallFunction:
- case SymbolUseKind::ParameterType:
- case SymbolUseKind::LocalVarType:
- case SymbolUseKind::LocalVarArrayElemType:
- case SymbolUseKind::LocalVarArraySizeValue:
- case SymbolUseKind::LocalVarVectorElemType:
- case SymbolUseKind::LocalVarMatrixElemType:
- case SymbolUseKind::LocalVarValue:
- case SymbolUseKind::LocalLetType:
- case SymbolUseKind::LocalLetValue:
- return 1;
- case SymbolUseKind::NestedLocalVarType:
- case SymbolUseKind::NestedLocalVarValue:
- case SymbolUseKind::NestedLocalLetType:
- case SymbolUseKind::NestedLocalLetValue:
- return 2;
- }
- return -1;
+ switch (kind) {
+ case SymbolUseKind::GlobalVarType:
+ case SymbolUseKind::GlobalVarValue:
+ case SymbolUseKind::GlobalVarArrayElemType:
+ case SymbolUseKind::GlobalVarArraySizeValue:
+ case SymbolUseKind::GlobalVarVectorElemType:
+ case SymbolUseKind::GlobalVarMatrixElemType:
+ case SymbolUseKind::GlobalVarSampledTexElemType:
+ case SymbolUseKind::GlobalVarMultisampledTexElemType:
+ case SymbolUseKind::GlobalLetType:
+ case SymbolUseKind::GlobalLetValue:
+ case SymbolUseKind::GlobalLetArrayElemType:
+ case SymbolUseKind::GlobalLetArraySizeValue:
+ case SymbolUseKind::GlobalLetVectorElemType:
+ case SymbolUseKind::GlobalLetMatrixElemType:
+ case SymbolUseKind::AliasType:
+ case SymbolUseKind::StructMemberType:
+ case SymbolUseKind::WorkgroupSizeValue:
+ return 0;
+ case SymbolUseKind::CallFunction:
+ case SymbolUseKind::ParameterType:
+ case SymbolUseKind::LocalVarType:
+ case SymbolUseKind::LocalVarArrayElemType:
+ case SymbolUseKind::LocalVarArraySizeValue:
+ case SymbolUseKind::LocalVarVectorElemType:
+ case SymbolUseKind::LocalVarMatrixElemType:
+ case SymbolUseKind::LocalVarValue:
+ case SymbolUseKind::LocalLetType:
+ case SymbolUseKind::LocalLetValue:
+ return 1;
+ case SymbolUseKind::NestedLocalVarType:
+ case SymbolUseKind::NestedLocalVarValue:
+ case SymbolUseKind::NestedLocalLetType:
+ case SymbolUseKind::NestedLocalLetValue:
+ return 2;
+ }
+ return -1;
}
/// A helper for building programs that exercise symbol declaration tests.
struct SymbolTestHelper {
- /// The program builder
- ProgramBuilder* const builder;
- /// Parameters to a function that may need to be built
- std::vector<const ast::Variable*> parameters;
- /// Shallow function var / let declaration statements
- std::vector<const ast::Statement*> statements;
- /// Nested function local var / let declaration statements
- std::vector<const ast::Statement*> nested_statements;
- /// Function attributes
- ast::AttributeList func_attrs;
+ /// The program builder
+ ProgramBuilder* const builder;
+ /// Parameters to a function that may need to be built
+ std::vector<const ast::Variable*> parameters;
+ /// Shallow function var / let declaration statements
+ std::vector<const ast::Statement*> statements;
+ /// Nested function local var / let declaration statements
+ std::vector<const ast::Statement*> nested_statements;
+ /// Function attributes
+ ast::AttributeList func_attrs;
- /// Constructor
- /// @param builder the program builder
- explicit SymbolTestHelper(ProgramBuilder* builder);
+ /// Constructor
+ /// @param builder the program builder
+ explicit SymbolTestHelper(ProgramBuilder* builder);
- /// Destructor.
- ~SymbolTestHelper();
+ /// Destructor.
+ ~SymbolTestHelper();
- /// Declares a symbol with the given kind
- /// @param kind the kind of symbol declaration
- /// @param symbol the symbol to use for the declaration
- /// @param source the source of the declaration
- /// @returns the declaration node
- const ast::Node* Add(SymbolDeclKind kind, Symbol symbol, Source source);
+ /// Declares a symbol with the given kind
+ /// @param kind the kind of symbol declaration
+ /// @param symbol the symbol to use for the declaration
+ /// @param source the source of the declaration
+ /// @returns the declaration node
+ const ast::Node* Add(SymbolDeclKind kind, Symbol symbol, Source source);
- /// Declares a use of a symbol with the given kind
- /// @param kind the kind of symbol use
- /// @param symbol the declaration symbol to use
- /// @param source the source of the use
- /// @returns the use node
- const ast::Node* Add(SymbolUseKind kind, Symbol symbol, Source source);
+ /// Declares a use of a symbol with the given kind
+ /// @param kind the kind of symbol use
+ /// @param symbol the declaration symbol to use
+ /// @param source the source of the use
+ /// @returns the use node
+ const ast::Node* Add(SymbolUseKind kind, Symbol symbol, Source source);
- /// Builds a function, if any parameter or local declarations have been added
- void Build();
+ /// Builds a function, if any parameter or local declarations have been added
+ void Build();
};
SymbolTestHelper::SymbolTestHelper(ProgramBuilder* b) : builder(b) {}
SymbolTestHelper::~SymbolTestHelper() {}
-const ast::Node* SymbolTestHelper::Add(SymbolDeclKind kind,
- Symbol symbol,
- Source source) {
- auto& b = *builder;
- switch (kind) {
- case SymbolDeclKind::GlobalVar:
- return b.Global(source, symbol, b.ty.i32(), ast::StorageClass::kPrivate);
- case SymbolDeclKind::GlobalConst:
- return b.GlobalConst(source, symbol, b.ty.i32(), b.Expr(1));
- case SymbolDeclKind::Alias:
- return b.Alias(source, symbol, b.ty.i32());
- case SymbolDeclKind::Struct:
- return b.Structure(source, symbol, {b.Member("m", b.ty.i32())});
- case SymbolDeclKind::Function:
- return b.Func(source, symbol, {}, b.ty.void_(), {});
- case SymbolDeclKind::Parameter: {
- auto* node = b.Param(source, symbol, b.ty.i32());
- parameters.emplace_back(node);
- return node;
+const ast::Node* SymbolTestHelper::Add(SymbolDeclKind kind, Symbol symbol, Source source) {
+ auto& b = *builder;
+ switch (kind) {
+ case SymbolDeclKind::GlobalVar:
+ return b.Global(source, symbol, b.ty.i32(), ast::StorageClass::kPrivate);
+ case SymbolDeclKind::GlobalConst:
+ return b.GlobalConst(source, symbol, b.ty.i32(), b.Expr(1));
+ case SymbolDeclKind::Alias:
+ return b.Alias(source, symbol, b.ty.i32());
+ case SymbolDeclKind::Struct:
+ return b.Structure(source, symbol, {b.Member("m", b.ty.i32())});
+ case SymbolDeclKind::Function:
+ return b.Func(source, symbol, {}, b.ty.void_(), {});
+ case SymbolDeclKind::Parameter: {
+ auto* node = b.Param(source, symbol, b.ty.i32());
+ parameters.emplace_back(node);
+ return node;
+ }
+ case SymbolDeclKind::LocalVar: {
+ auto* node = b.Var(source, symbol, b.ty.i32());
+ statements.emplace_back(b.Decl(node));
+ return node;
+ }
+ case SymbolDeclKind::LocalLet: {
+ auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1));
+ statements.emplace_back(b.Decl(node));
+ return node;
+ }
+ case SymbolDeclKind::NestedLocalVar: {
+ auto* node = b.Var(source, symbol, b.ty.i32());
+ nested_statements.emplace_back(b.Decl(node));
+ return node;
+ }
+ case SymbolDeclKind::NestedLocalLet: {
+ auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1));
+ nested_statements.emplace_back(b.Decl(node));
+ return node;
+ }
}
- case SymbolDeclKind::LocalVar: {
- auto* node = b.Var(source, symbol, b.ty.i32());
- statements.emplace_back(b.Decl(node));
- return node;
- }
- case SymbolDeclKind::LocalLet: {
- auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1));
- statements.emplace_back(b.Decl(node));
- return node;
- }
- case SymbolDeclKind::NestedLocalVar: {
- auto* node = b.Var(source, symbol, b.ty.i32());
- nested_statements.emplace_back(b.Decl(node));
- return node;
- }
- case SymbolDeclKind::NestedLocalLet: {
- auto* node = b.Let(source, symbol, b.ty.i32(), b.Expr(1));
- nested_statements.emplace_back(b.Decl(node));
- return node;
- }
- }
- return nullptr;
+ return nullptr;
}
-const ast::Node* SymbolTestHelper::Add(SymbolUseKind kind,
- Symbol symbol,
- Source source) {
- auto& b = *builder;
- switch (kind) {
- case SymbolUseKind::GlobalVarType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Global(b.Sym(), node, ast::StorageClass::kPrivate);
- return node;
+const ast::Node* SymbolTestHelper::Add(SymbolUseKind kind, Symbol symbol, Source source) {
+ auto& b = *builder;
+ switch (kind) {
+ case SymbolUseKind::GlobalVarType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Global(b.Sym(), node, ast::StorageClass::kPrivate);
+ return node;
+ }
+ case SymbolUseKind::GlobalVarArrayElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Global(b.Sym(), b.ty.array(node, 4), ast::StorageClass::kPrivate);
+ return node;
+ }
+ case SymbolUseKind::GlobalVarArraySizeValue: {
+ auto* node = b.Expr(source, symbol);
+ b.Global(b.Sym(), b.ty.array(b.ty.i32(), node), ast::StorageClass::kPrivate);
+ return node;
+ }
+ case SymbolUseKind::GlobalVarVectorElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Global(b.Sym(), b.ty.vec3(node), ast::StorageClass::kPrivate);
+ return node;
+ }
+ case SymbolUseKind::GlobalVarMatrixElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Global(b.Sym(), b.ty.mat3x4(node), ast::StorageClass::kPrivate);
+ return node;
+ }
+ case SymbolUseKind::GlobalVarSampledTexElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Global(b.Sym(), b.ty.sampled_texture(ast::TextureDimension::k2d, node));
+ return node;
+ }
+ case SymbolUseKind::GlobalVarMultisampledTexElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Global(b.Sym(), b.ty.multisampled_texture(ast::TextureDimension::k2d, node));
+ return node;
+ }
+ case SymbolUseKind::GlobalVarValue: {
+ auto* node = b.Expr(source, symbol);
+ b.Global(b.Sym(), b.ty.i32(), ast::StorageClass::kPrivate, node);
+ return node;
+ }
+ case SymbolUseKind::GlobalLetType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.GlobalConst(b.Sym(), node, b.Expr(1));
+ return node;
+ }
+ case SymbolUseKind::GlobalLetArrayElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.GlobalConst(b.Sym(), b.ty.array(node, 4), b.Expr(1));
+ return node;
+ }
+ case SymbolUseKind::GlobalLetArraySizeValue: {
+ auto* node = b.Expr(source, symbol);
+ b.GlobalConst(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1));
+ return node;
+ }
+ case SymbolUseKind::GlobalLetVectorElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.GlobalConst(b.Sym(), b.ty.vec3(node), b.Expr(1));
+ return node;
+ }
+ case SymbolUseKind::GlobalLetMatrixElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.GlobalConst(b.Sym(), b.ty.mat3x4(node), b.Expr(1));
+ return node;
+ }
+ case SymbolUseKind::GlobalLetValue: {
+ auto* node = b.Expr(source, symbol);
+ b.GlobalConst(b.Sym(), b.ty.i32(), node);
+ return node;
+ }
+ case SymbolUseKind::AliasType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Alias(b.Sym(), node);
+ return node;
+ }
+ case SymbolUseKind::StructMemberType: {
+ auto* node = b.ty.type_name(source, symbol);
+ b.Structure(b.Sym(), {b.Member("m", node)});
+ return node;
+ }
+ case SymbolUseKind::CallFunction: {
+ auto* node = b.Expr(source, symbol);
+ statements.emplace_back(b.CallStmt(b.Call(node)));
+ return node;
+ }
+ case SymbolUseKind::ParameterType: {
+ auto* node = b.ty.type_name(source, symbol);
+ parameters.emplace_back(b.Param(b.Sym(), node));
+ return node;
+ }
+ case SymbolUseKind::LocalVarType: {
+ auto* node = b.ty.type_name(source, symbol);
+ statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
+ return node;
+ }
+ case SymbolUseKind::LocalVarArrayElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.array(node, 4), b.Expr(1))));
+ return node;
+ }
+ case SymbolUseKind::LocalVarArraySizeValue: {
+ auto* node = b.Expr(source, symbol);
+ statements.emplace_back(
+ b.Decl(b.Var(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1))));
+ return node;
+ }
+ case SymbolUseKind::LocalVarVectorElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.vec3(node))));
+ return node;
+ }
+ case SymbolUseKind::LocalVarMatrixElemType: {
+ auto* node = b.ty.type_name(source, symbol);
+ statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.mat3x4(node))));
+ return node;
+ }
+ case SymbolUseKind::LocalVarValue: {
+ auto* node = b.Expr(source, symbol);
+ statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
+ return node;
+ }
+ case SymbolUseKind::LocalLetType: {
+ auto* node = b.ty.type_name(source, symbol);
+ statements.emplace_back(b.Decl(b.Let(b.Sym(), node, b.Expr(1))));
+ return node;
+ }
+ case SymbolUseKind::LocalLetValue: {
+ auto* node = b.Expr(source, symbol);
+ statements.emplace_back(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
+ return node;
+ }
+ case SymbolUseKind::NestedLocalVarType: {
+ auto* node = b.ty.type_name(source, symbol);
+ nested_statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
+ return node;
+ }
+ case SymbolUseKind::NestedLocalVarValue: {
+ auto* node = b.Expr(source, symbol);
+ nested_statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
+ return node;
+ }
+ case SymbolUseKind::NestedLocalLetType: {
+ auto* node = b.ty.type_name(source, symbol);
+ nested_statements.emplace_back(b.Decl(b.Let(b.Sym(), node, b.Expr(1))));
+ return node;
+ }
+ case SymbolUseKind::NestedLocalLetValue: {
+ auto* node = b.Expr(source, symbol);
+ nested_statements.emplace_back(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
+ return node;
+ }
+ case SymbolUseKind::WorkgroupSizeValue: {
+ auto* node = b.Expr(source, symbol);
+ func_attrs.emplace_back(b.WorkgroupSize(1, node, 2));
+ return node;
+ }
}
- case SymbolUseKind::GlobalVarArrayElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Global(b.Sym(), b.ty.array(node, 4), ast::StorageClass::kPrivate);
- return node;
- }
- case SymbolUseKind::GlobalVarArraySizeValue: {
- auto* node = b.Expr(source, symbol);
- b.Global(b.Sym(), b.ty.array(b.ty.i32(), node),
- ast::StorageClass::kPrivate);
- return node;
- }
- case SymbolUseKind::GlobalVarVectorElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Global(b.Sym(), b.ty.vec3(node), ast::StorageClass::kPrivate);
- return node;
- }
- case SymbolUseKind::GlobalVarMatrixElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Global(b.Sym(), b.ty.mat3x4(node), ast::StorageClass::kPrivate);
- return node;
- }
- case SymbolUseKind::GlobalVarSampledTexElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Global(b.Sym(), b.ty.sampled_texture(ast::TextureDimension::k2d, node));
- return node;
- }
- case SymbolUseKind::GlobalVarMultisampledTexElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Global(b.Sym(),
- b.ty.multisampled_texture(ast::TextureDimension::k2d, node));
- return node;
- }
- case SymbolUseKind::GlobalVarValue: {
- auto* node = b.Expr(source, symbol);
- b.Global(b.Sym(), b.ty.i32(), ast::StorageClass::kPrivate, node);
- return node;
- }
- case SymbolUseKind::GlobalLetType: {
- auto* node = b.ty.type_name(source, symbol);
- b.GlobalConst(b.Sym(), node, b.Expr(1));
- return node;
- }
- case SymbolUseKind::GlobalLetArrayElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.GlobalConst(b.Sym(), b.ty.array(node, 4), b.Expr(1));
- return node;
- }
- case SymbolUseKind::GlobalLetArraySizeValue: {
- auto* node = b.Expr(source, symbol);
- b.GlobalConst(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1));
- return node;
- }
- case SymbolUseKind::GlobalLetVectorElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.GlobalConst(b.Sym(), b.ty.vec3(node), b.Expr(1));
- return node;
- }
- case SymbolUseKind::GlobalLetMatrixElemType: {
- auto* node = b.ty.type_name(source, symbol);
- b.GlobalConst(b.Sym(), b.ty.mat3x4(node), b.Expr(1));
- return node;
- }
- case SymbolUseKind::GlobalLetValue: {
- auto* node = b.Expr(source, symbol);
- b.GlobalConst(b.Sym(), b.ty.i32(), node);
- return node;
- }
- case SymbolUseKind::AliasType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Alias(b.Sym(), node);
- return node;
- }
- case SymbolUseKind::StructMemberType: {
- auto* node = b.ty.type_name(source, symbol);
- b.Structure(b.Sym(), {b.Member("m", node)});
- return node;
- }
- case SymbolUseKind::CallFunction: {
- auto* node = b.Expr(source, symbol);
- statements.emplace_back(b.CallStmt(b.Call(node)));
- return node;
- }
- case SymbolUseKind::ParameterType: {
- auto* node = b.ty.type_name(source, symbol);
- parameters.emplace_back(b.Param(b.Sym(), node));
- return node;
- }
- case SymbolUseKind::LocalVarType: {
- auto* node = b.ty.type_name(source, symbol);
- statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
- return node;
- }
- case SymbolUseKind::LocalVarArrayElemType: {
- auto* node = b.ty.type_name(source, symbol);
- statements.emplace_back(
- b.Decl(b.Var(b.Sym(), b.ty.array(node, 4), b.Expr(1))));
- return node;
- }
- case SymbolUseKind::LocalVarArraySizeValue: {
- auto* node = b.Expr(source, symbol);
- statements.emplace_back(
- b.Decl(b.Var(b.Sym(), b.ty.array(b.ty.i32(), node), b.Expr(1))));
- return node;
- }
- case SymbolUseKind::LocalVarVectorElemType: {
- auto* node = b.ty.type_name(source, symbol);
- statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.vec3(node))));
- return node;
- }
- case SymbolUseKind::LocalVarMatrixElemType: {
- auto* node = b.ty.type_name(source, symbol);
- statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.mat3x4(node))));
- return node;
- }
- case SymbolUseKind::LocalVarValue: {
- auto* node = b.Expr(source, symbol);
- statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
- return node;
- }
- case SymbolUseKind::LocalLetType: {
- auto* node = b.ty.type_name(source, symbol);
- statements.emplace_back(b.Decl(b.Let(b.Sym(), node, b.Expr(1))));
- return node;
- }
- case SymbolUseKind::LocalLetValue: {
- auto* node = b.Expr(source, symbol);
- statements.emplace_back(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
- return node;
- }
- case SymbolUseKind::NestedLocalVarType: {
- auto* node = b.ty.type_name(source, symbol);
- nested_statements.emplace_back(b.Decl(b.Var(b.Sym(), node)));
- return node;
- }
- case SymbolUseKind::NestedLocalVarValue: {
- auto* node = b.Expr(source, symbol);
- nested_statements.emplace_back(b.Decl(b.Var(b.Sym(), b.ty.i32(), node)));
- return node;
- }
- case SymbolUseKind::NestedLocalLetType: {
- auto* node = b.ty.type_name(source, symbol);
- nested_statements.emplace_back(b.Decl(b.Let(b.Sym(), node, b.Expr(1))));
- return node;
- }
- case SymbolUseKind::NestedLocalLetValue: {
- auto* node = b.Expr(source, symbol);
- nested_statements.emplace_back(b.Decl(b.Let(b.Sym(), b.ty.i32(), node)));
- return node;
- }
- case SymbolUseKind::WorkgroupSizeValue: {
- auto* node = b.Expr(source, symbol);
- func_attrs.emplace_back(b.WorkgroupSize(1, node, 2));
- return node;
- }
- }
- return nullptr;
+ return nullptr;
}
void SymbolTestHelper::Build() {
- auto& b = *builder;
- if (!nested_statements.empty()) {
- statements.emplace_back(b.Block(nested_statements));
- nested_statements.clear();
- }
- if (!parameters.empty() || !statements.empty() || !func_attrs.empty()) {
- b.Func("func", parameters, b.ty.void_(), statements, func_attrs);
- parameters.clear();
- statements.clear();
- func_attrs.clear();
- }
+ auto& b = *builder;
+ if (!nested_statements.empty()) {
+ statements.emplace_back(b.Block(nested_statements));
+ nested_statements.clear();
+ }
+ if (!parameters.empty() || !statements.empty() || !func_attrs.empty()) {
+ b.Func("func", parameters, b.ty.void_(), statements, func_attrs);
+ parameters.clear();
+ statements.clear();
+ func_attrs.clear();
+ }
}
////////////////////////////////////////////////////////////////////////////////
@@ -658,84 +646,80 @@
using ResolverDependencyGraphUsedBeforeDeclTest = ResolverDependencyGraphTest;
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, FuncCall) {
- // fn A() { B(); }
- // fn B() {}
+ // fn A() { B(); }
+ // fn B() {}
- Func("A", {}, ty.void_(), {CallStmt(Call(Expr(Source{{12, 34}}, "B")))});
- Func(Source{{56, 78}}, "B", {}, ty.void_(), {Return()});
+ Func("A", {}, ty.void_(), {CallStmt(Call(Expr(Source{{12, 34}}, "B")))});
+ Func(Source{{56, 78}}, "B", {}, ty.void_(), {Return()});
- Build();
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeConstructed) {
- // fn F() {
- // { _ = T(); }
- // }
- // type T = i32;
+ // fn F() {
+ // { _ = T(); }
+ // }
+ // type T = i32;
- Func("F", {}, ty.void_(),
- {Block(Ignore(Construct(ty.type_name(Source{{12, 34}}, "T"))))});
- Alias(Source{{56, 78}}, "T", ty.i32());
+ Func("F", {}, ty.void_(), {Block(Ignore(Construct(ty.type_name(Source{{12, 34}}, "T"))))});
+ Alias(Source{{56, 78}}, "T", ty.i32());
- Build();
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByLocal) {
- // fn F() {
- // { var v : T; }
- // }
- // type T = i32;
+ // fn F() {
+ // { var v : T; }
+ // }
+ // type T = i32;
- Func("F", {}, ty.void_(),
- {Block(Decl(Var("v", ty.type_name(Source{{12, 34}}, "T"))))});
- Alias(Source{{56, 78}}, "T", ty.i32());
+ Func("F", {}, ty.void_(), {Block(Decl(Var("v", ty.type_name(Source{{12, 34}}, "T"))))});
+ Alias(Source{{56, 78}}, "T", ty.i32());
- Build();
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedByParam) {
- // fn F(p : T) {}
- // type T = i32;
+ // fn F(p : T) {}
+ // type T = i32;
- Func("F", {Param("p", ty.type_name(Source{{12, 34}}, "T"))}, ty.void_(), {});
- Alias(Source{{56, 78}}, "T", ty.i32());
+ Func("F", {Param("p", ty.type_name(Source{{12, 34}}, "T"))}, ty.void_(), {});
+ Alias(Source{{56, 78}}, "T", ty.i32());
- Build();
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeUsedAsReturnType) {
- // fn F() -> T {}
- // type T = i32;
+ // fn F() -> T {}
+ // type T = i32;
- Func("F", {}, ty.type_name(Source{{12, 34}}, "T"), {});
- Alias(Source{{56, 78}}, "T", ty.i32());
+ Func("F", {}, ty.type_name(Source{{12, 34}}, "T"), {});
+ Alias(Source{{56, 78}}, "T", ty.i32());
- Build();
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, TypeByStructMember) {
- // struct S { m : T };
- // type T = i32;
+ // struct S { m : T };
+ // type T = i32;
- Structure("S", {Member("m", ty.type_name(Source{{12, 34}}, "T"))});
- Alias(Source{{56, 78}}, "T", ty.i32());
+ Structure("S", {Member("m", ty.type_name(Source{{12, 34}}, "T"))});
+ Alias(Source{{56, 78}}, "T", ty.i32());
- Build();
+ Build();
}
TEST_F(ResolverDependencyGraphUsedBeforeDeclTest, VarUsed) {
- // fn F() {
- // { G = 3.14f; }
- // }
- // var G: f32 = 2.1;
+ // fn F() {
+ // { G = 3.14f; }
+ // }
+ // var G: f32 = 2.1;
- Func("F", ast::VariableList{}, ty.void_(),
- {Block(Assign(Expr(Source{{12, 34}}, "G"), 3.14f))});
+ Func("F", ast::VariableList{}, ty.void_(), {Block(Assign(Expr(Source{{12, 34}}, "G"), 3.14f))});
- Global(Source{{56, 78}}, "G", ty.f32(), ast::StorageClass::kPrivate,
- Expr(2.1f));
+ Global(Source{{56, 78}}, "G", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
- Build();
+ Build();
}
} // namespace used_before_decl_tests
@@ -749,15 +733,15 @@
ResolverDependencyGraphTestWithParam<SymbolUseKind>;
TEST_P(ResolverDependencyGraphUndeclaredSymbolTest, Test) {
- const Symbol symbol = Sym("SYMBOL");
- const auto use_kind = GetParam();
+ const Symbol symbol = Sym("SYMBOL");
+ const auto use_kind = GetParam();
- // Build a use of a non-existent symbol
- SymbolTestHelper helper(this);
- helper.Add(use_kind, symbol, Source{{56, 78}});
- helper.Build();
+ // Build a use of a non-existent symbol
+ SymbolTestHelper helper(this);
+ helper.Add(use_kind, symbol, Source{{56, 78}});
+ helper.Build();
- Build("56:78 error: unknown " + DiagString(use_kind) + ": 'SYMBOL'");
+ Build("56:78 error: unknown " + DiagString(use_kind) + ": 'SYMBOL'");
}
INSTANTIATE_TEST_SUITE_P(Types,
@@ -782,31 +766,29 @@
using ResolverDependencyGraphDeclSelfUse = ResolverDependencyGraphTest;
TEST_F(ResolverDependencyGraphDeclSelfUse, GlobalVar) {
- const Symbol symbol = Sym("SYMBOL");
- Global(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123));
- Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL'
+ const Symbol symbol = Sym("SYMBOL");
+ Global(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123));
+ Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL'
12:34 note: var 'SYMBOL' references var 'SYMBOL' here)");
}
TEST_F(ResolverDependencyGraphDeclSelfUse, GlobalConst) {
- const Symbol symbol = Sym("SYMBOL");
- GlobalConst(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123));
- Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL'
+ const Symbol symbol = Sym("SYMBOL");
+ GlobalConst(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123));
+ Build(R"(error: cyclic dependency found: 'SYMBOL' -> 'SYMBOL'
12:34 note: let 'SYMBOL' references let 'SYMBOL' here)");
}
TEST_F(ResolverDependencyGraphDeclSelfUse, LocalVar) {
- const Symbol symbol = Sym("SYMBOL");
- WrapInFunction(
- Decl(Var(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123))));
- Build("12:34 error: unknown identifier: 'SYMBOL'");
+ const Symbol symbol = Sym("SYMBOL");
+ WrapInFunction(Decl(Var(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123))));
+ Build("12:34 error: unknown identifier: 'SYMBOL'");
}
TEST_F(ResolverDependencyGraphDeclSelfUse, LocalLet) {
- const Symbol symbol = Sym("SYMBOL");
- WrapInFunction(
- Decl(Let(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123))));
- Build("12:34 error: unknown identifier: 'SYMBOL'");
+ const Symbol symbol = Sym("SYMBOL");
+ WrapInFunction(Decl(Let(symbol, ty.i32(), Mul(Expr(Source{{12, 34}}, symbol), 123))));
+ Build("12:34 error: unknown identifier: 'SYMBOL'");
}
} // namespace undeclared_tests
@@ -819,180 +801,171 @@
using ResolverDependencyGraphCyclicRefTest = ResolverDependencyGraphTest;
TEST_F(ResolverDependencyGraphCyclicRefTest, DirectCall) {
- // fn main() { main(); }
+ // fn main() { main(); }
- Func(Source{{12, 34}}, "main", {}, ty.void_(),
- {CallStmt(Call(Expr(Source{{56, 78}}, "main")))});
+ Func(Source{{12, 34}}, "main", {}, ty.void_(),
+ {CallStmt(Call(Expr(Source{{56, 78}}, "main")))});
- Build(R"(12:34 error: cyclic dependency found: 'main' -> 'main'
+ Build(R"(12:34 error: cyclic dependency found: 'main' -> 'main'
56:78 note: function 'main' calls function 'main' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, IndirectCall) {
- // 1: fn a() { b(); }
- // 2: fn e() { }
- // 3: fn d() { e(); b(); }
- // 4: fn c() { d(); }
- // 5: fn b() { c(); }
+ // 1: fn a() { b(); }
+ // 2: fn e() { }
+ // 3: fn d() { e(); b(); }
+ // 4: fn c() { d(); }
+ // 5: fn b() { c(); }
- Func(Source{{1, 1}}, "a", {}, ty.void_(),
- {CallStmt(Call(Expr(Source{{1, 10}}, "b")))});
- Func(Source{{2, 1}}, "e", {}, ty.void_(), {});
- Func(Source{{3, 1}}, "d", {}, ty.void_(),
- {
- CallStmt(Call(Expr(Source{{3, 10}}, "e"))),
- CallStmt(Call(Expr(Source{{3, 10}}, "b"))),
- });
- Func(Source{{4, 1}}, "c", {}, ty.void_(),
- {CallStmt(Call(Expr(Source{{4, 10}}, "d")))});
- Func(Source{{5, 1}}, "b", {}, ty.void_(),
- {CallStmt(Call(Expr(Source{{5, 10}}, "c")))});
+ Func(Source{{1, 1}}, "a", {}, ty.void_(), {CallStmt(Call(Expr(Source{{1, 10}}, "b")))});
+ Func(Source{{2, 1}}, "e", {}, ty.void_(), {});
+ Func(Source{{3, 1}}, "d", {}, ty.void_(),
+ {
+ CallStmt(Call(Expr(Source{{3, 10}}, "e"))),
+ CallStmt(Call(Expr(Source{{3, 10}}, "b"))),
+ });
+ Func(Source{{4, 1}}, "c", {}, ty.void_(), {CallStmt(Call(Expr(Source{{4, 10}}, "d")))});
+ Func(Source{{5, 1}}, "b", {}, ty.void_(), {CallStmt(Call(Expr(Source{{5, 10}}, "c")))});
- Build(R"(5:1 error: cyclic dependency found: 'b' -> 'c' -> 'd' -> 'b'
+ Build(R"(5:1 error: cyclic dependency found: 'b' -> 'c' -> 'd' -> 'b'
5:10 note: function 'b' calls function 'c' here
4:10 note: function 'c' calls function 'd' here
3:10 note: function 'd' calls function 'b' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, Alias_Direct) {
- // type T = T;
+ // type T = T;
- Alias(Source{{12, 34}}, "T", ty.type_name(Source{{56, 78}}, "T"));
+ Alias(Source{{12, 34}}, "T", ty.type_name(Source{{56, 78}}, "T"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: cyclic dependency found: 'T' -> 'T'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: cyclic dependency found: 'T' -> 'T'
56:78 note: alias 'T' references alias 'T' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, Alias_Indirect) {
- // 1: type Y = Z;
- // 2: type X = Y;
- // 3: type Z = X;
+ // 1: type Y = Z;
+ // 2: type X = Y;
+ // 3: type Z = X;
- Alias(Source{{1, 1}}, "Y", ty.type_name(Source{{1, 10}}, "Z"));
- Alias(Source{{2, 1}}, "X", ty.type_name(Source{{2, 10}}, "Y"));
- Alias(Source{{3, 1}}, "Z", ty.type_name(Source{{3, 10}}, "X"));
+ Alias(Source{{1, 1}}, "Y", ty.type_name(Source{{1, 10}}, "Z"));
+ Alias(Source{{2, 1}}, "X", ty.type_name(Source{{2, 10}}, "Y"));
+ Alias(Source{{3, 1}}, "Z", ty.type_name(Source{{3, 10}}, "X"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
1:10 note: alias 'Y' references alias 'Z' here
3:10 note: alias 'Z' references alias 'X' here
2:10 note: alias 'X' references alias 'Y' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, Struct_Direct) {
- // struct S {
- // a: S;
- // };
+ // struct S {
+ // a: S;
+ // };
- Structure(Source{{12, 34}}, "S",
- {Member("a", ty.type_name(Source{{56, 78}}, "S"))});
+ Structure(Source{{12, 34}}, "S", {Member("a", ty.type_name(Source{{56, 78}}, "S"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: cyclic dependency found: 'S' -> 'S'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: cyclic dependency found: 'S' -> 'S'
56:78 note: struct 'S' references struct 'S' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, Struct_Indirect) {
- // 1: struct Y { z: Z; };
- // 2: struct X { y: Y; };
- // 3: struct Z { x: X; };
+ // 1: struct Y { z: Z; };
+ // 2: struct X { y: Y; };
+ // 3: struct Z { x: X; };
- Structure(Source{{1, 1}}, "Y",
- {Member("z", ty.type_name(Source{{1, 10}}, "Z"))});
- Structure(Source{{2, 1}}, "X",
- {Member("y", ty.type_name(Source{{2, 10}}, "Y"))});
- Structure(Source{{3, 1}}, "Z",
- {Member("x", ty.type_name(Source{{3, 10}}, "X"))});
+ Structure(Source{{1, 1}}, "Y", {Member("z", ty.type_name(Source{{1, 10}}, "Z"))});
+ Structure(Source{{2, 1}}, "X", {Member("y", ty.type_name(Source{{2, 10}}, "Y"))});
+ Structure(Source{{3, 1}}, "Z", {Member("x", ty.type_name(Source{{3, 10}}, "X"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
1:10 note: struct 'Y' references struct 'Z' here
3:10 note: struct 'Z' references struct 'X' here
2:10 note: struct 'X' references struct 'Y' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalVar_Direct) {
- // var<private> V : i32 = V;
+ // var<private> V : i32 = V;
- Global(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
+ Global(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: cyclic dependency found: 'V' -> 'V'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: cyclic dependency found: 'V' -> 'V'
56:78 note: var 'V' references var 'V' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalLet_Direct) {
- // let V : i32 = V;
+ // let V : i32 = V;
- GlobalConst(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
+ GlobalConst(Source{{12, 34}}, "V", ty.i32(), Expr(Source{{56, 78}}, "V"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 error: cyclic dependency found: 'V' -> 'V'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: cyclic dependency found: 'V' -> 'V'
56:78 note: let 'V' references let 'V' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalVar_Indirect) {
- // 1: var<private> Y : i32 = Z;
- // 2: var<private> X : i32 = Y;
- // 3: var<private> Z : i32 = X;
+ // 1: var<private> Y : i32 = Z;
+ // 2: var<private> X : i32 = Y;
+ // 3: var<private> Z : i32 = X;
- Global(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
- Global(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
- Global(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X"));
+ Global(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
+ Global(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
+ Global(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
1:10 note: var 'Y' references var 'Z' here
3:10 note: var 'Z' references var 'X' here
2:10 note: var 'X' references var 'Y' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, GlobalLet_Indirect) {
- // 1: let Y : i32 = Z;
- // 2: let X : i32 = Y;
- // 3: let Z : i32 = X;
+ // 1: let Y : i32 = Z;
+ // 2: let X : i32 = Y;
+ // 3: let Z : i32 = X;
- GlobalConst(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
- GlobalConst(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
- GlobalConst(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X"));
+ GlobalConst(Source{{1, 1}}, "Y", ty.i32(), Expr(Source{{1, 10}}, "Z"));
+ GlobalConst(Source{{2, 1}}, "X", ty.i32(), Expr(Source{{2, 10}}, "Y"));
+ GlobalConst(Source{{3, 1}}, "Z", ty.i32(), Expr(Source{{3, 10}}, "X"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(1:1 error: cyclic dependency found: 'Y' -> 'Z' -> 'X' -> 'Y'
1:10 note: let 'Y' references let 'Z' here
3:10 note: let 'Z' references let 'X' here
2:10 note: let 'X' references let 'Y' here)");
}
TEST_F(ResolverDependencyGraphCyclicRefTest, Mixed_RecursiveDependencies) {
- // 1: fn F() -> R { return Z; }
- // 2: type A = S;
- // 3: struct S { a : A };
- // 4: var Z = L;
- // 5: type R = A;
- // 6: let L : S = Z;
+ // 1: fn F() -> R { return Z; }
+ // 2: type A = S;
+ // 3: struct S { a : A };
+ // 4: var Z = L;
+ // 5: type R = A;
+ // 6: let L : S = Z;
- Func(Source{{1, 1}}, "F", {}, ty.type_name(Source{{1, 5}}, "R"),
- {Return(Expr(Source{{1, 10}}, "Z"))});
- Alias(Source{{2, 1}}, "A", ty.type_name(Source{{2, 10}}, "S"));
- Structure(Source{{3, 1}}, "S",
- {Member("a", ty.type_name(Source{{3, 10}}, "A"))});
- Global(Source{{4, 1}}, "Z", nullptr, Expr(Source{{4, 10}}, "L"));
- Alias(Source{{5, 1}}, "R", ty.type_name(Source{{5, 10}}, "A"));
- GlobalConst(Source{{6, 1}}, "L", ty.type_name(Source{{5, 5}}, "S"),
- Expr(Source{{5, 10}}, "Z"));
+ Func(Source{{1, 1}}, "F", {}, ty.type_name(Source{{1, 5}}, "R"),
+ {Return(Expr(Source{{1, 10}}, "Z"))});
+ Alias(Source{{2, 1}}, "A", ty.type_name(Source{{2, 10}}, "S"));
+ Structure(Source{{3, 1}}, "S", {Member("a", ty.type_name(Source{{3, 10}}, "A"))});
+ Global(Source{{4, 1}}, "Z", nullptr, Expr(Source{{4, 10}}, "L"));
+ Alias(Source{{5, 1}}, "R", ty.type_name(Source{{5, 10}}, "A"));
+ GlobalConst(Source{{6, 1}}, "L", ty.type_name(Source{{5, 5}}, "S"), Expr(Source{{5, 10}}, "Z"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(2:1 error: cyclic dependency found: 'A' -> 'S' -> 'A'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(2:1 error: cyclic dependency found: 'A' -> 'S' -> 'A'
2:10 note: alias 'A' references struct 'S' here
3:10 note: struct 'S' references alias 'A' here
4:1 error: cyclic dependency found: 'Z' -> 'L' -> 'Z'
@@ -1008,40 +981,37 @@
namespace redeclaration_tests {
using ResolverDependencyGraphRedeclarationTest =
- ResolverDependencyGraphTestWithParam<
- std::tuple<SymbolDeclKind, SymbolDeclKind>>;
+ ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolDeclKind>>;
TEST_P(ResolverDependencyGraphRedeclarationTest, Test) {
- const auto symbol = Sym("SYMBOL");
+ const auto symbol = Sym("SYMBOL");
- auto a_kind = std::get<0>(GetParam());
- auto b_kind = std::get<1>(GetParam());
+ auto a_kind = std::get<0>(GetParam());
+ auto b_kind = std::get<1>(GetParam());
- auto a_source = Source{{12, 34}};
- auto b_source = Source{{56, 78}};
+ auto a_source = Source{{12, 34}};
+ auto b_source = Source{{56, 78}};
- if (a_kind != SymbolDeclKind::Parameter &&
- b_kind == SymbolDeclKind::Parameter) {
- std::swap(a_source, b_source); // Parameters are declared before locals
- }
+ if (a_kind != SymbolDeclKind::Parameter && b_kind == SymbolDeclKind::Parameter) {
+ std::swap(a_source, b_source); // Parameters are declared before locals
+ }
- SymbolTestHelper helper(this);
- helper.Add(a_kind, symbol, a_source);
- helper.Add(b_kind, symbol, b_source);
- helper.Build();
+ SymbolTestHelper helper(this);
+ helper.Add(a_kind, symbol, a_source);
+ helper.Add(b_kind, symbol, b_source);
+ helper.Build();
- bool error = ScopeDepth(a_kind) == ScopeDepth(b_kind);
+ bool error = ScopeDepth(a_kind) == ScopeDepth(b_kind);
- Build(error ? R"(56:78 error: redeclaration of 'SYMBOL'
+ Build(error ? R"(56:78 error: redeclaration of 'SYMBOL'
12:34 note: 'SYMBOL' previously declared here)"
- : "");
+ : "");
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverDependencyGraphRedeclarationTest,
- testing::Combine(testing::ValuesIn(kAllSymbolDeclKinds),
- testing::ValuesIn(kAllSymbolDeclKinds)));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverDependencyGraphRedeclarationTest,
+ testing::Combine(testing::ValuesIn(kAllSymbolDeclKinds),
+ testing::ValuesIn(kAllSymbolDeclKinds)));
} // namespace redeclaration_tests
@@ -1051,45 +1021,44 @@
namespace ordered_globals {
using ResolverDependencyGraphOrderedGlobalsTest =
- ResolverDependencyGraphTestWithParam<
- std::tuple<SymbolDeclKind, SymbolUseKind>>;
+ ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolUseKind>>;
TEST_P(ResolverDependencyGraphOrderedGlobalsTest, InOrder) {
- const Symbol symbol = Sym("SYMBOL");
- const auto decl_kind = std::get<0>(GetParam());
- const auto use_kind = std::get<1>(GetParam());
+ const Symbol symbol = Sym("SYMBOL");
+ const auto decl_kind = std::get<0>(GetParam());
+ const auto use_kind = std::get<1>(GetParam());
- // Declaration before use
- SymbolTestHelper helper(this);
- helper.Add(decl_kind, symbol, Source{{12, 34}});
- helper.Add(use_kind, symbol, Source{{56, 78}});
- helper.Build();
+ // Declaration before use
+ SymbolTestHelper helper(this);
+ helper.Add(decl_kind, symbol, Source{{12, 34}});
+ helper.Add(use_kind, symbol, Source{{56, 78}});
+ helper.Build();
- ASSERT_EQ(AST().GlobalDeclarations().size(), 2u);
+ ASSERT_EQ(AST().GlobalDeclarations().size(), 2u);
- auto* decl = AST().GlobalDeclarations()[0];
- auto* use = AST().GlobalDeclarations()[1];
- EXPECT_THAT(Build().ordered_globals, ElementsAre(decl, use));
+ auto* decl = AST().GlobalDeclarations()[0];
+ auto* use = AST().GlobalDeclarations()[1];
+ EXPECT_THAT(Build().ordered_globals, ElementsAre(decl, use));
}
TEST_P(ResolverDependencyGraphOrderedGlobalsTest, OutOfOrder) {
- const Symbol symbol = Sym("SYMBOL");
- const auto decl_kind = std::get<0>(GetParam());
- const auto use_kind = std::get<1>(GetParam());
+ const Symbol symbol = Sym("SYMBOL");
+ const auto decl_kind = std::get<0>(GetParam());
+ const auto use_kind = std::get<1>(GetParam());
- // Use before declaration
- SymbolTestHelper helper(this);
- helper.Add(use_kind, symbol, Source{{56, 78}});
- helper.Build(); // If the use is in a function, then ensure this function is
- // built before the symbol declaration
- helper.Add(decl_kind, symbol, Source{{12, 34}});
- helper.Build();
+ // Use before declaration
+ SymbolTestHelper helper(this);
+ helper.Add(use_kind, symbol, Source{{56, 78}});
+ helper.Build(); // If the use is in a function, then ensure this function is
+ // built before the symbol declaration
+ helper.Add(decl_kind, symbol, Source{{12, 34}});
+ helper.Build();
- ASSERT_EQ(AST().GlobalDeclarations().size(), 2u);
+ ASSERT_EQ(AST().GlobalDeclarations().size(), 2u);
- auto* use = AST().GlobalDeclarations()[0];
- auto* decl = AST().GlobalDeclarations()[1];
- EXPECT_THAT(Build().ordered_globals, ElementsAre(decl, use));
+ auto* use = AST().GlobalDeclarations()[0];
+ auto* decl = AST().GlobalDeclarations()[1];
+ EXPECT_THAT(Build().ordered_globals, ElementsAre(decl, use));
}
INSTANTIATE_TEST_SUITE_P(Types,
@@ -1097,11 +1066,10 @@
testing::Combine(testing::ValuesIn(kTypeDeclKinds),
testing::ValuesIn(kTypeUseKinds)));
-INSTANTIATE_TEST_SUITE_P(
- Values,
- ResolverDependencyGraphOrderedGlobalsTest,
- testing::Combine(testing::ValuesIn(kGlobalValueDeclKinds),
- testing::ValuesIn(kValueUseKinds)));
+INSTANTIATE_TEST_SUITE_P(Values,
+ ResolverDependencyGraphOrderedGlobalsTest,
+ testing::Combine(testing::ValuesIn(kGlobalValueDeclKinds),
+ testing::ValuesIn(kValueUseKinds)));
INSTANTIATE_TEST_SUITE_P(Functions,
ResolverDependencyGraphOrderedGlobalsTest,
@@ -1115,35 +1083,32 @@
namespace resolved_symbols {
using ResolverDependencyGraphResolvedSymbolTest =
- ResolverDependencyGraphTestWithParam<
- std::tuple<SymbolDeclKind, SymbolUseKind>>;
+ ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolUseKind>>;
TEST_P(ResolverDependencyGraphResolvedSymbolTest, Test) {
- const Symbol symbol = Sym("SYMBOL");
- const auto decl_kind = std::get<0>(GetParam());
- const auto use_kind = std::get<1>(GetParam());
+ const Symbol symbol = Sym("SYMBOL");
+ const auto decl_kind = std::get<0>(GetParam());
+ const auto use_kind = std::get<1>(GetParam());
- // Build a symbol declaration and a use of that symbol
- SymbolTestHelper helper(this);
- auto* decl = helper.Add(decl_kind, symbol, Source{{12, 34}});
- auto* use = helper.Add(use_kind, symbol, Source{{56, 78}});
- helper.Build();
+ // Build a symbol declaration and a use of that symbol
+ SymbolTestHelper helper(this);
+ auto* decl = helper.Add(decl_kind, symbol, Source{{12, 34}});
+ auto* use = helper.Add(use_kind, symbol, Source{{56, 78}});
+ helper.Build();
- // If the declaration is visible to the use, then we expect the analysis to
- // succeed.
- bool expect_pass = ScopeDepth(decl_kind) <= ScopeDepth(use_kind);
- auto graph =
- Build(expect_pass ? "" : "56:78 error: unknown identifier: 'SYMBOL'");
+ // If the declaration is visible to the use, then we expect the analysis to
+ // succeed.
+ bool expect_pass = ScopeDepth(decl_kind) <= ScopeDepth(use_kind);
+ auto graph = Build(expect_pass ? "" : "56:78 error: unknown identifier: 'SYMBOL'");
- if (expect_pass) {
- // Check that the use resolves to the declaration
- auto* resolved_symbol = graph.resolved_symbols[use];
- EXPECT_EQ(resolved_symbol, decl)
- << "resolved: "
- << (resolved_symbol ? resolved_symbol->TypeInfo().name : "<null>")
- << "\n"
- << "decl: " << decl->TypeInfo().name;
- }
+ if (expect_pass) {
+ // Check that the use resolves to the declaration
+ auto* resolved_symbol = graph.resolved_symbols[use];
+ EXPECT_EQ(resolved_symbol, decl)
+ << "resolved: " << (resolved_symbol ? resolved_symbol->TypeInfo().name : "<null>")
+ << "\n"
+ << "decl: " << decl->TypeInfo().name;
+ }
}
INSTANTIATE_TEST_SUITE_P(Types,
@@ -1168,28 +1133,26 @@
////////////////////////////////////////////////////////////////////////////////
namespace shadowing {
-using ResolverDependencyShadowTest = ResolverDependencyGraphTestWithParam<
- std::tuple<SymbolDeclKind, SymbolDeclKind>>;
+using ResolverDependencyShadowTest =
+ ResolverDependencyGraphTestWithParam<std::tuple<SymbolDeclKind, SymbolDeclKind>>;
TEST_P(ResolverDependencyShadowTest, Test) {
- const Symbol symbol = Sym("SYMBOL");
- const auto outer_kind = std::get<0>(GetParam());
- const auto inner_kind = std::get<1>(GetParam());
+ const Symbol symbol = Sym("SYMBOL");
+ const auto outer_kind = std::get<0>(GetParam());
+ const auto inner_kind = std::get<1>(GetParam());
- // Build a symbol declaration and a use of that symbol
- SymbolTestHelper helper(this);
- auto* outer = helper.Add(outer_kind, symbol, Source{{12, 34}});
- helper.Add(inner_kind, symbol, Source{{56, 78}});
- auto* inner_var =
- helper.nested_statements.size() ? helper.nested_statements[0]
- ->As<ast::VariableDeclStatement>()
- ->variable
- : helper.statements.size()
- ? helper.statements[0]->As<ast::VariableDeclStatement>()->variable
- : helper.parameters[0];
- helper.Build();
+ // Build a symbol declaration and a use of that symbol
+ SymbolTestHelper helper(this);
+ auto* outer = helper.Add(outer_kind, symbol, Source{{12, 34}});
+ helper.Add(inner_kind, symbol, Source{{56, 78}});
+ auto* inner_var = helper.nested_statements.size()
+ ? helper.nested_statements[0]->As<ast::VariableDeclStatement>()->variable
+ : helper.statements.size()
+ ? helper.statements[0]->As<ast::VariableDeclStatement>()->variable
+ : helper.parameters[0];
+ helper.Build();
- EXPECT_EQ(Build().shadows[inner_var], outer);
+ EXPECT_EQ(Build().shadows[inner_var], outer);
}
INSTANTIATE_TEST_SUITE_P(LocalShadowGlobal,
@@ -1197,14 +1160,13 @@
testing::Combine(testing::ValuesIn(kGlobalDeclKinds),
testing::ValuesIn(kLocalDeclKinds)));
-INSTANTIATE_TEST_SUITE_P(
- NestedLocalShadowLocal,
- ResolverDependencyShadowTest,
- testing::Combine(testing::Values(SymbolDeclKind::Parameter,
- SymbolDeclKind::LocalVar,
- SymbolDeclKind::LocalLet),
- testing::Values(SymbolDeclKind::NestedLocalVar,
- SymbolDeclKind::NestedLocalLet)));
+INSTANTIATE_TEST_SUITE_P(NestedLocalShadowLocal,
+ ResolverDependencyShadowTest,
+ testing::Combine(testing::Values(SymbolDeclKind::Parameter,
+ SymbolDeclKind::LocalVar,
+ SymbolDeclKind::LocalLet),
+ testing::Values(SymbolDeclKind::NestedLocalVar,
+ SymbolDeclKind::NestedLocalLet)));
} // namespace shadowing
@@ -1216,120 +1178,116 @@
using ResolverDependencyGraphTraversalTest = ResolverDependencyGraphTest;
TEST_F(ResolverDependencyGraphTraversalTest, SymbolsReached) {
- const auto value_sym = Sym("VALUE");
- const auto type_sym = Sym("TYPE");
- const auto func_sym = Sym("FUNC");
+ const auto value_sym = Sym("VALUE");
+ const auto type_sym = Sym("TYPE");
+ const auto func_sym = Sym("FUNC");
- const auto* value_decl =
- Global(value_sym, ty.i32(), ast::StorageClass::kPrivate);
- const auto* type_decl = Alias(type_sym, ty.i32());
- const auto* func_decl = Func(func_sym, {}, ty.void_(), {});
+ const auto* value_decl = Global(value_sym, ty.i32(), ast::StorageClass::kPrivate);
+ const auto* type_decl = Alias(type_sym, ty.i32());
+ const auto* func_decl = Func(func_sym, {}, ty.void_(), {});
- struct SymbolUse {
- const ast::Node* decl = nullptr;
- const ast::Node* use = nullptr;
- std::string where;
- };
+ struct SymbolUse {
+ const ast::Node* decl = nullptr;
+ const ast::Node* use = nullptr;
+ std::string where;
+ };
- std::vector<SymbolUse> symbol_uses;
+ std::vector<SymbolUse> symbol_uses;
- auto add_use = [&](const ast::Node* decl, auto* use, int line,
- const char* kind) {
- symbol_uses.emplace_back(SymbolUse{
- decl, use,
- std::string(__FILE__) + ":" + std::to_string(line) + ": " + kind});
- return use;
- };
+ auto add_use = [&](const ast::Node* decl, auto* use, int line, const char* kind) {
+ symbol_uses.emplace_back(
+ SymbolUse{decl, use, std::string(__FILE__) + ":" + std::to_string(line) + ": " + kind});
+ return use;
+ };
#define V add_use(value_decl, Expr(value_sym), __LINE__, "V()")
#define T add_use(type_decl, ty.type_name(type_sym), __LINE__, "T()")
#define F add_use(func_decl, Expr(func_sym), __LINE__, "F()")
- Alias(Sym(), T);
- Structure(Sym(), {Member(Sym(), T)});
- Global(Sym(), T, V);
- GlobalConst(Sym(), T, V);
- Func(Sym(), //
- {Param(Sym(), T)}, //
- T, // Return type
- {
- Decl(Var(Sym(), T, V)), //
- Decl(Let(Sym(), T, V)), //
- CallStmt(Call(F, V)), //
- Block( //
- Assign(V, V)), //
- If(V, //
- Block(Assign(V, V)), //
- If(V, //
- Block(Assign(V, V)))), //
- Ignore(Bitcast(T, V)), //
- For(Decl(Var(Sym(), T, V)), //
- Equal(V, V), //
- Assign(V, V), //
- Block( //
- Assign(V, V))), //
- Loop(Block(Assign(V, V)), //
- Block(Assign(V, V))), //
- Switch(V, //
- Case(Expr(1), //
- Block(Assign(V, V))), //
- Case(Expr(2), //
- Block(Fallthrough())), //
- DefaultCase(Block(Assign(V, V)))), //
- Return(V), //
- Break(), //
- Discard(), //
- }); //
- // Exercise type traversal
- Global(Sym(), ty.atomic(T));
- Global(Sym(), ty.bool_());
- Global(Sym(), ty.i32());
- Global(Sym(), ty.u32());
- Global(Sym(), ty.f32());
- Global(Sym(), ty.array(T, V, 4));
- Global(Sym(), ty.vec3(T));
- Global(Sym(), ty.mat3x2(T));
- Global(Sym(), ty.pointer(T, ast::StorageClass::kPrivate));
- Global(Sym(), ty.sampled_texture(ast::TextureDimension::k2d, T));
- Global(Sym(), ty.depth_texture(ast::TextureDimension::k2d));
- Global(Sym(), ty.depth_multisampled_texture(ast::TextureDimension::k2d));
- Global(Sym(), ty.external_texture());
- Global(Sym(), ty.multisampled_texture(ast::TextureDimension::k2d, T));
- Global(Sym(), ty.storage_texture(ast::TextureDimension::k2d,
- ast::TexelFormat::kR32Float,
- ast::Access::kRead)); //
- Global(Sym(), ty.sampler(ast::SamplerKind::kSampler));
- Func(Sym(), {}, ty.void_(), {});
+ Alias(Sym(), T);
+ Structure(Sym(), {Member(Sym(), T)});
+ Global(Sym(), T, V);
+ GlobalConst(Sym(), T, V);
+ Func(Sym(), //
+ {Param(Sym(), T)}, //
+ T, // Return type
+ {
+ Decl(Var(Sym(), T, V)), //
+ Decl(Let(Sym(), T, V)), //
+ CallStmt(Call(F, V)), //
+ Block( //
+ Assign(V, V)), //
+ If(V, //
+ Block(Assign(V, V)), //
+ If(V, //
+ Block(Assign(V, V)))), //
+ Ignore(Bitcast(T, V)), //
+ For(Decl(Var(Sym(), T, V)), //
+ Equal(V, V), //
+ Assign(V, V), //
+ Block( //
+ Assign(V, V))), //
+ Loop(Block(Assign(V, V)), //
+ Block(Assign(V, V))), //
+ Switch(V, //
+ Case(Expr(1), //
+ Block(Assign(V, V))), //
+ Case(Expr(2), //
+ Block(Fallthrough())), //
+ DefaultCase(Block(Assign(V, V)))), //
+ Return(V), //
+ Break(), //
+ Discard(), //
+ }); //
+ // Exercise type traversal
+ Global(Sym(), ty.atomic(T));
+ Global(Sym(), ty.bool_());
+ Global(Sym(), ty.i32());
+ Global(Sym(), ty.u32());
+ Global(Sym(), ty.f32());
+ Global(Sym(), ty.array(T, V, 4));
+ Global(Sym(), ty.vec3(T));
+ Global(Sym(), ty.mat3x2(T));
+ Global(Sym(), ty.pointer(T, ast::StorageClass::kPrivate));
+ Global(Sym(), ty.sampled_texture(ast::TextureDimension::k2d, T));
+ Global(Sym(), ty.depth_texture(ast::TextureDimension::k2d));
+ Global(Sym(), ty.depth_multisampled_texture(ast::TextureDimension::k2d));
+ Global(Sym(), ty.external_texture());
+ Global(Sym(), ty.multisampled_texture(ast::TextureDimension::k2d, T));
+ Global(Sym(), ty.storage_texture(ast::TextureDimension::k2d, ast::TexelFormat::kR32Float,
+ ast::Access::kRead)); //
+ Global(Sym(), ty.sampler(ast::SamplerKind::kSampler));
+ Func(Sym(), {}, ty.void_(), {});
#undef V
#undef T
#undef F
- auto graph = Build();
- for (auto use : symbol_uses) {
- auto* resolved_symbol = graph.resolved_symbols[use.use];
- EXPECT_EQ(resolved_symbol, use.decl) << use.where;
- }
+ auto graph = Build();
+ for (auto use : symbol_uses) {
+ auto* resolved_symbol = graph.resolved_symbols[use.use];
+ EXPECT_EQ(resolved_symbol, use.decl) << use.where;
+ }
}
TEST_F(ResolverDependencyGraphTraversalTest, InferredType) {
- // Check that the nullptr of the var / let type doesn't make things explode
- Global("a", nullptr, Expr(1));
- GlobalConst("b", nullptr, Expr(1));
- WrapInFunction(Var("c", nullptr, Expr(1)), //
- Let("d", nullptr, Expr(1)));
- Build();
+ // Check that the nullptr of the var / let type doesn't make things explode
+ Global("a", nullptr, Expr(1));
+ GlobalConst("b", nullptr, Expr(1));
+ WrapInFunction(Var("c", nullptr, Expr(1)), //
+ Let("d", nullptr, Expr(1)));
+ Build();
}
// Reproduces an unbalanced stack push / pop bug in
// DependencyAnalysis::SortGlobals(), found by clusterfuzz.
// See: crbug.com/chromium/1273451
TEST_F(ResolverDependencyGraphTraversalTest, chromium_1273451) {
- Structure("A", {Member("a", ty.i32())});
- Structure("B", {Member("b", ty.i32())});
- Func("f", {Param("a", ty.type_name("A"))}, ty.type_name("B"),
- {
- Return(Construct(ty.type_name("B"))),
- });
- Build();
+ Structure("A", {Member("a", ty.i32())});
+ Structure("B", {Member("b", ty.i32())});
+ Func("f", {Param("a", ty.type_name("A"))}, ty.type_name("B"),
+ {
+ Return(Construct(ty.type_name("B"))),
+ });
+ Build();
}
} // namespace ast_traversal
diff --git a/src/tint/resolver/entry_point_validation_test.cc b/src/tint/resolver/entry_point_validation_test.cc
index 8458a10..5a10bf2 100644
--- a/src/tint/resolver/entry_point_validation_test.cc
+++ b/src/tint/resolver/entry_point_validation_test.cc
@@ -45,307 +45,277 @@
using i32 = builder::i32;
using u32 = builder::u32;
-class ResolverEntryPointValidationTest : public TestHelper,
- public testing::Test {};
+class ResolverEntryPointValidationTest : public TestHelper, public testing::Test {};
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Location) {
- // @stage(fragment)
- // fn main() -> @location(0) f32 { return 1.0; }
- Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(1.0f)},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ // @stage(fragment)
+ // fn main() -> @location(0) f32 { return 1.0; }
+ Func(Source{{12, 34}}, "main", {}, ty.f32(), {Return(1.0f)},
+ {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Builtin) {
- // @stage(vertex)
- // fn main() -> @builtin(position) vec4<f32> { return vec4<f32>(); }
- Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kVertex)},
- {Builtin(ast::Builtin::kPosition)});
+ // @stage(vertex)
+ // fn main() -> @builtin(position) vec4<f32> { return vec4<f32>(); }
+ Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::Builtin::kPosition)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Missing) {
- // @stage(vertex)
- // fn main() -> f32 {
- // return 1.0;
- // }
- Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kVertex)});
+ // @stage(vertex)
+ // fn main() -> f32 {
+ // return 1.0;
+ // }
+ Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kVertex)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: missing entry point IO attribute on return type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing entry point IO attribute on return type");
}
TEST_F(ResolverEntryPointValidationTest, ReturnTypeAttribute_Multiple) {
- // @stage(vertex)
- // fn main() -> @location(0) @builtin(position) vec4<f32> {
- // return vec4<f32>();
- // }
- Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kVertex)},
- {Location(Source{{13, 43}}, 0),
- Builtin(Source{{14, 52}}, ast::Builtin::kPosition)});
+ // @stage(vertex)
+ // fn main() -> @location(0) @builtin(position) vec4<f32> {
+ // return vec4<f32>();
+ // }
+ Func(Source{{12, 34}}, "main", {}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kVertex)},
+ {Location(Source{{13, 43}}, 0), Builtin(Source{{14, 52}}, ast::Builtin::kPosition)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0))");
}
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_Valid) {
- // struct Output {
- // @location(0) a : f32;
- // @builtin(frag_depth) b : f32;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto* output = Structure(
- "Output", {Member("a", ty.f32(), {Location(0)}),
- Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Output {
+ // @location(0) a : f32;
+ // @builtin(frag_depth) b : f32;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto* output =
+ Structure("Output", {Member("a", ty.f32(), {Location(0)}),
+ Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverEntryPointValidationTest,
- ReturnType_Struct_MemberMultipleAttributes) {
- // struct Output {
- // @location(0) @builtin(frag_depth) a : f32;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto* output = Structure(
- "Output",
- {Member("a", ty.f32(),
- {Location(Source{{13, 43}}, 0),
- Builtin(Source{{14, 52}}, ast::Builtin::kFragDepth)})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_MemberMultipleAttributes) {
+ // struct Output {
+ // @location(0) @builtin(frag_depth) a : f32;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto* output =
+ Structure("Output", {Member("a", ty.f32(),
+ {Location(Source{{13, 43}}, 0),
+ Builtin(Source{{14, 52}}, ast::Builtin::kFragDepth)})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0)
12:34 note: while analysing entry point 'main')");
}
-TEST_F(ResolverEntryPointValidationTest,
- ReturnType_Struct_MemberMissingAttribute) {
- // struct Output {
- // @location(0) a : f32;
- // b : f32;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto* output = Structure(
- "Output", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
- Member(Source{{14, 52}}, "b", ty.f32(), {})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_MemberMissingAttribute) {
+ // struct Output {
+ // @location(0) a : f32;
+ // b : f32;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto* output = Structure("Output", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
+ Member(Source{{14, 52}}, "b", ty.f32(), {})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(14:52 error: missing entry point IO attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(14:52 error: missing entry point IO attribute
12:34 note: while analysing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, ReturnType_Struct_DuplicateBuiltins) {
- // struct Output {
- // @builtin(frag_depth) a : f32;
- // @builtin(frag_depth) b : f32;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto* output = Structure(
- "Output", {Member("a", ty.f32(), {Builtin(ast::Builtin::kFragDepth)}),
- Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Output {
+ // @builtin(frag_depth) a : f32;
+ // @builtin(frag_depth) b : f32;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto* output =
+ Structure("Output", {Member("a", ty.f32(), {Builtin(ast::Builtin::kFragDepth)}),
+ Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: builtin(frag_depth) attribute appears multiple times as pipeline output
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: builtin(frag_depth) attribute appears multiple times as pipeline output
12:34 note: while analysing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Location) {
- // @stage(fragment)
- // fn main(@location(0) param : f32) {}
- auto* param = Param("param", ty.f32(), {Location(0)});
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // @stage(fragment)
+ // fn main(@location(0) param : f32) {}
+ auto* param = Param("param", ty.f32(), {Location(0)});
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Missing) {
- // @stage(fragment)
- // fn main(param : f32) {}
- auto* param = Param(Source{{13, 43}}, "param", ty.vec4<f32>());
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // @stage(fragment)
+ // fn main(param : f32) {}
+ auto* param = Param(Source{{13, 43}}, "param", ty.vec4<f32>());
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "13:43 error: missing entry point IO attribute on parameter");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "13:43 error: missing entry point IO attribute on parameter");
}
TEST_F(ResolverEntryPointValidationTest, ParameterAttribute_Multiple) {
- // @stage(fragment)
- // fn main(@location(0) @builtin(sample_index) param : u32) {}
- auto* param = Param("param", ty.u32(),
- {Location(Source{{13, 43}}, 0),
- Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)});
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // @stage(fragment)
+ // fn main(@location(0) @builtin(sample_index) param : u32) {}
+ auto* param = Param(
+ "param", ty.u32(),
+ {Location(Source{{13, 43}}, 0), Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)});
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0))");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_Valid) {
- // struct Input {
- // @location(0) a : f32;
- // @builtin(sample_index) b : u32;
- // };
- // @stage(fragment)
- // fn main(param : Input) {}
- auto* input = Structure(
- "Input", {Member("a", ty.f32(), {Location(0)}),
- Member("b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
- auto* param = Param("param", ty.Of(input));
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Input {
+ // @location(0) a : f32;
+ // @builtin(sample_index) b : u32;
+ // };
+ // @stage(fragment)
+ // fn main(param : Input) {}
+ auto* input =
+ Structure("Input", {Member("a", ty.f32(), {Location(0)}),
+ Member("b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
+ auto* param = Param("param", ty.Of(input));
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverEntryPointValidationTest,
- Parameter_Struct_MemberMultipleAttributes) {
- // struct Input {
- // @location(0) @builtin(sample_index) a : u32;
- // };
- // @stage(fragment)
- // fn main(param : Input) {}
- auto* input = Structure(
- "Input",
- {Member("a", ty.u32(),
- {Location(Source{{13, 43}}, 0),
- Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
- auto* param = Param("param", ty.Of(input));
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_MemberMultipleAttributes) {
+ // struct Input {
+ // @location(0) @builtin(sample_index) a : u32;
+ // };
+ // @stage(fragment)
+ // fn main(param : Input) {}
+ auto* input =
+ Structure("Input", {Member("a", ty.u32(),
+ {Location(Source{{13, 43}}, 0),
+ Builtin(Source{{14, 52}}, ast::Builtin::kSampleIndex)})});
+ auto* param = Param("param", ty.Of(input));
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(14:52 error: multiple entry point IO attributes
13:43 note: previously consumed location(0)
12:34 note: while analysing entry point 'main')");
}
-TEST_F(ResolverEntryPointValidationTest,
- Parameter_Struct_MemberMissingAttribute) {
- // struct Input {
- // @location(0) a : f32;
- // b : f32;
- // };
- // @stage(fragment)
- // fn main(param : Input) {}
- auto* input = Structure(
- "Input", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
- Member(Source{{14, 52}}, "b", ty.f32(), {})});
- auto* param = Param("param", ty.Of(input));
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_MemberMissingAttribute) {
+ // struct Input {
+ // @location(0) a : f32;
+ // b : f32;
+ // };
+ // @stage(fragment)
+ // fn main(param : Input) {}
+ auto* input = Structure("Input", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)}),
+ Member(Source{{14, 52}}, "b", ty.f32(), {})});
+ auto* param = Param("param", ty.Of(input));
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(14:52 error: missing entry point IO attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(14:52 error: missing entry point IO attribute
12:34 note: while analysing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_DuplicateBuiltins) {
- // @stage(fragment)
- // fn main(@builtin(sample_index) param_a : u32,
- // @builtin(sample_index) param_b : u32) {}
- auto* param_a =
- Param("param_a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
- auto* param_b =
- Param("param_b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
- Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // @stage(fragment)
+ // fn main(@builtin(sample_index) param_a : u32,
+ // @builtin(sample_index) param_b : u32) {}
+ auto* param_a = Param("param_a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
+ auto* param_b = Param("param_b", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)});
+ Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: builtin(sample_index) attribute appears multiple times as "
- "pipeline input");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: builtin(sample_index) attribute appears multiple times as "
+ "pipeline input");
}
TEST_F(ResolverEntryPointValidationTest, Parameter_Struct_DuplicateBuiltins) {
- // struct InputA {
- // @builtin(sample_index) a : u32;
- // };
- // struct InputB {
- // @builtin(sample_index) a : u32;
- // };
- // @stage(fragment)
- // fn main(param_a : InputA, param_b : InputB) {}
- auto* input_a = Structure(
- "InputA", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
- auto* input_b = Structure(
- "InputB", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
- auto* param_a = Param("param_a", ty.Of(input_a));
- auto* param_b = Param("param_b", ty.Of(input_b));
- Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct InputA {
+ // @builtin(sample_index) a : u32;
+ // };
+ // struct InputB {
+ // @builtin(sample_index) a : u32;
+ // };
+ // @stage(fragment)
+ // fn main(param_a : InputA, param_b : InputB) {}
+ auto* input_a =
+ Structure("InputA", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
+ auto* input_b =
+ Structure("InputB", {Member("a", ty.u32(), {Builtin(ast::Builtin::kSampleIndex)})});
+ auto* param_a = Param("param_a", ty.Of(input_a));
+ auto* param_b = Param("param_b", ty.Of(input_b));
+ Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: builtin(sample_index) attribute appears multiple times as pipeline input
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: builtin(sample_index) attribute appears multiple times as pipeline input
12:34 note: while analysing entry point 'main')");
}
TEST_F(ResolverEntryPointValidationTest, VertexShaderMustReturnPosition) {
- // @stage(vertex)
- // fn main() {}
- Func(Source{{12, 34}}, "main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kVertex)});
+ // @stage(vertex)
+ // fn main() {}
+ Func(Source{{12, 34}}, "main", {}, ty.void_(), {}, {Stage(ast::PipelineStage::kVertex)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: a vertex shader must include the 'position' builtin "
- "in its return type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: a vertex shader must include the 'position' builtin "
+ "in its return type");
}
namespace TypeValidationTests {
struct Params {
- builder::ast_type_func_ptr create_ast_type;
- bool is_valid;
+ builder::ast_type_func_ptr create_ast_type;
+ bool is_valid;
};
template <typename T>
constexpr Params ParamsFor(bool is_valid) {
- return Params{DataType<T>::AST, is_valid};
+ return Params{DataType<T>::AST, is_valid};
}
using TypeValidationTest = resolver::ResolverTestWithParam<Params>;
@@ -368,77 +338,73 @@
};
TEST_P(TypeValidationTest, BareInputs) {
- // @stage(fragment)
- // fn main(@location(0) @interpolate(flat) a : *) {}
- auto params = GetParam();
- auto* a = Param("a", params.create_ast_type(*this), {Location(0), Flat()});
- Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // @stage(fragment)
+ // fn main(@location(0) @interpolate(flat) a : *) {}
+ auto params = GetParam();
+ auto* a = Param("a", params.create_ast_type(*this), {Location(0), Flat()});
+ Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ }
}
TEST_P(TypeValidationTest, StructInputs) {
- // struct Input {
- // @location(0) @interpolate(flat) a : *;
- // };
- // @stage(fragment)
- // fn main(a : Input) {}
- auto params = GetParam();
- auto* input = Structure("Input", {Member("a", params.create_ast_type(*this),
- {Location(0), Flat()})});
- auto* a = Param("a", ty.Of(input), {});
- Func(Source{{12, 34}}, "main", {a}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Input {
+ // @location(0) @interpolate(flat) a : *;
+ // };
+ // @stage(fragment)
+ // fn main(a : Input) {}
+ auto params = GetParam();
+ auto* input =
+ Structure("Input", {Member("a", params.create_ast_type(*this), {Location(0), Flat()})});
+ auto* a = Param("a", ty.Of(input), {});
+ Func(Source{{12, 34}}, "main", {a}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ }
}
TEST_P(TypeValidationTest, BareOutputs) {
- // @stage(fragment)
- // fn main() -> @location(0) * {
- // return *();
- // }
- auto params = GetParam();
- Func(Source{{12, 34}}, "main", {}, params.create_ast_type(*this),
- {Return(Construct(params.create_ast_type(*this)))},
- {Stage(ast::PipelineStage::kFragment)}, {Location(0)});
+ // @stage(fragment)
+ // fn main() -> @location(0) * {
+ // return *();
+ // }
+ auto params = GetParam();
+ Func(Source{{12, 34}}, "main", {}, params.create_ast_type(*this),
+ {Return(Construct(params.create_ast_type(*this)))}, {Stage(ast::PipelineStage::kFragment)},
+ {Location(0)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ }
}
TEST_P(TypeValidationTest, StructOutputs) {
- // struct Output {
- // @location(0) a : *;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto params = GetParam();
- auto* output = Structure(
- "Output", {Member("a", params.create_ast_type(*this), {Location(0)})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Output {
+ // @location(0) a : *;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto params = GetParam();
+ auto* output = Structure("Output", {Member("a", params.create_ast_type(*this), {Location(0)})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverEntryPointValidationTest,
TypeValidationTest,
@@ -451,341 +417,317 @@
using LocationAttributeTests = ResolverTest;
TEST_F(LocationAttributeTests, Pass) {
- // @stage(fragment)
- // fn frag_main(@location(0) @interpolate(flat) a: i32) {}
+ // @stage(fragment)
+ // fn frag_main(@location(0) @interpolate(flat) a: i32) {}
- auto* p = Param(Source{{12, 34}}, "a", ty.i32(), {Location(0), Flat()});
- Func("frag_main", {p}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ auto* p = Param(Source{{12, 34}}, "a", ty.i32(), {Location(0), Flat()});
+ Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(LocationAttributeTests, BadType_Input_bool) {
- // @stage(fragment)
- // fn frag_main(@location(0) a: bool) {}
+ // @stage(fragment)
+ // fn frag_main(@location(0) a: bool) {}
- auto* p =
- Param(Source{{12, 34}}, "a", ty.bool_(), {Location(Source{{34, 56}}, 0)});
- Func("frag_main", {p}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ auto* p = Param(Source{{12, 34}}, "a", ty.bool_(), {Location(Source{{34, 56}}, 0)});
+ Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot apply 'location' attribute to declaration of "
- "type 'bool'\n"
- "34:56 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot apply 'location' attribute to declaration of "
+ "type 'bool'\n"
+ "34:56 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, BadType_Output_Array) {
- // @stage(fragment)
- // fn frag_main()->@location(0) array<f32, 2> { return array<f32, 2>(); }
+ // @stage(fragment)
+ // fn frag_main()->@location(0) array<f32, 2> { return array<f32, 2>(); }
- Func(Source{{12, 34}}, "frag_main", {}, ty.array<f32, 2>(),
- {Return(Construct(ty.array<f32, 2>()))},
- {Stage(ast::PipelineStage::kFragment)}, {Location(Source{{34, 56}}, 0)});
+ Func(Source{{12, 34}}, "frag_main", {}, ty.array<f32, 2>(),
+ {Return(Construct(ty.array<f32, 2>()))}, {Stage(ast::PipelineStage::kFragment)},
+ {Location(Source{{34, 56}}, 0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot apply 'location' attribute to declaration of "
- "type 'array<f32, 2>'\n"
- "34:56 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot apply 'location' attribute to declaration of "
+ "type 'array<f32, 2>'\n"
+ "34:56 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct) {
- // struct Input {
- // a : f32;
- // };
- // @stage(fragment)
- // fn main(@location(0) param : Input) {}
- auto* input = Structure("Input", {Member("a", ty.f32())});
- auto* param = Param(Source{{12, 34}}, "param", ty.Of(input),
- {Location(Source{{13, 43}}, 0)});
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Input {
+ // a : f32;
+ // };
+ // @stage(fragment)
+ // fn main(@location(0) param : Input) {}
+ auto* input = Structure("Input", {Member("a", ty.f32())});
+ auto* param = Param(Source{{12, 34}}, "param", ty.Of(input), {Location(Source{{13, 43}}, 0)});
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot apply 'location' attribute to declaration of "
- "type 'Input'\n"
- "13:43 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot apply 'location' attribute to declaration of "
+ "type 'Input'\n"
+ "13:43 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct_NestedStruct) {
- // struct Inner {
- // @location(0) b : f32;
- // };
- // struct Input {
- // a : Inner;
- // };
- // @stage(fragment)
- // fn main(param : Input) {}
- auto* inner = Structure(
- "Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
- auto* input =
- Structure("Input", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
- auto* param = Param("param", ty.Of(input));
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Inner {
+ // @location(0) b : f32;
+ // };
+ // struct Input {
+ // a : Inner;
+ // };
+ // @stage(fragment)
+ // fn main(param : Input) {}
+ auto* inner = Structure("Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
+ auto* input = Structure("Input", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
+ auto* param = Param("param", ty.Of(input));
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "14:52 error: nested structures cannot be used for entry point IO\n"
- "12:34 note: while analysing entry point 'main'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "14:52 error: nested structures cannot be used for entry point IO\n"
+ "12:34 note: while analysing entry point 'main'");
}
TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
- // struct Input {
- // @location(0) a : array<f32>;
- // };
- // @stage(fragment)
- // fn main(param : Input) {}
- auto* input = Structure("Input", {Member(Source{{13, 43}}, "a",
- ty.array<float>(), {Location(0)})});
- auto* param = Param("param", ty.Of(input));
- Func(Source{{12, 34}}, "main", {param}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Input {
+ // @location(0) a : array<f32>;
+ // };
+ // @stage(fragment)
+ // fn main(param : Input) {}
+ auto* input =
+ Structure("Input", {Member(Source{{13, 43}}, "a", ty.array<float>(), {Location(0)})});
+ auto* param = Param("param", ty.Of(input));
+ Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "13:43 error: cannot apply 'location' attribute to declaration of "
- "type 'array<f32>'\n"
- "note: 'location' attribute must only be applied to declarations "
- "of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "13:43 error: cannot apply 'location' attribute to declaration of "
+ "type 'array<f32>'\n"
+ "note: 'location' attribute must only be applied to declarations "
+ "of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, BadMemberType_Input) {
- // struct S { @location(0) m: array<i32>; };
- // @stage(fragment)
- // fn frag_main( a: S) {}
+ // struct S { @location(0) m: array<i32>; };
+ // @stage(fragment)
+ // fn frag_main( a: S) {}
- auto* m = Member(Source{{34, 56}}, "m", ty.array<i32>(),
- ast::AttributeList{Location(Source{{12, 34}}, 0u)});
- auto* s = Structure("S", {m});
- auto* p = Param("a", ty.Of(s));
+ auto* m = Member(Source{{34, 56}}, "m", ty.array<i32>(),
+ ast::AttributeList{Location(Source{{12, 34}}, 0u)});
+ auto* s = Structure("S", {m});
+ auto* p = Param("a", ty.Of(s));
- Func("frag_main", {p}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ Func("frag_main", {p}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "34:56 error: cannot apply 'location' attribute to declaration of "
- "type 'array<i32>'\n"
- "12:34 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "34:56 error: cannot apply 'location' attribute to declaration of "
+ "type 'array<i32>'\n"
+ "12:34 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, BadMemberType_Output) {
- // struct S { @location(0) m: atomic<i32>; };
- // @stage(fragment)
- // fn frag_main() -> S {}
- auto* m = Member(Source{{34, 56}}, "m", ty.atomic<i32>(),
- ast::AttributeList{Location(Source{{12, 34}}, 0u)});
- auto* s = Structure("S", {m});
+ // struct S { @location(0) m: atomic<i32>; };
+ // @stage(fragment)
+ // fn frag_main() -> S {}
+ auto* m = Member(Source{{34, 56}}, "m", ty.atomic<i32>(),
+ ast::AttributeList{Location(Source{{12, 34}}, 0u)});
+ auto* s = Structure("S", {m});
- Func("frag_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
- {Stage(ast::PipelineStage::kFragment)}, {});
+ Func("frag_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
+ {Stage(ast::PipelineStage::kFragment)}, {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "34:56 error: cannot apply 'location' attribute to declaration of "
- "type 'atomic<i32>'\n"
- "12:34 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "34:56 error: cannot apply 'location' attribute to declaration of "
+ "type 'atomic<i32>'\n"
+ "12:34 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, BadMemberType_Unused) {
- // struct S { @location(0) m: mat3x2<f32>; };
+ // struct S { @location(0) m: mat3x2<f32>; };
- auto* m = Member(Source{{34, 56}}, "m", ty.mat3x2<f32>(),
- ast::AttributeList{Location(Source{{12, 34}}, 0u)});
- Structure("S", {m});
+ auto* m = Member(Source{{34, 56}}, "m", ty.mat3x2<f32>(),
+ ast::AttributeList{Location(Source{{12, 34}}, 0u)});
+ Structure("S", {m});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "34:56 error: cannot apply 'location' attribute to declaration of "
- "type 'mat3x2<f32>'\n"
- "12:34 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "34:56 error: cannot apply 'location' attribute to declaration of "
+ "type 'mat3x2<f32>'\n"
+ "12:34 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_Valid) {
- // struct Output {
- // @location(0) a : f32;
- // @builtin(frag_depth) b : f32;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto* output = Structure(
- "Output", {Member("a", ty.f32(), {Location(0)}),
- Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Output {
+ // @location(0) a : f32;
+ // @builtin(frag_depth) b : f32;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto* output =
+ Structure("Output", {Member("a", ty.f32(), {Location(0)}),
+ Member("b", ty.f32(), {Builtin(ast::Builtin::kFragDepth)})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(LocationAttributeTests, ReturnType_Struct) {
- // struct Output {
- // a : f32;
- // };
- // @stage(vertex)
- // fn main() -> @location(0) Output {
- // return Output();
- // }
- auto* output = Structure("Output", {Member("a", ty.f32())});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))}, {Stage(ast::PipelineStage::kVertex)},
- {Location(Source{{13, 43}}, 0)});
+ // struct Output {
+ // a : f32;
+ // };
+ // @stage(vertex)
+ // fn main() -> @location(0) Output {
+ // return Output();
+ // }
+ auto* output = Structure("Output", {Member("a", ty.f32())});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kVertex)}, {Location(Source{{13, 43}}, 0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot apply 'location' attribute to declaration of "
- "type 'Output'\n"
- "13:43 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot apply 'location' attribute to declaration of "
+ "type 'Output'\n"
+ "13:43 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_NestedStruct) {
- // struct Inner {
- // @location(0) b : f32;
- // };
- // struct Output {
- // a : Inner;
- // };
- // @stage(fragment)
- // fn main() -> Output { return Output(); }
- auto* inner = Structure(
- "Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
- auto* output =
- Structure("Output", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Inner {
+ // @location(0) b : f32;
+ // };
+ // struct Output {
+ // a : Inner;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output { return Output(); }
+ auto* inner = Structure("Inner", {Member(Source{{13, 43}}, "a", ty.f32(), {Location(0)})});
+ auto* output = Structure("Output", {Member(Source{{14, 52}}, "a", ty.Of(inner))});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "14:52 error: nested structures cannot be used for entry point IO\n"
- "12:34 note: while analysing entry point 'main'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "14:52 error: nested structures cannot be used for entry point IO\n"
+ "12:34 note: while analysing entry point 'main'");
}
TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
- // struct Output {
- // @location(0) a : array<f32>;
- // };
- // @stage(fragment)
- // fn main() -> Output {
- // return Output();
- // }
- auto* output =
- Structure("Output", {Member(Source{{13, 43}}, "a", ty.array<float>(),
- {Location(Source{{12, 34}}, 0)})});
- Func(Source{{12, 34}}, "main", {}, ty.Of(output),
- {Return(Construct(ty.Of(output)))},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct Output {
+ // @location(0) a : array<f32>;
+ // };
+ // @stage(fragment)
+ // fn main() -> Output {
+ // return Output();
+ // }
+ auto* output = Structure("Output", {Member(Source{{13, 43}}, "a", ty.array<float>(),
+ {Location(Source{{12, 34}}, 0)})});
+ Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "13:43 error: cannot apply 'location' attribute to declaration of "
- "type 'array<f32>'\n"
- "12:34 note: 'location' attribute must only be applied to "
- "declarations of numeric scalar or numeric vector type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "13:43 error: cannot apply 'location' attribute to declaration of "
+ "type 'array<f32>'\n"
+ "12:34 note: 'location' attribute must only be applied to "
+ "declarations of numeric scalar or numeric vector type");
}
TEST_F(LocationAttributeTests, ComputeShaderLocation_Input) {
- Func("main", {}, ty.i32(), {Return(Expr(1))},
- {Stage(ast::PipelineStage::kCompute),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))},
- ast::AttributeList{Location(Source{{12, 34}}, 1)});
+ Func("main", {}, ty.i32(), {Return(Expr(1))},
+ {Stage(ast::PipelineStage::kCompute),
+ create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))},
+ ast::AttributeList{Location(Source{{12, 34}}, 1)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for compute shader output");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader output");
}
TEST_F(LocationAttributeTests, ComputeShaderLocation_Output) {
- auto* input = Param("input", ty.i32(),
- ast::AttributeList{Location(Source{{12, 34}}, 0u)});
- Func("main", {input}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
+ auto* input = Param("input", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
+ Func("main", {input}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for compute shader inputs");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attribute is not valid for compute shader inputs");
}
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Output) {
- auto* m =
- Member("m", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
- auto* s = Structure("S", {m});
- Func(Source{{56, 78}}, "main", {}, ty.Of(s),
- ast::StatementList{Return(Expr(Construct(ty.Of(s))))},
- {Stage(ast::PipelineStage::kCompute),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
+ auto* m = Member("m", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
+ auto* s = Structure("S", {m});
+ Func(Source{{56, 78}}, "main", {}, ty.Of(s),
+ ast::StatementList{Return(Expr(Construct(ty.Of(s))))},
+ {Stage(ast::PipelineStage::kCompute),
+ create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for compute shader output\n"
- "56:78 note: while analysing entry point 'main'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for compute shader output\n"
+ "56:78 note: while analysing entry point 'main'");
}
TEST_F(LocationAttributeTests, ComputeShaderLocationStructMember_Input) {
- auto* m =
- Member("m", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
- auto* s = Structure("S", {m});
- auto* input = Param("input", ty.Of(s));
- Func(Source{{56, 78}}, "main", {input}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
+ auto* m = Member("m", ty.i32(), ast::AttributeList{Location(Source{{12, 34}}, 0u)});
+ auto* s = Structure("S", {m});
+ auto* input = Param("input", ty.Of(s));
+ Func(Source{{56, 78}}, "main", {input}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ create<ast::WorkgroupAttribute>(Source{{12, 34}}, Expr(1))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: attribute is not valid for compute shader inputs\n"
- "56:78 note: while analysing entry point 'main'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: attribute is not valid for compute shader inputs\n"
+ "56:78 note: while analysing entry point 'main'");
}
TEST_F(LocationAttributeTests, Duplicate_input) {
- // @stage(fragment)
- // fn main(@location(1) param_a : f32,
- // @location(1) param_b : f32) {}
- auto* param_a = Param("param_a", ty.f32(), {Location(1)});
- auto* param_b = Param("param_b", ty.f32(), {Location(Source{{12, 34}}, 1)});
- Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // @stage(fragment)
+ // fn main(@location(1) param_a : f32,
+ // @location(1) param_b : f32) {}
+ auto* param_a = Param("param_a", ty.f32(), {Location(1)});
+ auto* param_b = Param("param_b", ty.f32(), {Location(Source{{12, 34}}, 1)});
+ Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: location(1) attribute appears multiple times");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: location(1) attribute appears multiple times");
}
TEST_F(LocationAttributeTests, Duplicate_struct) {
- // struct InputA {
- // @location(1) a : f32;
- // };
- // struct InputB {
- // @location(1) a : f32;
- // };
- // @stage(fragment)
- // fn main(param_a : InputA, param_b : InputB) {}
- auto* input_a = Structure("InputA", {Member("a", ty.f32(), {Location(1)})});
- auto* input_b = Structure(
- "InputB", {Member("a", ty.f32(), {Location(Source{{34, 56}}, 1)})});
- auto* param_a = Param("param_a", ty.Of(input_a));
- auto* param_b = Param("param_b", ty.Of(input_b));
- Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ // struct InputA {
+ // @location(1) a : f32;
+ // };
+ // struct InputB {
+ // @location(1) a : f32;
+ // };
+ // @stage(fragment)
+ // fn main(param_a : InputA, param_b : InputB) {}
+ auto* input_a = Structure("InputA", {Member("a", ty.f32(), {Location(1)})});
+ auto* input_b = Structure("InputB", {Member("a", ty.f32(), {Location(Source{{34, 56}}, 1)})});
+ auto* param_a = Param("param_a", ty.Of(input_a));
+ auto* param_b = Param("param_b", ty.Of(input_b));
+ Func(Source{{12, 34}}, "main", {param_a, param_b}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "34:56 error: location(1) attribute appears multiple times\n"
- "12:34 note: while analysing entry point 'main'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "34:56 error: location(1) attribute appears multiple times\n"
+ "12:34 note: while analysing entry point 'main'");
}
} // namespace
diff --git a/src/tint/resolver/function_validation_test.cc b/src/tint/resolver/function_validation_test.cc
index 4583439..0df7d3f 100644
--- a/src/tint/resolver/function_validation_test.cc
+++ b/src/tint/resolver/function_validation_test.cc
@@ -23,808 +23,765 @@
namespace tint::resolver {
namespace {
-class ResolverFunctionValidationTest : public TestHelper,
- public testing::Test {};
+class ResolverFunctionValidationTest : public TestHelper, public testing::Test {};
TEST_F(ResolverFunctionValidationTest, DuplicateParameterName) {
- // fn func_a(common_name : f32) { }
- // fn func_b(common_name : f32) { }
- Func("func_a", {Param("common_name", ty.f32())}, ty.void_(), {});
- Func("func_b", {Param("common_name", ty.f32())}, ty.void_(), {});
+ // fn func_a(common_name : f32) { }
+ // fn func_b(common_name : f32) { }
+ Func("func_a", {Param("common_name", ty.f32())}, ty.void_(), {});
+ Func("func_b", {Param("common_name", ty.f32())}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, ParameterMayShadowGlobal) {
- // var<private> common_name : f32;
- // fn func(common_name : f32) { }
- Global("common_name", ty.f32(), ast::StorageClass::kPrivate);
- Func("func", {Param("common_name", ty.f32())}, ty.void_(), {});
+ // var<private> common_name : f32;
+ // fn func(common_name : f32) { }
+ Global("common_name", ty.f32(), ast::StorageClass::kPrivate);
+ Func("func", {Param("common_name", ty.f32())}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, LocalConflictsWithParameter) {
- // fn func(common_name : f32) {
- // let common_name = 1;
- // }
- Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
- {Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1)))});
+ // fn func(common_name : f32) {
+ // let common_name = 1;
+ // }
+ Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
+ {Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1)))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: redeclaration of 'common_name'
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(56:78 error: redeclaration of 'common_name'
12:34 note: 'common_name' previously declared here)");
}
TEST_F(ResolverFunctionValidationTest, NestedLocalMayShadowParameter) {
- // fn func(common_name : f32) {
- // {
- // let common_name = 1;
- // }
- // }
- Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
- {Block(Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1))))});
+ // fn func(common_name : f32) {
+ // {
+ // let common_name = 1;
+ // }
+ // }
+ Func("func", {Param(Source{{12, 34}}, "common_name", ty.f32())}, ty.void_(),
+ {Block(Decl(Let(Source{{56, 78}}, "common_name", nullptr, Expr(1))))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverFunctionValidationTest,
- VoidFunctionEndWithoutReturnStatement_Pass) {
- // fn func { var a:i32 = 2; }
- auto* var = Var("a", ty.i32(), Expr(2));
+TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatement_Pass) {
+ // fn func { var a:i32 = 2; }
+ auto* var = Var("a", ty.i32(), Expr(2));
- Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Decl(var),
- });
+ Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Decl(var),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, FunctionUsingSameVariableName_Pass) {
- // fn func() -> i32 {
- // var func:i32 = 0;
- // return func;
- // }
+ // fn func() -> i32 {
+ // var func:i32 = 0;
+ // return func;
+ // }
- auto* var = Var("func", ty.i32(), Expr(0));
- Func("func", ast::VariableList{}, ty.i32(),
- ast::StatementList{
- Decl(var),
- Return(Source{{12, 34}}, Expr("func")),
- },
- ast::AttributeList{});
+ auto* var = Var("func", ty.i32(), Expr(0));
+ Func("func", ast::VariableList{}, ty.i32(),
+ ast::StatementList{
+ Decl(var),
+ Return(Source{{12, 34}}, Expr("func")),
+ },
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionNameSameAsFunctionScopeVariableName_Pass) {
- // fn a() -> void { var b:i32 = 0; }
- // fn b() -> i32 { return 2; }
+TEST_F(ResolverFunctionValidationTest, FunctionNameSameAsFunctionScopeVariableName_Pass) {
+ // fn a() -> void { var b:i32 = 0; }
+ // fn b() -> i32 { return 2; }
- auto* var = Var("b", ty.i32(), Expr(0));
- Func("a", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Decl(var),
- },
- ast::AttributeList{});
+ auto* var = Var("b", ty.i32(), Expr(0));
+ Func("a", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Decl(var),
+ },
+ ast::AttributeList{});
- Func(Source{{12, 34}}, "b", ast::VariableList{}, ty.i32(),
- ast::StatementList{
- Return(2),
- },
- ast::AttributeList{});
+ Func(Source{{12, 34}}, "b", ast::VariableList{}, ty.i32(),
+ ast::StatementList{
+ Return(2),
+ },
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, UnreachableCode_return) {
- // fn func() -> {
- // var a : i32;
- // return;
- // a = 2;
- //}
+ // fn func() -> {
+ // var a : i32;
+ // return;
+ // a = 2;
+ //}
- auto* decl_a = Decl(Var("a", ty.i32()));
- auto* ret = Return();
- auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
+ auto* decl_a = Decl(Var("a", ty.i32()));
+ auto* ret = Return();
+ auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
- Func("func", ast::VariableList{}, ty.void_(), {decl_a, ret, assign_a});
+ Func("func", ast::VariableList{}, ty.void_(), {decl_a, ret, assign_a});
- ASSERT_TRUE(r()->Resolve());
+ ASSERT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
- EXPECT_TRUE(Sem().Get(ret)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
+ EXPECT_TRUE(Sem().Get(ret)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
}
TEST_F(ResolverFunctionValidationTest, UnreachableCode_return_InBlocks) {
- // fn func() -> {
- // var a : i32;
- // {{{return;}}}
- // a = 2;
- //}
+ // fn func() -> {
+ // var a : i32;
+ // {{{return;}}}
+ // a = 2;
+ //}
- auto* decl_a = Decl(Var("a", ty.i32()));
- auto* ret = Return();
- auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
+ auto* decl_a = Decl(Var("a", ty.i32()));
+ auto* ret = Return();
+ auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
- Func("func", ast::VariableList{}, ty.void_(),
- {decl_a, Block(Block(Block(ret))), assign_a});
+ Func("func", ast::VariableList{}, ty.void_(), {decl_a, Block(Block(Block(ret))), assign_a});
- ASSERT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
- EXPECT_TRUE(Sem().Get(ret)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
+ ASSERT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
+ EXPECT_TRUE(Sem().Get(ret)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
}
TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard) {
- // fn func() -> {
- // var a : i32;
- // discard;
- // a = 2;
- //}
+ // fn func() -> {
+ // var a : i32;
+ // discard;
+ // a = 2;
+ //}
- auto* decl_a = Decl(Var("a", ty.i32()));
- auto* discard = Discard();
- auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
+ auto* decl_a = Decl(Var("a", ty.i32()));
+ auto* discard = Discard();
+ auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
- Func("func", ast::VariableList{}, ty.void_(), {decl_a, discard, assign_a});
+ Func("func", ast::VariableList{}, ty.void_(), {decl_a, discard, assign_a});
- ASSERT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
- EXPECT_TRUE(Sem().Get(discard)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
+ ASSERT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
+ EXPECT_TRUE(Sem().Get(discard)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
}
TEST_F(ResolverFunctionValidationTest, UnreachableCode_discard_InBlocks) {
- // fn func() -> {
- // var a : i32;
- // {{{discard;}}}
- // a = 2;
- //}
+ // fn func() -> {
+ // var a : i32;
+ // {{{discard;}}}
+ // a = 2;
+ //}
- auto* decl_a = Decl(Var("a", ty.i32()));
- auto* discard = Discard();
- auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
+ auto* decl_a = Decl(Var("a", ty.i32()));
+ auto* discard = Discard();
+ auto* assign_a = Assign(Source{{12, 34}}, "a", 2);
- Func("func", ast::VariableList{}, ty.void_(),
- {decl_a, Block(Block(Block(discard))), assign_a});
+ Func("func", ast::VariableList{}, ty.void_(), {decl_a, Block(Block(Block(discard))), assign_a});
- ASSERT_TRUE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
- EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
- EXPECT_TRUE(Sem().Get(discard)->IsReachable());
- EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
+ ASSERT_TRUE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 warning: code is unreachable");
+ EXPECT_TRUE(Sem().Get(decl_a)->IsReachable());
+ EXPECT_TRUE(Sem().Get(discard)->IsReachable());
+ EXPECT_FALSE(Sem().Get(assign_a)->IsReachable());
}
TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatement_Fail) {
- // fn func() -> int { var a:i32 = 2; }
+ // fn func() -> int { var a:i32 = 2; }
- auto* var = Var("a", ty.i32(), Expr(2));
+ auto* var = Var("a", ty.i32(), Expr(2));
- Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
- ast::StatementList{
- Decl(var),
- },
- ast::AttributeList{});
+ Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
+ ast::StatementList{
+ Decl(var),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
}
-TEST_F(ResolverFunctionValidationTest,
- VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
- // fn func {}
+TEST_F(ResolverFunctionValidationTest, VoidFunctionEndWithoutReturnStatementEmptyBody_Pass) {
+ // fn func {}
- Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(),
- ast::StatementList{});
+ Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.void_(), ast::StatementList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionEndWithoutReturnStatementEmptyBody_Fail) {
- // fn func() -> int {}
+TEST_F(ResolverFunctionValidationTest, FunctionEndWithoutReturnStatementEmptyBody_Fail) {
+ // fn func() -> int {}
- Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(),
- ast::StatementList{}, ast::AttributeList{});
+ Func(Source{{12, 34}}, "func", ast::VariableList{}, ty.i32(), ast::StatementList{},
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing return at end of function");
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementType_Pass) {
- // fn func { return; }
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_Pass) {
+ // fn func { return; }
- Func("func", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Return(),
- });
+ Func("func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Return(),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementType_fail) {
- // fn func { return 2; }
- Func("func", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Return(Source{{12, 34}}, Expr(2)),
- },
- ast::AttributeList{});
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_fail) {
+ // fn func { return 2; }
+ Func("func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Return(Source{{12, 34}}, Expr(2)),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: return statement type must match its function return "
- "type, returned 'i32', expected 'void'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: return statement type must match its function return "
+ "type, returned 'i32', expected 'void'");
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementType_void_fail) {
- // fn v { return; }
- // fn func { return v(); }
- Func("v", {}, ty.void_(), {Return()});
- Func("func", {}, ty.void_(),
- {
- Return(Call(Source{{12, 34}}, "v")),
- });
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementType_void_fail) {
+ // fn v { return; }
+ // fn func { return v(); }
+ Func("v", {}, ty.void_(), {Return()});
+ Func("func", {}, ty.void_(),
+ {
+ Return(Call(Source{{12, 34}}, "v")),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: function 'v' does not return a value");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function 'v' does not return a value");
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementTypeMissing_fail) {
- // fn func() -> f32 { return; }
- Func("func", ast::VariableList{}, ty.f32(),
- ast::StatementList{
- Return(Source{{12, 34}}, nullptr),
- },
- ast::AttributeList{});
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeMissing_fail) {
+ // fn func() -> f32 { return; }
+ Func("func", ast::VariableList{}, ty.f32(),
+ ast::StatementList{
+ Return(Source{{12, 34}}, nullptr),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: return statement type must match its function return "
- "type, returned 'void', expected 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: return statement type must match its function return "
+ "type, returned 'void', expected 'f32'");
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementTypeF32_pass) {
- // fn func() -> f32 { return 2.0; }
- Func("func", ast::VariableList{}, ty.f32(),
- ast::StatementList{
- Return(Source{{12, 34}}, Expr(2.f)),
- },
- ast::AttributeList{});
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_pass) {
+ // fn func() -> f32 { return 2.0; }
+ Func("func", ast::VariableList{}, ty.f32(),
+ ast::StatementList{
+ Return(Source{{12, 34}}, Expr(2.f)),
+ },
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementTypeF32_fail) {
- // fn func() -> f32 { return 2; }
- Func("func", ast::VariableList{}, ty.f32(),
- ast::StatementList{
- Return(Source{{12, 34}}, Expr(2)),
- },
- ast::AttributeList{});
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32_fail) {
+ // fn func() -> f32 { return 2; }
+ Func("func", ast::VariableList{}, ty.f32(),
+ ast::StatementList{
+ Return(Source{{12, 34}}, Expr(2)),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: return statement type must match its function return "
- "type, returned 'i32', expected 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: return statement type must match its function return "
+ "type, returned 'i32', expected 'f32'");
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementTypeF32Alias_pass) {
- // type myf32 = f32;
- // fn func() -> myf32 { return 2.0; }
- auto* myf32 = Alias("myf32", ty.f32());
- Func("func", ast::VariableList{}, ty.Of(myf32),
- ast::StatementList{
- Return(Source{{12, 34}}, Expr(2.f)),
- },
- ast::AttributeList{});
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32Alias_pass) {
+ // type myf32 = f32;
+ // fn func() -> myf32 { return 2.0; }
+ auto* myf32 = Alias("myf32", ty.f32());
+ Func("func", ast::VariableList{}, ty.Of(myf32),
+ ast::StatementList{
+ Return(Source{{12, 34}}, Expr(2.f)),
+ },
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverFunctionValidationTest,
- FunctionTypeMustMatchReturnStatementTypeF32Alias_fail) {
- // type myf32 = f32;
- // fn func() -> myf32 { return 2; }
- auto* myf32 = Alias("myf32", ty.f32());
- Func("func", ast::VariableList{}, ty.Of(myf32),
- ast::StatementList{
- Return(Source{{12, 34}}, Expr(2u)),
- },
- ast::AttributeList{});
+TEST_F(ResolverFunctionValidationTest, FunctionTypeMustMatchReturnStatementTypeF32Alias_fail) {
+ // type myf32 = f32;
+ // fn func() -> myf32 { return 2; }
+ auto* myf32 = Alias("myf32", ty.f32());
+ Func("func", ast::VariableList{}, ty.Of(myf32),
+ ast::StatementList{
+ Return(Source{{12, 34}}, Expr(2u)),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: return statement type must match its function return "
- "type, returned 'u32', expected 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: return statement type must match its function return "
+ "type, returned 'u32', expected 'f32'");
}
TEST_F(ResolverFunctionValidationTest, CannotCallEntryPoint) {
- // @stage(compute) @workgroup_size(1) fn entrypoint() {}
- // fn func() { return entrypoint(); }
- Func("entrypoint", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
+ // @stage(compute) @workgroup_size(1) fn entrypoint() {}
+ // fn func() { return entrypoint(); }
+ Func("entrypoint", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- Func("func", ast::VariableList{}, ty.void_(),
- {
- CallStmt(Call(Source{{12, 34}}, "entrypoint")),
- });
+ Func("func", ast::VariableList{}, ty.void_(),
+ {
+ CallStmt(Call(Source{{12, 34}}, "entrypoint")),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
- R"(12:34 error: entry point functions cannot be the target of a function call)");
+ R"(12:34 error: entry point functions cannot be the target of a function call)");
}
TEST_F(ResolverFunctionValidationTest, PipelineStage_MustBeUnique_Fail) {
- // @stage(fragment)
- // @stage(vertex)
- // fn main() { return; }
- Func(Source{{12, 34}}, "main", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Return(),
- },
- ast::AttributeList{
- Stage(Source{{12, 34}}, ast::PipelineStage::kVertex),
- Stage(Source{{56, 78}}, ast::PipelineStage::kFragment),
- });
+ // @stage(fragment)
+ // @stage(vertex)
+ // fn main() { return; }
+ Func(Source{{12, 34}}, "main", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Return(),
+ },
+ ast::AttributeList{
+ Stage(Source{{12, 34}}, ast::PipelineStage::kVertex),
+ Stage(Source{{56, 78}}, ast::PipelineStage::kFragment),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(56:78 error: duplicate stage attribute
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: duplicate stage attribute
12:34 note: first attribute declared here)");
}
TEST_F(ResolverFunctionValidationTest, NoPipelineEntryPoints) {
- Func("vtx_func", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Return(),
- },
- ast::AttributeList{});
+ Func("vtx_func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Return(),
+ },
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, FunctionVarInitWithParam) {
- // fn foo(bar : f32){
- // var baz : f32 = bar;
- // }
+ // fn foo(bar : f32){
+ // var baz : f32 = bar;
+ // }
- auto* bar = Param("bar", ty.f32());
- auto* baz = Var("baz", ty.f32(), Expr("bar"));
+ auto* bar = Param("bar", ty.f32());
+ auto* baz = Var("baz", ty.f32(), Expr("bar"));
- Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
- ast::AttributeList{});
+ Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, FunctionConstInitWithParam) {
- // fn foo(bar : f32){
- // let baz : f32 = bar;
- // }
+ // fn foo(bar : f32){
+ // let baz : f32 = bar;
+ // }
- auto* bar = Param("bar", ty.f32());
- auto* baz = Let("baz", ty.f32(), Expr("bar"));
+ auto* bar = Param("bar", ty.f32());
+ auto* baz = Let("baz", ty.f32(), Expr("bar"));
- Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
- ast::AttributeList{});
+ Func("foo", ast::VariableList{bar}, ty.void_(), ast::StatementList{Decl(baz)},
+ ast::AttributeList{});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, FunctionParamsConst) {
- Func("foo", {Param(Sym("arg"), ty.i32())}, ty.void_(),
- {Assign(Expr(Source{{12, 34}}, "arg"), Expr(1)), Return()});
+ Func("foo", {Param(Sym("arg"), ty.i32())}, ty.void_(),
+ {Assign(Expr(Source{{12, 34}}, "arg"), Expr(1)), Return()});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot assign to function parameter\nnote: 'arg' is "
- "declared here:");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot assign to function parameter\nnote: 'arg' is "
+ "declared here:");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_ConstU32) {
- // let x = 4u;
- // let x = 8u;
- // @stage(compute) @workgroup_size(x, y, 16u)
- // fn main() {}
- auto* x = GlobalConst("x", ty.u32(), Expr(4u));
- auto* y = GlobalConst("y", ty.u32(), Expr(8u));
- auto* func = Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr("x"), Expr("y"), Expr(16u))});
+ // let x = 4u;
+ // let x = 8u;
+ // @stage(compute) @workgroup_size(x, y, 16u)
+ // fn main() {}
+ auto* x = GlobalConst("x", ty.u32(), Expr(4u));
+ auto* y = GlobalConst("y", ty.u32(), Expr(8u));
+ auto* func =
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr("x"), Expr("y"), Expr(16u))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_func = Sem().Get(func);
- auto* sem_x = Sem().Get<sem::GlobalVariable>(x);
- auto* sem_y = Sem().Get<sem::GlobalVariable>(y);
+ auto* sem_func = Sem().Get(func);
+ auto* sem_x = Sem().Get<sem::GlobalVariable>(x);
+ auto* sem_y = Sem().Get<sem::GlobalVariable>(y);
- ASSERT_NE(sem_func, nullptr);
- ASSERT_NE(sem_x, nullptr);
- ASSERT_NE(sem_y, nullptr);
+ ASSERT_NE(sem_func, nullptr);
+ ASSERT_NE(sem_x, nullptr);
+ ASSERT_NE(sem_y, nullptr);
- EXPECT_TRUE(sem_func->DirectlyReferencedGlobals().contains(sem_x));
- EXPECT_TRUE(sem_func->DirectlyReferencedGlobals().contains(sem_y));
+ EXPECT_TRUE(sem_func->DirectlyReferencedGlobals().contains(sem_x));
+ EXPECT_TRUE(sem_func->DirectlyReferencedGlobals().contains(sem_y));
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_GoodType_U32) {
- // @stage(compute) @workgroup_size(1u, 2u, 3u)
- // fn main() {}
+ // @stage(compute) @workgroup_size(1u, 2u, 3u)
+ // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Source{{12, 34}}, Expr(1u), Expr(2u), Expr(3u))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Source{{12, 34}}, Expr(1u), Expr(2u), Expr(3u))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_MismatchTypeU32) {
- // @stage(compute) @workgroup_size(1u, 2u, 3)
- // fn main() {}
+ // @stage(compute) @workgroup_size(1u, 2u, 3)
+ // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(1u), Expr(2u), Expr(Source{{12, 34}}, 3))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(1u), Expr(2u), Expr(Source{{12, 34}}, 3))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size arguments must be of the same type, "
- "either i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size arguments must be of the same type, "
+ "either i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_MismatchTypeI32) {
- // @stage(compute) @workgroup_size(1, 2u, 3)
- // fn main() {}
+ // @stage(compute) @workgroup_size(1, 2u, 3)
+ // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(1), Expr(Source{{12, 34}}, 2u), Expr(3))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr(1), Expr(Source{{12, 34}}, 2u), Expr(3))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size arguments must be of the same type, "
- "either i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size arguments must be of the same type, "
+ "either i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_TypeMismatch) {
- // let x = 64u;
- // @stage(compute) @workgroup_size(1, x)
- // fn main() {}
- GlobalConst("x", ty.u32(), Expr(64u));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(1), Expr(Source{{12, 34}}, "x"))});
+ // let x = 64u;
+ // @stage(compute) @workgroup_size(1, x)
+ // fn main() {}
+ GlobalConst("x", ty.u32(), Expr(64u));
+ Func(
+ "main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(1), Expr(Source{{12, 34}}, "x"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size arguments must be of the same type, "
- "either i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size arguments must be of the same type, "
+ "either i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_TypeMismatch2) {
- // let x = 64u;
- // let y = 32;
- // @stage(compute) @workgroup_size(x, y)
- // fn main() {}
- GlobalConst("x", ty.u32(), Expr(64u));
- GlobalConst("y", ty.i32(), Expr(32));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr("x"), Expr(Source{{12, 34}}, "y"))});
+ // let x = 64u;
+ // let y = 32;
+ // @stage(compute) @workgroup_size(x, y)
+ // fn main() {}
+ GlobalConst("x", ty.u32(), Expr(64u));
+ GlobalConst("y", ty.i32(), Expr(32));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr("x"), Expr(Source{{12, 34}}, "y"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size arguments must be of the same type, "
- "either i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size arguments must be of the same type, "
+ "either i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Mismatch_ConstU32) {
- // let x = 4u;
- // let x = 8u;
- // @stage(compute) @workgroup_size(x, y, 16
- // fn main() {}
- GlobalConst("x", ty.u32(), Expr(4u));
- GlobalConst("y", ty.u32(), Expr(8u));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr("x"), Expr("y"), Expr(Source{{12, 34}}, 16))});
+ // let x = 4u;
+ // let x = 8u;
+ // @stage(compute) @workgroup_size(x, y, 16
+ // fn main() {}
+ GlobalConst("x", ty.u32(), Expr(4u));
+ GlobalConst("y", ty.u32(), Expr(8u));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Expr("x"), Expr("y"), Expr(Source{{12, 34}}, 16))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size arguments must be of the same type, "
- "either i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size arguments must be of the same type, "
+ "either i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_BadType) {
- // @stage(compute) @workgroup_size(64.0)
- // fn main() {}
+ // @stage(compute) @workgroup_size(64.0)
+ // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, 64.f))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, 64.f))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be either literal or "
- "module-scope constant of type i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size argument must be either literal or "
+ "module-scope constant of type i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_Negative) {
- // @stage(compute) @workgroup_size(-2)
- // fn main() {}
+ // @stage(compute) @workgroup_size(-2)
+ // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, -2))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, -2))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be at least 1");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Literal_Zero) {
- // @stage(compute) @workgroup_size(0)
- // fn main() {}
+ // @stage(compute) @workgroup_size(0)
+ // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, 0))});
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, 0))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be at least 1");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_BadType) {
- // let x = 64.0;
- // @stage(compute) @workgroup_size(x)
- // fn main() {}
- GlobalConst("x", ty.f32(), Expr(64.f));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+ // let x = 64.0;
+ // @stage(compute) @workgroup_size(x)
+ // fn main() {}
+ GlobalConst("x", ty.f32(), Expr(64.f));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be either literal or "
- "module-scope constant of type i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size argument must be either literal or "
+ "module-scope constant of type i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_Negative) {
- // let x = -2;
- // @stage(compute) @workgroup_size(x)
- // fn main() {}
- GlobalConst("x", ty.i32(), Expr(-2));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+ // let x = -2;
+ // @stage(compute) @workgroup_size(x)
+ // fn main() {}
+ GlobalConst("x", ty.i32(), Expr(-2));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be at least 1");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_Zero) {
- // let x = 0;
- // @stage(compute) @workgroup_size(x)
- // fn main() {}
- GlobalConst("x", ty.i32(), Expr(0));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+ // let x = 0;
+ // @stage(compute) @workgroup_size(x)
+ // fn main() {}
+ GlobalConst("x", ty.i32(), Expr(0));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be at least 1");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
}
-TEST_F(ResolverFunctionValidationTest,
- WorkgroupSize_Const_NestedZeroValueConstructor) {
- // let x = i32(i32(i32()));
- // @stage(compute) @workgroup_size(x)
- // fn main() {}
- GlobalConst("x", ty.i32(),
- Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32()))));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+TEST_F(ResolverFunctionValidationTest, WorkgroupSize_Const_NestedZeroValueConstructor) {
+ // let x = i32(i32(i32()));
+ // @stage(compute) @workgroup_size(x)
+ // fn main() {}
+ GlobalConst("x", ty.i32(), Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32()))));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be at least 1");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: workgroup_size argument must be at least 1");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_NonConst) {
- // var<private> x = 0;
- // @stage(compute) @workgroup_size(x)
- // fn main() {}
- Global("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64));
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
+ // var<private> x = 0;
+ // @stage(compute) @workgroup_size(x)
+ // fn main() {}
+ Global("x", ty.i32(), ast::StorageClass::kPrivate, Expr(64));
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{{12, 34}}, "x"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be either literal or "
- "module-scope constant of type i32 or u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size argument must be either literal or "
+ "module-scope constant of type i32 or u32");
}
TEST_F(ResolverFunctionValidationTest, WorkgroupSize_InvalidExpr) {
- // @stage(compute) @workgroup_size(i32(1))
- // fn main() {}
- Func("main", {}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(Construct(Source{{12, 34}}, ty.i32(), 1))});
+ // @stage(compute) @workgroup_size(i32(1))
+ // fn main() {}
+ Func("main", {}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute),
+ WorkgroupSize(Construct(Source{{12, 34}}, ty.i32(), 1))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: workgroup_size argument must be either a literal or "
- "a module-scope constant");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: workgroup_size argument must be either a literal or "
+ "a module-scope constant");
}
TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_NonPlain) {
- auto* ret_type =
- ty.pointer(Source{{12, 34}}, ty.i32(), ast::StorageClass::kFunction);
- Func("f", {}, ret_type, {});
+ auto* ret_type = ty.pointer(Source{{12, 34}}, ty.i32(), ast::StorageClass::kFunction);
+ Func("f", {}, ret_type, {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function return type must be a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
}
TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_AtomicInt) {
- auto* ret_type = ty.atomic(Source{{12, 34}}, ty.i32());
- Func("f", {}, ret_type, {});
+ auto* ret_type = ty.atomic(Source{{12, 34}}, ty.i32());
+ Func("f", {}, ret_type, {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function return type must be a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
}
TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_ArrayOfAtomic) {
- auto* ret_type = ty.array(Source{{12, 34}}, ty.atomic(ty.i32()), 10);
- Func("f", {}, ret_type, {});
+ auto* ret_type = ty.array(Source{{12, 34}}, ty.atomic(ty.i32()), 10);
+ Func("f", {}, ret_type, {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function return type must be a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
}
TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_StructOfAtomic) {
- Structure("S", {Member("m", ty.atomic(ty.i32()))});
- auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
- Func("f", {}, ret_type, {});
+ Structure("S", {Member("m", ty.atomic(ty.i32()))});
+ auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
+ Func("f", {}, ret_type, {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function return type must be a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
}
TEST_F(ResolverFunctionValidationTest, ReturnIsConstructible_RuntimeArray) {
- auto* ret_type = ty.array(Source{{12, 34}}, ty.i32());
- Func("f", {}, ret_type, {});
+ auto* ret_type = ty.array(Source{{12, 34}}, ty.i32());
+ Func("f", {}, ret_type, {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function return type must be a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function return type must be a constructible type");
}
TEST_F(ResolverFunctionValidationTest, ParameterStoreType_NonAtomicFree) {
- Structure("S", {Member("m", ty.atomic(ty.i32()))});
- auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
- auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
- Func("f", ast::VariableList{bar}, ty.void_(), {});
+ Structure("S", {Member("m", ty.atomic(ty.i32()))});
+ auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
+ auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
+ Func("f", ast::VariableList{bar}, ty.void_(), {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: store type of function parameter must be a "
- "constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: store type of function parameter must be a "
+ "constructible type");
}
TEST_F(ResolverFunctionValidationTest, ParameterSotreType_AtomicFree) {
- Structure("S", {Member("m", ty.i32())});
- auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
- auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
- Func("f", ast::VariableList{bar}, ty.void_(), {});
+ Structure("S", {Member("m", ty.i32())});
+ auto* ret_type = ty.type_name(Source{{12, 34}}, "S");
+ auto* bar = Param(Source{{12, 34}}, "bar", ret_type);
+ Func("f", ast::VariableList{bar}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, ParametersAtLimit) {
- ast::VariableList params;
- for (int i = 0; i < 255; i++) {
- params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
- }
- Func(Source{{12, 34}}, "f", params, ty.void_(), {});
+ ast::VariableList params;
+ for (int i = 0; i < 255; i++) {
+ params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
+ }
+ Func(Source{{12, 34}}, "f", params, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverFunctionValidationTest, ParametersOverLimit) {
- ast::VariableList params;
- for (int i = 0; i < 256; i++) {
- params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
- }
- Func(Source{{12, 34}}, "f", params, ty.void_(), {});
+ ast::VariableList params;
+ for (int i = 0; i < 256; i++) {
+ params.emplace_back(Param("param_" + std::to_string(i), ty.i32()));
+ }
+ Func(Source{{12, 34}}, "f", params, ty.void_(), {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: functions may declare at most 255 parameters");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: functions may declare at most 255 parameters");
}
TEST_F(ResolverFunctionValidationTest, ParameterVectorNoType) {
- // fn f(p : vec3) {}
+ // fn f(p : vec3) {}
- Func(Source{{12, 34}}, "f",
- {Param("p", create<ast::Vector>(Source{{12, 34}}, nullptr, 3))},
- ty.void_(), {});
+ Func(Source{{12, 34}}, "f", {Param("p", create<ast::Vector>(Source{{12, 34}}, nullptr, 3))},
+ ty.void_(), {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
}
TEST_F(ResolverFunctionValidationTest, ParameterMatrixNoType) {
- // fn f(p : vec3) {}
+ // fn f(p : vec3) {}
- Func(Source{{12, 34}}, "f",
- {Param("p", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3))},
- ty.void_(), {});
+ Func(Source{{12, 34}}, "f", {Param("p", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3))},
+ ty.void_(), {});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
}
struct TestParams {
- ast::StorageClass storage_class;
- bool should_pass;
+ ast::StorageClass storage_class;
+ bool should_pass;
};
struct TestWithParams : ResolverTestWithParam<TestParams> {};
using ResolverFunctionParameterValidationTest = TestWithParams;
TEST_P(ResolverFunctionParameterValidationTest, StorageClass) {
- auto& param = GetParam();
- auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.storage_class);
- auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
- Func("f", ast::VariableList{arg}, ty.void_(), {});
+ auto& param = GetParam();
+ auto* ptr_type = ty.pointer(Source{{12, 34}}, ty.i32(), param.storage_class);
+ auto* arg = Param(Source{{12, 34}}, "p", ptr_type);
+ Func("f", ast::VariableList{arg}, ty.void_(), {});
- if (param.should_pass) {
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- } else {
- std::stringstream ss;
- ss << param.storage_class;
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function parameter of pointer type cannot be in '" +
- ss.str() + "' storage class");
- }
+ if (param.should_pass) {
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ std::stringstream ss;
+ ss << param.storage_class;
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function parameter of pointer type cannot be in '" +
+ ss.str() + "' storage class");
+ }
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverFunctionParameterValidationTest,
- testing::Values(TestParams{ast::StorageClass::kNone, false},
- TestParams{ast::StorageClass::kInput, false},
- TestParams{ast::StorageClass::kOutput, false},
- TestParams{ast::StorageClass::kUniform, false},
- TestParams{ast::StorageClass::kWorkgroup, true},
- TestParams{ast::StorageClass::kHandle, false},
- TestParams{ast::StorageClass::kStorage, false},
- TestParams{ast::StorageClass::kPrivate, true},
- TestParams{ast::StorageClass::kFunction, true}));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverFunctionParameterValidationTest,
+ testing::Values(TestParams{ast::StorageClass::kNone, false},
+ TestParams{ast::StorageClass::kInput, false},
+ TestParams{ast::StorageClass::kOutput, false},
+ TestParams{ast::StorageClass::kUniform, false},
+ TestParams{ast::StorageClass::kWorkgroup, true},
+ TestParams{ast::StorageClass::kHandle, false},
+ TestParams{ast::StorageClass::kStorage, false},
+ TestParams{ast::StorageClass::kPrivate, true},
+ TestParams{ast::StorageClass::kFunction, true}));
} // namespace
} // namespace tint::resolver
diff --git a/src/tint/resolver/host_shareable_validation_test.cc b/src/tint/resolver/host_shareable_validation_test.cc
index 80254f6..01fbfb0 100644
--- a/src/tint/resolver/host_shareable_validation_test.cc
+++ b/src/tint/resolver/host_shareable_validation_test.cc
@@ -24,82 +24,78 @@
using ResolverHostShareableValidationTest = ResolverTest;
TEST_F(ResolverHostShareableValidationTest, BoolMember) {
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())});
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.bool_())});
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
12:34 note: while analysing structure member S.x
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverHostShareableValidationTest, BoolVectorMember) {
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.vec3<bool>())});
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'vec3<bool>' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'vec3<bool>' cannot be used in storage class 'storage' as it is non-host-shareable
12:34 note: while analysing structure member S.x
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverHostShareableValidationTest, Aliases) {
- auto* a1 = Alias("a1", ty.bool_());
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.Of(a1))});
- auto* a2 = Alias("a2", ty.Of(s));
- Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* a1 = Alias("a1", ty.bool_());
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.Of(a1))});
+ auto* a2 = Alias("a2", ty.Of(s));
+ Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
12:34 note: while analysing structure member S.x
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverHostShareableValidationTest, NestedStructures) {
- auto* i1 = Structure("I1", {Member(Source{{1, 2}}, "x", ty.bool_())});
- auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", ty.Of(i1))});
- auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", ty.Of(i2))});
+ auto* i1 = Structure("I1", {Member(Source{{1, 2}}, "x", ty.bool_())});
+ auto* i2 = Structure("I2", {Member(Source{{3, 4}}, "y", ty.Of(i1))});
+ auto* i3 = Structure("I3", {Member(Source{{5, 6}}, "z", ty.Of(i2))});
- auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
+ auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
- Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(9:10 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(9:10 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
1:2 note: while analysing structure member I1.x
3:4 note: while analysing structure member I2.y
5:6 note: while analysing structure member I3.z
@@ -108,35 +104,33 @@
}
TEST_F(ResolverHostShareableValidationTest, NoError) {
- auto* i1 =
- Structure("I1", {
- Member(Source{{1, 1}}, "x1", ty.f32()),
- Member(Source{{2, 1}}, "y1", ty.vec3<f32>()),
- Member(Source{{3, 1}}, "z1", ty.array<i32, 4>()),
- });
- auto* a1 = Alias("a1", ty.Of(i1));
- auto* i2 = Structure("I2", {
- Member(Source{{4, 1}}, "x2", ty.mat2x2<f32>()),
- Member(Source{{5, 1}}, "y2", ty.Of(i1)),
- });
- auto* a2 = Alias("a2", ty.Of(i2));
- auto* i3 = Structure("I3", {
- Member(Source{{4, 1}}, "x3", ty.Of(a1)),
- Member(Source{{5, 1}}, "y3", ty.Of(i2)),
- Member(Source{{6, 1}}, "z3", ty.Of(a2)),
- });
+ auto* i1 = Structure("I1", {
+ Member(Source{{1, 1}}, "x1", ty.f32()),
+ Member(Source{{2, 1}}, "y1", ty.vec3<f32>()),
+ Member(Source{{3, 1}}, "z1", ty.array<i32, 4>()),
+ });
+ auto* a1 = Alias("a1", ty.Of(i1));
+ auto* i2 = Structure("I2", {
+ Member(Source{{4, 1}}, "x2", ty.mat2x2<f32>()),
+ Member(Source{{5, 1}}, "y2", ty.Of(i1)),
+ });
+ auto* a2 = Alias("a2", ty.Of(i2));
+ auto* i3 = Structure("I3", {
+ Member(Source{{4, 1}}, "x3", ty.Of(a1)),
+ Member(Source{{5, 1}}, "y3", ty.Of(i2)),
+ Member(Source{{6, 1}}, "z3", ty.Of(a2)),
+ });
- auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
+ auto* s = Structure("S", {Member(Source{{7, 8}}, "m", ty.Of(i3))});
- Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- WrapInFunction();
+ Global(Source{{9, 10}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ WrapInFunction();
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/increment_decrement_validation_test.cc b/src/tint/resolver/increment_decrement_validation_test.cc
index ff999bb..e6f760b 100644
--- a/src/tint/resolver/increment_decrement_validation_test.cc
+++ b/src/tint/resolver/increment_decrement_validation_test.cc
@@ -23,211 +23,205 @@
using ResolverIncrementDecrementValidationTest = ResolverTest;
TEST_F(ResolverIncrementDecrementValidationTest, Increment_Signed) {
- // var a : i32 = 2;
- // a++;
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Increment(Source{{12, 34}}, "a"));
+ // var a : i32 = 2;
+ // a++;
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Increment(Source{{12, 34}}, "a"));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, Decrement_Signed) {
- // var a : i32 = 2;
- // a--;
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- WrapInFunction(var, Decrement(Source{{12, 34}}, "a"));
+ // var a : i32 = 2;
+ // a--;
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ WrapInFunction(var, Decrement(Source{{12, 34}}, "a"));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, Increment_Unsigned) {
- // var a : u32 = 2u;
- // a++;
- auto* var = Var("a", ty.u32(), ast::StorageClass::kNone, Expr(2u));
- WrapInFunction(var, Increment(Source{{12, 34}}, "a"));
+ // var a : u32 = 2u;
+ // a++;
+ auto* var = Var("a", ty.u32(), ast::StorageClass::kNone, Expr(2u));
+ WrapInFunction(var, Increment(Source{{12, 34}}, "a"));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, Decrement_Unsigned) {
- // var a : u32 = 2u;
- // a--;
- auto* var = Var("a", ty.u32(), ast::StorageClass::kNone, Expr(2u));
- WrapInFunction(var, Decrement(Source{{12, 34}}, "a"));
+ // var a : u32 = 2u;
+ // a--;
+ auto* var = Var("a", ty.u32(), ast::StorageClass::kNone, Expr(2u));
+ WrapInFunction(var, Decrement(Source{{12, 34}}, "a"));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, ThroughPointer) {
- // var a : i32;
- // let b : ptr<function,i32> = &a;
- // *b++;
- auto* var_a = Var("a", ty.i32(), ast::StorageClass::kFunction);
- auto* var_b = Let("b", ty.pointer<int>(ast::StorageClass::kFunction),
- AddressOf(Expr("a")));
- WrapInFunction(var_a, var_b, Increment(Source{{12, 34}}, Deref("b")));
+ // var a : i32;
+ // let b : ptr<function,i32> = &a;
+ // *b++;
+ auto* var_a = Var("a", ty.i32(), ast::StorageClass::kFunction);
+ auto* var_b = Let("b", ty.pointer<int>(ast::StorageClass::kFunction), AddressOf(Expr("a")));
+ WrapInFunction(var_a, var_b, Increment(Source{{12, 34}}, Deref("b")));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, ThroughArray) {
- // var a : array<i32, 4>;
- // a[1]++;
- auto* var_a = Var("a", ty.array(ty.i32(), 4), ast::StorageClass::kNone);
- WrapInFunction(var_a, Increment(Source{{12, 34}}, IndexAccessor("a", 1)));
+ // var a : array<i32, 4>;
+ // a[1]++;
+ auto* var_a = Var("a", ty.array(ty.i32(), 4), ast::StorageClass::kNone);
+ WrapInFunction(var_a, Increment(Source{{12, 34}}, IndexAccessor("a", 1)));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, ThroughVector_Index) {
- // var a : vec4<i32>;
- // a.y++;
- auto* var_a = Var("a", ty.vec4(ty.i32()), ast::StorageClass::kNone);
- WrapInFunction(var_a, Increment(Source{{12, 34}}, IndexAccessor("a", 1)));
+ // var a : vec4<i32>;
+ // a.y++;
+ auto* var_a = Var("a", ty.vec4(ty.i32()), ast::StorageClass::kNone);
+ WrapInFunction(var_a, Increment(Source{{12, 34}}, IndexAccessor("a", 1)));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, ThroughVector_Member) {
- // var a : vec4<i32>;
- // a.y++;
- auto* var_a = Var("a", ty.vec4(ty.i32()), ast::StorageClass::kNone);
- WrapInFunction(var_a, Increment(Source{{12, 34}}, MemberAccessor("a", "y")));
+ // var a : vec4<i32>;
+ // a.y++;
+ auto* var_a = Var("a", ty.vec4(ty.i32()), ast::StorageClass::kNone);
+ WrapInFunction(var_a, Increment(Source{{12, 34}}, MemberAccessor("a", "y")));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, Float) {
- // var a : f32 = 2.0;
- // a++;
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.f));
- auto* inc = Increment(Expr(Source{{12, 34}}, "a"));
- WrapInFunction(var, inc);
+ // var a : f32 = 2.0;
+ // a++;
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.f));
+ auto* inc = Increment(Expr(Source{{12, 34}}, "a"));
+ WrapInFunction(var, inc);
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: increment statement can only be applied to an "
- "integer scalar");
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: increment statement can only be applied to an "
+ "integer scalar");
}
TEST_F(ResolverIncrementDecrementValidationTest, Vector) {
- // var a : vec4<f32>;
- // a++;
- auto* var = Var("a", ty.vec4<i32>(), ast::StorageClass::kNone);
- auto* inc = Increment(Expr(Source{{12, 34}}, "a"));
- WrapInFunction(var, inc);
+ // var a : vec4<f32>;
+ // a++;
+ auto* var = Var("a", ty.vec4<i32>(), ast::StorageClass::kNone);
+ auto* inc = Increment(Expr(Source{{12, 34}}, "a"));
+ WrapInFunction(var, inc);
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: increment statement can only be applied to an "
- "integer scalar");
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: increment statement can only be applied to an "
+ "integer scalar");
}
TEST_F(ResolverIncrementDecrementValidationTest, Atomic) {
- // var<workgroup> a : atomic<i32>;
- // a++;
- Global(Source{{12, 34}}, "a", ty.atomic(ty.i32()),
- ast::StorageClass::kWorkgroup);
- WrapInFunction(Increment(Expr(Source{{56, 78}}, "a")));
+ // var<workgroup> a : atomic<i32>;
+ // a++;
+ Global(Source{{12, 34}}, "a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+ WrapInFunction(Increment(Expr(Source{{56, 78}}, "a")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: increment statement can only be applied to an "
- "integer scalar");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: increment statement can only be applied to an "
+ "integer scalar");
}
TEST_F(ResolverIncrementDecrementValidationTest, Literal) {
- // 1++;
- WrapInFunction(Increment(Expr(Source{{56, 78}}, 1)));
+ // 1++;
+ WrapInFunction(Increment(Expr(Source{{56, 78}}, 1)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "56:78 error: cannot modify value of type 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: cannot modify value of type 'i32'");
}
TEST_F(ResolverIncrementDecrementValidationTest, Constant) {
- // let a = 1;
- // a++;
- auto* a = Let(Source{{12, 34}}, "a", nullptr, Expr(1));
- WrapInFunction(a, Increment(Expr(Source{{56, 78}}, "a")));
+ // let a = 1;
+ // a++;
+ auto* a = Let(Source{{12, 34}}, "a", nullptr, Expr(1));
+ WrapInFunction(a, Increment(Expr(Source{{56, 78}}, "a")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify constant value
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify constant value
12:34 note: 'a' is declared here:)");
}
TEST_F(ResolverIncrementDecrementValidationTest, Parameter) {
- // fn func(a : i32)
- // {
- // a++;
- // }
- auto* a = Param(Source{{12, 34}}, "a", ty.i32());
- Func("func", {a}, ty.void_(), {Increment(Expr(Source{{56, 78}}, "a"))});
+ // fn func(a : i32)
+ // {
+ // a++;
+ // }
+ auto* a = Param(Source{{12, 34}}, "a", ty.i32());
+ Func("func", {a}, ty.void_(), {Increment(Expr(Source{{56, 78}}, "a"))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify function parameter
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify function parameter
12:34 note: 'a' is declared here:)");
}
TEST_F(ResolverIncrementDecrementValidationTest, ReturnValue) {
- // fn func() -> i32 {
- // return 0;
- // }
- // {
- // a++;
- // }
- Func("func", {}, ty.i32(), {Return(0)});
- WrapInFunction(Increment(Call(Source{{56, 78}}, "func")));
+ // fn func() -> i32 {
+ // return 0;
+ // }
+ // {
+ // a++;
+ // }
+ Func("func", {}, ty.i32(), {Return(0)});
+ WrapInFunction(Increment(Call(Source{{56, 78}}, "func")));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify value of type 'i32')");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), R"(56:78 error: cannot modify value of type 'i32')");
}
TEST_F(ResolverIncrementDecrementValidationTest, ReadOnlyBuffer) {
- // @group(0) @binding(0) var<storage,read> a : i32;
- // {
- // a++;
- // }
- Global(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage,
- ast::Access::kRead, GroupAndBinding(0, 0));
- WrapInFunction(Increment(Source{{56, 78}}, "a"));
+ // @group(0) @binding(0) var<storage,read> a : i32;
+ // {
+ // a++;
+ // }
+ Global(Source{{12, 34}}, "a", ty.i32(), ast::StorageClass::kStorage, ast::Access::kRead,
+ GroupAndBinding(0, 0));
+ WrapInFunction(Increment(Source{{56, 78}}, "a"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "56:78 error: cannot modify read-only type 'ref<storage, i32, read>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: cannot modify read-only type 'ref<storage, i32, read>'");
}
TEST_F(ResolverIncrementDecrementValidationTest, Phony) {
- // _++;
- WrapInFunction(Increment(Phony(Source{{56, 78}})));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "56:78 error: cannot modify value of type 'void'");
+ // _++;
+ WrapInFunction(Increment(Phony(Source{{56, 78}})));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: cannot modify value of type 'void'");
}
TEST_F(ResolverIncrementDecrementValidationTest, InForLoopInit) {
- // var a : i32 = 2;
- // for (a++; ; ) {
- // break;
- // }
- auto* a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* loop =
- For(Increment(Source{{56, 78}}, "a"), nullptr, nullptr, Block(Break()));
- WrapInFunction(a, loop);
+ // var a : i32 = 2;
+ // for (a++; ; ) {
+ // break;
+ // }
+ auto* a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* loop = For(Increment(Source{{56, 78}}, "a"), nullptr, nullptr, Block(Break()));
+ WrapInFunction(a, loop);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIncrementDecrementValidationTest, InForLoopCont) {
- // var a : i32 = 2;
- // for (; ; a++) {
- // break;
- // }
- auto* a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* loop =
- For(nullptr, nullptr, Increment(Source{{56, 78}}, "a"), Block(Break()));
- WrapInFunction(a, loop);
+ // var a : i32 = 2;
+ // for (; ; a++) {
+ // break;
+ // }
+ auto* a = Var("a", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* loop = For(nullptr, nullptr, Increment(Source{{56, 78}}, "a"), Block(Break()));
+ WrapInFunction(a, loop);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/inferred_type_test.cc b/src/tint/resolver/inferred_type_test.cc
index 4d19b50..bafdd80 100644
--- a/src/tint/resolver/inferred_type_test.cc
+++ b/src/tint/resolver/inferred_type_test.cc
@@ -41,17 +41,16 @@
using i32 = builder::i32;
using u32 = builder::u32;
-struct ResolverInferredTypeTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverInferredTypeTest : public resolver::TestHelper, public testing::Test {};
struct Params {
- builder::ast_expr_func_ptr create_value;
- builder::sem_type_func_ptr create_expected_type;
+ builder::ast_expr_func_ptr create_value;
+ builder::sem_type_func_ptr create_expected_type;
};
template <typename T>
constexpr Params ParamsFor() {
- return Params{DataType<T>::Expr, DataType<T>::Sem};
+ return Params{DataType<T>::Expr, DataType<T>::Sem};
}
Params all_cases[] = {
@@ -78,95 +77,90 @@
using ResolverInferredTypeParamTest = ResolverTestWithParam<Params>;
TEST_P(ResolverInferredTypeParamTest, GlobalLet_Pass) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* expected_type = params.create_expected_type(*this);
+ auto* expected_type = params.create_expected_type(*this);
- // let a = <type constructor>;
- auto* ctor_expr = params.create_value(*this, 0);
- auto* var = GlobalConst("a", nullptr, ctor_expr);
- WrapInFunction();
+ // let a = <type constructor>;
+ auto* ctor_expr = params.create_value(*this, 0);
+ auto* var = GlobalConst("a", nullptr, ctor_expr);
+ WrapInFunction();
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(TypeOf(var), expected_type);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(TypeOf(var), expected_type);
}
TEST_P(ResolverInferredTypeParamTest, GlobalVar_Fail) {
- auto& params = GetParam();
+ auto& params = GetParam();
- // var a = <type constructor>;
- auto* ctor_expr = params.create_value(*this, 0);
- Global(Source{{12, 34}}, "a", nullptr, ast::StorageClass::kPrivate,
- ctor_expr);
- WrapInFunction();
+ // var a = <type constructor>;
+ auto* ctor_expr = params.create_value(*this, 0);
+ Global(Source{{12, 34}}, "a", nullptr, ast::StorageClass::kPrivate, ctor_expr);
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: global var declaration must specify a type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: global var declaration must specify a type");
}
TEST_P(ResolverInferredTypeParamTest, LocalLet_Pass) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* expected_type = params.create_expected_type(*this);
+ auto* expected_type = params.create_expected_type(*this);
- // let a = <type constructor>;
- auto* ctor_expr = params.create_value(*this, 0);
- auto* var = Let("a", nullptr, ctor_expr);
- WrapInFunction(var);
+ // let a = <type constructor>;
+ auto* ctor_expr = params.create_value(*this, 0);
+ auto* var = Let("a", nullptr, ctor_expr);
+ WrapInFunction(var);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(TypeOf(var), expected_type);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(TypeOf(var), expected_type);
}
TEST_P(ResolverInferredTypeParamTest, LocalVar_Pass) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* expected_type = params.create_expected_type(*this);
+ auto* expected_type = params.create_expected_type(*this);
- // var a = <type constructor>;
- auto* ctor_expr = params.create_value(*this, 0);
- auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
- WrapInFunction(var);
+ // var a = <type constructor>;
+ auto* ctor_expr = params.create_value(*this, 0);
+ auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
+ WrapInFunction(var);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
}
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
- ResolverInferredTypeParamTest,
- testing::ValuesIn(all_cases));
+INSTANTIATE_TEST_SUITE_P(ResolverTest, ResolverInferredTypeParamTest, testing::ValuesIn(all_cases));
TEST_F(ResolverInferredTypeTest, InferArray_Pass) {
- auto* type = ty.array(ty.u32(), 10);
- auto* expected_type =
- create<sem::Array>(create<sem::U32>(), 10u, 4u, 4u * 10u, 4u, 4u);
+ auto* type = ty.array(ty.u32(), 10);
+ auto* expected_type = create<sem::Array>(create<sem::U32>(), 10u, 4u, 4u * 10u, 4u, 4u);
- auto* ctor_expr = Construct(type);
- auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
- WrapInFunction(var);
+ auto* ctor_expr = Construct(type);
+ auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
+ WrapInFunction(var);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
}
TEST_F(ResolverInferredTypeTest, InferStruct_Pass) {
- auto* member = Member("x", ty.i32());
- auto* str = Structure("S", {member});
+ auto* member = Member("x", ty.i32());
+ auto* str = Structure("S", {member});
- auto* expected_type = create<sem::Struct>(
- str, str->name,
- sem::StructMemberList{create<sem::StructMember>(
- member, member->symbol, create<sem::I32>(), 0u, 0u, 0u, 4u)},
- 0u, 4u, 4u);
+ auto* expected_type =
+ create<sem::Struct>(str, str->name,
+ sem::StructMemberList{create<sem::StructMember>(
+ member, member->symbol, create<sem::I32>(), 0u, 0u, 0u, 4u)},
+ 0u, 4u, 4u);
- auto* ctor_expr = Construct(ty.Of(str));
+ auto* ctor_expr = Construct(ty.Of(str));
- auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
- WrapInFunction(var);
+ auto* var = Var("a", nullptr, ast::StorageClass::kFunction, ctor_expr);
+ WrapInFunction(var);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(TypeOf(var)->UnwrapRef(), expected_type);
}
} // namespace
diff --git a/src/tint/resolver/is_host_shareable_test.cc b/src/tint/resolver/is_host_shareable_test.cc
index fbd6728..a167903 100644
--- a/src/tint/resolver/is_host_shareable_test.cc
+++ b/src/tint/resolver/is_host_shareable_test.cc
@@ -24,96 +24,78 @@
using ResolverIsHostShareable = ResolverTest;
TEST_F(ResolverIsHostShareable, Void) {
- EXPECT_FALSE(r()->IsHostShareable(create<sem::Void>()));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Void>()));
}
TEST_F(ResolverIsHostShareable, Bool) {
- EXPECT_FALSE(r()->IsHostShareable(create<sem::Bool>()));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Bool>()));
}
TEST_F(ResolverIsHostShareable, NumericScalar) {
- EXPECT_TRUE(r()->IsHostShareable(create<sem::I32>()));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::U32>()));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::F32>()));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::I32>()));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::U32>()));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::F32>()));
}
TEST_F(ResolverIsHostShareable, NumericVector) {
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::I32>(), 2u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::I32>(), 3u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::I32>(), 4u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::U32>(), 2u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::U32>(), 3u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::U32>(), 4u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 2u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 3u)));
- EXPECT_TRUE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 4u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::I32>(), 2u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::I32>(), 3u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::I32>(), 4u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::U32>(), 2u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::U32>(), 3u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::U32>(), 4u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 2u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 3u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Vector>(create<sem::F32>(), 4u)));
}
TEST_F(ResolverIsHostShareable, BoolVector) {
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 2u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 3u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 4u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 2u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 3u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 4u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 2u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 3u)));
- EXPECT_FALSE(
- r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 4u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 2u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 3u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 4u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 2u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 3u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 4u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 2u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 3u)));
+ EXPECT_FALSE(r()->IsHostShareable(create<sem::Vector>(create<sem::Bool>(), 4u)));
}
TEST_F(ResolverIsHostShareable, Matrix) {
- auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
- auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
- auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
+ auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
+ auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
+ auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 2u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 3u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 4u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 2u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 3u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 4u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 2u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 3u)));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 4u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 2u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 3u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec2, 4u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 2u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 3u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec3, 4u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 2u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 3u)));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Matrix>(vec4, 4u)));
}
TEST_F(ResolverIsHostShareable, Pointer) {
- auto* ptr = create<sem::Pointer>(
- create<sem::I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
- EXPECT_FALSE(r()->IsHostShareable(ptr));
+ auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::StorageClass::kPrivate,
+ ast::Access::kReadWrite);
+ EXPECT_FALSE(r()->IsHostShareable(ptr));
}
TEST_F(ResolverIsHostShareable, Atomic) {
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Atomic>(create<sem::I32>())));
- EXPECT_TRUE(r()->IsHostShareable(create<sem::Atomic>(create<sem::U32>())));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Atomic>(create<sem::I32>())));
+ EXPECT_TRUE(r()->IsHostShareable(create<sem::Atomic>(create<sem::U32>())));
}
TEST_F(ResolverIsHostShareable, ArraySizedOfHostShareable) {
- auto* arr = create<sem::Array>(create<sem::I32>(), 5u, 4u, 20u, 4u, 4u);
- EXPECT_TRUE(r()->IsHostShareable(arr));
+ auto* arr = create<sem::Array>(create<sem::I32>(), 5u, 4u, 20u, 4u, 4u);
+ EXPECT_TRUE(r()->IsHostShareable(arr));
}
TEST_F(ResolverIsHostShareable, ArrayUnsizedOfHostShareable) {
- auto* arr = create<sem::Array>(create<sem::I32>(), 0u, 4u, 4u, 4u, 4u);
- EXPECT_TRUE(r()->IsHostShareable(arr));
+ auto* arr = create<sem::Array>(create<sem::I32>(), 0u, 4u, 4u, 4u, 4u);
+ EXPECT_TRUE(r()->IsHostShareable(arr));
}
// Note: Structure tests covered in host_shareable_validation_test.cc
diff --git a/src/tint/resolver/is_storeable_test.cc b/src/tint/resolver/is_storeable_test.cc
index 23f798d..2d37d8b 100644
--- a/src/tint/resolver/is_storeable_test.cc
+++ b/src/tint/resolver/is_storeable_test.cc
@@ -24,114 +24,113 @@
using ResolverIsStorableTest = ResolverTest;
TEST_F(ResolverIsStorableTest, Void) {
- EXPECT_FALSE(r()->IsStorable(create<sem::Void>()));
+ EXPECT_FALSE(r()->IsStorable(create<sem::Void>()));
}
TEST_F(ResolverIsStorableTest, Scalar) {
- EXPECT_TRUE(r()->IsStorable(create<sem::Bool>()));
- EXPECT_TRUE(r()->IsStorable(create<sem::I32>()));
- EXPECT_TRUE(r()->IsStorable(create<sem::U32>()));
- EXPECT_TRUE(r()->IsStorable(create<sem::F32>()));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Bool>()));
+ EXPECT_TRUE(r()->IsStorable(create<sem::I32>()));
+ EXPECT_TRUE(r()->IsStorable(create<sem::U32>()));
+ EXPECT_TRUE(r()->IsStorable(create<sem::F32>()));
}
TEST_F(ResolverIsStorableTest, Vector) {
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::I32>(), 2u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::I32>(), 3u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::I32>(), 4u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::U32>(), 2u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::U32>(), 3u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::U32>(), 4u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 2u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 3u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 4u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::I32>(), 2u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::I32>(), 3u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::I32>(), 4u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::U32>(), 2u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::U32>(), 3u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::U32>(), 4u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 2u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 3u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Vector>(create<sem::F32>(), 4u)));
}
TEST_F(ResolverIsStorableTest, Matrix) {
- auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
- auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
- auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 2u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 3u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 4u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 2u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 3u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 4u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 2u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 3u)));
- EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 4u)));
+ auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
+ auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
+ auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 2u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 3u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec2, 4u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 2u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 3u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec3, 4u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 2u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 3u)));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Matrix>(vec4, 4u)));
}
TEST_F(ResolverIsStorableTest, Pointer) {
- auto* ptr = create<sem::Pointer>(
- create<sem::I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
- EXPECT_FALSE(r()->IsStorable(ptr));
+ auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::StorageClass::kPrivate,
+ ast::Access::kReadWrite);
+ EXPECT_FALSE(r()->IsStorable(ptr));
}
TEST_F(ResolverIsStorableTest, Atomic) {
- EXPECT_TRUE(r()->IsStorable(create<sem::Atomic>(create<sem::I32>())));
- EXPECT_TRUE(r()->IsStorable(create<sem::Atomic>(create<sem::U32>())));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Atomic>(create<sem::I32>())));
+ EXPECT_TRUE(r()->IsStorable(create<sem::Atomic>(create<sem::U32>())));
}
TEST_F(ResolverIsStorableTest, ArraySizedOfStorable) {
- auto* arr = create<sem::Array>(create<sem::I32>(), 5u, 4u, 20u, 4u, 4u);
- EXPECT_TRUE(r()->IsStorable(arr));
+ auto* arr = create<sem::Array>(create<sem::I32>(), 5u, 4u, 20u, 4u, 4u);
+ EXPECT_TRUE(r()->IsStorable(arr));
}
TEST_F(ResolverIsStorableTest, ArrayUnsizedOfStorable) {
- auto* arr = create<sem::Array>(create<sem::I32>(), 0u, 4u, 4u, 4u, 4u);
- EXPECT_TRUE(r()->IsStorable(arr));
+ auto* arr = create<sem::Array>(create<sem::I32>(), 0u, 4u, 4u, 4u, 4u);
+ EXPECT_TRUE(r()->IsStorable(arr));
}
TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.f32()),
- });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.f32()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
- });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
}
TEST_F(ResolverIsStorableTest, Struct_NestedStorable) {
- auto* storable = Structure("Storable", {
- Member("a", ty.i32()),
- Member("b", ty.f32()),
- });
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.Of(storable)),
- });
+ auto* storable = Structure("Storable", {
+ Member("a", ty.i32()),
+ Member("b", ty.f32()),
+ });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.Of(storable)),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIsStorableTest, Struct_NestedNonStorable) {
- auto* non_storable =
- Structure("nonstorable",
- {
- Member("a", ty.i32()),
- Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
- });
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.Of(non_storable)),
- });
+ auto* non_storable =
+ Structure("nonstorable", {
+ Member("a", ty.i32()),
+ Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+ });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.Of(non_storable)),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
}
} // namespace
diff --git a/src/tint/resolver/pipeline_overridable_constant_test.cc b/src/tint/resolver/pipeline_overridable_constant_test.cc
index 222a5c0..35c145e 100644
--- a/src/tint/resolver/pipeline_overridable_constant_test.cc
+++ b/src/tint/resolver/pipeline_overridable_constant_test.cc
@@ -20,86 +20,85 @@
namespace {
class ResolverPipelineOverridableConstantTest : public ResolverTest {
- protected:
- /// Verify that the AST node `var` was resolved to an overridable constant
- /// with an ID equal to `id`.
- /// @param var the overridable constant AST node
- /// @param id the expected constant ID
- void ExpectConstantId(const ast::Variable* var, uint16_t id) {
- auto* sem = Sem().Get<sem::GlobalVariable>(var);
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Declaration(), var);
- EXPECT_TRUE(sem->IsOverridable());
- EXPECT_EQ(sem->ConstantId(), id);
- EXPECT_FALSE(sem->ConstantValue());
- }
+ protected:
+ /// Verify that the AST node `var` was resolved to an overridable constant
+ /// with an ID equal to `id`.
+ /// @param var the overridable constant AST node
+ /// @param id the expected constant ID
+ void ExpectConstantId(const ast::Variable* var, uint16_t id) {
+ auto* sem = Sem().Get<sem::GlobalVariable>(var);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Declaration(), var);
+ EXPECT_TRUE(sem->IsOverridable());
+ EXPECT_EQ(sem->ConstantId(), id);
+ EXPECT_FALSE(sem->ConstantValue());
+ }
};
TEST_F(ResolverPipelineOverridableConstantTest, NonOverridable) {
- auto* a = GlobalConst("a", ty.f32(), Expr(1.f));
+ auto* a = GlobalConst("a", ty.f32(), Expr(1.f));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get<sem::GlobalVariable>(a);
- ASSERT_NE(sem_a, nullptr);
- EXPECT_EQ(sem_a->Declaration(), a);
- EXPECT_FALSE(sem_a->IsOverridable());
- EXPECT_TRUE(sem_a->ConstantValue());
+ auto* sem_a = Sem().Get<sem::GlobalVariable>(a);
+ ASSERT_NE(sem_a, nullptr);
+ EXPECT_EQ(sem_a->Declaration(), a);
+ EXPECT_FALSE(sem_a->IsOverridable());
+ EXPECT_TRUE(sem_a->ConstantValue());
}
TEST_F(ResolverPipelineOverridableConstantTest, WithId) {
- auto* a = Override("a", ty.f32(), Expr(1.f), {Id(7u)});
+ auto* a = Override("a", ty.f32(), Expr(1.f), {Id(7u)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ExpectConstantId(a, 7u);
+ ExpectConstantId(a, 7u);
}
TEST_F(ResolverPipelineOverridableConstantTest, WithoutId) {
- auto* a = Override("a", ty.f32(), Expr(1.f));
+ auto* a = Override("a", ty.f32(), Expr(1.f));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ExpectConstantId(a, 0u);
+ ExpectConstantId(a, 0u);
}
TEST_F(ResolverPipelineOverridableConstantTest, WithAndWithoutIds) {
- std::vector<ast::Variable*> variables;
- auto* a = Override("a", ty.f32(), Expr(1.f));
- auto* b = Override("b", ty.f32(), Expr(1.f));
- auto* c = Override("c", ty.f32(), Expr(1.f), {Id(2u)});
- auto* d = Override("d", ty.f32(), Expr(1.f), {Id(4u)});
- auto* e = Override("e", ty.f32(), Expr(1.f));
- auto* f = Override("f", ty.f32(), Expr(1.f), {Id(1u)});
+ std::vector<ast::Variable*> variables;
+ auto* a = Override("a", ty.f32(), Expr(1.f));
+ auto* b = Override("b", ty.f32(), Expr(1.f));
+ auto* c = Override("c", ty.f32(), Expr(1.f), {Id(2u)});
+ auto* d = Override("d", ty.f32(), Expr(1.f), {Id(4u)});
+ auto* e = Override("e", ty.f32(), Expr(1.f));
+ auto* f = Override("f", ty.f32(), Expr(1.f), {Id(1u)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- // Verify that constant id allocation order is deterministic.
- ExpectConstantId(a, 0u);
- ExpectConstantId(b, 3u);
- ExpectConstantId(c, 2u);
- ExpectConstantId(d, 4u);
- ExpectConstantId(e, 5u);
- ExpectConstantId(f, 1u);
+ // Verify that constant id allocation order is deterministic.
+ ExpectConstantId(a, 0u);
+ ExpectConstantId(b, 3u);
+ ExpectConstantId(c, 2u);
+ ExpectConstantId(d, 4u);
+ ExpectConstantId(e, 5u);
+ ExpectConstantId(f, 1u);
}
TEST_F(ResolverPipelineOverridableConstantTest, DuplicateIds) {
- Override("a", ty.f32(), Expr(1.f), {Id(Source{{12, 34}}, 7u)});
- Override("b", ty.f32(), Expr(1.f), {Id(Source{{56, 78}}, 7u)});
+ Override("a", ty.f32(), Expr(1.f), {Id(Source{{12, 34}}, 7u)});
+ Override("b", ty.f32(), Expr(1.f), {Id(Source{{56, 78}}, 7u)});
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), R"(56:78 error: pipeline constant IDs must be unique
+ EXPECT_EQ(r()->error(), R"(56:78 error: pipeline constant IDs must be unique
12:34 note: a pipeline constant with an ID of 7 was previously declared here:)");
}
TEST_F(ResolverPipelineOverridableConstantTest, IdTooLarge) {
- Override("a", ty.f32(), Expr(1.f), {Id(Source{{12, 34}}, 65536u)});
+ Override("a", ty.f32(), Expr(1.f), {Id(Source{{12, 34}}, 65536u)});
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: pipeline constant IDs must be between 0 and 65535");
+ EXPECT_EQ(r()->error(), "12:34 error: pipeline constant IDs must be between 0 and 65535");
}
} // namespace
diff --git a/src/tint/resolver/ptr_ref_test.cc b/src/tint/resolver/ptr_ref_test.cc
index 36273b3..69b7e54 100644
--- a/src/tint/resolver/ptr_ref_test.cc
+++ b/src/tint/resolver/ptr_ref_test.cc
@@ -21,101 +21,88 @@
namespace tint::resolver {
namespace {
-struct ResolverPtrRefTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverPtrRefTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverPtrRefTest, AddressOf) {
- // var v : i32;
- // &v
+ // var v : i32;
+ // &v
- auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
- auto* expr = AddressOf(v);
+ auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
+ auto* expr = AddressOf(v);
- WrapInFunction(v, expr);
+ WrapInFunction(v, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(expr)->Is<sem::Pointer>());
- EXPECT_TRUE(TypeOf(expr)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
- EXPECT_EQ(TypeOf(expr)->As<sem::Pointer>()->StorageClass(),
- ast::StorageClass::kFunction);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::Pointer>());
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
+ EXPECT_EQ(TypeOf(expr)->As<sem::Pointer>()->StorageClass(), ast::StorageClass::kFunction);
}
TEST_F(ResolverPtrRefTest, AddressOfThenDeref) {
- // var v : i32;
- // *(&v)
+ // var v : i32;
+ // *(&v)
- auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
- auto* expr = Deref(AddressOf(v));
+ auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
+ auto* expr = Deref(AddressOf(v));
- WrapInFunction(v, expr);
+ WrapInFunction(v, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(expr)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(expr)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
}
TEST_F(ResolverPtrRefTest, DefaultPtrStorageClass) {
- // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
+ // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
- auto* buf = Structure("S", {Member("m", ty.i32())});
- auto* function = Var("f", ty.i32());
- auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
- auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* uniform = Global("ub", ty.Of(buf), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(0),
- });
+ auto* buf = Structure("S", {Member("m", ty.i32())});
+ auto* function = Var("f", ty.i32());
+ auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
+ auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* uniform = Global("ub", ty.Of(buf), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(0),
+ });
- auto* function_ptr =
- Let("f_ptr", ty.pointer(ty.i32(), ast::StorageClass::kFunction),
- AddressOf(function));
- auto* private_ptr =
- Let("p_ptr", ty.pointer(ty.i32(), ast::StorageClass::kPrivate),
- AddressOf(private_));
- auto* workgroup_ptr =
- Let("w_ptr", ty.pointer(ty.i32(), ast::StorageClass::kWorkgroup),
- AddressOf(workgroup));
- auto* uniform_ptr =
- Let("ub_ptr", ty.pointer(ty.Of(buf), ast::StorageClass::kUniform),
- AddressOf(uniform));
- auto* storage_ptr =
- Let("sb_ptr", ty.pointer(ty.Of(buf), ast::StorageClass::kStorage),
- AddressOf(storage));
+ auto* function_ptr =
+ Let("f_ptr", ty.pointer(ty.i32(), ast::StorageClass::kFunction), AddressOf(function));
+ auto* private_ptr =
+ Let("p_ptr", ty.pointer(ty.i32(), ast::StorageClass::kPrivate), AddressOf(private_));
+ auto* workgroup_ptr =
+ Let("w_ptr", ty.pointer(ty.i32(), ast::StorageClass::kWorkgroup), AddressOf(workgroup));
+ auto* uniform_ptr =
+ Let("ub_ptr", ty.pointer(ty.Of(buf), ast::StorageClass::kUniform), AddressOf(uniform));
+ auto* storage_ptr =
+ Let("sb_ptr", ty.pointer(ty.Of(buf), ast::StorageClass::kStorage), AddressOf(storage));
- WrapInFunction(function, function_ptr, private_ptr, workgroup_ptr,
- uniform_ptr, storage_ptr);
+ WrapInFunction(function, function_ptr, private_ptr, workgroup_ptr, uniform_ptr, storage_ptr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(function_ptr)->Is<sem::Pointer>())
- << "function_ptr is " << TypeOf(function_ptr)->TypeInfo().name;
- ASSERT_TRUE(TypeOf(private_ptr)->Is<sem::Pointer>())
- << "private_ptr is " << TypeOf(private_ptr)->TypeInfo().name;
- ASSERT_TRUE(TypeOf(workgroup_ptr)->Is<sem::Pointer>())
- << "workgroup_ptr is " << TypeOf(workgroup_ptr)->TypeInfo().name;
- ASSERT_TRUE(TypeOf(uniform_ptr)->Is<sem::Pointer>())
- << "uniform_ptr is " << TypeOf(uniform_ptr)->TypeInfo().name;
- ASSERT_TRUE(TypeOf(storage_ptr)->Is<sem::Pointer>())
- << "storage_ptr is " << TypeOf(storage_ptr)->TypeInfo().name;
+ ASSERT_TRUE(TypeOf(function_ptr)->Is<sem::Pointer>())
+ << "function_ptr is " << TypeOf(function_ptr)->TypeInfo().name;
+ ASSERT_TRUE(TypeOf(private_ptr)->Is<sem::Pointer>())
+ << "private_ptr is " << TypeOf(private_ptr)->TypeInfo().name;
+ ASSERT_TRUE(TypeOf(workgroup_ptr)->Is<sem::Pointer>())
+ << "workgroup_ptr is " << TypeOf(workgroup_ptr)->TypeInfo().name;
+ ASSERT_TRUE(TypeOf(uniform_ptr)->Is<sem::Pointer>())
+ << "uniform_ptr is " << TypeOf(uniform_ptr)->TypeInfo().name;
+ ASSERT_TRUE(TypeOf(storage_ptr)->Is<sem::Pointer>())
+ << "storage_ptr is " << TypeOf(storage_ptr)->TypeInfo().name;
- EXPECT_EQ(TypeOf(function_ptr)->As<sem::Pointer>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(private_ptr)->As<sem::Pointer>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(workgroup_ptr)->As<sem::Pointer>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(uniform_ptr)->As<sem::Pointer>()->Access(),
- ast::Access::kRead);
- EXPECT_EQ(TypeOf(storage_ptr)->As<sem::Pointer>()->Access(),
- ast::Access::kRead);
+ EXPECT_EQ(TypeOf(function_ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(private_ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(workgroup_ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(uniform_ptr)->As<sem::Pointer>()->Access(), ast::Access::kRead);
+ EXPECT_EQ(TypeOf(storage_ptr)->As<sem::Pointer>()->Access(), ast::Access::kRead);
}
} // namespace
diff --git a/src/tint/resolver/ptr_ref_validation_test.cc b/src/tint/resolver/ptr_ref_validation_test.cc
index 6c0a76a..328b35d 100644
--- a/src/tint/resolver/ptr_ref_validation_test.cc
+++ b/src/tint/resolver/ptr_ref_validation_test.cc
@@ -22,150 +22,140 @@
namespace tint::resolver {
namespace {
-struct ResolverPtrRefValidationTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverPtrRefValidationTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverPtrRefValidationTest, AddressOfLiteral) {
- // &1
+ // &1
- auto* expr = AddressOf(Expr(Source{{12, 34}}, 1));
+ auto* expr = AddressOf(Expr(Source{{12, 34}}, 1));
- WrapInFunction(expr);
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
}
TEST_F(ResolverPtrRefValidationTest, AddressOfLet) {
- // let l : i32 = 1;
- // &l
- auto* l = Let("l", ty.i32(), Expr(1));
- auto* expr = AddressOf(Expr(Source{{12, 34}}, "l"));
+ // let l : i32 = 1;
+ // &l
+ auto* l = Let("l", ty.i32(), Expr(1));
+ auto* expr = AddressOf(Expr(Source{{12, 34}}, "l"));
- WrapInFunction(l, expr);
+ WrapInFunction(l, expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of expression");
}
TEST_F(ResolverPtrRefValidationTest, AddressOfHandle) {
- // @group(0) @binding(0) var t: texture_3d<f32>;
- // &t
- Global("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()),
- GroupAndBinding(0u, 0u));
- auto* expr = AddressOf(Expr(Source{{12, 34}}, "t"));
- WrapInFunction(expr);
+ // @group(0) @binding(0) var t: texture_3d<f32>;
+ // &t
+ Global("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()), GroupAndBinding(0u, 0u));
+ auto* expr = AddressOf(Expr(Source{{12, 34}}, "t"));
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot take the address of expression in handle "
- "storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot take the address of expression in handle "
+ "storage class");
}
TEST_F(ResolverPtrRefValidationTest, AddressOfVectorComponent_MemberAccessor) {
- // var v : vec4<i32>;
- // &v.y
- auto* v = Var("v", ty.vec4<i32>());
- auto* expr = AddressOf(MemberAccessor(Source{{12, 34}}, "v", "y"));
+ // var v : vec4<i32>;
+ // &v.y
+ auto* v = Var("v", ty.vec4<i32>());
+ auto* expr = AddressOf(MemberAccessor(Source{{12, 34}}, "v", "y"));
- WrapInFunction(v, expr);
+ WrapInFunction(v, expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot take the address of a vector component");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of a vector component");
}
TEST_F(ResolverPtrRefValidationTest, AddressOfVectorComponent_IndexAccessor) {
- // var v : vec4<i32>;
- // &v[2]
- auto* v = Var("v", ty.vec4<i32>());
- auto* expr = AddressOf(IndexAccessor(Source{{12, 34}}, "v", 2));
+ // var v : vec4<i32>;
+ // &v[2]
+ auto* v = Var("v", ty.vec4<i32>());
+ auto* expr = AddressOf(IndexAccessor(Source{{12, 34}}, "v", 2));
- WrapInFunction(v, expr);
+ WrapInFunction(v, expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot take the address of a vector component");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot take the address of a vector component");
}
TEST_F(ResolverPtrRefValidationTest, IndirectOfAddressOfHandle) {
- // @group(0) @binding(0) var t: texture_3d<f32>;
- // *&t
- Global("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()),
- GroupAndBinding(0u, 0u));
- auto* expr = Deref(AddressOf(Expr(Source{{12, 34}}, "t")));
- WrapInFunction(expr);
+ // @group(0) @binding(0) var t: texture_3d<f32>;
+ // *&t
+ Global("t", ty.sampled_texture(ast::TextureDimension::k3d, ty.f32()), GroupAndBinding(0u, 0u));
+ auto* expr = Deref(AddressOf(Expr(Source{{12, 34}}, "t")));
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot take the address of expression in handle "
- "storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot take the address of expression in handle "
+ "storage class");
}
TEST_F(ResolverPtrRefValidationTest, DerefOfLiteral) {
- // *1
+ // *1
- auto* expr = Deref(Expr(Source{{12, 34}}, 1));
+ auto* expr = Deref(Expr(Source{{12, 34}}, 1));
- WrapInFunction(expr);
+ WrapInFunction(expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot dereference expression of type 'i32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot dereference expression of type 'i32'");
}
TEST_F(ResolverPtrRefValidationTest, DerefOfVar) {
- // var v : i32 = 1;
- // *1
- auto* v = Var("v", ty.i32());
- auto* expr = Deref(Expr(Source{{12, 34}}, "v"));
+ // var v : i32 = 1;
+ // *1
+ auto* v = Var("v", ty.i32());
+ auto* expr = Deref(Expr(Source{{12, 34}}, "v"));
- WrapInFunction(v, expr);
+ WrapInFunction(v, expr);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot dereference expression of type 'i32'");
+ EXPECT_EQ(r()->error(), "12:34 error: cannot dereference expression of type 'i32'");
}
TEST_F(ResolverPtrRefValidationTest, InferredPtrAccessMismatch) {
- // struct Inner {
- // arr: array<i32, 4>;
- // }
- // struct S {
- // inner: Inner;
- // }
- // @group(0) @binding(0) var<storage, read_write> s : S;
- // fn f() {
- // let p : pointer<storage, i32> = &s.inner.arr[2];
- // }
- auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
- auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
- auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct Inner {
+ // arr: array<i32, 4>;
+ // }
+ // struct S {
+ // inner: Inner;
+ // }
+ // @group(0) @binding(0) var<storage, read_write> s : S;
+ // fn f() {
+ // let p : pointer<storage, i32> = &s.inner.arr[2];
+ // }
+ auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
+ auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+ auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- auto* expr =
- IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
- auto* ptr =
- Let(Source{{12, 34}}, "p", ty.pointer<i32>(ast::StorageClass::kStorage),
- AddressOf(expr));
+ auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
+ auto* ptr =
+ Let(Source{{12, 34}}, "p", ty.pointer<i32>(ast::StorageClass::kStorage), AddressOf(expr));
- WrapInFunction(ptr);
+ WrapInFunction(ptr);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot initialize let of type "
- "'ptr<storage, i32, read>' with value of type "
- "'ptr<storage, i32, read_write>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot initialize let of type "
+ "'ptr<storage, i32, read>' with value of type "
+ "'ptr<storage, i32, read_write>'");
}
} // namespace
diff --git a/src/tint/resolver/resolver.cc b/src/tint/resolver/resolver.cc
index d01fc2c..fec6d70 100644
--- a/src/tint/resolver/resolver.cc
+++ b/src/tint/resolver/resolver.cc
@@ -91,2711 +91,2559 @@
Resolver::~Resolver() = default;
bool Resolver::Resolve() {
- if (builder_->Diagnostics().contains_errors()) {
- return false;
- }
+ if (builder_->Diagnostics().contains_errors()) {
+ return false;
+ }
- if (!DependencyGraph::Build(builder_->AST(), builder_->Symbols(),
- builder_->Diagnostics(), dependencies_)) {
- return false;
- }
+ if (!DependencyGraph::Build(builder_->AST(), builder_->Symbols(), builder_->Diagnostics(),
+ dependencies_)) {
+ return false;
+ }
- // Create the semantic module
- builder_->Sem().SetModule(
- builder_->create<sem::Module>(dependencies_.ordered_globals));
+ // Create the semantic module
+ builder_->Sem().SetModule(builder_->create<sem::Module>(dependencies_.ordered_globals));
- bool result = ResolveInternal();
+ bool result = ResolveInternal();
- if (!result && !diagnostics_.contains_errors()) {
- TINT_ICE(Resolver, diagnostics_)
- << "resolving failed, but no error was raised";
- return false;
- }
+ if (!result && !diagnostics_.contains_errors()) {
+ TINT_ICE(Resolver, diagnostics_) << "resolving failed, but no error was raised";
+ return false;
+ }
- return result;
+ return result;
}
bool Resolver::ResolveInternal() {
- Mark(&builder_->AST());
+ Mark(&builder_->AST());
- // Process all module-scope declarations in dependency order.
- for (auto* decl : dependencies_.ordered_globals) {
- Mark(decl);
- // Enable directives don't have sem node.
- if (decl->Is<ast::Enable>()) {
- continue;
+ // Process all module-scope declarations in dependency order.
+ for (auto* decl : dependencies_.ordered_globals) {
+ Mark(decl);
+ // Enable directives don't have sem node.
+ if (decl->Is<ast::Enable>()) {
+ continue;
+ }
+ if (!Switch(
+ decl, //
+ [&](const ast::TypeDecl* td) { return TypeDecl(td); },
+ [&](const ast::Function* func) { return Function(func); },
+ [&](const ast::Variable* var) { return GlobalVariable(var); },
+ [&](Default) {
+ TINT_UNREACHABLE(Resolver, diagnostics_)
+ << "unhandled global declaration: " << decl->TypeInfo().name;
+ return nullptr;
+ })) {
+ return false;
+ }
}
- if (!Switch(
- decl, //
- [&](const ast::TypeDecl* td) { return TypeDecl(td); },
- [&](const ast::Function* func) { return Function(func); },
- [&](const ast::Variable* var) { return GlobalVariable(var); },
- [&](Default) {
- TINT_UNREACHABLE(Resolver, diagnostics_)
- << "unhandled global declaration: " << decl->TypeInfo().name;
- return nullptr;
- })) {
- return false;
+
+ AllocateOverridableConstantIds();
+
+ SetShadows();
+
+ if (!validator_.PipelineStages(entry_points_)) {
+ return false;
}
- }
- AllocateOverridableConstantIds();
-
- SetShadows();
-
- if (!validator_.PipelineStages(entry_points_)) {
- return false;
- }
-
- bool result = true;
- for (auto* node : builder_->ASTNodes().Objects()) {
- if (marked_.count(node) == 0) {
- TINT_ICE(Resolver, diagnostics_) << "AST node '" << node->TypeInfo().name
- << "' was not reached by the resolver\n"
- << "At: " << node->source << "\n"
- << "Pointer: " << node;
- result = false;
+ bool result = true;
+ for (auto* node : builder_->ASTNodes().Objects()) {
+ if (marked_.count(node) == 0) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "AST node '" << node->TypeInfo().name << "' was not reached by the resolver\n"
+ << "At: " << node->source << "\n"
+ << "Pointer: " << node;
+ result = false;
+ }
}
- }
- return result;
+ return result;
}
sem::Type* Resolver::Type(const ast::Type* ty) {
- Mark(ty);
- auto* s = Switch(
- ty, //
- [&](const ast::Void*) { return builder_->create<sem::Void>(); },
- [&](const ast::Bool*) { return builder_->create<sem::Bool>(); },
- [&](const ast::I32*) { return builder_->create<sem::I32>(); },
- [&](const ast::U32*) { return builder_->create<sem::U32>(); },
- [&](const ast::F32*) { return builder_->create<sem::F32>(); },
- [&](const ast::Vector* t) -> sem::Vector* {
- if (!t->type) {
- AddError("missing vector element type", t->source.End());
- return nullptr;
- }
- if (auto* el = Type(t->type)) {
- if (auto* vector = builder_->create<sem::Vector>(el, t->width)) {
- if (validator_.Vector(vector, t->source)) {
- return vector;
+ Mark(ty);
+ auto* s = Switch(
+ ty, //
+ [&](const ast::Void*) { return builder_->create<sem::Void>(); },
+ [&](const ast::Bool*) { return builder_->create<sem::Bool>(); },
+ [&](const ast::I32*) { return builder_->create<sem::I32>(); },
+ [&](const ast::U32*) { return builder_->create<sem::U32>(); },
+ [&](const ast::F32*) { return builder_->create<sem::F32>(); },
+ [&](const ast::Vector* t) -> sem::Vector* {
+ if (!t->type) {
+ AddError("missing vector element type", t->source.End());
+ return nullptr;
}
- }
- }
- return nullptr;
- },
- [&](const ast::Matrix* t) -> sem::Matrix* {
- if (!t->type) {
- AddError("missing matrix element type", t->source.End());
- return nullptr;
- }
- if (auto* el = Type(t->type)) {
- if (auto* column_type = builder_->create<sem::Vector>(el, t->rows)) {
- if (auto* matrix =
- builder_->create<sem::Matrix>(column_type, t->columns)) {
- if (validator_.Matrix(matrix, t->source)) {
- return matrix;
- }
- }
- }
- }
- return nullptr;
- },
- [&](const ast::Array* t) { return Array(t); },
- [&](const ast::Atomic* t) -> sem::Atomic* {
- if (auto* el = Type(t->type)) {
- auto* a = builder_->create<sem::Atomic>(el);
- if (!validator_.Atomic(t, a)) {
- return nullptr;
- }
- return a;
- }
- return nullptr;
- },
- [&](const ast::Pointer* t) -> sem::Pointer* {
- if (auto* el = Type(t->type)) {
- auto access = t->access;
- if (access == ast::kUndefined) {
- access = DefaultAccessForStorageClass(t->storage_class);
- }
- return builder_->create<sem::Pointer>(el, t->storage_class, access);
- }
- return nullptr;
- },
- [&](const ast::Sampler* t) {
- return builder_->create<sem::Sampler>(t->kind);
- },
- [&](const ast::SampledTexture* t) -> sem::SampledTexture* {
- if (auto* el = Type(t->type)) {
- return builder_->create<sem::SampledTexture>(t->dim, el);
- }
- return nullptr;
- },
- [&](const ast::MultisampledTexture* t) -> sem::MultisampledTexture* {
- if (auto* el = Type(t->type)) {
- return builder_->create<sem::MultisampledTexture>(t->dim, el);
- }
- return nullptr;
- },
- [&](const ast::DepthTexture* t) {
- return builder_->create<sem::DepthTexture>(t->dim);
- },
- [&](const ast::DepthMultisampledTexture* t) {
- return builder_->create<sem::DepthMultisampledTexture>(t->dim);
- },
- [&](const ast::StorageTexture* t) -> sem::StorageTexture* {
- if (auto* el = Type(t->type)) {
- if (!validator_.StorageTexture(t)) {
- return nullptr;
- }
- return builder_->create<sem::StorageTexture>(t->dim, t->format,
- t->access, el);
- }
- return nullptr;
- },
- [&](const ast::ExternalTexture*) {
- return builder_->create<sem::ExternalTexture>();
- },
- [&](Default) {
- auto* resolved = sem_.ResolvedSymbol(ty);
- return Switch(
- resolved, //
- [&](sem::Type* type) { return type; },
- [&](sem::Variable* var) {
- auto name =
- builder_->Symbols().NameFor(var->Declaration()->symbol);
- AddError("cannot use variable '" + name + "' as type",
- ty->source);
- AddNote("'" + name + "' declared here",
- var->Declaration()->source);
- return nullptr;
- },
- [&](sem::Function* func) {
- auto name =
- builder_->Symbols().NameFor(func->Declaration()->symbol);
- AddError("cannot use function '" + name + "' as type",
- ty->source);
- AddNote("'" + name + "' declared here",
- func->Declaration()->source);
- return nullptr;
- },
- [&](Default) {
- if (auto* tn = ty->As<ast::TypeName>()) {
- if (IsBuiltin(tn->name)) {
- auto name = builder_->Symbols().NameFor(tn->name);
- AddError("cannot use builtin '" + name + "' as type",
- ty->source);
- return nullptr;
+ if (auto* el = Type(t->type)) {
+ if (auto* vector = builder_->create<sem::Vector>(el, t->width)) {
+ if (validator_.Vector(vector, t->source)) {
+ return vector;
+ }
}
- }
- TINT_UNREACHABLE(Resolver, diagnostics_)
- << "Unhandled resolved type '"
- << (resolved ? resolved->TypeInfo().name : "<null>")
- << "' resolved from ast::Type '" << ty->TypeInfo().name
- << "'";
- return nullptr;
- });
- });
+ }
+ return nullptr;
+ },
+ [&](const ast::Matrix* t) -> sem::Matrix* {
+ if (!t->type) {
+ AddError("missing matrix element type", t->source.End());
+ return nullptr;
+ }
+ if (auto* el = Type(t->type)) {
+ if (auto* column_type = builder_->create<sem::Vector>(el, t->rows)) {
+ if (auto* matrix = builder_->create<sem::Matrix>(column_type, t->columns)) {
+ if (validator_.Matrix(matrix, t->source)) {
+ return matrix;
+ }
+ }
+ }
+ }
+ return nullptr;
+ },
+ [&](const ast::Array* t) { return Array(t); },
+ [&](const ast::Atomic* t) -> sem::Atomic* {
+ if (auto* el = Type(t->type)) {
+ auto* a = builder_->create<sem::Atomic>(el);
+ if (!validator_.Atomic(t, a)) {
+ return nullptr;
+ }
+ return a;
+ }
+ return nullptr;
+ },
+ [&](const ast::Pointer* t) -> sem::Pointer* {
+ if (auto* el = Type(t->type)) {
+ auto access = t->access;
+ if (access == ast::kUndefined) {
+ access = DefaultAccessForStorageClass(t->storage_class);
+ }
+ return builder_->create<sem::Pointer>(el, t->storage_class, access);
+ }
+ return nullptr;
+ },
+ [&](const ast::Sampler* t) { return builder_->create<sem::Sampler>(t->kind); },
+ [&](const ast::SampledTexture* t) -> sem::SampledTexture* {
+ if (auto* el = Type(t->type)) {
+ return builder_->create<sem::SampledTexture>(t->dim, el);
+ }
+ return nullptr;
+ },
+ [&](const ast::MultisampledTexture* t) -> sem::MultisampledTexture* {
+ if (auto* el = Type(t->type)) {
+ return builder_->create<sem::MultisampledTexture>(t->dim, el);
+ }
+ return nullptr;
+ },
+ [&](const ast::DepthTexture* t) { return builder_->create<sem::DepthTexture>(t->dim); },
+ [&](const ast::DepthMultisampledTexture* t) {
+ return builder_->create<sem::DepthMultisampledTexture>(t->dim);
+ },
+ [&](const ast::StorageTexture* t) -> sem::StorageTexture* {
+ if (auto* el = Type(t->type)) {
+ if (!validator_.StorageTexture(t)) {
+ return nullptr;
+ }
+ return builder_->create<sem::StorageTexture>(t->dim, t->format, t->access, el);
+ }
+ return nullptr;
+ },
+ [&](const ast::ExternalTexture*) { return builder_->create<sem::ExternalTexture>(); },
+ [&](Default) {
+ auto* resolved = sem_.ResolvedSymbol(ty);
+ return Switch(
+ resolved, //
+ [&](sem::Type* type) { return type; },
+ [&](sem::Variable* var) {
+ auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+ AddError("cannot use variable '" + name + "' as type", ty->source);
+ AddNote("'" + name + "' declared here", var->Declaration()->source);
+ return nullptr;
+ },
+ [&](sem::Function* func) {
+ auto name = builder_->Symbols().NameFor(func->Declaration()->symbol);
+ AddError("cannot use function '" + name + "' as type", ty->source);
+ AddNote("'" + name + "' declared here", func->Declaration()->source);
+ return nullptr;
+ },
+ [&](Default) {
+ if (auto* tn = ty->As<ast::TypeName>()) {
+ if (IsBuiltin(tn->name)) {
+ auto name = builder_->Symbols().NameFor(tn->name);
+ AddError("cannot use builtin '" + name + "' as type", ty->source);
+ return nullptr;
+ }
+ }
+ TINT_UNREACHABLE(Resolver, diagnostics_)
+ << "Unhandled resolved type '"
+ << (resolved ? resolved->TypeInfo().name : "<null>")
+ << "' resolved from ast::Type '" << ty->TypeInfo().name << "'";
+ return nullptr;
+ });
+ });
- if (s) {
- builder_->Sem().Add(ty, s);
- }
- return s;
+ if (s) {
+ builder_->Sem().Add(ty, s);
+ }
+ return s;
}
sem::Variable* Resolver::Variable(const ast::Variable* var,
VariableKind kind,
uint32_t index /* = 0 */) {
- const sem::Type* storage_ty = nullptr;
+ const sem::Type* storage_ty = nullptr;
- // If the variable has a declared type, resolve it.
- if (auto* ty = var->type) {
- storage_ty = Type(ty);
- if (!storage_ty) {
- return nullptr;
- }
- }
-
- const sem::Expression* rhs = nullptr;
-
- // Does the variable have a constructor?
- if (var->constructor) {
- rhs = Expression(var->constructor);
- if (!rhs) {
- return nullptr;
+ // If the variable has a declared type, resolve it.
+ if (auto* ty = var->type) {
+ storage_ty = Type(ty);
+ if (!storage_ty) {
+ return nullptr;
+ }
}
- // If the variable has no declared type, infer it from the RHS
- if (!storage_ty) {
- if (!var->is_const && kind == VariableKind::kGlobal) {
- AddError("global var declaration must specify a type", var->source);
+ const sem::Expression* rhs = nullptr;
+
+ // Does the variable have a constructor?
+ if (var->constructor) {
+ rhs = Expression(var->constructor);
+ if (!rhs) {
+ return nullptr;
+ }
+
+ // If the variable has no declared type, infer it from the RHS
+ if (!storage_ty) {
+ if (!var->is_const && kind == VariableKind::kGlobal) {
+ AddError("global var declaration must specify a type", var->source);
+ return nullptr;
+ }
+
+ storage_ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
+ }
+ } else if (var->is_const && !var->is_overridable && kind != VariableKind::kParameter) {
+ AddError("let declaration must have an initializer", var->source);
return nullptr;
- }
-
- storage_ty = rhs->Type()->UnwrapRef(); // Implicit load of RHS
+ } else if (!var->type) {
+ AddError((kind == VariableKind::kGlobal)
+ ? "module scope var declaration requires a type and initializer"
+ : "function scope var declaration requires a type or initializer",
+ var->source);
+ return nullptr;
}
- } else if (var->is_const && !var->is_overridable &&
- kind != VariableKind::kParameter) {
- AddError("let declaration must have an initializer", var->source);
- return nullptr;
- } else if (!var->type) {
- AddError(
- (kind == VariableKind::kGlobal)
- ? "module scope var declaration requires a type and initializer"
- : "function scope var declaration requires a type or initializer",
- var->source);
- return nullptr;
- }
- if (!storage_ty) {
- TINT_ICE(Resolver, diagnostics_)
- << "failed to determine storage type for variable '" +
- builder_->Symbols().NameFor(var->symbol) + "'\n"
- << "Source: " << var->source;
- return nullptr;
- }
-
- auto storage_class = var->declared_storage_class;
- if (storage_class == ast::StorageClass::kNone && !var->is_const) {
- // No declared storage class. Infer from usage / type.
- if (kind == VariableKind::kLocal) {
- storage_class = ast::StorageClass::kFunction;
- } else if (storage_ty->UnwrapRef()->is_handle()) {
- // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
- // If the store type is a texture type or a sampler type, then the
- // variable declaration must not have a storage class attribute. The
- // storage class will always be handle.
- storage_class = ast::StorageClass::kHandle;
+ if (!storage_ty) {
+ TINT_ICE(Resolver, diagnostics_) << "failed to determine storage type for variable '" +
+ builder_->Symbols().NameFor(var->symbol) + "'\n"
+ << "Source: " << var->source;
+ return nullptr;
}
- }
- if (kind == VariableKind::kLocal && !var->is_const &&
- storage_class != ast::StorageClass::kFunction &&
- validator_.IsValidationEnabled(
- var->attributes, ast::DisabledValidation::kIgnoreStorageClass)) {
- AddError("function variable has a non-function storage class", var->source);
- return nullptr;
- }
+ auto storage_class = var->declared_storage_class;
+ if (storage_class == ast::StorageClass::kNone && !var->is_const) {
+ // No declared storage class. Infer from usage / type.
+ if (kind == VariableKind::kLocal) {
+ storage_class = ast::StorageClass::kFunction;
+ } else if (storage_ty->UnwrapRef()->is_handle()) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
+ // If the store type is a texture type or a sampler type, then the
+ // variable declaration must not have a storage class attribute. The
+ // storage class will always be handle.
+ storage_class = ast::StorageClass::kHandle;
+ }
+ }
- auto access = var->declared_access;
- if (access == ast::Access::kUndefined) {
- access = DefaultAccessForStorageClass(storage_class);
- }
+ if (kind == VariableKind::kLocal && !var->is_const &&
+ storage_class != ast::StorageClass::kFunction &&
+ validator_.IsValidationEnabled(var->attributes,
+ ast::DisabledValidation::kIgnoreStorageClass)) {
+ AddError("function variable has a non-function storage class", var->source);
+ return nullptr;
+ }
- auto* var_ty = storage_ty;
- if (!var->is_const) {
- // Variable declaration. Unlike `let`, `var` has storage.
- // Variables are always of a reference type to the declared storage type.
- var_ty =
- builder_->create<sem::Reference>(storage_ty, storage_class, access);
- }
+ auto access = var->declared_access;
+ if (access == ast::Access::kUndefined) {
+ access = DefaultAccessForStorageClass(storage_class);
+ }
- if (rhs && !validator_.VariableConstructorOrCast(var, storage_class,
- storage_ty, rhs->Type())) {
- return nullptr;
- }
+ auto* var_ty = storage_ty;
+ if (!var->is_const) {
+ // Variable declaration. Unlike `let`, `var` has storage.
+ // Variables are always of a reference type to the declared storage type.
+ var_ty = builder_->create<sem::Reference>(storage_ty, storage_class, access);
+ }
- if (!ApplyStorageClassUsageToType(
- storage_class, const_cast<sem::Type*>(var_ty), var->source)) {
- AddNote(
- std::string("while instantiating ") +
- ((kind == VariableKind::kParameter) ? "parameter " : "variable ") +
- builder_->Symbols().NameFor(var->symbol),
- var->source);
- return nullptr;
- }
+ if (rhs && !validator_.VariableConstructorOrCast(var, storage_class, storage_ty, rhs->Type())) {
+ return nullptr;
+ }
- if (kind == VariableKind::kParameter) {
- if (auto* ptr = var_ty->As<sem::Pointer>()) {
- // For MSL, we push module-scope variables into the entry point as pointer
- // parameters, so we also need to handle their store type.
- if (!ApplyStorageClassUsageToType(
- ptr->StorageClass(), const_cast<sem::Type*>(ptr->StoreType()),
- var->source)) {
- AddNote("while instantiating parameter " +
+ if (!ApplyStorageClassUsageToType(storage_class, const_cast<sem::Type*>(var_ty), var->source)) {
+ AddNote(std::string("while instantiating ") +
+ ((kind == VariableKind::kParameter) ? "parameter " : "variable ") +
builder_->Symbols().NameFor(var->symbol),
var->source);
return nullptr;
- }
}
- }
- switch (kind) {
- case VariableKind::kGlobal: {
- sem::BindingPoint binding_point;
- if (auto bp = var->BindingPoint()) {
- binding_point = {bp.group->value, bp.binding->value};
- }
-
- bool has_const_val = rhs && var->is_const && !var->is_overridable;
- auto* global = builder_->create<sem::GlobalVariable>(
- var, var_ty, storage_class, access,
- has_const_val ? rhs->ConstantValue() : sem::Constant{},
- binding_point);
-
- if (var->is_overridable) {
- global->SetIsOverridable();
- if (auto* id = ast::GetAttribute<ast::IdAttribute>(var->attributes)) {
- global->SetConstantId(static_cast<uint16_t>(id->value));
+ if (kind == VariableKind::kParameter) {
+ if (auto* ptr = var_ty->As<sem::Pointer>()) {
+ // For MSL, we push module-scope variables into the entry point as pointer
+ // parameters, so we also need to handle their store type.
+ if (!ApplyStorageClassUsageToType(
+ ptr->StorageClass(), const_cast<sem::Type*>(ptr->StoreType()), var->source)) {
+ AddNote("while instantiating parameter " + builder_->Symbols().NameFor(var->symbol),
+ var->source);
+ return nullptr;
+ }
}
- }
-
- global->SetConstructor(rhs);
-
- builder_->Sem().Add(var, global);
- return global;
}
- case VariableKind::kLocal: {
- auto* local = builder_->create<sem::LocalVariable>(
- var, var_ty, storage_class, access, current_statement_,
- (rhs && var->is_const) ? rhs->ConstantValue() : sem::Constant{});
- builder_->Sem().Add(var, local);
- local->SetConstructor(rhs);
- return local;
- }
- case VariableKind::kParameter: {
- auto* param = builder_->create<sem::Parameter>(var, index, var_ty,
- storage_class, access);
- builder_->Sem().Add(var, param);
- return param;
- }
- }
- TINT_UNREACHABLE(Resolver, diagnostics_)
- << "unhandled VariableKind " << static_cast<int>(kind);
- return nullptr;
+ switch (kind) {
+ case VariableKind::kGlobal: {
+ sem::BindingPoint binding_point;
+ if (auto bp = var->BindingPoint()) {
+ binding_point = {bp.group->value, bp.binding->value};
+ }
+
+ bool has_const_val = rhs && var->is_const && !var->is_overridable;
+ auto* global = builder_->create<sem::GlobalVariable>(
+ var, var_ty, storage_class, access,
+ has_const_val ? rhs->ConstantValue() : sem::Constant{}, binding_point);
+
+ if (var->is_overridable) {
+ global->SetIsOverridable();
+ if (auto* id = ast::GetAttribute<ast::IdAttribute>(var->attributes)) {
+ global->SetConstantId(static_cast<uint16_t>(id->value));
+ }
+ }
+
+ global->SetConstructor(rhs);
+
+ builder_->Sem().Add(var, global);
+ return global;
+ }
+ case VariableKind::kLocal: {
+ auto* local = builder_->create<sem::LocalVariable>(
+ var, var_ty, storage_class, access, current_statement_,
+ (rhs && var->is_const) ? rhs->ConstantValue() : sem::Constant{});
+ builder_->Sem().Add(var, local);
+ local->SetConstructor(rhs);
+ return local;
+ }
+ case VariableKind::kParameter: {
+ auto* param =
+ builder_->create<sem::Parameter>(var, index, var_ty, storage_class, access);
+ builder_->Sem().Add(var, param);
+ return param;
+ }
+ }
+
+ TINT_UNREACHABLE(Resolver, diagnostics_) << "unhandled VariableKind " << static_cast<int>(kind);
+ return nullptr;
}
-ast::Access Resolver::DefaultAccessForStorageClass(
- ast::StorageClass storage_class) {
- // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
- switch (storage_class) {
- case ast::StorageClass::kStorage:
- case ast::StorageClass::kUniform:
- case ast::StorageClass::kHandle:
- return ast::Access::kRead;
- default:
- break;
- }
- return ast::Access::kReadWrite;
+ast::Access Resolver::DefaultAccessForStorageClass(ast::StorageClass storage_class) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
+ switch (storage_class) {
+ case ast::StorageClass::kStorage:
+ case ast::StorageClass::kUniform:
+ case ast::StorageClass::kHandle:
+ return ast::Access::kRead;
+ default:
+ break;
+ }
+ return ast::Access::kReadWrite;
}
void Resolver::AllocateOverridableConstantIds() {
- // The next pipeline constant ID to try to allocate.
- uint16_t next_constant_id = 0;
+ // The next pipeline constant ID to try to allocate.
+ uint16_t next_constant_id = 0;
- // Allocate constant IDs in global declaration order, so that they are
- // deterministic.
- // TODO(crbug.com/tint/1192): If a transform changes the order or removes an
- // unused constant, the allocation may change on the next Resolver pass.
- for (auto* decl : builder_->AST().GlobalDeclarations()) {
- auto* var = decl->As<ast::Variable>();
- if (!var || !var->is_overridable) {
- continue;
- }
-
- uint16_t constant_id;
- if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(var->attributes)) {
- constant_id = static_cast<uint16_t>(id_attr->value);
- } else {
- // No ID was specified, so allocate the next available ID.
- constant_id = next_constant_id;
- while (constant_ids_.count(constant_id)) {
- if (constant_id == UINT16_MAX) {
- TINT_ICE(Resolver, builder_->Diagnostics())
- << "no more pipeline constant IDs available";
- return;
+ // Allocate constant IDs in global declaration order, so that they are
+ // deterministic.
+ // TODO(crbug.com/tint/1192): If a transform changes the order or removes an
+ // unused constant, the allocation may change on the next Resolver pass.
+ for (auto* decl : builder_->AST().GlobalDeclarations()) {
+ auto* var = decl->As<ast::Variable>();
+ if (!var || !var->is_overridable) {
+ continue;
}
- constant_id++;
- }
- next_constant_id = constant_id + 1;
- }
- auto* sem = sem_.Get<sem::GlobalVariable>(var);
- const_cast<sem::GlobalVariable*>(sem)->SetConstantId(constant_id);
- }
+ uint16_t constant_id;
+ if (auto* id_attr = ast::GetAttribute<ast::IdAttribute>(var->attributes)) {
+ constant_id = static_cast<uint16_t>(id_attr->value);
+ } else {
+ // No ID was specified, so allocate the next available ID.
+ constant_id = next_constant_id;
+ while (constant_ids_.count(constant_id)) {
+ if (constant_id == UINT16_MAX) {
+ TINT_ICE(Resolver, builder_->Diagnostics())
+ << "no more pipeline constant IDs available";
+ return;
+ }
+ constant_id++;
+ }
+ next_constant_id = constant_id + 1;
+ }
+
+ auto* sem = sem_.Get<sem::GlobalVariable>(var);
+ const_cast<sem::GlobalVariable*>(sem)->SetConstantId(constant_id);
+ }
}
void Resolver::SetShadows() {
- for (auto it : dependencies_.shadows) {
- Switch(
- sem_.Get(it.first), //
- [&](sem::LocalVariable* local) {
- local->SetShadows(sem_.Get(it.second));
- },
- [&](sem::Parameter* param) { param->SetShadows(sem_.Get(it.second)); });
- }
+ for (auto it : dependencies_.shadows) {
+ Switch(
+ sem_.Get(it.first), //
+ [&](sem::LocalVariable* local) { local->SetShadows(sem_.Get(it.second)); },
+ [&](sem::Parameter* param) { param->SetShadows(sem_.Get(it.second)); });
+ }
}
sem::GlobalVariable* Resolver::GlobalVariable(const ast::Variable* var) {
- auto* sem = Variable(var, VariableKind::kGlobal);
- if (!sem) {
- return nullptr;
- }
-
- auto storage_class = sem->StorageClass();
- if (!var->is_const && storage_class == ast::StorageClass::kNone) {
- AddError("global variables must have a storage class", var->source);
- return nullptr;
- }
- if (var->is_const && storage_class != ast::StorageClass::kNone) {
- AddError("global constants shouldn't have a storage class", var->source);
- return nullptr;
- }
-
- for (auto* attr : var->attributes) {
- Mark(attr);
-
- if (auto* id_attr = attr->As<ast::IdAttribute>()) {
- // Track the constant IDs that are specified in the shader.
- constant_ids_.emplace(id_attr->value, sem);
+ auto* sem = Variable(var, VariableKind::kGlobal);
+ if (!sem) {
+ return nullptr;
}
- }
- if (!validator_.NoDuplicateAttributes(var->attributes)) {
- return nullptr;
- }
+ auto storage_class = sem->StorageClass();
+ if (!var->is_const && storage_class == ast::StorageClass::kNone) {
+ AddError("global variables must have a storage class", var->source);
+ return nullptr;
+ }
+ if (var->is_const && storage_class != ast::StorageClass::kNone) {
+ AddError("global constants shouldn't have a storage class", var->source);
+ return nullptr;
+ }
- if (!validator_.GlobalVariable(sem, constant_ids_, atomic_composite_info_)) {
- return nullptr;
- }
+ for (auto* attr : var->attributes) {
+ Mark(attr);
- // TODO(bclayton): Call this at the end of resolve on all uniform and storage
- // referenced structs
- if (!validator_.StorageClassLayout(sem, valid_type_storage_layouts_)) {
- return nullptr;
- }
+ if (auto* id_attr = attr->As<ast::IdAttribute>()) {
+ // Track the constant IDs that are specified in the shader.
+ constant_ids_.emplace(id_attr->value, sem);
+ }
+ }
- return sem->As<sem::GlobalVariable>();
+ if (!validator_.NoDuplicateAttributes(var->attributes)) {
+ return nullptr;
+ }
+
+ if (!validator_.GlobalVariable(sem, constant_ids_, atomic_composite_info_)) {
+ return nullptr;
+ }
+
+ // TODO(bclayton): Call this at the end of resolve on all uniform and storage
+ // referenced structs
+ if (!validator_.StorageClassLayout(sem, valid_type_storage_layouts_)) {
+ return nullptr;
+ }
+
+ return sem->As<sem::GlobalVariable>();
}
sem::Function* Resolver::Function(const ast::Function* decl) {
- uint32_t parameter_index = 0;
- std::unordered_map<Symbol, Source> parameter_names;
- std::vector<sem::Parameter*> parameters;
+ uint32_t parameter_index = 0;
+ std::unordered_map<Symbol, Source> parameter_names;
+ std::vector<sem::Parameter*> parameters;
- // Resolve all the parameters
- for (auto* param : decl->params) {
- Mark(param);
+ // Resolve all the parameters
+ for (auto* param : decl->params) {
+ Mark(param);
- { // Check the parameter name is unique for the function
- auto emplaced = parameter_names.emplace(param->symbol, param->source);
- if (!emplaced.second) {
- auto name = builder_->Symbols().NameFor(param->symbol);
- AddError("redefinition of parameter '" + name + "'", param->source);
- AddNote("previous definition is here", emplaced.first->second);
+ { // Check the parameter name is unique for the function
+ auto emplaced = parameter_names.emplace(param->symbol, param->source);
+ if (!emplaced.second) {
+ auto name = builder_->Symbols().NameFor(param->symbol);
+ AddError("redefinition of parameter '" + name + "'", param->source);
+ AddNote("previous definition is here", emplaced.first->second);
+ return nullptr;
+ }
+ }
+
+ auto* var =
+ As<sem::Parameter>(Variable(param, VariableKind::kParameter, parameter_index++));
+ if (!var) {
+ return nullptr;
+ }
+
+ for (auto* attr : param->attributes) {
+ Mark(attr);
+ }
+ if (!validator_.NoDuplicateAttributes(param->attributes)) {
+ return nullptr;
+ }
+
+ parameters.emplace_back(var);
+
+ auto* var_ty = const_cast<sem::Type*>(var->Type());
+ if (auto* str = var_ty->As<sem::Struct>()) {
+ switch (decl->PipelineStage()) {
+ case ast::PipelineStage::kVertex:
+ str->AddUsage(sem::PipelineStageUsage::kVertexInput);
+ break;
+ case ast::PipelineStage::kFragment:
+ str->AddUsage(sem::PipelineStageUsage::kFragmentInput);
+ break;
+ case ast::PipelineStage::kCompute:
+ str->AddUsage(sem::PipelineStageUsage::kComputeInput);
+ break;
+ case ast::PipelineStage::kNone:
+ break;
+ }
+ }
+ }
+
+ // Resolve the return type
+ sem::Type* return_type = nullptr;
+ if (auto* ty = decl->return_type) {
+ return_type = Type(ty);
+ if (!return_type) {
+ return nullptr;
+ }
+ } else {
+ return_type = builder_->create<sem::Void>();
+ }
+
+ if (auto* str = return_type->As<sem::Struct>()) {
+ if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, str, decl->source)) {
+ AddNote(
+ "while instantiating return type for " + builder_->Symbols().NameFor(decl->symbol),
+ decl->source);
+ return nullptr;
+ }
+
+ switch (decl->PipelineStage()) {
+ case ast::PipelineStage::kVertex:
+ str->AddUsage(sem::PipelineStageUsage::kVertexOutput);
+ break;
+ case ast::PipelineStage::kFragment:
+ str->AddUsage(sem::PipelineStageUsage::kFragmentOutput);
+ break;
+ case ast::PipelineStage::kCompute:
+ str->AddUsage(sem::PipelineStageUsage::kComputeOutput);
+ break;
+ case ast::PipelineStage::kNone:
+ break;
+ }
+ }
+
+ auto* func = builder_->create<sem::Function>(decl, return_type, parameters);
+ builder_->Sem().Add(decl, func);
+
+ TINT_SCOPED_ASSIGNMENT(current_function_, func);
+
+ if (!WorkgroupSize(decl)) {
return nullptr;
- }
}
- auto* var = As<sem::Parameter>(
- Variable(param, VariableKind::kParameter, parameter_index++));
- if (!var) {
- return nullptr;
+ if (decl->IsEntryPoint()) {
+ entry_points_.emplace_back(func);
}
- for (auto* attr : param->attributes) {
- Mark(attr);
- }
- if (!validator_.NoDuplicateAttributes(param->attributes)) {
- return nullptr;
+ if (decl->body) {
+ Mark(decl->body);
+ if (current_compound_statement_) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "Resolver::Function() called with a current compound statement";
+ return nullptr;
+ }
+ auto* body = StatementScope(decl->body, builder_->create<sem::FunctionBlockStatement>(func),
+ [&] { return Statements(decl->body->statements); });
+ if (!body) {
+ return nullptr;
+ }
+ func->Behaviors() = body->Behaviors();
+ if (func->Behaviors().Contains(sem::Behavior::kReturn)) {
+ // https://www.w3.org/TR/WGSL/#behaviors-rules
+ // We assign a behavior to each function: it is its body’s behavior
+ // (treating the body as a regular statement), with any "Return" replaced
+ // by "Next".
+ func->Behaviors().Remove(sem::Behavior::kReturn);
+ func->Behaviors().Add(sem::Behavior::kNext);
+ }
}
- parameters.emplace_back(var);
-
- auto* var_ty = const_cast<sem::Type*>(var->Type());
- if (auto* str = var_ty->As<sem::Struct>()) {
- switch (decl->PipelineStage()) {
- case ast::PipelineStage::kVertex:
- str->AddUsage(sem::PipelineStageUsage::kVertexInput);
- break;
- case ast::PipelineStage::kFragment:
- str->AddUsage(sem::PipelineStageUsage::kFragmentInput);
- break;
- case ast::PipelineStage::kCompute:
- str->AddUsage(sem::PipelineStageUsage::kComputeInput);
- break;
- case ast::PipelineStage::kNone:
- break;
- }
+ for (auto* attr : decl->attributes) {
+ Mark(attr);
}
- }
-
- // Resolve the return type
- sem::Type* return_type = nullptr;
- if (auto* ty = decl->return_type) {
- return_type = Type(ty);
- if (!return_type) {
- return nullptr;
- }
- } else {
- return_type = builder_->create<sem::Void>();
- }
-
- if (auto* str = return_type->As<sem::Struct>()) {
- if (!ApplyStorageClassUsageToType(ast::StorageClass::kNone, str,
- decl->source)) {
- AddNote("while instantiating return type for " +
- builder_->Symbols().NameFor(decl->symbol),
- decl->source);
- return nullptr;
+ if (!validator_.NoDuplicateAttributes(decl->attributes)) {
+ return nullptr;
}
- switch (decl->PipelineStage()) {
- case ast::PipelineStage::kVertex:
- str->AddUsage(sem::PipelineStageUsage::kVertexOutput);
- break;
- case ast::PipelineStage::kFragment:
- str->AddUsage(sem::PipelineStageUsage::kFragmentOutput);
- break;
- case ast::PipelineStage::kCompute:
- str->AddUsage(sem::PipelineStageUsage::kComputeOutput);
- break;
- case ast::PipelineStage::kNone:
- break;
+ for (auto* attr : decl->return_type_attributes) {
+ Mark(attr);
}
- }
-
- auto* func = builder_->create<sem::Function>(decl, return_type, parameters);
- builder_->Sem().Add(decl, func);
-
- TINT_SCOPED_ASSIGNMENT(current_function_, func);
-
- if (!WorkgroupSize(decl)) {
- return nullptr;
- }
-
- if (decl->IsEntryPoint()) {
- entry_points_.emplace_back(func);
- }
-
- if (decl->body) {
- Mark(decl->body);
- if (current_compound_statement_) {
- TINT_ICE(Resolver, diagnostics_)
- << "Resolver::Function() called with a current compound statement";
- return nullptr;
+ if (!validator_.NoDuplicateAttributes(decl->return_type_attributes)) {
+ return nullptr;
}
- auto* body = StatementScope(
- decl->body, builder_->create<sem::FunctionBlockStatement>(func),
- [&] { return Statements(decl->body->statements); });
- if (!body) {
- return nullptr;
+
+ auto stage = current_function_ ? current_function_->Declaration()->PipelineStage()
+ : ast::PipelineStage::kNone;
+ if (!validator_.Function(func, stage)) {
+ return nullptr;
}
- func->Behaviors() = body->Behaviors();
- if (func->Behaviors().Contains(sem::Behavior::kReturn)) {
- // https://www.w3.org/TR/WGSL/#behaviors-rules
- // We assign a behavior to each function: it is its body’s behavior
- // (treating the body as a regular statement), with any "Return" replaced
- // by "Next".
- func->Behaviors().Remove(sem::Behavior::kReturn);
- func->Behaviors().Add(sem::Behavior::kNext);
+
+ // If this is an entry point, mark all transitively called functions as being
+ // used by this entry point.
+ if (decl->IsEntryPoint()) {
+ for (auto* f : func->TransitivelyCalledFunctions()) {
+ const_cast<sem::Function*>(f)->AddAncestorEntryPoint(func);
+ }
}
- }
- for (auto* attr : decl->attributes) {
- Mark(attr);
- }
- if (!validator_.NoDuplicateAttributes(decl->attributes)) {
- return nullptr;
- }
-
- for (auto* attr : decl->return_type_attributes) {
- Mark(attr);
- }
- if (!validator_.NoDuplicateAttributes(decl->return_type_attributes)) {
- return nullptr;
- }
-
- auto stage = current_function_
- ? current_function_->Declaration()->PipelineStage()
- : ast::PipelineStage::kNone;
- if (!validator_.Function(func, stage)) {
- return nullptr;
- }
-
- // If this is an entry point, mark all transitively called functions as being
- // used by this entry point.
- if (decl->IsEntryPoint()) {
- for (auto* f : func->TransitivelyCalledFunctions()) {
- const_cast<sem::Function*>(f)->AddAncestorEntryPoint(func);
- }
- }
-
- return func;
+ return func;
}
bool Resolver::WorkgroupSize(const ast::Function* func) {
- // Set work-group size defaults.
- sem::WorkgroupSize ws;
- for (int i = 0; i < 3; i++) {
- ws[i].value = 1;
- ws[i].overridable_const = nullptr;
- }
+ // Set work-group size defaults.
+ sem::WorkgroupSize ws;
+ for (int i = 0; i < 3; i++) {
+ ws[i].value = 1;
+ ws[i].overridable_const = nullptr;
+ }
- auto* attr = ast::GetAttribute<ast::WorkgroupAttribute>(func->attributes);
- if (!attr) {
+ auto* attr = ast::GetAttribute<ast::WorkgroupAttribute>(func->attributes);
+ if (!attr) {
+ return true;
+ }
+
+ auto values = attr->Values();
+ auto any_i32 = false;
+ auto any_u32 = false;
+ for (int i = 0; i < 3; i++) {
+ // Each argument to this attribute can either be a literal, an
+ // identifier for a module-scope constants, or nullptr if not specified.
+
+ auto* expr = values[i];
+ if (!expr) {
+ // Not specified, just use the default.
+ continue;
+ }
+
+ auto* expr_sem = Expression(expr);
+ if (!expr_sem) {
+ return false;
+ }
+
+ constexpr const char* kErrBadType =
+ "workgroup_size argument must be either literal or module-scope "
+ "constant of type i32 or u32";
+ constexpr const char* kErrInconsistentType =
+ "workgroup_size arguments must be of the same type, either i32 "
+ "or u32";
+
+ auto* ty = sem_.TypeOf(expr);
+ bool is_i32 = ty->UnwrapRef()->Is<sem::I32>();
+ bool is_u32 = ty->UnwrapRef()->Is<sem::U32>();
+ if (!is_i32 && !is_u32) {
+ AddError(kErrBadType, expr->source);
+ return false;
+ }
+
+ any_i32 = any_i32 || is_i32;
+ any_u32 = any_u32 || is_u32;
+ if (any_i32 && any_u32) {
+ AddError(kErrInconsistentType, expr->source);
+ return false;
+ }
+
+ sem::Constant value;
+
+ if (auto* user = sem_.Get(expr)->As<sem::VariableUser>()) {
+ // We have an variable of a module-scope constant.
+ auto* decl = user->Variable()->Declaration();
+ if (!decl->is_const) {
+ AddError(kErrBadType, expr->source);
+ return false;
+ }
+ // Capture the constant if it is pipeline-overridable.
+ if (decl->is_overridable) {
+ ws[i].overridable_const = decl;
+ }
+
+ if (decl->constructor) {
+ value = sem_.Get(decl->constructor)->ConstantValue();
+ } else {
+ // No constructor means this value must be overriden by the user.
+ ws[i].value = 0;
+ continue;
+ }
+ } else if (expr->Is<ast::LiteralExpression>()) {
+ value = sem_.Get(expr)->ConstantValue();
+ } else {
+ AddError(
+ "workgroup_size argument must be either a literal or a "
+ "module-scope constant",
+ values[i]->source);
+ return false;
+ }
+
+ if (!value) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "could not resolve constant workgroup_size constant value";
+ continue;
+ }
+ // validator_.Validate and set the default value for this dimension.
+ if (is_i32 ? value.Elements()[0].i32 < 1 : value.Elements()[0].u32 < 1) {
+ AddError("workgroup_size argument must be at least 1", values[i]->source);
+ return false;
+ }
+
+ ws[i].value =
+ is_i32 ? static_cast<uint32_t>(value.Elements()[0].i32) : value.Elements()[0].u32;
+ }
+
+ current_function_->SetWorkgroupSize(std::move(ws));
return true;
- }
-
- auto values = attr->Values();
- auto any_i32 = false;
- auto any_u32 = false;
- for (int i = 0; i < 3; i++) {
- // Each argument to this attribute can either be a literal, an
- // identifier for a module-scope constants, or nullptr if not specified.
-
- auto* expr = values[i];
- if (!expr) {
- // Not specified, just use the default.
- continue;
- }
-
- auto* expr_sem = Expression(expr);
- if (!expr_sem) {
- return false;
- }
-
- constexpr const char* kErrBadType =
- "workgroup_size argument must be either literal or module-scope "
- "constant of type i32 or u32";
- constexpr const char* kErrInconsistentType =
- "workgroup_size arguments must be of the same type, either i32 "
- "or u32";
-
- auto* ty = sem_.TypeOf(expr);
- bool is_i32 = ty->UnwrapRef()->Is<sem::I32>();
- bool is_u32 = ty->UnwrapRef()->Is<sem::U32>();
- if (!is_i32 && !is_u32) {
- AddError(kErrBadType, expr->source);
- return false;
- }
-
- any_i32 = any_i32 || is_i32;
- any_u32 = any_u32 || is_u32;
- if (any_i32 && any_u32) {
- AddError(kErrInconsistentType, expr->source);
- return false;
- }
-
- sem::Constant value;
-
- if (auto* user = sem_.Get(expr)->As<sem::VariableUser>()) {
- // We have an variable of a module-scope constant.
- auto* decl = user->Variable()->Declaration();
- if (!decl->is_const) {
- AddError(kErrBadType, expr->source);
- return false;
- }
- // Capture the constant if it is pipeline-overridable.
- if (decl->is_overridable) {
- ws[i].overridable_const = decl;
- }
-
- if (decl->constructor) {
- value = sem_.Get(decl->constructor)->ConstantValue();
- } else {
- // No constructor means this value must be overriden by the user.
- ws[i].value = 0;
- continue;
- }
- } else if (expr->Is<ast::LiteralExpression>()) {
- value = sem_.Get(expr)->ConstantValue();
- } else {
- AddError(
- "workgroup_size argument must be either a literal or a "
- "module-scope constant",
- values[i]->source);
- return false;
- }
-
- if (!value) {
- TINT_ICE(Resolver, diagnostics_)
- << "could not resolve constant workgroup_size constant value";
- continue;
- }
- // validator_.Validate and set the default value for this dimension.
- if (is_i32 ? value.Elements()[0].i32 < 1 : value.Elements()[0].u32 < 1) {
- AddError("workgroup_size argument must be at least 1", values[i]->source);
- return false;
- }
-
- ws[i].value = is_i32 ? static_cast<uint32_t>(value.Elements()[0].i32)
- : value.Elements()[0].u32;
- }
-
- current_function_->SetWorkgroupSize(std::move(ws));
- return true;
}
bool Resolver::Statements(const ast::StatementList& stmts) {
- sem::Behaviors behaviors{sem::Behavior::kNext};
+ sem::Behaviors behaviors{sem::Behavior::kNext};
- bool reachable = true;
- for (auto* stmt : stmts) {
- Mark(stmt);
- auto* sem = Statement(stmt);
- if (!sem) {
- return false;
+ bool reachable = true;
+ for (auto* stmt : stmts) {
+ Mark(stmt);
+ auto* sem = Statement(stmt);
+ if (!sem) {
+ return false;
+ }
+ // s1 s2:(B1∖{Next}) ∪ B2
+ sem->SetIsReachable(reachable);
+ if (reachable) {
+ behaviors = (behaviors - sem::Behavior::kNext) + sem->Behaviors();
+ }
+ reachable = reachable && sem->Behaviors().Contains(sem::Behavior::kNext);
}
- // s1 s2:(B1∖{Next}) ∪ B2
- sem->SetIsReachable(reachable);
- if (reachable) {
- behaviors = (behaviors - sem::Behavior::kNext) + sem->Behaviors();
+
+ current_statement_->Behaviors() = behaviors;
+
+ if (!validator_.Statements(stmts)) {
+ return false;
}
- reachable = reachable && sem->Behaviors().Contains(sem::Behavior::kNext);
- }
- current_statement_->Behaviors() = behaviors;
-
- if (!validator_.Statements(stmts)) {
- return false;
- }
-
- return true;
+ return true;
}
sem::Statement* Resolver::Statement(const ast::Statement* stmt) {
- return Switch(
- stmt,
- // Compound statements. These create their own sem::CompoundStatement
- // bindings.
- [&](const ast::BlockStatement* b) { return BlockStatement(b); },
- [&](const ast::ForLoopStatement* l) { return ForLoopStatement(l); },
- [&](const ast::LoopStatement* l) { return LoopStatement(l); },
- [&](const ast::IfStatement* i) { return IfStatement(i); },
- [&](const ast::SwitchStatement* s) { return SwitchStatement(s); },
+ return Switch(
+ stmt,
+ // Compound statements. These create their own sem::CompoundStatement
+ // bindings.
+ [&](const ast::BlockStatement* b) { return BlockStatement(b); },
+ [&](const ast::ForLoopStatement* l) { return ForLoopStatement(l); },
+ [&](const ast::LoopStatement* l) { return LoopStatement(l); },
+ [&](const ast::IfStatement* i) { return IfStatement(i); },
+ [&](const ast::SwitchStatement* s) { return SwitchStatement(s); },
- // Non-Compound statements
- [&](const ast::AssignmentStatement* a) { return AssignmentStatement(a); },
- [&](const ast::BreakStatement* b) { return BreakStatement(b); },
- [&](const ast::CallStatement* c) { return CallStatement(c); },
- [&](const ast::CompoundAssignmentStatement* c) {
- return CompoundAssignmentStatement(c);
- },
- [&](const ast::ContinueStatement* c) { return ContinueStatement(c); },
- [&](const ast::DiscardStatement* d) { return DiscardStatement(d); },
- [&](const ast::FallthroughStatement* f) {
- return FallthroughStatement(f);
- },
- [&](const ast::IncrementDecrementStatement* i) {
- return IncrementDecrementStatement(i);
- },
- [&](const ast::ReturnStatement* r) { return ReturnStatement(r); },
- [&](const ast::VariableDeclStatement* v) {
- return VariableDeclStatement(v);
- },
+ // Non-Compound statements
+ [&](const ast::AssignmentStatement* a) { return AssignmentStatement(a); },
+ [&](const ast::BreakStatement* b) { return BreakStatement(b); },
+ [&](const ast::CallStatement* c) { return CallStatement(c); },
+ [&](const ast::CompoundAssignmentStatement* c) { return CompoundAssignmentStatement(c); },
+ [&](const ast::ContinueStatement* c) { return ContinueStatement(c); },
+ [&](const ast::DiscardStatement* d) { return DiscardStatement(d); },
+ [&](const ast::FallthroughStatement* f) { return FallthroughStatement(f); },
+ [&](const ast::IncrementDecrementStatement* i) { return IncrementDecrementStatement(i); },
+ [&](const ast::ReturnStatement* r) { return ReturnStatement(r); },
+ [&](const ast::VariableDeclStatement* v) { return VariableDeclStatement(v); },
- // Error cases
- [&](const ast::CaseStatement*) {
- AddError("case statement can only be used inside a switch statement",
- stmt->source);
- return nullptr;
- },
- [&](Default) {
- AddError(
- "unknown statement type: " + std::string(stmt->TypeInfo().name),
- stmt->source);
- return nullptr;
- });
+ // Error cases
+ [&](const ast::CaseStatement*) {
+ AddError("case statement can only be used inside a switch statement", stmt->source);
+ return nullptr;
+ },
+ [&](Default) {
+ AddError("unknown statement type: " + std::string(stmt->TypeInfo().name), stmt->source);
+ return nullptr;
+ });
}
sem::CaseStatement* Resolver::CaseStatement(const ast::CaseStatement* stmt) {
- auto* sem = builder_->create<sem::CaseStatement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- for (auto* sel : stmt->selectors) {
- Mark(sel);
- }
- Mark(stmt->body);
- auto* body = BlockStatement(stmt->body);
- if (!body) {
- return false;
- }
- sem->SetBlock(body);
- sem->Behaviors() = body->Behaviors();
- return true;
- });
+ auto* sem =
+ builder_->create<sem::CaseStatement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ for (auto* sel : stmt->selectors) {
+ Mark(sel);
+ }
+ Mark(stmt->body);
+ auto* body = BlockStatement(stmt->body);
+ if (!body) {
+ return false;
+ }
+ sem->SetBlock(body);
+ sem->Behaviors() = body->Behaviors();
+ return true;
+ });
}
sem::IfStatement* Resolver::IfStatement(const ast::IfStatement* stmt) {
- auto* sem = builder_->create<sem::IfStatement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto* cond = Expression(stmt->condition);
- if (!cond) {
- return false;
- }
- sem->SetCondition(cond);
- sem->Behaviors() = cond->Behaviors();
- sem->Behaviors().Remove(sem::Behavior::kNext);
+ auto* sem =
+ builder_->create<sem::IfStatement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto* cond = Expression(stmt->condition);
+ if (!cond) {
+ return false;
+ }
+ sem->SetCondition(cond);
+ sem->Behaviors() = cond->Behaviors();
+ sem->Behaviors().Remove(sem::Behavior::kNext);
- Mark(stmt->body);
- auto* body = builder_->create<sem::BlockStatement>(
- stmt->body, current_compound_statement_, current_function_);
- if (!StatementScope(stmt->body, body,
- [&] { return Statements(stmt->body->statements); })) {
- return false;
- }
- sem->Behaviors().Add(body->Behaviors());
+ Mark(stmt->body);
+ auto* body = builder_->create<sem::BlockStatement>(stmt->body, current_compound_statement_,
+ current_function_);
+ if (!StatementScope(stmt->body, body, [&] { return Statements(stmt->body->statements); })) {
+ return false;
+ }
+ sem->Behaviors().Add(body->Behaviors());
- if (stmt->else_statement) {
- Mark(stmt->else_statement);
- auto* else_sem = Statement(stmt->else_statement);
- if (!else_sem) {
- return false;
- }
- sem->Behaviors().Add(else_sem->Behaviors());
- } else {
- // https://www.w3.org/TR/WGSL/#behaviors-rules
- // if statements without an else branch are treated as if they had an
- // empty else branch (which adds Next to their behavior)
- sem->Behaviors().Add(sem::Behavior::kNext);
- }
+ if (stmt->else_statement) {
+ Mark(stmt->else_statement);
+ auto* else_sem = Statement(stmt->else_statement);
+ if (!else_sem) {
+ return false;
+ }
+ sem->Behaviors().Add(else_sem->Behaviors());
+ } else {
+ // https://www.w3.org/TR/WGSL/#behaviors-rules
+ // if statements without an else branch are treated as if they had an
+ // empty else branch (which adds Next to their behavior)
+ sem->Behaviors().Add(sem::Behavior::kNext);
+ }
- return validator_.IfStatement(sem);
- });
+ return validator_.IfStatement(sem);
+ });
}
sem::BlockStatement* Resolver::BlockStatement(const ast::BlockStatement* stmt) {
- auto* sem = builder_->create<sem::BlockStatement>(
- stmt->As<ast::BlockStatement>(), current_compound_statement_,
- current_function_);
- return StatementScope(stmt, sem,
- [&] { return Statements(stmt->statements); });
+ auto* sem = builder_->create<sem::BlockStatement>(
+ stmt->As<ast::BlockStatement>(), current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] { return Statements(stmt->statements); });
}
sem::LoopStatement* Resolver::LoopStatement(const ast::LoopStatement* stmt) {
- auto* sem = builder_->create<sem::LoopStatement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- Mark(stmt->body);
+ auto* sem =
+ builder_->create<sem::LoopStatement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ Mark(stmt->body);
- auto* body = builder_->create<sem::LoopBlockStatement>(
- stmt->body, current_compound_statement_, current_function_);
- return StatementScope(stmt->body, body, [&] {
- if (!Statements(stmt->body->statements)) {
- return false;
- }
- auto& behaviors = sem->Behaviors();
- behaviors = body->Behaviors();
+ auto* body = builder_->create<sem::LoopBlockStatement>(
+ stmt->body, current_compound_statement_, current_function_);
+ return StatementScope(stmt->body, body, [&] {
+ if (!Statements(stmt->body->statements)) {
+ return false;
+ }
+ auto& behaviors = sem->Behaviors();
+ behaviors = body->Behaviors();
- if (stmt->continuing) {
- Mark(stmt->continuing);
- if (!stmt->continuing->Empty()) {
- auto* continuing = StatementScope(
- stmt->continuing,
- builder_->create<sem::LoopContinuingBlockStatement>(
- stmt->continuing, current_compound_statement_,
- current_function_),
- [&] { return Statements(stmt->continuing->statements); });
- if (!continuing) {
- return false;
- }
- behaviors.Add(continuing->Behaviors());
- }
- }
+ if (stmt->continuing) {
+ Mark(stmt->continuing);
+ if (!stmt->continuing->Empty()) {
+ auto* continuing = StatementScope(
+ stmt->continuing,
+ builder_->create<sem::LoopContinuingBlockStatement>(
+ stmt->continuing, current_compound_statement_, current_function_),
+ [&] { return Statements(stmt->continuing->statements); });
+ if (!continuing) {
+ return false;
+ }
+ behaviors.Add(continuing->Behaviors());
+ }
+ }
- if (behaviors.Contains(sem::Behavior::kBreak)) { // Does the loop exit?
- behaviors.Add(sem::Behavior::kNext);
- } else {
- behaviors.Remove(sem::Behavior::kNext);
- }
- behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kContinue);
+ if (behaviors.Contains(sem::Behavior::kBreak)) { // Does the loop exit?
+ behaviors.Add(sem::Behavior::kNext);
+ } else {
+ behaviors.Remove(sem::Behavior::kNext);
+ }
+ behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kContinue);
- return validator_.LoopStatement(sem);
+ return validator_.LoopStatement(sem);
+ });
});
- });
}
-sem::ForLoopStatement* Resolver::ForLoopStatement(
- const ast::ForLoopStatement* stmt) {
- auto* sem = builder_->create<sem::ForLoopStatement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto& behaviors = sem->Behaviors();
- if (auto* initializer = stmt->initializer) {
- Mark(initializer);
- auto* init = Statement(initializer);
- if (!init) {
- return false;
- }
- behaviors.Add(init->Behaviors());
- }
+sem::ForLoopStatement* Resolver::ForLoopStatement(const ast::ForLoopStatement* stmt) {
+ auto* sem = builder_->create<sem::ForLoopStatement>(stmt, current_compound_statement_,
+ current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto& behaviors = sem->Behaviors();
+ if (auto* initializer = stmt->initializer) {
+ Mark(initializer);
+ auto* init = Statement(initializer);
+ if (!init) {
+ return false;
+ }
+ behaviors.Add(init->Behaviors());
+ }
- if (auto* cond_expr = stmt->condition) {
- auto* cond = Expression(cond_expr);
- if (!cond) {
- return false;
- }
- sem->SetCondition(cond);
- behaviors.Add(cond->Behaviors());
- }
+ if (auto* cond_expr = stmt->condition) {
+ auto* cond = Expression(cond_expr);
+ if (!cond) {
+ return false;
+ }
+ sem->SetCondition(cond);
+ behaviors.Add(cond->Behaviors());
+ }
- if (auto* continuing = stmt->continuing) {
- Mark(continuing);
- auto* cont = Statement(continuing);
- if (!cont) {
- return false;
- }
- behaviors.Add(cont->Behaviors());
- }
+ if (auto* continuing = stmt->continuing) {
+ Mark(continuing);
+ auto* cont = Statement(continuing);
+ if (!cont) {
+ return false;
+ }
+ behaviors.Add(cont->Behaviors());
+ }
- Mark(stmt->body);
+ Mark(stmt->body);
- auto* body = builder_->create<sem::LoopBlockStatement>(
- stmt->body, current_compound_statement_, current_function_);
- if (!StatementScope(stmt->body, body,
- [&] { return Statements(stmt->body->statements); })) {
- return false;
- }
+ auto* body = builder_->create<sem::LoopBlockStatement>(
+ stmt->body, current_compound_statement_, current_function_);
+ if (!StatementScope(stmt->body, body, [&] { return Statements(stmt->body->statements); })) {
+ return false;
+ }
- behaviors.Add(body->Behaviors());
- if (stmt->condition ||
- behaviors.Contains(sem::Behavior::kBreak)) { // Does the loop exit?
- behaviors.Add(sem::Behavior::kNext);
- } else {
- behaviors.Remove(sem::Behavior::kNext);
- }
- behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kContinue);
+ behaviors.Add(body->Behaviors());
+ if (stmt->condition || behaviors.Contains(sem::Behavior::kBreak)) { // Does the loop exit?
+ behaviors.Add(sem::Behavior::kNext);
+ } else {
+ behaviors.Remove(sem::Behavior::kNext);
+ }
+ behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kContinue);
- return validator_.ForLoopStatement(sem);
- });
+ return validator_.ForLoopStatement(sem);
+ });
}
sem::Expression* Resolver::Expression(const ast::Expression* root) {
- std::vector<const ast::Expression*> sorted;
- bool mark_failed = false;
- if (!ast::TraverseExpressions<ast::TraverseOrder::RightToLeft>(
- root, diagnostics_, [&](const ast::Expression* expr) {
- if (!Mark(expr)) {
- mark_failed = true;
- return ast::TraverseAction::Stop;
- }
- sorted.emplace_back(expr);
- return ast::TraverseAction::Descend;
- })) {
- return nullptr;
- }
-
- if (mark_failed) {
- return nullptr;
- }
-
- for (auto* expr : utils::Reverse(sorted)) {
- auto* sem_expr = Switch(
- expr,
- [&](const ast::IndexAccessorExpression* array) -> sem::Expression* {
- return IndexAccessor(array);
- },
- [&](const ast::BinaryExpression* bin_op) -> sem::Expression* {
- return Binary(bin_op);
- },
- [&](const ast::BitcastExpression* bitcast) -> sem::Expression* {
- return Bitcast(bitcast);
- },
- [&](const ast::CallExpression* call) -> sem::Expression* {
- return Call(call);
- },
- [&](const ast::IdentifierExpression* ident) -> sem::Expression* {
- return Identifier(ident);
- },
- [&](const ast::LiteralExpression* literal) -> sem::Expression* {
- return Literal(literal);
- },
- [&](const ast::MemberAccessorExpression* member) -> sem::Expression* {
- return MemberAccessor(member);
- },
- [&](const ast::UnaryOpExpression* unary) -> sem::Expression* {
- return UnaryOp(unary);
- },
- [&](const ast::PhonyExpression*) -> sem::Expression* {
- return builder_->create<sem::Expression>(
- expr, builder_->create<sem::Void>(), current_statement_,
- sem::Constant{}, /* has_side_effects */ false);
- },
- [&](Default) {
- TINT_ICE(Resolver, diagnostics_)
- << "unhandled expression type: " << expr->TypeInfo().name;
- return nullptr;
- });
- if (!sem_expr) {
- return nullptr;
+ std::vector<const ast::Expression*> sorted;
+ bool mark_failed = false;
+ if (!ast::TraverseExpressions<ast::TraverseOrder::RightToLeft>(
+ root, diagnostics_, [&](const ast::Expression* expr) {
+ if (!Mark(expr)) {
+ mark_failed = true;
+ return ast::TraverseAction::Stop;
+ }
+ sorted.emplace_back(expr);
+ return ast::TraverseAction::Descend;
+ })) {
+ return nullptr;
}
- builder_->Sem().Add(expr, sem_expr);
- if (expr == root) {
- return sem_expr;
+ if (mark_failed) {
+ return nullptr;
}
- }
- TINT_ICE(Resolver, diagnostics_) << "Expression() did not find root node";
- return nullptr;
+ for (auto* expr : utils::Reverse(sorted)) {
+ auto* sem_expr = Switch(
+ expr,
+ [&](const ast::IndexAccessorExpression* array) -> sem::Expression* {
+ return IndexAccessor(array);
+ },
+ [&](const ast::BinaryExpression* bin_op) -> sem::Expression* { return Binary(bin_op); },
+ [&](const ast::BitcastExpression* bitcast) -> sem::Expression* {
+ return Bitcast(bitcast);
+ },
+ [&](const ast::CallExpression* call) -> sem::Expression* { return Call(call); },
+ [&](const ast::IdentifierExpression* ident) -> sem::Expression* {
+ return Identifier(ident);
+ },
+ [&](const ast::LiteralExpression* literal) -> sem::Expression* {
+ return Literal(literal);
+ },
+ [&](const ast::MemberAccessorExpression* member) -> sem::Expression* {
+ return MemberAccessor(member);
+ },
+ [&](const ast::UnaryOpExpression* unary) -> sem::Expression* { return UnaryOp(unary); },
+ [&](const ast::PhonyExpression*) -> sem::Expression* {
+ return builder_->create<sem::Expression>(expr, builder_->create<sem::Void>(),
+ current_statement_, sem::Constant{},
+ /* has_side_effects */ false);
+ },
+ [&](Default) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "unhandled expression type: " << expr->TypeInfo().name;
+ return nullptr;
+ });
+ if (!sem_expr) {
+ return nullptr;
+ }
+
+ builder_->Sem().Add(expr, sem_expr);
+ if (expr == root) {
+ return sem_expr;
+ }
+ }
+
+ TINT_ICE(Resolver, diagnostics_) << "Expression() did not find root node";
+ return nullptr;
}
-sem::Expression* Resolver::IndexAccessor(
- const ast::IndexAccessorExpression* expr) {
- auto* idx = sem_.Get(expr->index);
- auto* obj = sem_.Get(expr->object);
- auto* obj_raw_ty = obj->Type();
- auto* obj_ty = obj_raw_ty->UnwrapRef();
- auto* ty = Switch(
- obj_ty, //
- [&](const sem::Array* arr) { return arr->ElemType(); },
- [&](const sem::Vector* vec) { return vec->type(); },
- [&](const sem::Matrix* mat) {
- return builder_->create<sem::Vector>(mat->type(), mat->rows());
- },
- [&](Default) {
- AddError("cannot index type '" + sem_.TypeNameOf(obj_ty) + "'",
- expr->source);
+sem::Expression* Resolver::IndexAccessor(const ast::IndexAccessorExpression* expr) {
+ auto* idx = sem_.Get(expr->index);
+ auto* obj = sem_.Get(expr->object);
+ auto* obj_raw_ty = obj->Type();
+ auto* obj_ty = obj_raw_ty->UnwrapRef();
+ auto* ty = Switch(
+ obj_ty, //
+ [&](const sem::Array* arr) { return arr->ElemType(); },
+ [&](const sem::Vector* vec) { return vec->type(); },
+ [&](const sem::Matrix* mat) {
+ return builder_->create<sem::Vector>(mat->type(), mat->rows());
+ },
+ [&](Default) {
+ AddError("cannot index type '" + sem_.TypeNameOf(obj_ty) + "'", expr->source);
+ return nullptr;
+ });
+ if (ty == nullptr) {
return nullptr;
- });
- if (ty == nullptr) {
- return nullptr;
- }
+ }
- auto* idx_ty = idx->Type()->UnwrapRef();
- if (!idx_ty->IsAnyOf<sem::I32, sem::U32>()) {
- AddError("index must be of type 'i32' or 'u32', found: '" +
- sem_.TypeNameOf(idx_ty) + "'",
- idx->Declaration()->source);
- return nullptr;
- }
+ auto* idx_ty = idx->Type()->UnwrapRef();
+ if (!idx_ty->IsAnyOf<sem::I32, sem::U32>()) {
+ AddError("index must be of type 'i32' or 'u32', found: '" + sem_.TypeNameOf(idx_ty) + "'",
+ idx->Declaration()->source);
+ return nullptr;
+ }
- // If we're extracting from a reference, we return a reference.
- if (auto* ref = obj_raw_ty->As<sem::Reference>()) {
- ty = builder_->create<sem::Reference>(ty, ref->StorageClass(),
- ref->Access());
- }
+ // If we're extracting from a reference, we return a reference.
+ if (auto* ref = obj_raw_ty->As<sem::Reference>()) {
+ ty = builder_->create<sem::Reference>(ty, ref->StorageClass(), ref->Access());
+ }
- auto val = EvaluateConstantValue(expr, ty);
- bool has_side_effects = idx->HasSideEffects() || obj->HasSideEffects();
- auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_,
- val, has_side_effects,
- obj->SourceVariable());
- sem->Behaviors() = idx->Behaviors() + obj->Behaviors();
- return sem;
+ auto val = EvaluateConstantValue(expr, ty);
+ bool has_side_effects = idx->HasSideEffects() || obj->HasSideEffects();
+ auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_, val,
+ has_side_effects, obj->SourceVariable());
+ sem->Behaviors() = idx->Behaviors() + obj->Behaviors();
+ return sem;
}
sem::Expression* Resolver::Bitcast(const ast::BitcastExpression* expr) {
- auto* inner = sem_.Get(expr->expr);
- auto* ty = Type(expr->type);
- if (!ty) {
- return nullptr;
- }
+ auto* inner = sem_.Get(expr->expr);
+ auto* ty = Type(expr->type);
+ if (!ty) {
+ return nullptr;
+ }
- auto val = EvaluateConstantValue(expr, ty);
- auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_,
- val, inner->HasSideEffects());
+ auto val = EvaluateConstantValue(expr, ty);
+ auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_, val,
+ inner->HasSideEffects());
- sem->Behaviors() = inner->Behaviors();
+ sem->Behaviors() = inner->Behaviors();
- if (!validator_.Bitcast(expr, ty)) {
- return nullptr;
- }
+ if (!validator_.Bitcast(expr, ty)) {
+ return nullptr;
+ }
- return sem;
+ return sem;
}
sem::Call* Resolver::Call(const ast::CallExpression* expr) {
- std::vector<const sem::Expression*> args(expr->args.size());
- std::vector<const sem::Type*> arg_tys(args.size());
- sem::Behaviors arg_behaviors;
+ std::vector<const sem::Expression*> args(expr->args.size());
+ std::vector<const sem::Type*> arg_tys(args.size());
+ sem::Behaviors arg_behaviors;
- // The element type of all the arguments. Nullptr if argument types are
- // different.
- const sem::Type* arg_el_ty = nullptr;
+ // The element type of all the arguments. Nullptr if argument types are
+ // different.
+ const sem::Type* arg_el_ty = nullptr;
- for (size_t i = 0; i < expr->args.size(); i++) {
- auto* arg = sem_.Get(expr->args[i]);
- if (!arg) {
- return nullptr;
+ for (size_t i = 0; i < expr->args.size(); i++) {
+ auto* arg = sem_.Get(expr->args[i]);
+ if (!arg) {
+ return nullptr;
+ }
+ args[i] = arg;
+ arg_tys[i] = args[i]->Type();
+ arg_behaviors.Add(arg->Behaviors());
+
+ // Determine the common argument element type
+ auto* el_ty = arg_tys[i]->UnwrapRef();
+ if (auto* vec = el_ty->As<sem::Vector>()) {
+ el_ty = vec->type();
+ } else if (auto* mat = el_ty->As<sem::Matrix>()) {
+ el_ty = mat->type();
+ }
+ if (i == 0) {
+ arg_el_ty = el_ty;
+ } else if (arg_el_ty != el_ty) {
+ arg_el_ty = nullptr;
+ }
}
- args[i] = arg;
- arg_tys[i] = args[i]->Type();
- arg_behaviors.Add(arg->Behaviors());
- // Determine the common argument element type
- auto* el_ty = arg_tys[i]->UnwrapRef();
- if (auto* vec = el_ty->As<sem::Vector>()) {
- el_ty = vec->type();
- } else if (auto* mat = el_ty->As<sem::Matrix>()) {
- el_ty = mat->type();
- }
- if (i == 0) {
- arg_el_ty = el_ty;
- } else if (arg_el_ty != el_ty) {
- arg_el_ty = nullptr;
- }
- }
+ arg_behaviors.Remove(sem::Behavior::kNext);
- arg_behaviors.Remove(sem::Behavior::kNext);
-
- auto type_ctor_or_conv = [&](const sem::Type* ty) -> sem::Call* {
- // The call has resolved to a type constructor or cast.
- if (args.size() == 1) {
- auto* target = ty;
- auto* source = args[0]->Type()->UnwrapRef();
- if ((source != target) && //
- ((source->is_scalar() && target->is_scalar()) ||
- (source->Is<sem::Vector>() && target->Is<sem::Vector>()) ||
- (source->Is<sem::Matrix>() && target->Is<sem::Matrix>()))) {
- // Note: Matrix types currently cannot be converted (the element type
- // must only be f32). We implement this for the day we support other
- // matrix element types.
- return TypeConversion(expr, ty, args[0], arg_tys[0]);
- }
- }
- return TypeConstructor(expr, ty, std::move(args), std::move(arg_tys));
- };
-
- // Resolve the target of the CallExpression to determine whether this is a
- // function call, cast or type constructor expression.
- if (expr->target.type) {
- const sem::Type* ty = nullptr;
-
- auto err_cannot_infer_el_ty = [&](std::string name) {
- AddError(
- "cannot infer " + name +
- " element type, as constructor arguments have different types",
- expr->source);
- for (size_t i = 0; i < args.size(); i++) {
- auto* arg = args[i];
- AddNote("argument " + std::to_string(i) + " has type " +
- arg->Type()->FriendlyName(builder_->Symbols()),
- arg->Declaration()->source);
- }
+ auto type_ctor_or_conv = [&](const sem::Type* ty) -> sem::Call* {
+ // The call has resolved to a type constructor or cast.
+ if (args.size() == 1) {
+ auto* target = ty;
+ auto* source = args[0]->Type()->UnwrapRef();
+ if ((source != target) && //
+ ((source->is_scalar() && target->is_scalar()) ||
+ (source->Is<sem::Vector>() && target->Is<sem::Vector>()) ||
+ (source->Is<sem::Matrix>() && target->Is<sem::Matrix>()))) {
+ // Note: Matrix types currently cannot be converted (the element type
+ // must only be f32). We implement this for the day we support other
+ // matrix element types.
+ return TypeConversion(expr, ty, args[0], arg_tys[0]);
+ }
+ }
+ return TypeConstructor(expr, ty, std::move(args), std::move(arg_tys));
};
- if (!expr->args.empty()) {
- // vecN() without explicit element type?
- // Try to infer element type from args
- if (auto* vec = expr->target.type->As<ast::Vector>()) {
- if (!vec->type) {
- if (!arg_el_ty) {
- err_cannot_infer_el_ty("vector");
- return nullptr;
- }
+ // Resolve the target of the CallExpression to determine whether this is a
+ // function call, cast or type constructor expression.
+ if (expr->target.type) {
+ const sem::Type* ty = nullptr;
- Mark(vec);
- auto* v = builder_->create<sem::Vector>(
- arg_el_ty, static_cast<uint32_t>(vec->width));
- if (!validator_.Vector(v, vec->source)) {
- return nullptr;
- }
- builder_->Sem().Add(vec, v);
- ty = v;
+ auto err_cannot_infer_el_ty = [&](std::string name) {
+ AddError("cannot infer " + name +
+ " element type, as constructor arguments have different types",
+ expr->source);
+ for (size_t i = 0; i < args.size(); i++) {
+ auto* arg = args[i];
+ AddNote("argument " + std::to_string(i) + " has type " +
+ arg->Type()->FriendlyName(builder_->Symbols()),
+ arg->Declaration()->source);
+ }
+ };
+
+ if (!expr->args.empty()) {
+ // vecN() without explicit element type?
+ // Try to infer element type from args
+ if (auto* vec = expr->target.type->As<ast::Vector>()) {
+ if (!vec->type) {
+ if (!arg_el_ty) {
+ err_cannot_infer_el_ty("vector");
+ return nullptr;
+ }
+
+ Mark(vec);
+ auto* v =
+ builder_->create<sem::Vector>(arg_el_ty, static_cast<uint32_t>(vec->width));
+ if (!validator_.Vector(v, vec->source)) {
+ return nullptr;
+ }
+ builder_->Sem().Add(vec, v);
+ ty = v;
+ }
+ }
+
+ // matNxM() without explicit element type?
+ // Try to infer element type from args
+ if (auto* mat = expr->target.type->As<ast::Matrix>()) {
+ if (!mat->type) {
+ if (!arg_el_ty) {
+ err_cannot_infer_el_ty("matrix");
+ return nullptr;
+ }
+
+ Mark(mat);
+ auto* column_type = builder_->create<sem::Vector>(arg_el_ty, mat->rows);
+ auto* m = builder_->create<sem::Matrix>(column_type, mat->columns);
+ if (!validator_.Matrix(m, mat->source)) {
+ return nullptr;
+ }
+ builder_->Sem().Add(mat, m);
+ ty = m;
+ }
+ }
}
- }
- // matNxM() without explicit element type?
- // Try to infer element type from args
- if (auto* mat = expr->target.type->As<ast::Matrix>()) {
- if (!mat->type) {
- if (!arg_el_ty) {
- err_cannot_infer_el_ty("matrix");
- return nullptr;
- }
-
- Mark(mat);
- auto* column_type =
- builder_->create<sem::Vector>(arg_el_ty, mat->rows);
- auto* m = builder_->create<sem::Matrix>(column_type, mat->columns);
- if (!validator_.Matrix(m, mat->source)) {
- return nullptr;
- }
- builder_->Sem().Add(mat, m);
- ty = m;
+ if (ty == nullptr) {
+ ty = Type(expr->target.type);
+ if (!ty) {
+ return nullptr;
+ }
}
- }
+
+ return type_ctor_or_conv(ty);
}
- if (ty == nullptr) {
- ty = Type(expr->target.type);
- if (!ty) {
- return nullptr;
- }
- }
+ auto* ident = expr->target.name;
+ Mark(ident);
- return type_ctor_or_conv(ty);
- }
+ auto* resolved = sem_.ResolvedSymbol(ident);
+ return Switch(
+ resolved, //
+ [&](sem::Type* type) { return type_ctor_or_conv(type); },
+ [&](sem::Function* func) {
+ return FunctionCall(expr, func, std::move(args), arg_behaviors);
+ },
+ [&](sem::Variable* var) {
+ auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
+ AddError("cannot call variable '" + name + "'", ident->source);
+ AddNote("'" + name + "' declared here", var->Declaration()->source);
+ return nullptr;
+ },
+ [&](Default) -> sem::Call* {
+ auto name = builder_->Symbols().NameFor(ident->symbol);
+ auto builtin_type = sem::ParseBuiltinType(name);
+ if (builtin_type != sem::BuiltinType::kNone) {
+ return BuiltinCall(expr, builtin_type, std::move(args), std::move(arg_tys));
+ }
- auto* ident = expr->target.name;
- Mark(ident);
-
- auto* resolved = sem_.ResolvedSymbol(ident);
- return Switch(
- resolved, //
- [&](sem::Type* type) { return type_ctor_or_conv(type); },
- [&](sem::Function* func) {
- return FunctionCall(expr, func, std::move(args), arg_behaviors);
- },
- [&](sem::Variable* var) {
- auto name = builder_->Symbols().NameFor(var->Declaration()->symbol);
- AddError("cannot call variable '" + name + "'", ident->source);
- AddNote("'" + name + "' declared here", var->Declaration()->source);
- return nullptr;
- },
- [&](Default) -> sem::Call* {
- auto name = builder_->Symbols().NameFor(ident->symbol);
- auto builtin_type = sem::ParseBuiltinType(name);
- if (builtin_type != sem::BuiltinType::kNone) {
- return BuiltinCall(expr, builtin_type, std::move(args),
- std::move(arg_tys));
- }
-
- TINT_ICE(Resolver, diagnostics_)
- << expr->source << " unresolved CallExpression target:\n"
- << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
- << "\n"
- << "name: " << builder_->Symbols().NameFor(ident->symbol);
- return nullptr;
- });
+ TINT_ICE(Resolver, diagnostics_)
+ << expr->source << " unresolved CallExpression target:\n"
+ << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>") << "\n"
+ << "name: " << builder_->Symbols().NameFor(ident->symbol);
+ return nullptr;
+ });
}
sem::Call* Resolver::BuiltinCall(const ast::CallExpression* expr,
sem::BuiltinType builtin_type,
const std::vector<const sem::Expression*> args,
const std::vector<const sem::Type*> arg_tys) {
- auto* builtin =
- builtin_table_->Lookup(builtin_type, std::move(arg_tys), expr->source);
- if (!builtin) {
- return nullptr;
- }
-
- if (builtin->IsDeprecated()) {
- AddWarning("use of deprecated builtin", expr->source);
- }
-
- bool has_side_effects = builtin->HasSideEffects() ||
- std::any_of(args.begin(), args.end(), [](auto* e) {
- return e->HasSideEffects();
- });
- auto* call = builder_->create<sem::Call>(expr, builtin, std::move(args),
- current_statement_, sem::Constant{},
- has_side_effects);
-
- current_function_->AddDirectlyCalledBuiltin(builtin);
-
- if (IsTextureBuiltin(builtin_type)) {
- if (!validator_.TextureBuiltinFunction(call)) {
- return nullptr;
- }
- // Collect a texture/sampler pair for this builtin.
- const auto& signature = builtin->Signature();
- int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
- if (texture_index == -1) {
- TINT_ICE(Resolver, diagnostics_)
- << "texture builtin without texture parameter";
+ auto* builtin = builtin_table_->Lookup(builtin_type, std::move(arg_tys), expr->source);
+ if (!builtin) {
+ return nullptr;
}
- auto* texture = args[texture_index]->As<sem::VariableUser>()->Variable();
- if (!texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
- int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
- const sem::Variable* sampler =
- sampler_index != -1
- ? args[sampler_index]->As<sem::VariableUser>()->Variable()
- : nullptr;
- current_function_->AddTextureSamplerPair(texture, sampler);
+ if (builtin->IsDeprecated()) {
+ AddWarning("use of deprecated builtin", expr->source);
}
- }
- if (!validator_.BuiltinCall(call)) {
- return nullptr;
- }
+ bool has_side_effects =
+ builtin->HasSideEffects() ||
+ std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
+ auto* call = builder_->create<sem::Call>(expr, builtin, std::move(args), current_statement_,
+ sem::Constant{}, has_side_effects);
- current_function_->AddDirectCall(call);
+ current_function_->AddDirectlyCalledBuiltin(builtin);
- return call;
+ if (IsTextureBuiltin(builtin_type)) {
+ if (!validator_.TextureBuiltinFunction(call)) {
+ return nullptr;
+ }
+ // Collect a texture/sampler pair for this builtin.
+ const auto& signature = builtin->Signature();
+ int texture_index = signature.IndexOf(sem::ParameterUsage::kTexture);
+ if (texture_index == -1) {
+ TINT_ICE(Resolver, diagnostics_) << "texture builtin without texture parameter";
+ }
+
+ auto* texture = args[texture_index]->As<sem::VariableUser>()->Variable();
+ if (!texture->Type()->UnwrapRef()->Is<sem::StorageTexture>()) {
+ int sampler_index = signature.IndexOf(sem::ParameterUsage::kSampler);
+ const sem::Variable* sampler =
+ sampler_index != -1 ? args[sampler_index]->As<sem::VariableUser>()->Variable()
+ : nullptr;
+ current_function_->AddTextureSamplerPair(texture, sampler);
+ }
+ }
+
+ if (!validator_.BuiltinCall(call)) {
+ return nullptr;
+ }
+
+ current_function_->AddDirectCall(call);
+
+ return call;
}
-sem::Call* Resolver::FunctionCall(
- const ast::CallExpression* expr,
- sem::Function* target,
- const std::vector<const sem::Expression*> args,
- sem::Behaviors arg_behaviors) {
- auto sym = expr->target.name->symbol;
- auto name = builder_->Symbols().NameFor(sym);
+sem::Call* Resolver::FunctionCall(const ast::CallExpression* expr,
+ sem::Function* target,
+ const std::vector<const sem::Expression*> args,
+ sem::Behaviors arg_behaviors) {
+ auto sym = expr->target.name->symbol;
+ auto name = builder_->Symbols().NameFor(sym);
- // TODO(crbug.com/tint/1420): For now, assume all function calls have side
- // effects.
- bool has_side_effects = true;
- auto* call = builder_->create<sem::Call>(expr, target, std::move(args),
- current_statement_, sem::Constant{},
- has_side_effects);
+ // TODO(crbug.com/tint/1420): For now, assume all function calls have side
+ // effects.
+ bool has_side_effects = true;
+ auto* call = builder_->create<sem::Call>(expr, target, std::move(args), current_statement_,
+ sem::Constant{}, has_side_effects);
- if (current_function_) {
- // Note: Requires called functions to be resolved first.
- // This is currently guaranteed as functions must be declared before
- // use.
- current_function_->AddTransitivelyCalledFunction(target);
- current_function_->AddDirectCall(call);
- for (auto* transitive_call : target->TransitivelyCalledFunctions()) {
- current_function_->AddTransitivelyCalledFunction(transitive_call);
- }
-
- // We inherit any referenced variables from the callee.
- for (auto* var : target->TransitivelyReferencedGlobals()) {
- current_function_->AddTransitivelyReferencedGlobal(var);
- }
-
- // Map all texture/sampler pairs from the target function to the
- // current function. These can only be global or parameter
- // variables. Resolve any parameter variables to the corresponding
- // argument passed to the current function. Leave global variables
- // as-is. Then add the mapped pair to the current function's list of
- // texture/sampler pairs.
- for (sem::VariablePair pair : target->TextureSamplerPairs()) {
- const sem::Variable* texture = pair.first;
- const sem::Variable* sampler = pair.second;
- if (auto* param = texture->As<sem::Parameter>()) {
- texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
- }
- if (sampler) {
- if (auto* param = sampler->As<sem::Parameter>()) {
- sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
+ if (current_function_) {
+ // Note: Requires called functions to be resolved first.
+ // This is currently guaranteed as functions must be declared before
+ // use.
+ current_function_->AddTransitivelyCalledFunction(target);
+ current_function_->AddDirectCall(call);
+ for (auto* transitive_call : target->TransitivelyCalledFunctions()) {
+ current_function_->AddTransitivelyCalledFunction(transitive_call);
}
- }
- current_function_->AddTextureSamplerPair(texture, sampler);
+
+ // We inherit any referenced variables from the callee.
+ for (auto* var : target->TransitivelyReferencedGlobals()) {
+ current_function_->AddTransitivelyReferencedGlobal(var);
+ }
+
+ // Map all texture/sampler pairs from the target function to the
+ // current function. These can only be global or parameter
+ // variables. Resolve any parameter variables to the corresponding
+ // argument passed to the current function. Leave global variables
+ // as-is. Then add the mapped pair to the current function's list of
+ // texture/sampler pairs.
+ for (sem::VariablePair pair : target->TextureSamplerPairs()) {
+ const sem::Variable* texture = pair.first;
+ const sem::Variable* sampler = pair.second;
+ if (auto* param = texture->As<sem::Parameter>()) {
+ texture = args[param->Index()]->As<sem::VariableUser>()->Variable();
+ }
+ if (sampler) {
+ if (auto* param = sampler->As<sem::Parameter>()) {
+ sampler = args[param->Index()]->As<sem::VariableUser>()->Variable();
+ }
+ }
+ current_function_->AddTextureSamplerPair(texture, sampler);
+ }
}
- }
- target->AddCallSite(call);
+ target->AddCallSite(call);
- call->Behaviors() = arg_behaviors + target->Behaviors();
+ call->Behaviors() = arg_behaviors + target->Behaviors();
- if (!validator_.FunctionCall(call, current_statement_)) {
- return nullptr;
- }
+ if (!validator_.FunctionCall(call, current_statement_)) {
+ return nullptr;
+ }
- return call;
+ return call;
}
sem::Call* Resolver::TypeConversion(const ast::CallExpression* expr,
const sem::Type* target,
const sem::Expression* arg,
const sem::Type* source) {
- // It is not valid to have a type-cast call expression inside a call
- // statement.
- if (IsCallStatement(expr)) {
- AddError("type cast evaluated but not used", expr->source);
- return nullptr;
- }
+ // It is not valid to have a type-cast call expression inside a call
+ // statement.
+ if (IsCallStatement(expr)) {
+ AddError("type cast evaluated but not used", expr->source);
+ return nullptr;
+ }
- auto* call_target = utils::GetOrCreate(
- type_conversions_, TypeConversionSig{target, source},
- [&]() -> sem::TypeConversion* {
- // Now that the argument types have been determined, make sure that
- // they obey the conversion rules laid out in
- // https://gpuweb.github.io/gpuweb/wgsl/#conversion-expr.
- bool ok = Switch(
- target,
- [&](const sem::Vector* vec_type) {
- return validator_.VectorConstructorOrCast(expr, vec_type);
- },
- [&](const sem::Matrix* mat_type) {
- // Note: Matrix types currently cannot be converted (the element
- // type must only be f32). We implement this for the day we
- // support other matrix element types.
- return validator_.MatrixConstructorOrCast(expr, mat_type);
- },
- [&](const sem::Array* arr_type) {
- return validator_.ArrayConstructorOrCast(expr, arr_type);
- },
- [&](const sem::Struct* struct_type) {
- return validator_.StructureConstructorOrCast(expr, struct_type);
- },
- [&](Default) {
- if (target->is_scalar()) {
- return validator_.ScalarConstructorOrCast(expr, target);
- }
- AddError("type is not constructible", expr->source);
- return false;
- });
- if (!ok) {
- return nullptr;
- }
+ auto* call_target = utils::GetOrCreate(
+ type_conversions_, TypeConversionSig{target, source}, [&]() -> sem::TypeConversion* {
+ // Now that the argument types have been determined, make sure that
+ // they obey the conversion rules laid out in
+ // https://gpuweb.github.io/gpuweb/wgsl/#conversion-expr.
+ bool ok = Switch(
+ target,
+ [&](const sem::Vector* vec_type) {
+ return validator_.VectorConstructorOrCast(expr, vec_type);
+ },
+ [&](const sem::Matrix* mat_type) {
+ // Note: Matrix types currently cannot be converted (the element
+ // type must only be f32). We implement this for the day we
+ // support other matrix element types.
+ return validator_.MatrixConstructorOrCast(expr, mat_type);
+ },
+ [&](const sem::Array* arr_type) {
+ return validator_.ArrayConstructorOrCast(expr, arr_type);
+ },
+ [&](const sem::Struct* struct_type) {
+ return validator_.StructureConstructorOrCast(expr, struct_type);
+ },
+ [&](Default) {
+ if (target->is_scalar()) {
+ return validator_.ScalarConstructorOrCast(expr, target);
+ }
+ AddError("type is not constructible", expr->source);
+ return false;
+ });
+ if (!ok) {
+ return nullptr;
+ }
- auto* param = builder_->create<sem::Parameter>(
- nullptr, // declaration
- 0, // index
- source->UnwrapRef(), // type
- ast::StorageClass::kNone, // storage_class
- ast::Access::kUndefined); // access
- return builder_->create<sem::TypeConversion>(target, param);
- });
+ auto* param =
+ builder_->create<sem::Parameter>(nullptr, // declaration
+ 0, // index
+ source->UnwrapRef(), // type
+ ast::StorageClass::kNone, // storage_class
+ ast::Access::kUndefined); // access
+ return builder_->create<sem::TypeConversion>(target, param);
+ });
- if (!call_target) {
- return nullptr;
- }
+ if (!call_target) {
+ return nullptr;
+ }
- auto val = EvaluateConstantValue(expr, target);
- bool has_side_effects = arg->HasSideEffects();
- return builder_->create<sem::Call>(expr, call_target,
- std::vector<const sem::Expression*>{arg},
- current_statement_, val, has_side_effects);
+ auto val = EvaluateConstantValue(expr, target);
+ bool has_side_effects = arg->HasSideEffects();
+ return builder_->create<sem::Call>(expr, call_target, std::vector<const sem::Expression*>{arg},
+ current_statement_, val, has_side_effects);
}
-sem::Call* Resolver::TypeConstructor(
- const ast::CallExpression* expr,
- const sem::Type* ty,
- const std::vector<const sem::Expression*> args,
- const std::vector<const sem::Type*> arg_tys) {
- // It is not valid to have a type-constructor call expression as a call
- // statement.
- if (IsCallStatement(expr)) {
- AddError("type constructor evaluated but not used", expr->source);
- return nullptr;
- }
+sem::Call* Resolver::TypeConstructor(const ast::CallExpression* expr,
+ const sem::Type* ty,
+ const std::vector<const sem::Expression*> args,
+ const std::vector<const sem::Type*> arg_tys) {
+ // It is not valid to have a type-constructor call expression as a call
+ // statement.
+ if (IsCallStatement(expr)) {
+ AddError("type constructor evaluated but not used", expr->source);
+ return nullptr;
+ }
- auto* call_target = utils::GetOrCreate(
- type_ctors_, TypeConstructorSig{ty, arg_tys},
- [&]() -> sem::TypeConstructor* {
- // Now that the argument types have been determined, make sure that
- // they obey the constructor type rules laid out in
- // https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr.
- bool ok = Switch(
- ty,
- [&](const sem::Vector* vec_type) {
- return validator_.VectorConstructorOrCast(expr, vec_type);
- },
- [&](const sem::Matrix* mat_type) {
- return validator_.MatrixConstructorOrCast(expr, mat_type);
- },
- [&](const sem::Array* arr_type) {
- return validator_.ArrayConstructorOrCast(expr, arr_type);
- },
- [&](const sem::Struct* struct_type) {
- return validator_.StructureConstructorOrCast(expr, struct_type);
- },
- [&](Default) {
- if (ty->is_scalar()) {
- return validator_.ScalarConstructorOrCast(expr, ty);
- }
- AddError("type is not constructible", expr->source);
- return false;
- });
- if (!ok) {
- return nullptr;
- }
+ auto* call_target = utils::GetOrCreate(
+ type_ctors_, TypeConstructorSig{ty, arg_tys}, [&]() -> sem::TypeConstructor* {
+ // Now that the argument types have been determined, make sure that
+ // they obey the constructor type rules laid out in
+ // https://gpuweb.github.io/gpuweb/wgsl/#type-constructor-expr.
+ bool ok = Switch(
+ ty,
+ [&](const sem::Vector* vec_type) {
+ return validator_.VectorConstructorOrCast(expr, vec_type);
+ },
+ [&](const sem::Matrix* mat_type) {
+ return validator_.MatrixConstructorOrCast(expr, mat_type);
+ },
+ [&](const sem::Array* arr_type) {
+ return validator_.ArrayConstructorOrCast(expr, arr_type);
+ },
+ [&](const sem::Struct* struct_type) {
+ return validator_.StructureConstructorOrCast(expr, struct_type);
+ },
+ [&](Default) {
+ if (ty->is_scalar()) {
+ return validator_.ScalarConstructorOrCast(expr, ty);
+ }
+ AddError("type is not constructible", expr->source);
+ return false;
+ });
+ if (!ok) {
+ return nullptr;
+ }
- return builder_->create<sem::TypeConstructor>(
- ty, utils::Transform(
- arg_tys,
- [&](const sem::Type* t, size_t i) -> const sem::Parameter* {
- return builder_->create<sem::Parameter>(
- nullptr, // declaration
- static_cast<uint32_t>(i), // index
- t->UnwrapRef(), // type
- ast::StorageClass::kNone, // storage_class
- ast::Access::kUndefined); // access
- }));
- });
+ return builder_->create<sem::TypeConstructor>(
+ ty, utils::Transform(arg_tys,
+ [&](const sem::Type* t, size_t i) -> const sem::Parameter* {
+ return builder_->create<sem::Parameter>(
+ nullptr, // declaration
+ static_cast<uint32_t>(i), // index
+ t->UnwrapRef(), // type
+ ast::StorageClass::kNone, // storage_class
+ ast::Access::kUndefined); // access
+ }));
+ });
- if (!call_target) {
- return nullptr;
- }
+ if (!call_target) {
+ return nullptr;
+ }
- auto val = EvaluateConstantValue(expr, ty);
- bool has_side_effects = std::any_of(
- args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
- return builder_->create<sem::Call>(expr, call_target, std::move(args),
- current_statement_, val, has_side_effects);
+ auto val = EvaluateConstantValue(expr, ty);
+ bool has_side_effects =
+ std::any_of(args.begin(), args.end(), [](auto* e) { return e->HasSideEffects(); });
+ return builder_->create<sem::Call>(expr, call_target, std::move(args), current_statement_, val,
+ has_side_effects);
}
sem::Expression* Resolver::Literal(const ast::LiteralExpression* literal) {
- auto* ty = sem_.TypeOf(literal);
- if (!ty) {
- return nullptr;
- }
+ auto* ty = sem_.TypeOf(literal);
+ if (!ty) {
+ return nullptr;
+ }
- auto val = EvaluateConstantValue(literal, ty);
- return builder_->create<sem::Expression>(literal, ty, current_statement_, val,
- /* has_side_effects */ false);
+ auto val = EvaluateConstantValue(literal, ty);
+ return builder_->create<sem::Expression>(literal, ty, current_statement_, val,
+ /* has_side_effects */ false);
}
sem::Expression* Resolver::Identifier(const ast::IdentifierExpression* expr) {
- auto symbol = expr->symbol;
- auto* resolved = sem_.ResolvedSymbol(expr);
- if (auto* var = As<sem::Variable>(resolved)) {
- auto* user =
- builder_->create<sem::VariableUser>(expr, current_statement_, var);
+ auto symbol = expr->symbol;
+ auto* resolved = sem_.ResolvedSymbol(expr);
+ if (auto* var = As<sem::Variable>(resolved)) {
+ auto* user = builder_->create<sem::VariableUser>(expr, current_statement_, var);
- if (current_statement_) {
- // If identifier is part of a loop continuing block, make sure it
- // doesn't refer to a variable that is bypassed by a continue statement
- // in the loop's body block.
- if (auto* continuing_block =
- current_statement_
- ->FindFirstParent<sem::LoopContinuingBlockStatement>()) {
- auto* loop_block =
- continuing_block->FindFirstParent<sem::LoopBlockStatement>();
- if (loop_block->FirstContinue()) {
- auto& decls = loop_block->Decls();
- // If our identifier is in loop_block->decls, make sure its index is
- // less than first_continue
- auto iter =
- std::find_if(decls.begin(), decls.end(),
- [&symbol](auto* v) { return v->symbol == symbol; });
- if (iter != decls.end()) {
- auto var_decl_index =
- static_cast<size_t>(std::distance(decls.begin(), iter));
- if (var_decl_index >= loop_block->NumDeclsAtFirstContinue()) {
- AddError("continue statement bypasses declaration of '" +
- builder_->Symbols().NameFor(symbol) + "'",
- loop_block->FirstContinue()->source);
- AddNote("identifier '" + builder_->Symbols().NameFor(symbol) +
- "' declared here",
- (*iter)->source);
- AddNote("identifier '" + builder_->Symbols().NameFor(symbol) +
- "' referenced in continuing block here",
- expr->source);
- return nullptr;
+ if (current_statement_) {
+ // If identifier is part of a loop continuing block, make sure it
+ // doesn't refer to a variable that is bypassed by a continue statement
+ // in the loop's body block.
+ if (auto* continuing_block =
+ current_statement_->FindFirstParent<sem::LoopContinuingBlockStatement>()) {
+ auto* loop_block = continuing_block->FindFirstParent<sem::LoopBlockStatement>();
+ if (loop_block->FirstContinue()) {
+ auto& decls = loop_block->Decls();
+ // If our identifier is in loop_block->decls, make sure its index is
+ // less than first_continue
+ auto iter = std::find_if(decls.begin(), decls.end(),
+ [&symbol](auto* v) { return v->symbol == symbol; });
+ if (iter != decls.end()) {
+ auto var_decl_index =
+ static_cast<size_t>(std::distance(decls.begin(), iter));
+ if (var_decl_index >= loop_block->NumDeclsAtFirstContinue()) {
+ AddError("continue statement bypasses declaration of '" +
+ builder_->Symbols().NameFor(symbol) + "'",
+ loop_block->FirstContinue()->source);
+ AddNote("identifier '" + builder_->Symbols().NameFor(symbol) +
+ "' declared here",
+ (*iter)->source);
+ AddNote("identifier '" + builder_->Symbols().NameFor(symbol) +
+ "' referenced in continuing block here",
+ expr->source);
+ return nullptr;
+ }
+ }
+ }
}
- }
}
- }
+
+ if (current_function_) {
+ if (auto* global = var->As<sem::GlobalVariable>()) {
+ current_function_->AddDirectlyReferencedGlobal(global);
+ }
+ }
+
+ var->AddUser(user);
+ return user;
}
- if (current_function_) {
- if (auto* global = var->As<sem::GlobalVariable>()) {
- current_function_->AddDirectlyReferencedGlobal(global);
- }
+ if (Is<sem::Function>(resolved)) {
+ AddError("missing '(' for function call", expr->source.End());
+ return nullptr;
}
- var->AddUser(user);
- return user;
- }
+ if (IsBuiltin(symbol)) {
+ AddError("missing '(' for builtin call", expr->source.End());
+ return nullptr;
+ }
- if (Is<sem::Function>(resolved)) {
- AddError("missing '(' for function call", expr->source.End());
+ if (resolved->Is<sem::Type>()) {
+ AddError("missing '(' for type constructor or cast", expr->source.End());
+ return nullptr;
+ }
+
+ TINT_ICE(Resolver, diagnostics_)
+ << expr->source << " unresolved identifier:\n"
+ << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>") << "\n"
+ << "name: " << builder_->Symbols().NameFor(symbol);
return nullptr;
- }
-
- if (IsBuiltin(symbol)) {
- AddError("missing '(' for builtin call", expr->source.End());
- return nullptr;
- }
-
- if (resolved->Is<sem::Type>()) {
- AddError("missing '(' for type constructor or cast", expr->source.End());
- return nullptr;
- }
-
- TINT_ICE(Resolver, diagnostics_)
- << expr->source << " unresolved identifier:\n"
- << "resolved: " << (resolved ? resolved->TypeInfo().name : "<null>")
- << "\n"
- << "name: " << builder_->Symbols().NameFor(symbol);
- return nullptr;
}
-sem::Expression* Resolver::MemberAccessor(
- const ast::MemberAccessorExpression* expr) {
- auto* structure = sem_.TypeOf(expr->structure);
- auto* storage_ty = structure->UnwrapRef();
- auto* source_var = sem_.Get(expr->structure)->SourceVariable();
+sem::Expression* Resolver::MemberAccessor(const ast::MemberAccessorExpression* expr) {
+ auto* structure = sem_.TypeOf(expr->structure);
+ auto* storage_ty = structure->UnwrapRef();
+ auto* source_var = sem_.Get(expr->structure)->SourceVariable();
- const sem::Type* ret = nullptr;
- std::vector<uint32_t> swizzle;
+ const sem::Type* ret = nullptr;
+ std::vector<uint32_t> swizzle;
- // Structure may be a side-effecting expression (e.g. function call).
- auto* sem_structure = sem_.Get(expr->structure);
- bool has_side_effects = sem_structure && sem_structure->HasSideEffects();
+ // Structure may be a side-effecting expression (e.g. function call).
+ auto* sem_structure = sem_.Get(expr->structure);
+ bool has_side_effects = sem_structure && sem_structure->HasSideEffects();
- if (auto* str = storage_ty->As<sem::Struct>()) {
- Mark(expr->member);
- auto symbol = expr->member->symbol;
+ if (auto* str = storage_ty->As<sem::Struct>()) {
+ Mark(expr->member);
+ auto symbol = expr->member->symbol;
- const sem::StructMember* member = nullptr;
- for (auto* m : str->Members()) {
- if (m->Name() == symbol) {
- ret = m->Type();
- member = m;
- break;
- }
+ const sem::StructMember* member = nullptr;
+ for (auto* m : str->Members()) {
+ if (m->Name() == symbol) {
+ ret = m->Type();
+ member = m;
+ break;
+ }
+ }
+
+ if (ret == nullptr) {
+ AddError("struct member " + builder_->Symbols().NameFor(symbol) + " not found",
+ expr->source);
+ return nullptr;
+ }
+
+ // If we're extracting from a reference, we return a reference.
+ if (auto* ref = structure->As<sem::Reference>()) {
+ ret = builder_->create<sem::Reference>(ret, ref->StorageClass(), ref->Access());
+ }
+
+ return builder_->create<sem::StructMemberAccess>(expr, ret, current_statement_, member,
+ has_side_effects, source_var);
}
- if (ret == nullptr) {
- AddError(
- "struct member " + builder_->Symbols().NameFor(symbol) + " not found",
- expr->source);
- return nullptr;
+ if (auto* vec = storage_ty->As<sem::Vector>()) {
+ Mark(expr->member);
+ std::string s = builder_->Symbols().NameFor(expr->member->symbol);
+ auto size = s.size();
+ swizzle.reserve(s.size());
+
+ for (auto c : s) {
+ switch (c) {
+ case 'x':
+ case 'r':
+ swizzle.emplace_back(0);
+ break;
+ case 'y':
+ case 'g':
+ swizzle.emplace_back(1);
+ break;
+ case 'z':
+ case 'b':
+ swizzle.emplace_back(2);
+ break;
+ case 'w':
+ case 'a':
+ swizzle.emplace_back(3);
+ break;
+ default:
+ AddError("invalid vector swizzle character",
+ expr->member->source.Begin() + swizzle.size());
+ return nullptr;
+ }
+
+ if (swizzle.back() >= vec->Width()) {
+ AddError("invalid vector swizzle member", expr->member->source);
+ return nullptr;
+ }
+ }
+
+ if (size < 1 || size > 4) {
+ AddError("invalid vector swizzle size", expr->member->source);
+ return nullptr;
+ }
+
+ // All characters are valid, check if they're being mixed
+ auto is_rgba = [](char c) { return c == 'r' || c == 'g' || c == 'b' || c == 'a'; };
+ auto is_xyzw = [](char c) { return c == 'x' || c == 'y' || c == 'z' || c == 'w'; };
+ if (!std::all_of(s.begin(), s.end(), is_rgba) &&
+ !std::all_of(s.begin(), s.end(), is_xyzw)) {
+ AddError("invalid mixing of vector swizzle characters rgba with xyzw",
+ expr->member->source);
+ return nullptr;
+ }
+
+ if (size == 1) {
+ // A single element swizzle is just the type of the vector.
+ ret = vec->type();
+ // If we're extracting from a reference, we return a reference.
+ if (auto* ref = structure->As<sem::Reference>()) {
+ ret = builder_->create<sem::Reference>(ret, ref->StorageClass(), ref->Access());
+ }
+ } else {
+ // The vector will have a number of components equal to the length of
+ // the swizzle.
+ ret = builder_->create<sem::Vector>(vec->type(), static_cast<uint32_t>(size));
+ }
+ return builder_->create<sem::Swizzle>(expr, ret, current_statement_, std::move(swizzle),
+ has_side_effects, source_var);
}
- // If we're extracting from a reference, we return a reference.
- if (auto* ref = structure->As<sem::Reference>()) {
- ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
- ref->Access());
- }
-
- return builder_->create<sem::StructMemberAccess>(
- expr, ret, current_statement_, member, has_side_effects, source_var);
- }
-
- if (auto* vec = storage_ty->As<sem::Vector>()) {
- Mark(expr->member);
- std::string s = builder_->Symbols().NameFor(expr->member->symbol);
- auto size = s.size();
- swizzle.reserve(s.size());
-
- for (auto c : s) {
- switch (c) {
- case 'x':
- case 'r':
- swizzle.emplace_back(0);
- break;
- case 'y':
- case 'g':
- swizzle.emplace_back(1);
- break;
- case 'z':
- case 'b':
- swizzle.emplace_back(2);
- break;
- case 'w':
- case 'a':
- swizzle.emplace_back(3);
- break;
- default:
- AddError("invalid vector swizzle character",
- expr->member->source.Begin() + swizzle.size());
- return nullptr;
- }
-
- if (swizzle.back() >= vec->Width()) {
- AddError("invalid vector swizzle member", expr->member->source);
- return nullptr;
- }
- }
-
- if (size < 1 || size > 4) {
- AddError("invalid vector swizzle size", expr->member->source);
- return nullptr;
- }
-
- // All characters are valid, check if they're being mixed
- auto is_rgba = [](char c) {
- return c == 'r' || c == 'g' || c == 'b' || c == 'a';
- };
- auto is_xyzw = [](char c) {
- return c == 'x' || c == 'y' || c == 'z' || c == 'w';
- };
- if (!std::all_of(s.begin(), s.end(), is_rgba) &&
- !std::all_of(s.begin(), s.end(), is_xyzw)) {
- AddError("invalid mixing of vector swizzle characters rgba with xyzw",
- expr->member->source);
- return nullptr;
- }
-
- if (size == 1) {
- // A single element swizzle is just the type of the vector.
- ret = vec->type();
- // If we're extracting from a reference, we return a reference.
- if (auto* ref = structure->As<sem::Reference>()) {
- ret = builder_->create<sem::Reference>(ret, ref->StorageClass(),
- ref->Access());
- }
- } else {
- // The vector will have a number of components equal to the length of
- // the swizzle.
- ret = builder_->create<sem::Vector>(vec->type(),
- static_cast<uint32_t>(size));
- }
- return builder_->create<sem::Swizzle>(expr, ret, current_statement_,
- std::move(swizzle), has_side_effects,
- source_var);
- }
-
- AddError(
- "invalid member accessor expression. Expected vector or struct, got '" +
- sem_.TypeNameOf(storage_ty) + "'",
- expr->structure->source);
- return nullptr;
+ AddError("invalid member accessor expression. Expected vector or struct, got '" +
+ sem_.TypeNameOf(storage_ty) + "'",
+ expr->structure->source);
+ return nullptr;
}
sem::Expression* Resolver::Binary(const ast::BinaryExpression* expr) {
- auto* lhs = sem_.Get(expr->lhs);
- auto* rhs = sem_.Get(expr->rhs);
- auto* lhs_ty = lhs->Type()->UnwrapRef();
- auto* rhs_ty = rhs->Type()->UnwrapRef();
+ auto* lhs = sem_.Get(expr->lhs);
+ auto* rhs = sem_.Get(expr->rhs);
+ auto* lhs_ty = lhs->Type()->UnwrapRef();
+ auto* rhs_ty = rhs->Type()->UnwrapRef();
- auto* ty = BinaryOpType(lhs_ty, rhs_ty, expr->op);
- if (!ty) {
- AddError(
- "Binary expression operand types are invalid for this operation: " +
- sem_.TypeNameOf(lhs_ty) + " " + FriendlyName(expr->op) + " " +
- sem_.TypeNameOf(rhs_ty),
- expr->source);
- return nullptr;
- }
+ auto* ty = BinaryOpType(lhs_ty, rhs_ty, expr->op);
+ if (!ty) {
+ AddError("Binary expression operand types are invalid for this operation: " +
+ sem_.TypeNameOf(lhs_ty) + " " + FriendlyName(expr->op) + " " +
+ sem_.TypeNameOf(rhs_ty),
+ expr->source);
+ return nullptr;
+ }
- auto val = EvaluateConstantValue(expr, ty);
- bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
- auto* sem = builder_->create<sem::Expression>(expr, ty, current_statement_,
- val, has_side_effects);
- sem->Behaviors() = lhs->Behaviors() + rhs->Behaviors();
+ auto val = EvaluateConstantValue(expr, ty);
+ bool has_side_effects = lhs->HasSideEffects() || rhs->HasSideEffects();
+ auto* sem =
+ builder_->create<sem::Expression>(expr, ty, current_statement_, val, has_side_effects);
+ sem->Behaviors() = lhs->Behaviors() + rhs->Behaviors();
- return sem;
+ return sem;
}
const sem::Type* Resolver::BinaryOpType(const sem::Type* lhs_ty,
const sem::Type* rhs_ty,
ast::BinaryOp op) {
- using Bool = sem::Bool;
- using F32 = sem::F32;
- using I32 = sem::I32;
- using U32 = sem::U32;
- using Matrix = sem::Matrix;
- using Vector = sem::Vector;
+ using Bool = sem::Bool;
+ using F32 = sem::F32;
+ using I32 = sem::I32;
+ using U32 = sem::U32;
+ using Matrix = sem::Matrix;
+ using Vector = sem::Vector;
- auto* lhs_vec = lhs_ty->As<Vector>();
- auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr;
- auto* rhs_vec = rhs_ty->As<Vector>();
- auto* rhs_vec_elem_type = rhs_vec ? rhs_vec->type() : nullptr;
+ auto* lhs_vec = lhs_ty->As<Vector>();
+ auto* lhs_vec_elem_type = lhs_vec ? lhs_vec->type() : nullptr;
+ auto* rhs_vec = rhs_ty->As<Vector>();
+ auto* rhs_vec_elem_type = rhs_vec ? rhs_vec->type() : nullptr;
- const bool matching_vec_elem_types =
- lhs_vec_elem_type && rhs_vec_elem_type &&
- (lhs_vec_elem_type == rhs_vec_elem_type) &&
- (lhs_vec->Width() == rhs_vec->Width());
+ const bool matching_vec_elem_types = lhs_vec_elem_type && rhs_vec_elem_type &&
+ (lhs_vec_elem_type == rhs_vec_elem_type) &&
+ (lhs_vec->Width() == rhs_vec->Width());
- const bool matching_types = matching_vec_elem_types || (lhs_ty == rhs_ty);
+ const bool matching_types = matching_vec_elem_types || (lhs_ty == rhs_ty);
- // Binary logical expressions
- if (op == ast::BinaryOp::kLogicalAnd || op == ast::BinaryOp::kLogicalOr) {
- if (matching_types && lhs_ty->Is<Bool>()) {
- return lhs_ty;
+ // Binary logical expressions
+ if (op == ast::BinaryOp::kLogicalAnd || op == ast::BinaryOp::kLogicalOr) {
+ if (matching_types && lhs_ty->Is<Bool>()) {
+ return lhs_ty;
+ }
}
- }
- if (op == ast::BinaryOp::kOr || op == ast::BinaryOp::kAnd) {
- if (matching_types && lhs_ty->Is<Bool>()) {
- return lhs_ty;
- }
- if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->Is<Bool>()) {
- return lhs_ty;
- }
- }
-
- // Arithmetic expressions
- if (ast::IsArithmetic(op)) {
- // Binary arithmetic expressions over scalars
- if (matching_types && lhs_ty->is_numeric_scalar()) {
- return lhs_ty;
+ if (op == ast::BinaryOp::kOr || op == ast::BinaryOp::kAnd) {
+ if (matching_types && lhs_ty->Is<Bool>()) {
+ return lhs_ty;
+ }
+ if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->Is<Bool>()) {
+ return lhs_ty;
+ }
}
- // Binary arithmetic expressions over vectors
- if (matching_types && lhs_vec_elem_type &&
- lhs_vec_elem_type->is_numeric_scalar()) {
- return lhs_ty;
+ // Arithmetic expressions
+ if (ast::IsArithmetic(op)) {
+ // Binary arithmetic expressions over scalars
+ if (matching_types && lhs_ty->is_numeric_scalar()) {
+ return lhs_ty;
+ }
+
+ // Binary arithmetic expressions over vectors
+ if (matching_types && lhs_vec_elem_type && lhs_vec_elem_type->is_numeric_scalar()) {
+ return lhs_ty;
+ }
+
+ // Binary arithmetic expressions with mixed scalar and vector operands
+ if (lhs_vec_elem_type && (lhs_vec_elem_type == rhs_ty) && rhs_ty->is_numeric_scalar()) {
+ return lhs_ty;
+ }
+ if (rhs_vec_elem_type && (rhs_vec_elem_type == lhs_ty) && lhs_ty->is_numeric_scalar()) {
+ return rhs_ty;
+ }
}
- // Binary arithmetic expressions with mixed scalar and vector operands
- if (lhs_vec_elem_type && (lhs_vec_elem_type == rhs_ty) &&
- rhs_ty->is_numeric_scalar()) {
- return lhs_ty;
+ // Matrix arithmetic
+ auto* lhs_mat = lhs_ty->As<Matrix>();
+ auto* lhs_mat_elem_type = lhs_mat ? lhs_mat->type() : nullptr;
+ auto* rhs_mat = rhs_ty->As<Matrix>();
+ auto* rhs_mat_elem_type = rhs_mat ? rhs_mat->type() : nullptr;
+ // Addition and subtraction of float matrices
+ if ((op == ast::BinaryOp::kAdd || op == ast::BinaryOp::kSubtract) && lhs_mat_elem_type &&
+ lhs_mat_elem_type->Is<F32>() && rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
+ (lhs_mat->columns() == rhs_mat->columns()) && (lhs_mat->rows() == rhs_mat->rows())) {
+ return rhs_ty;
}
- if (rhs_vec_elem_type && (rhs_vec_elem_type == lhs_ty) &&
- lhs_ty->is_numeric_scalar()) {
- return rhs_ty;
- }
- }
+ if (op == ast::BinaryOp::kMultiply) {
+ // Multiplication of a matrix and a scalar
+ if (lhs_ty->Is<F32>() && rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>()) {
+ return rhs_ty;
+ }
+ if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_ty->Is<F32>()) {
+ return lhs_ty;
+ }
- // Matrix arithmetic
- auto* lhs_mat = lhs_ty->As<Matrix>();
- auto* lhs_mat_elem_type = lhs_mat ? lhs_mat->type() : nullptr;
- auto* rhs_mat = rhs_ty->As<Matrix>();
- auto* rhs_mat_elem_type = rhs_mat ? rhs_mat->type() : nullptr;
- // Addition and subtraction of float matrices
- if ((op == ast::BinaryOp::kAdd || op == ast::BinaryOp::kSubtract) &&
- lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_mat_elem_type &&
- rhs_mat_elem_type->Is<F32>() &&
- (lhs_mat->columns() == rhs_mat->columns()) &&
- (lhs_mat->rows() == rhs_mat->rows())) {
- return rhs_ty;
- }
- if (op == ast::BinaryOp::kMultiply) {
- // Multiplication of a matrix and a scalar
- if (lhs_ty->Is<F32>() && rhs_mat_elem_type &&
- rhs_mat_elem_type->Is<F32>()) {
- return rhs_ty;
- }
- if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
- rhs_ty->Is<F32>()) {
- return lhs_ty;
+ // Vector times matrix
+ if (lhs_vec_elem_type && lhs_vec_elem_type->Is<F32>() && rhs_mat_elem_type &&
+ rhs_mat_elem_type->Is<F32>() && (lhs_vec->Width() == rhs_mat->rows())) {
+ return builder_->create<sem::Vector>(lhs_vec->type(), rhs_mat->columns());
+ }
+
+ // Matrix times vector
+ if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_vec_elem_type &&
+ rhs_vec_elem_type->Is<F32>() && (lhs_mat->columns() == rhs_vec->Width())) {
+ return builder_->create<sem::Vector>(rhs_vec->type(), lhs_mat->rows());
+ }
+
+ // Matrix times matrix
+ if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() && rhs_mat_elem_type &&
+ rhs_mat_elem_type->Is<F32>() && (lhs_mat->columns() == rhs_mat->rows())) {
+ return builder_->create<sem::Matrix>(
+ builder_->create<sem::Vector>(lhs_mat_elem_type, lhs_mat->rows()),
+ rhs_mat->columns());
+ }
}
- // Vector times matrix
- if (lhs_vec_elem_type && lhs_vec_elem_type->Is<F32>() &&
- rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
- (lhs_vec->Width() == rhs_mat->rows())) {
- return builder_->create<sem::Vector>(lhs_vec->type(), rhs_mat->columns());
+ // Comparison expressions
+ if (ast::IsComparison(op)) {
+ if (matching_types) {
+ // Special case for bools: only == and !=
+ if (lhs_ty->Is<Bool>() &&
+ (op == ast::BinaryOp::kEqual || op == ast::BinaryOp::kNotEqual)) {
+ return builder_->create<sem::Bool>();
+ }
+
+ // For the rest, we can compare i32, u32, and f32
+ if (lhs_ty->IsAnyOf<I32, U32, F32>()) {
+ return builder_->create<sem::Bool>();
+ }
+ }
+
+ // Same for vectors
+ if (matching_vec_elem_types) {
+ if (lhs_vec_elem_type->Is<Bool>() &&
+ (op == ast::BinaryOp::kEqual || op == ast::BinaryOp::kNotEqual)) {
+ return builder_->create<sem::Vector>(builder_->create<sem::Bool>(),
+ lhs_vec->Width());
+ }
+
+ if (lhs_vec_elem_type->is_numeric_scalar()) {
+ return builder_->create<sem::Vector>(builder_->create<sem::Bool>(),
+ lhs_vec->Width());
+ }
+ }
}
- // Matrix times vector
- if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
- rhs_vec_elem_type && rhs_vec_elem_type->Is<F32>() &&
- (lhs_mat->columns() == rhs_vec->Width())) {
- return builder_->create<sem::Vector>(rhs_vec->type(), lhs_mat->rows());
+ // Binary bitwise operations
+ if (ast::IsBitwise(op)) {
+ if (matching_types && lhs_ty->is_integer_scalar_or_vector()) {
+ return lhs_ty;
+ }
}
- // Matrix times matrix
- if (lhs_mat_elem_type && lhs_mat_elem_type->Is<F32>() &&
- rhs_mat_elem_type && rhs_mat_elem_type->Is<F32>() &&
- (lhs_mat->columns() == rhs_mat->rows())) {
- return builder_->create<sem::Matrix>(
- builder_->create<sem::Vector>(lhs_mat_elem_type, lhs_mat->rows()),
- rhs_mat->columns());
- }
- }
+ // Bit shift expressions
+ if (ast::IsBitshift(op)) {
+ // Type validation rules are the same for left or right shift, despite
+ // differences in computation rules (i.e. right shift can be arithmetic or
+ // logical depending on lhs type).
- // Comparison expressions
- if (ast::IsComparison(op)) {
- if (matching_types) {
- // Special case for bools: only == and !=
- if (lhs_ty->Is<Bool>() &&
- (op == ast::BinaryOp::kEqual || op == ast::BinaryOp::kNotEqual)) {
- return builder_->create<sem::Bool>();
- }
+ if (lhs_ty->IsAnyOf<I32, U32>() && rhs_ty->Is<U32>()) {
+ return lhs_ty;
+ }
- // For the rest, we can compare i32, u32, and f32
- if (lhs_ty->IsAnyOf<I32, U32, F32>()) {
- return builder_->create<sem::Bool>();
- }
+ if (lhs_vec_elem_type && lhs_vec_elem_type->IsAnyOf<I32, U32>() && rhs_vec_elem_type &&
+ rhs_vec_elem_type->Is<U32>()) {
+ return lhs_ty;
+ }
}
- // Same for vectors
- if (matching_vec_elem_types) {
- if (lhs_vec_elem_type->Is<Bool>() &&
- (op == ast::BinaryOp::kEqual || op == ast::BinaryOp::kNotEqual)) {
- return builder_->create<sem::Vector>(builder_->create<sem::Bool>(),
- lhs_vec->Width());
- }
-
- if (lhs_vec_elem_type->is_numeric_scalar()) {
- return builder_->create<sem::Vector>(builder_->create<sem::Bool>(),
- lhs_vec->Width());
- }
- }
- }
-
- // Binary bitwise operations
- if (ast::IsBitwise(op)) {
- if (matching_types && lhs_ty->is_integer_scalar_or_vector()) {
- return lhs_ty;
- }
- }
-
- // Bit shift expressions
- if (ast::IsBitshift(op)) {
- // Type validation rules are the same for left or right shift, despite
- // differences in computation rules (i.e. right shift can be arithmetic or
- // logical depending on lhs type).
-
- if (lhs_ty->IsAnyOf<I32, U32>() && rhs_ty->Is<U32>()) {
- return lhs_ty;
- }
-
- if (lhs_vec_elem_type && lhs_vec_elem_type->IsAnyOf<I32, U32>() &&
- rhs_vec_elem_type && rhs_vec_elem_type->Is<U32>()) {
- return lhs_ty;
- }
- }
-
- return nullptr;
+ return nullptr;
}
sem::Expression* Resolver::UnaryOp(const ast::UnaryOpExpression* unary) {
- auto* expr = sem_.Get(unary->expr);
- auto* expr_ty = expr->Type();
- if (!expr_ty) {
- return nullptr;
- }
-
- const sem::Type* ty = nullptr;
- const sem::Variable* source_var = nullptr;
-
- switch (unary->op) {
- case ast::UnaryOp::kNot:
- // Result type matches the deref'd inner type.
- ty = expr_ty->UnwrapRef();
- if (!ty->Is<sem::Bool>() && !ty->is_bool_vector()) {
- AddError("cannot logical negate expression of type '" +
- sem_.TypeNameOf(expr_ty),
- unary->expr->source);
+ auto* expr = sem_.Get(unary->expr);
+ auto* expr_ty = expr->Type();
+ if (!expr_ty) {
return nullptr;
- }
- break;
+ }
- case ast::UnaryOp::kComplement:
- // Result type matches the deref'd inner type.
- ty = expr_ty->UnwrapRef();
- if (!ty->is_integer_scalar_or_vector()) {
- AddError("cannot bitwise complement expression of type '" +
- sem_.TypeNameOf(expr_ty),
- unary->expr->source);
- return nullptr;
- }
- break;
+ const sem::Type* ty = nullptr;
+ const sem::Variable* source_var = nullptr;
- case ast::UnaryOp::kNegation:
- // Result type matches the deref'd inner type.
- ty = expr_ty->UnwrapRef();
- if (!(ty->IsAnyOf<sem::F32, sem::I32>() ||
- ty->is_signed_integer_vector() || ty->is_float_vector())) {
- AddError(
- "cannot negate expression of type '" + sem_.TypeNameOf(expr_ty),
- unary->expr->source);
- return nullptr;
- }
- break;
+ switch (unary->op) {
+ case ast::UnaryOp::kNot:
+ // Result type matches the deref'd inner type.
+ ty = expr_ty->UnwrapRef();
+ if (!ty->Is<sem::Bool>() && !ty->is_bool_vector()) {
+ AddError("cannot logical negate expression of type '" + sem_.TypeNameOf(expr_ty),
+ unary->expr->source);
+ return nullptr;
+ }
+ break;
- case ast::UnaryOp::kAddressOf:
- if (auto* ref = expr_ty->As<sem::Reference>()) {
- if (ref->StoreType()->UnwrapRef()->is_handle()) {
- AddError(
- "cannot take the address of expression in handle storage class",
- unary->expr->source);
- return nullptr;
- }
+ case ast::UnaryOp::kComplement:
+ // Result type matches the deref'd inner type.
+ ty = expr_ty->UnwrapRef();
+ if (!ty->is_integer_scalar_or_vector()) {
+ AddError(
+ "cannot bitwise complement expression of type '" + sem_.TypeNameOf(expr_ty),
+ unary->expr->source);
+ return nullptr;
+ }
+ break;
- auto* array = unary->expr->As<ast::IndexAccessorExpression>();
- auto* member = unary->expr->As<ast::MemberAccessorExpression>();
- if ((array &&
- sem_.TypeOf(array->object)->UnwrapRef()->Is<sem::Vector>()) ||
- (member &&
- sem_.TypeOf(member->structure)->UnwrapRef()->Is<sem::Vector>())) {
- AddError("cannot take the address of a vector component",
- unary->expr->source);
- return nullptr;
- }
+ case ast::UnaryOp::kNegation:
+ // Result type matches the deref'd inner type.
+ ty = expr_ty->UnwrapRef();
+ if (!(ty->IsAnyOf<sem::F32, sem::I32>() || ty->is_signed_integer_vector() ||
+ ty->is_float_vector())) {
+ AddError("cannot negate expression of type '" + sem_.TypeNameOf(expr_ty),
+ unary->expr->source);
+ return nullptr;
+ }
+ break;
- ty = builder_->create<sem::Pointer>(ref->StoreType(),
- ref->StorageClass(), ref->Access());
+ case ast::UnaryOp::kAddressOf:
+ if (auto* ref = expr_ty->As<sem::Reference>()) {
+ if (ref->StoreType()->UnwrapRef()->is_handle()) {
+ AddError("cannot take the address of expression in handle storage class",
+ unary->expr->source);
+ return nullptr;
+ }
- source_var = expr->SourceVariable();
- } else {
- AddError("cannot take the address of expression", unary->expr->source);
- return nullptr;
- }
- break;
+ auto* array = unary->expr->As<ast::IndexAccessorExpression>();
+ auto* member = unary->expr->As<ast::MemberAccessorExpression>();
+ if ((array && sem_.TypeOf(array->object)->UnwrapRef()->Is<sem::Vector>()) ||
+ (member && sem_.TypeOf(member->structure)->UnwrapRef()->Is<sem::Vector>())) {
+ AddError("cannot take the address of a vector component", unary->expr->source);
+ return nullptr;
+ }
- case ast::UnaryOp::kIndirection:
- if (auto* ptr = expr_ty->As<sem::Pointer>()) {
- ty = builder_->create<sem::Reference>(
- ptr->StoreType(), ptr->StorageClass(), ptr->Access());
- source_var = expr->SourceVariable();
- } else {
- AddError("cannot dereference expression of type '" +
- sem_.TypeNameOf(expr_ty) + "'",
- unary->expr->source);
- return nullptr;
- }
- break;
- }
+ ty = builder_->create<sem::Pointer>(ref->StoreType(), ref->StorageClass(),
+ ref->Access());
- auto val = EvaluateConstantValue(unary, ty);
- auto* sem = builder_->create<sem::Expression>(
- unary, ty, current_statement_, val, expr->HasSideEffects(), source_var);
- sem->Behaviors() = expr->Behaviors();
- return sem;
+ source_var = expr->SourceVariable();
+ } else {
+ AddError("cannot take the address of expression", unary->expr->source);
+ return nullptr;
+ }
+ break;
+
+ case ast::UnaryOp::kIndirection:
+ if (auto* ptr = expr_ty->As<sem::Pointer>()) {
+ ty = builder_->create<sem::Reference>(ptr->StoreType(), ptr->StorageClass(),
+ ptr->Access());
+ source_var = expr->SourceVariable();
+ } else {
+ AddError("cannot dereference expression of type '" + sem_.TypeNameOf(expr_ty) + "'",
+ unary->expr->source);
+ return nullptr;
+ }
+ break;
+ }
+
+ auto val = EvaluateConstantValue(unary, ty);
+ auto* sem = builder_->create<sem::Expression>(unary, ty, current_statement_, val,
+ expr->HasSideEffects(), source_var);
+ sem->Behaviors() = expr->Behaviors();
+ return sem;
}
sem::Type* Resolver::TypeDecl(const ast::TypeDecl* named_type) {
- sem::Type* result = nullptr;
- if (auto* alias = named_type->As<ast::Alias>()) {
- result = Alias(alias);
- } else if (auto* str = named_type->As<ast::Struct>()) {
- result = Structure(str);
- } else {
- TINT_UNREACHABLE(Resolver, diagnostics_) << "Unhandled TypeDecl";
- }
+ sem::Type* result = nullptr;
+ if (auto* alias = named_type->As<ast::Alias>()) {
+ result = Alias(alias);
+ } else if (auto* str = named_type->As<ast::Struct>()) {
+ result = Structure(str);
+ } else {
+ TINT_UNREACHABLE(Resolver, diagnostics_) << "Unhandled TypeDecl";
+ }
- if (!result) {
- return nullptr;
- }
+ if (!result) {
+ return nullptr;
+ }
- builder_->Sem().Add(named_type, result);
- return result;
+ builder_->Sem().Add(named_type, result);
+ return result;
}
sem::Array* Resolver::Array(const ast::Array* arr) {
- auto source = arr->source;
+ auto source = arr->source;
- auto* elem_type = Type(arr->type);
- if (!elem_type) {
- return nullptr;
- }
-
- if (!validator_.IsPlain(
- elem_type)) { // Check must come before GetDefaultAlignAndSize()
- AddError(sem_.TypeNameOf(elem_type) +
- " cannot be used as an element type of an array",
- source);
- return nullptr;
- }
-
- uint32_t el_align = elem_type->Align();
- uint32_t el_size = elem_type->Size();
-
- if (!validator_.NoDuplicateAttributes(arr->attributes)) {
- return nullptr;
- }
-
- // Look for explicit stride via @stride(n) attribute
- uint32_t explicit_stride = 0;
- for (auto* attr : arr->attributes) {
- Mark(attr);
- if (auto* sd = attr->As<ast::StrideAttribute>()) {
- explicit_stride = sd->stride;
- if (!validator_.ArrayStrideAttribute(sd, el_size, el_align, source)) {
+ auto* elem_type = Type(arr->type);
+ if (!elem_type) {
return nullptr;
- }
- continue;
}
- AddError("attribute is not valid for array types", attr->source);
- return nullptr;
- }
-
- // Calculate implicit stride
- uint64_t implicit_stride = utils::RoundUp<uint64_t>(el_align, el_size);
-
- uint64_t stride = explicit_stride ? explicit_stride : implicit_stride;
-
- // Evaluate the constant array size expression.
- // sem::Array uses a size of 0 for a runtime-sized array.
- uint32_t count = 0;
- if (auto* count_expr = arr->count) {
- auto* count_sem = Expression(count_expr);
- if (!count_sem) {
- return nullptr;
- }
-
- auto size_source = count_expr->source;
-
- auto* ty = count_sem->Type()->UnwrapRef();
- if (!ty->is_integer_scalar()) {
- AddError("array size must be integer scalar", size_source);
- return nullptr;
- }
-
- if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
- // Make sure the identifier is a non-overridable module-scope constant.
- auto* var = sem_.ResolvedSymbol<sem::GlobalVariable>(ident);
- if (!var || !var->Declaration()->is_const) {
- AddError("array size identifier must be a module-scope constant",
- size_source);
+ if (!validator_.IsPlain(elem_type)) { // Check must come before GetDefaultAlignAndSize()
+ AddError(sem_.TypeNameOf(elem_type) + " cannot be used as an element type of an array",
+ source);
return nullptr;
- }
- if (var->IsOverridable()) {
- AddError("array size expression must not be pipeline-overridable",
- size_source);
+ }
+
+ uint32_t el_align = elem_type->Align();
+ uint32_t el_size = elem_type->Size();
+
+ if (!validator_.NoDuplicateAttributes(arr->attributes)) {
return nullptr;
- }
-
- count_expr = var->Declaration()->constructor;
- } else if (!count_expr->Is<ast::LiteralExpression>()) {
- AddError(
- "array size expression must be either a literal or a module-scope "
- "constant",
- size_source);
- return nullptr;
}
- auto count_val = count_sem->ConstantValue();
- if (!count_val) {
- TINT_ICE(Resolver, diagnostics_)
- << "could not resolve array size expression";
- return nullptr;
+ // Look for explicit stride via @stride(n) attribute
+ uint32_t explicit_stride = 0;
+ for (auto* attr : arr->attributes) {
+ Mark(attr);
+ if (auto* sd = attr->As<ast::StrideAttribute>()) {
+ explicit_stride = sd->stride;
+ if (!validator_.ArrayStrideAttribute(sd, el_size, el_align, source)) {
+ return nullptr;
+ }
+ continue;
+ }
+
+ AddError("attribute is not valid for array types", attr->source);
+ return nullptr;
}
- if (ty->is_signed_integer_scalar() ? count_val.Elements()[0].i32 < 1
- : count_val.Elements()[0].u32 < 1u) {
- AddError("array size must be at least 1", size_source);
- return nullptr;
+ // Calculate implicit stride
+ uint64_t implicit_stride = utils::RoundUp<uint64_t>(el_align, el_size);
+
+ uint64_t stride = explicit_stride ? explicit_stride : implicit_stride;
+
+ // Evaluate the constant array size expression.
+ // sem::Array uses a size of 0 for a runtime-sized array.
+ uint32_t count = 0;
+ if (auto* count_expr = arr->count) {
+ auto* count_sem = Expression(count_expr);
+ if (!count_sem) {
+ return nullptr;
+ }
+
+ auto size_source = count_expr->source;
+
+ auto* ty = count_sem->Type()->UnwrapRef();
+ if (!ty->is_integer_scalar()) {
+ AddError("array size must be integer scalar", size_source);
+ return nullptr;
+ }
+
+ if (auto* ident = count_expr->As<ast::IdentifierExpression>()) {
+ // Make sure the identifier is a non-overridable module-scope constant.
+ auto* var = sem_.ResolvedSymbol<sem::GlobalVariable>(ident);
+ if (!var || !var->Declaration()->is_const) {
+ AddError("array size identifier must be a module-scope constant", size_source);
+ return nullptr;
+ }
+ if (var->IsOverridable()) {
+ AddError("array size expression must not be pipeline-overridable", size_source);
+ return nullptr;
+ }
+
+ count_expr = var->Declaration()->constructor;
+ } else if (!count_expr->Is<ast::LiteralExpression>()) {
+ AddError(
+ "array size expression must be either a literal or a module-scope "
+ "constant",
+ size_source);
+ return nullptr;
+ }
+
+ auto count_val = count_sem->ConstantValue();
+ if (!count_val) {
+ TINT_ICE(Resolver, diagnostics_) << "could not resolve array size expression";
+ return nullptr;
+ }
+
+ if (ty->is_signed_integer_scalar() ? count_val.Elements()[0].i32 < 1
+ : count_val.Elements()[0].u32 < 1u) {
+ AddError("array size must be at least 1", size_source);
+ return nullptr;
+ }
+
+ count = count_val.Elements()[0].u32;
}
- count = count_val.Elements()[0].u32;
- }
-
- auto size = std::max<uint64_t>(count, 1) * stride;
- if (size > std::numeric_limits<uint32_t>::max()) {
- std::stringstream msg;
- msg << "array size in bytes must not exceed 0x" << std::hex
- << std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex
- << size;
- AddError(msg.str(), arr->source);
- return nullptr;
- }
- if (stride > std::numeric_limits<uint32_t>::max() ||
- implicit_stride > std::numeric_limits<uint32_t>::max()) {
- TINT_ICE(Resolver, diagnostics_)
- << "calculated array stride exceeds uint32";
- return nullptr;
- }
- auto* out = builder_->create<sem::Array>(
- elem_type, count, el_align, static_cast<uint32_t>(size),
- static_cast<uint32_t>(stride), static_cast<uint32_t>(implicit_stride));
-
- if (!validator_.Array(out, source)) {
- return nullptr;
- }
-
- if (elem_type->Is<sem::Atomic>()) {
- atomic_composite_info_.emplace(out, arr->type->source);
- } else {
- auto found = atomic_composite_info_.find(elem_type);
- if (found != atomic_composite_info_.end()) {
- atomic_composite_info_.emplace(out, found->second);
+ auto size = std::max<uint64_t>(count, 1) * stride;
+ if (size > std::numeric_limits<uint32_t>::max()) {
+ std::stringstream msg;
+ msg << "array size in bytes must not exceed 0x" << std::hex
+ << std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex << size;
+ AddError(msg.str(), arr->source);
+ return nullptr;
}
- }
+ if (stride > std::numeric_limits<uint32_t>::max() ||
+ implicit_stride > std::numeric_limits<uint32_t>::max()) {
+ TINT_ICE(Resolver, diagnostics_) << "calculated array stride exceeds uint32";
+ return nullptr;
+ }
+ auto* out = builder_->create<sem::Array>(
+ elem_type, count, el_align, static_cast<uint32_t>(size), static_cast<uint32_t>(stride),
+ static_cast<uint32_t>(implicit_stride));
- return out;
+ if (!validator_.Array(out, source)) {
+ return nullptr;
+ }
+
+ if (elem_type->Is<sem::Atomic>()) {
+ atomic_composite_info_.emplace(out, arr->type->source);
+ } else {
+ auto found = atomic_composite_info_.find(elem_type);
+ if (found != atomic_composite_info_.end()) {
+ atomic_composite_info_.emplace(out, found->second);
+ }
+ }
+
+ return out;
}
sem::Type* Resolver::Alias(const ast::Alias* alias) {
- auto* ty = Type(alias->type);
- if (!ty) {
- return nullptr;
- }
- if (!validator_.Alias(alias)) {
- return nullptr;
- }
- return ty;
+ auto* ty = Type(alias->type);
+ if (!ty) {
+ return nullptr;
+ }
+ if (!validator_.Alias(alias)) {
+ return nullptr;
+ }
+ return ty;
}
sem::Struct* Resolver::Structure(const ast::Struct* str) {
- if (!validator_.NoDuplicateAttributes(str->attributes)) {
- return nullptr;
- }
- for (auto* attr : str->attributes) {
- Mark(attr);
- }
-
- sem::StructMemberList sem_members;
- sem_members.reserve(str->members.size());
-
- // Calculate the effective size and alignment of each field, and the overall
- // size of the structure.
- // For size, use the size attribute if provided, otherwise use the default
- // size for the type.
- // For alignment, use the alignment attribute if provided, otherwise use the
- // default alignment for the member type.
- // Diagnostic errors are raised if a basic rule is violated.
- // Validation of storage-class rules requires analysing the actual variable
- // usage of the structure, and so is performed as part of the variable
- // validation.
- uint64_t struct_size = 0;
- uint64_t struct_align = 1;
- std::unordered_map<Symbol, const ast::StructMember*> member_map;
-
- for (auto* member : str->members) {
- Mark(member);
- auto result = member_map.emplace(member->symbol, member);
- if (!result.second) {
- AddError("redefinition of '" +
- builder_->Symbols().NameFor(member->symbol) + "'",
- member->source);
- AddNote("previous definition is here", result.first->second->source);
- return nullptr;
+ if (!validator_.NoDuplicateAttributes(str->attributes)) {
+ return nullptr;
+ }
+ for (auto* attr : str->attributes) {
+ Mark(attr);
}
- // Resolve member type
- auto* type = Type(member->type);
- if (!type) {
- return nullptr;
- }
+ sem::StructMemberList sem_members;
+ sem_members.reserve(str->members.size());
- // validator_.Validate member type
- if (!validator_.IsPlain(type)) {
- AddError(sem_.TypeNameOf(type) +
- " cannot be used as the type of a structure member",
- member->source);
- return nullptr;
- }
+ // Calculate the effective size and alignment of each field, and the overall
+ // size of the structure.
+ // For size, use the size attribute if provided, otherwise use the default
+ // size for the type.
+ // For alignment, use the alignment attribute if provided, otherwise use the
+ // default alignment for the member type.
+ // Diagnostic errors are raised if a basic rule is violated.
+ // Validation of storage-class rules requires analysing the actual variable
+ // usage of the structure, and so is performed as part of the variable
+ // validation.
+ uint64_t struct_size = 0;
+ uint64_t struct_align = 1;
+ std::unordered_map<Symbol, const ast::StructMember*> member_map;
- uint64_t offset = struct_size;
- uint64_t align = type->Align();
- uint64_t size = type->Size();
-
- if (!validator_.NoDuplicateAttributes(member->attributes)) {
- return nullptr;
- }
-
- bool has_offset_attr = false;
- bool has_align_attr = false;
- bool has_size_attr = false;
- for (auto* attr : member->attributes) {
- Mark(attr);
- if (auto* o = attr->As<ast::StructMemberOffsetAttribute>()) {
- // Offset attributes are not part of the WGSL spec, but are emitted
- // by the SPIR-V reader.
- if (o->offset < struct_size) {
- AddError("offsets must be in ascending order", o->source);
- return nullptr;
+ for (auto* member : str->members) {
+ Mark(member);
+ auto result = member_map.emplace(member->symbol, member);
+ if (!result.second) {
+ AddError("redefinition of '" + builder_->Symbols().NameFor(member->symbol) + "'",
+ member->source);
+ AddNote("previous definition is here", result.first->second->source);
+ return nullptr;
}
- offset = o->offset;
- align = 1;
- has_offset_attr = true;
- } else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
- if (a->align <= 0 || !utils::IsPowerOfTwo(a->align)) {
- AddError("align value must be a positive, power-of-two integer",
- a->source);
- return nullptr;
+
+ // Resolve member type
+ auto* type = Type(member->type);
+ if (!type) {
+ return nullptr;
}
- align = a->align;
- has_align_attr = true;
- } else if (auto* s = attr->As<ast::StructMemberSizeAttribute>()) {
- if (s->size < size) {
- AddError("size must be at least as big as the type's size (" +
- std::to_string(size) + ")",
- s->source);
- return nullptr;
+
+ // validator_.Validate member type
+ if (!validator_.IsPlain(type)) {
+ AddError(sem_.TypeNameOf(type) + " cannot be used as the type of a structure member",
+ member->source);
+ return nullptr;
}
- size = s->size;
- has_size_attr = true;
- }
+
+ uint64_t offset = struct_size;
+ uint64_t align = type->Align();
+ uint64_t size = type->Size();
+
+ if (!validator_.NoDuplicateAttributes(member->attributes)) {
+ return nullptr;
+ }
+
+ bool has_offset_attr = false;
+ bool has_align_attr = false;
+ bool has_size_attr = false;
+ for (auto* attr : member->attributes) {
+ Mark(attr);
+ if (auto* o = attr->As<ast::StructMemberOffsetAttribute>()) {
+ // Offset attributes are not part of the WGSL spec, but are emitted
+ // by the SPIR-V reader.
+ if (o->offset < struct_size) {
+ AddError("offsets must be in ascending order", o->source);
+ return nullptr;
+ }
+ offset = o->offset;
+ align = 1;
+ has_offset_attr = true;
+ } else if (auto* a = attr->As<ast::StructMemberAlignAttribute>()) {
+ if (a->align <= 0 || !utils::IsPowerOfTwo(a->align)) {
+ AddError("align value must be a positive, power-of-two integer", a->source);
+ return nullptr;
+ }
+ align = a->align;
+ has_align_attr = true;
+ } else if (auto* s = attr->As<ast::StructMemberSizeAttribute>()) {
+ if (s->size < size) {
+ AddError("size must be at least as big as the type's size (" +
+ std::to_string(size) + ")",
+ s->source);
+ return nullptr;
+ }
+ size = s->size;
+ has_size_attr = true;
+ }
+ }
+
+ if (has_offset_attr && (has_align_attr || has_size_attr)) {
+ AddError("offset attributes cannot be used with align or size attributes",
+ member->source);
+ return nullptr;
+ }
+
+ offset = utils::RoundUp(align, offset);
+ if (offset > std::numeric_limits<uint32_t>::max()) {
+ std::stringstream msg;
+ msg << "struct member has byte offset 0x" << std::hex << offset
+ << ", but must not exceed 0x" << std::hex << std::numeric_limits<uint32_t>::max();
+ AddError(msg.str(), member->source);
+ return nullptr;
+ }
+
+ auto* sem_member = builder_->create<sem::StructMember>(
+ member, member->symbol, type, static_cast<uint32_t>(sem_members.size()),
+ static_cast<uint32_t>(offset), static_cast<uint32_t>(align),
+ static_cast<uint32_t>(size));
+ builder_->Sem().Add(member, sem_member);
+ sem_members.emplace_back(sem_member);
+
+ struct_size = offset + size;
+ struct_align = std::max(struct_align, align);
}
- if (has_offset_attr && (has_align_attr || has_size_attr)) {
- AddError("offset attributes cannot be used with align or size attributes",
- member->source);
- return nullptr;
+ uint64_t size_no_padding = struct_size;
+ struct_size = utils::RoundUp(struct_align, struct_size);
+
+ if (struct_size > std::numeric_limits<uint32_t>::max()) {
+ std::stringstream msg;
+ msg << "struct size in bytes must not exceed 0x" << std::hex
+ << std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex << struct_size;
+ AddError(msg.str(), str->source);
+ return nullptr;
+ }
+ if (struct_align > std::numeric_limits<uint32_t>::max()) {
+ TINT_ICE(Resolver, diagnostics_) << "calculated struct stride exceeds uint32";
+ return nullptr;
}
- offset = utils::RoundUp(align, offset);
- if (offset > std::numeric_limits<uint32_t>::max()) {
- std::stringstream msg;
- msg << "struct member has byte offset 0x" << std::hex << offset
- << ", but must not exceed 0x" << std::hex
- << std::numeric_limits<uint32_t>::max();
- AddError(msg.str(), member->source);
- return nullptr;
+ auto* out = builder_->create<sem::Struct>(
+ str, str->name, sem_members, static_cast<uint32_t>(struct_align),
+ static_cast<uint32_t>(struct_size), static_cast<uint32_t>(size_no_padding));
+
+ for (size_t i = 0; i < sem_members.size(); i++) {
+ auto* mem_type = sem_members[i]->Type();
+ if (mem_type->Is<sem::Atomic>()) {
+ atomic_composite_info_.emplace(out, sem_members[i]->Declaration()->source);
+ break;
+ } else {
+ auto found = atomic_composite_info_.find(mem_type);
+ if (found != atomic_composite_info_.end()) {
+ atomic_composite_info_.emplace(out, found->second);
+ break;
+ }
+ }
}
- auto* sem_member = builder_->create<sem::StructMember>(
- member, member->symbol, type, static_cast<uint32_t>(sem_members.size()),
- static_cast<uint32_t>(offset), static_cast<uint32_t>(align),
- static_cast<uint32_t>(size));
- builder_->Sem().Add(member, sem_member);
- sem_members.emplace_back(sem_member);
-
- struct_size = offset + size;
- struct_align = std::max(struct_align, align);
- }
-
- uint64_t size_no_padding = struct_size;
- struct_size = utils::RoundUp(struct_align, struct_size);
-
- if (struct_size > std::numeric_limits<uint32_t>::max()) {
- std::stringstream msg;
- msg << "struct size in bytes must not exceed 0x" << std::hex
- << std::numeric_limits<uint32_t>::max() << ", but is 0x" << std::hex
- << struct_size;
- AddError(msg.str(), str->source);
- return nullptr;
- }
- if (struct_align > std::numeric_limits<uint32_t>::max()) {
- TINT_ICE(Resolver, diagnostics_)
- << "calculated struct stride exceeds uint32";
- return nullptr;
- }
-
- auto* out = builder_->create<sem::Struct>(
- str, str->name, sem_members, static_cast<uint32_t>(struct_align),
- static_cast<uint32_t>(struct_size),
- static_cast<uint32_t>(size_no_padding));
-
- for (size_t i = 0; i < sem_members.size(); i++) {
- auto* mem_type = sem_members[i]->Type();
- if (mem_type->Is<sem::Atomic>()) {
- atomic_composite_info_.emplace(out,
- sem_members[i]->Declaration()->source);
- break;
- } else {
- auto found = atomic_composite_info_.find(mem_type);
- if (found != atomic_composite_info_.end()) {
- atomic_composite_info_.emplace(out, found->second);
- break;
- }
+ auto stage = current_function_ ? current_function_->Declaration()->PipelineStage()
+ : ast::PipelineStage::kNone;
+ if (!validator_.Structure(out, stage)) {
+ return nullptr;
}
- }
- auto stage = current_function_
- ? current_function_->Declaration()->PipelineStage()
- : ast::PipelineStage::kNone;
- if (!validator_.Structure(out, stage)) {
- return nullptr;
- }
-
- return out;
+ return out;
}
sem::Statement* Resolver::ReturnStatement(const ast::ReturnStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto& behaviors = current_statement_->Behaviors();
- behaviors = sem::Behavior::kReturn;
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto& behaviors = current_statement_->Behaviors();
+ behaviors = sem::Behavior::kReturn;
- if (auto* value = stmt->value) {
- auto* expr = Expression(value);
- if (!expr) {
- return false;
- }
- behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
- }
+ if (auto* value = stmt->value) {
+ auto* expr = Expression(value);
+ if (!expr) {
+ return false;
+ }
+ behaviors.Add(expr->Behaviors() - sem::Behavior::kNext);
+ }
- // Validate after processing the return value expression so that its type
- // is available for validation.
- auto* ret_type = stmt->value ? sem_.TypeOf(stmt->value)->UnwrapRef()
- : builder_->create<sem::Void>();
- return validator_.Return(stmt, current_function_->ReturnType(), ret_type,
- current_statement_);
- });
+ // Validate after processing the return value expression so that its type
+ // is available for validation.
+ auto* ret_type =
+ stmt->value ? sem_.TypeOf(stmt->value)->UnwrapRef() : builder_->create<sem::Void>();
+ return validator_.Return(stmt, current_function_->ReturnType(), ret_type,
+ current_statement_);
+ });
}
-sem::SwitchStatement* Resolver::SwitchStatement(
- const ast::SwitchStatement* stmt) {
- auto* sem = builder_->create<sem::SwitchStatement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto& behaviors = sem->Behaviors();
+sem::SwitchStatement* Resolver::SwitchStatement(const ast::SwitchStatement* stmt) {
+ auto* sem = builder_->create<sem::SwitchStatement>(stmt, current_compound_statement_,
+ current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto& behaviors = sem->Behaviors();
- auto* cond = Expression(stmt->condition);
- if (!cond) {
- return false;
- }
- behaviors = cond->Behaviors() - sem::Behavior::kNext;
+ auto* cond = Expression(stmt->condition);
+ if (!cond) {
+ return false;
+ }
+ behaviors = cond->Behaviors() - sem::Behavior::kNext;
- for (auto* case_stmt : stmt->body) {
- Mark(case_stmt);
- auto* c = CaseStatement(case_stmt);
- if (!c) {
- return false;
- }
- behaviors.Add(c->Behaviors());
- }
+ for (auto* case_stmt : stmt->body) {
+ Mark(case_stmt);
+ auto* c = CaseStatement(case_stmt);
+ if (!c) {
+ return false;
+ }
+ behaviors.Add(c->Behaviors());
+ }
- if (behaviors.Contains(sem::Behavior::kBreak)) {
- behaviors.Add(sem::Behavior::kNext);
- }
- behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kFallthrough);
+ if (behaviors.Contains(sem::Behavior::kBreak)) {
+ behaviors.Add(sem::Behavior::kNext);
+ }
+ behaviors.Remove(sem::Behavior::kBreak, sem::Behavior::kFallthrough);
- return validator_.SwitchStatement(stmt);
- });
+ return validator_.SwitchStatement(stmt);
+ });
}
-sem::Statement* Resolver::VariableDeclStatement(
- const ast::VariableDeclStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- Mark(stmt->variable);
+sem::Statement* Resolver::VariableDeclStatement(const ast::VariableDeclStatement* stmt) {
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ Mark(stmt->variable);
- auto* var = Variable(stmt->variable, VariableKind::kLocal);
- if (!var) {
- return false;
- }
+ auto* var = Variable(stmt->variable, VariableKind::kLocal);
+ if (!var) {
+ return false;
+ }
- for (auto* attr : stmt->variable->attributes) {
- Mark(attr);
- if (!attr->Is<ast::InternalAttribute>()) {
- AddError("attributes are not valid on local variables", attr->source);
- return false;
- }
- }
+ for (auto* attr : stmt->variable->attributes) {
+ Mark(attr);
+ if (!attr->Is<ast::InternalAttribute>()) {
+ AddError("attributes are not valid on local variables", attr->source);
+ return false;
+ }
+ }
- if (current_block_) { // Not all statements are inside a block
- current_block_->AddDecl(stmt->variable);
- }
+ if (current_block_) { // Not all statements are inside a block
+ current_block_->AddDecl(stmt->variable);
+ }
- if (auto* ctor = var->Constructor()) {
- sem->Behaviors() = ctor->Behaviors();
- }
+ if (auto* ctor = var->Constructor()) {
+ sem->Behaviors() = ctor->Behaviors();
+ }
- return validator_.Variable(var);
- });
+ return validator_.Variable(var);
+ });
}
-sem::Statement* Resolver::AssignmentStatement(
- const ast::AssignmentStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto* lhs = Expression(stmt->lhs);
- if (!lhs) {
- return false;
- }
+sem::Statement* Resolver::AssignmentStatement(const ast::AssignmentStatement* stmt) {
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto* lhs = Expression(stmt->lhs);
+ if (!lhs) {
+ return false;
+ }
- auto* rhs = Expression(stmt->rhs);
- if (!rhs) {
- return false;
- }
+ auto* rhs = Expression(stmt->rhs);
+ if (!rhs) {
+ return false;
+ }
- auto& behaviors = sem->Behaviors();
- behaviors = rhs->Behaviors();
- if (!stmt->lhs->Is<ast::PhonyExpression>()) {
- behaviors.Add(lhs->Behaviors());
- }
+ auto& behaviors = sem->Behaviors();
+ behaviors = rhs->Behaviors();
+ if (!stmt->lhs->Is<ast::PhonyExpression>()) {
+ behaviors.Add(lhs->Behaviors());
+ }
- return validator_.Assignment(stmt, sem_.TypeOf(stmt->rhs));
- });
+ return validator_.Assignment(stmt, sem_.TypeOf(stmt->rhs));
+ });
}
sem::Statement* Resolver::BreakStatement(const ast::BreakStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- sem->Behaviors() = sem::Behavior::kBreak;
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ sem->Behaviors() = sem::Behavior::kBreak;
- return validator_.BreakStatement(sem, current_statement_);
- });
+ return validator_.BreakStatement(sem, current_statement_);
+ });
}
sem::Statement* Resolver::CallStatement(const ast::CallStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- if (auto* expr = Expression(stmt->expr)) {
- sem->Behaviors() = expr->Behaviors();
- return true;
- }
- return false;
- });
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ if (auto* expr = Expression(stmt->expr)) {
+ sem->Behaviors() = expr->Behaviors();
+ return true;
+ }
+ return false;
+ });
}
sem::Statement* Resolver::CompoundAssignmentStatement(
const ast::CompoundAssignmentStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto* lhs = Expression(stmt->lhs);
- if (!lhs) {
- return false;
- }
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto* lhs = Expression(stmt->lhs);
+ if (!lhs) {
+ return false;
+ }
- auto* rhs = Expression(stmt->rhs);
- if (!rhs) {
- return false;
- }
+ auto* rhs = Expression(stmt->rhs);
+ if (!rhs) {
+ return false;
+ }
- sem->Behaviors() = rhs->Behaviors() + lhs->Behaviors();
+ sem->Behaviors() = rhs->Behaviors() + lhs->Behaviors();
- auto* lhs_ty = lhs->Type()->UnwrapRef();
- auto* rhs_ty = rhs->Type()->UnwrapRef();
- auto* ty = BinaryOpType(lhs_ty, rhs_ty, stmt->op);
- if (!ty) {
- AddError("compound assignment operand types are invalid: " +
- sem_.TypeNameOf(lhs_ty) + " " + FriendlyName(stmt->op) +
- " " + sem_.TypeNameOf(rhs_ty),
- stmt->source);
- return false;
- }
- return validator_.Assignment(stmt, ty);
- });
+ auto* lhs_ty = lhs->Type()->UnwrapRef();
+ auto* rhs_ty = rhs->Type()->UnwrapRef();
+ auto* ty = BinaryOpType(lhs_ty, rhs_ty, stmt->op);
+ if (!ty) {
+ AddError("compound assignment operand types are invalid: " + sem_.TypeNameOf(lhs_ty) +
+ " " + FriendlyName(stmt->op) + " " + sem_.TypeNameOf(rhs_ty),
+ stmt->source);
+ return false;
+ }
+ return validator_.Assignment(stmt, ty);
+ });
}
-sem::Statement* Resolver::ContinueStatement(
- const ast::ContinueStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- sem->Behaviors() = sem::Behavior::kContinue;
+sem::Statement* Resolver::ContinueStatement(const ast::ContinueStatement* stmt) {
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ sem->Behaviors() = sem::Behavior::kContinue;
- // Set if we've hit the first continue statement in our parent loop
- if (auto* block = sem->FindFirstParent<sem::LoopBlockStatement>()) {
- if (!block->FirstContinue()) {
- const_cast<sem::LoopBlockStatement*>(block)->SetFirstContinue(
- stmt, block->Decls().size());
- }
- }
+ // Set if we've hit the first continue statement in our parent loop
+ if (auto* block = sem->FindFirstParent<sem::LoopBlockStatement>()) {
+ if (!block->FirstContinue()) {
+ const_cast<sem::LoopBlockStatement*>(block)->SetFirstContinue(
+ stmt, block->Decls().size());
+ }
+ }
- return validator_.ContinueStatement(sem, current_statement_);
- });
+ return validator_.ContinueStatement(sem, current_statement_);
+ });
}
sem::Statement* Resolver::DiscardStatement(const ast::DiscardStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- sem->Behaviors() = sem::Behavior::kDiscard;
- current_function_->SetHasDiscard();
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ sem->Behaviors() = sem::Behavior::kDiscard;
+ current_function_->SetHasDiscard();
- return validator_.DiscardStatement(sem, current_statement_);
- });
+ return validator_.DiscardStatement(sem, current_statement_);
+ });
}
-sem::Statement* Resolver::FallthroughStatement(
- const ast::FallthroughStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- sem->Behaviors() = sem::Behavior::kFallthrough;
+sem::Statement* Resolver::FallthroughStatement(const ast::FallthroughStatement* stmt) {
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ sem->Behaviors() = sem::Behavior::kFallthrough;
- return validator_.FallthroughStatement(sem);
- });
+ return validator_.FallthroughStatement(sem);
+ });
}
sem::Statement* Resolver::IncrementDecrementStatement(
const ast::IncrementDecrementStatement* stmt) {
- auto* sem = builder_->create<sem::Statement>(
- stmt, current_compound_statement_, current_function_);
- return StatementScope(stmt, sem, [&] {
- auto* lhs = Expression(stmt->lhs);
- if (!lhs) {
- return false;
- }
- sem->Behaviors() = lhs->Behaviors();
+ auto* sem =
+ builder_->create<sem::Statement>(stmt, current_compound_statement_, current_function_);
+ return StatementScope(stmt, sem, [&] {
+ auto* lhs = Expression(stmt->lhs);
+ if (!lhs) {
+ return false;
+ }
+ sem->Behaviors() = lhs->Behaviors();
- return validator_.IncrementDecrementStatement(stmt);
- });
+ return validator_.IncrementDecrementStatement(stmt);
+ });
}
bool Resolver::ApplyStorageClassUsageToType(ast::StorageClass sc,
sem::Type* ty,
const Source& usage) {
- ty = const_cast<sem::Type*>(ty->UnwrapRef());
+ ty = const_cast<sem::Type*>(ty->UnwrapRef());
- if (auto* str = ty->As<sem::Struct>()) {
- if (str->StorageClassUsage().count(sc)) {
- return true; // Already applied
+ if (auto* str = ty->As<sem::Struct>()) {
+ if (str->StorageClassUsage().count(sc)) {
+ return true; // Already applied
+ }
+
+ str->AddUsage(sc);
+
+ for (auto* member : str->Members()) {
+ if (!ApplyStorageClassUsageToType(sc, member->Type(), usage)) {
+ std::stringstream err;
+ err << "while analysing structure member " << sem_.TypeNameOf(str) << "."
+ << builder_->Symbols().NameFor(member->Declaration()->symbol);
+ AddNote(err.str(), member->Declaration()->source);
+ return false;
+ }
+ }
+ return true;
}
- str->AddUsage(sc);
+ if (auto* arr = ty->As<sem::Array>()) {
+ if (arr->IsRuntimeSized() && sc != ast::StorageClass::kStorage) {
+ AddError(
+ "runtime-sized arrays can only be used in the <storage> storage "
+ "class",
+ usage);
+ return false;
+ }
- for (auto* member : str->Members()) {
- if (!ApplyStorageClassUsageToType(sc, member->Type(), usage)) {
+ return ApplyStorageClassUsageToType(sc, const_cast<sem::Type*>(arr->ElemType()), usage);
+ }
+
+ if (ast::IsHostShareable(sc) && !validator_.IsHostShareable(ty)) {
std::stringstream err;
- err << "while analysing structure member " << sem_.TypeNameOf(str)
- << "."
- << builder_->Symbols().NameFor(member->Declaration()->symbol);
- AddNote(err.str(), member->Declaration()->source);
+ err << "Type '" << sem_.TypeNameOf(ty) << "' cannot be used in storage class '" << sc
+ << "' as it is non-host-shareable";
+ AddError(err.str(), usage);
return false;
- }
}
+
return true;
- }
-
- if (auto* arr = ty->As<sem::Array>()) {
- if (arr->IsRuntimeSized() && sc != ast::StorageClass::kStorage) {
- AddError(
- "runtime-sized arrays can only be used in the <storage> storage "
- "class",
- usage);
- return false;
- }
-
- return ApplyStorageClassUsageToType(
- sc, const_cast<sem::Type*>(arr->ElemType()), usage);
- }
-
- if (ast::IsHostShareable(sc) && !validator_.IsHostShareable(ty)) {
- std::stringstream err;
- err << "Type '" << sem_.TypeNameOf(ty)
- << "' cannot be used in storage class '" << sc
- << "' as it is non-host-shareable";
- AddError(err.str(), usage);
- return false;
- }
-
- return true;
}
template <typename SEM, typename F>
-SEM* Resolver::StatementScope(const ast::Statement* ast,
- SEM* sem,
- F&& callback) {
- builder_->Sem().Add(ast, sem);
+SEM* Resolver::StatementScope(const ast::Statement* ast, SEM* sem, F&& callback) {
+ builder_->Sem().Add(ast, sem);
- auto* as_compound =
- As<sem::CompoundStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
- auto* as_block =
- As<sem::BlockStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
+ auto* as_compound = As<sem::CompoundStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
+ auto* as_block = As<sem::BlockStatement, CastFlags::kDontErrorOnImpossibleCast>(sem);
- TINT_SCOPED_ASSIGNMENT(current_statement_, sem);
- TINT_SCOPED_ASSIGNMENT(
- current_compound_statement_,
- as_compound ? as_compound : current_compound_statement_);
- TINT_SCOPED_ASSIGNMENT(current_block_, as_block ? as_block : current_block_);
+ TINT_SCOPED_ASSIGNMENT(current_statement_, sem);
+ TINT_SCOPED_ASSIGNMENT(current_compound_statement_,
+ as_compound ? as_compound : current_compound_statement_);
+ TINT_SCOPED_ASSIGNMENT(current_block_, as_block ? as_block : current_block_);
- if (!callback()) {
- return nullptr;
- }
+ if (!callback()) {
+ return nullptr;
+ }
- return sem;
+ return sem;
}
bool Resolver::Mark(const ast::Node* node) {
- if (node == nullptr) {
- TINT_ICE(Resolver, diagnostics_) << "Resolver::Mark() called with nullptr";
+ if (node == nullptr) {
+ TINT_ICE(Resolver, diagnostics_) << "Resolver::Mark() called with nullptr";
+ return false;
+ }
+ if (marked_.emplace(node).second) {
+ return true;
+ }
+ TINT_ICE(Resolver, diagnostics_) << "AST node '" << node->TypeInfo().name
+ << "' was encountered twice in the same AST of a Program\n"
+ << "At: " << node->source << "\n"
+ << "Pointer: " << node;
return false;
- }
- if (marked_.emplace(node).second) {
- return true;
- }
- TINT_ICE(Resolver, diagnostics_)
- << "AST node '" << node->TypeInfo().name
- << "' was encountered twice in the same AST of a Program\n"
- << "At: " << node->source << "\n"
- << "Pointer: " << node;
- return false;
}
void Resolver::AddError(const std::string& msg, const Source& source) const {
- diagnostics_.add_error(diag::System::Resolver, msg, source);
+ diagnostics_.add_error(diag::System::Resolver, msg, source);
}
void Resolver::AddWarning(const std::string& msg, const Source& source) const {
- diagnostics_.add_warning(diag::System::Resolver, msg, source);
+ diagnostics_.add_warning(diag::System::Resolver, msg, source);
}
void Resolver::AddNote(const std::string& msg, const Source& source) const {
- diagnostics_.add_note(diag::System::Resolver, msg, source);
+ diagnostics_.add_note(diag::System::Resolver, msg, source);
}
bool Resolver::IsBuiltin(Symbol symbol) const {
- std::string name = builder_->Symbols().NameFor(symbol);
- return sem::ParseBuiltinType(name) != sem::BuiltinType::kNone;
+ std::string name = builder_->Symbols().NameFor(symbol);
+ return sem::ParseBuiltinType(name) != sem::BuiltinType::kNone;
}
bool Resolver::IsCallStatement(const ast::Expression* expr) const {
- return current_statement_ &&
- Is<ast::CallStatement>(current_statement_->Declaration(),
- [&](auto* stmt) { return stmt->expr == expr; });
+ return current_statement_ &&
+ Is<ast::CallStatement>(current_statement_->Declaration(),
+ [&](auto* stmt) { return stmt->expr == expr; });
}
////////////////////////////////////////////////////////////////////////////////
// Resolver::TypeConversionSig
////////////////////////////////////////////////////////////////////////////////
-bool Resolver::TypeConversionSig::operator==(
- const TypeConversionSig& rhs) const {
- return target == rhs.target && source == rhs.source;
+bool Resolver::TypeConversionSig::operator==(const TypeConversionSig& rhs) const {
+ return target == rhs.target && source == rhs.source;
}
-std::size_t Resolver::TypeConversionSig::Hasher::operator()(
- const TypeConversionSig& sig) const {
- return utils::Hash(sig.target, sig.source);
+std::size_t Resolver::TypeConversionSig::Hasher::operator()(const TypeConversionSig& sig) const {
+ return utils::Hash(sig.target, sig.source);
}
////////////////////////////////////////////////////////////////////////////////
// Resolver::TypeConstructorSig
////////////////////////////////////////////////////////////////////////////////
-Resolver::TypeConstructorSig::TypeConstructorSig(
- const sem::Type* ty,
- const std::vector<const sem::Type*> params)
+Resolver::TypeConstructorSig::TypeConstructorSig(const sem::Type* ty,
+ const std::vector<const sem::Type*> params)
: type(ty), parameters(params) {}
-Resolver::TypeConstructorSig::TypeConstructorSig(const TypeConstructorSig&) =
- default;
+Resolver::TypeConstructorSig::TypeConstructorSig(const TypeConstructorSig&) = default;
Resolver::TypeConstructorSig::~TypeConstructorSig() = default;
-bool Resolver::TypeConstructorSig::operator==(
- const TypeConstructorSig& rhs) const {
- return type == rhs.type && parameters == rhs.parameters;
+bool Resolver::TypeConstructorSig::operator==(const TypeConstructorSig& rhs) const {
+ return type == rhs.type && parameters == rhs.parameters;
}
-std::size_t Resolver::TypeConstructorSig::Hasher::operator()(
- const TypeConstructorSig& sig) const {
- return utils::Hash(sig.type, sig.parameters);
+std::size_t Resolver::TypeConstructorSig::Hasher::operator()(const TypeConstructorSig& sig) const {
+ return utils::Hash(sig.type, sig.parameters);
}
} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver.h b/src/tint/resolver/resolver.h
index 740ea31..f8dec15 100644
--- a/src/tint/resolver/resolver.h
+++ b/src/tint/resolver/resolver.h
@@ -71,351 +71,330 @@
/// Resolves types for all items in the given tint program
class Resolver {
- public:
- /// Constructor
- /// @param builder the program builder
- explicit Resolver(ProgramBuilder* builder);
+ public:
+ /// Constructor
+ /// @param builder the program builder
+ explicit Resolver(ProgramBuilder* builder);
- /// Destructor
- ~Resolver();
+ /// Destructor
+ ~Resolver();
- /// @returns error messages from the resolver
- std::string error() const { return diagnostics_.str(); }
+ /// @returns error messages from the resolver
+ std::string error() const { return diagnostics_.str(); }
- /// @returns true if the resolver was successful
- bool Resolve();
+ /// @returns true if the resolver was successful
+ bool Resolve();
- /// @param type the given type
- /// @returns true if the given type is a plain type
- bool IsPlain(const sem::Type* type) const { return validator_.IsPlain(type); }
+ /// @param type the given type
+ /// @returns true if the given type is a plain type
+ bool IsPlain(const sem::Type* type) const { return validator_.IsPlain(type); }
- /// @param type the given type
- /// @returns true if the given type is a fixed-footprint type
- bool IsFixedFootprint(const sem::Type* type) const {
- return validator_.IsFixedFootprint(type);
- }
+ /// @param type the given type
+ /// @returns true if the given type is a fixed-footprint type
+ bool IsFixedFootprint(const sem::Type* type) const { return validator_.IsFixedFootprint(type); }
- /// @param type the given type
- /// @returns true if the given type is storable
- bool IsStorable(const sem::Type* type) const {
- return validator_.IsStorable(type);
- }
+ /// @param type the given type
+ /// @returns true if the given type is storable
+ bool IsStorable(const sem::Type* type) const { return validator_.IsStorable(type); }
- /// @param type the given type
- /// @returns true if the given type is host-shareable
- bool IsHostShareable(const sem::Type* type) const {
- return validator_.IsHostShareable(type);
- }
+ /// @param type the given type
+ /// @returns true if the given type is host-shareable
+ bool IsHostShareable(const sem::Type* type) const { return validator_.IsHostShareable(type); }
- /// @returns the validator for testing
- const Validator* GetValidatorForTesting() const { return &validator_; }
+ /// @returns the validator for testing
+ const Validator* GetValidatorForTesting() const { return &validator_; }
- private:
- /// Describes the context in which a variable is declared
- enum class VariableKind { kParameter, kLocal, kGlobal };
+ private:
+ /// Describes the context in which a variable is declared
+ enum class VariableKind { kParameter, kLocal, kGlobal };
- Validator::ValidTypeStorageLayouts valid_type_storage_layouts_;
+ Validator::ValidTypeStorageLayouts valid_type_storage_layouts_;
- /// Structure holding semantic information about a block (i.e. scope), such as
- /// parent block and variables declared in the block.
- /// Used to validate variable scoping rules.
- struct BlockInfo {
- enum class Type { kGeneric, kLoop, kLoopContinuing, kSwitchCase };
+ /// Structure holding semantic information about a block (i.e. scope), such as
+ /// parent block and variables declared in the block.
+ /// Used to validate variable scoping rules.
+ struct BlockInfo {
+ enum class Type { kGeneric, kLoop, kLoopContinuing, kSwitchCase };
- BlockInfo(const ast::BlockStatement* block, Type type, BlockInfo* parent);
- ~BlockInfo();
+ BlockInfo(const ast::BlockStatement* block, Type type, BlockInfo* parent);
+ ~BlockInfo();
- template <typename Pred>
- BlockInfo* FindFirstParent(Pred&& pred) {
- BlockInfo* curr = this;
- while (curr && !pred(curr)) {
- curr = curr->parent;
- }
- return curr;
- }
+ template <typename Pred>
+ BlockInfo* FindFirstParent(Pred&& pred) {
+ BlockInfo* curr = this;
+ while (curr && !pred(curr)) {
+ curr = curr->parent;
+ }
+ return curr;
+ }
- BlockInfo* FindFirstParent(BlockInfo::Type ty) {
- return FindFirstParent(
- [ty](auto* block_info) { return block_info->type == ty; });
- }
+ BlockInfo* FindFirstParent(BlockInfo::Type ty) {
+ return FindFirstParent([ty](auto* block_info) { return block_info->type == ty; });
+ }
- ast::BlockStatement const* const block;
- const Type type;
- BlockInfo* const parent;
- std::vector<const ast::Variable*> decls;
+ ast::BlockStatement const* const block;
+ const Type type;
+ BlockInfo* const parent;
+ std::vector<const ast::Variable*> decls;
- // first_continue is set to the index of the first variable in decls
- // declared after the first continue statement in a loop block, if any.
- constexpr static size_t kNoContinue = size_t(~0);
- size_t first_continue = kNoContinue;
- };
-
- // Structure holding information for a TypeDecl
- struct TypeDeclInfo {
- ast::TypeDecl const* const ast;
- sem::Type* const sem;
- };
-
- /// Resolves the program, without creating final the semantic nodes.
- /// @returns true on success, false on error
- bool ResolveInternal();
-
- /// Creates the nodes and adds them to the sem::Info mappings of the
- /// ProgramBuilder.
- void CreateSemanticNodes() const;
-
- /// Retrieves information for the requested import.
- /// @param src the source of the import
- /// @param path the import path
- /// @param name the method name to get information on
- /// @param params the parameters to the method call
- /// @param id out parameter for the external call ID. Must not be a nullptr.
- /// @returns the return type of `name` in `path` or nullptr on error.
- sem::Type* GetImportData(const Source& src,
- const std::string& path,
- const std::string& name,
- const ast::ExpressionList& params,
- uint32_t* id);
-
- //////////////////////////////////////////////////////////////////////////////
- // AST and Type traversal methods
- //////////////////////////////////////////////////////////////////////////////
-
- // Expression resolving methods
- // Returns the semantic node pointer on success, nullptr on failure.
- sem::Expression* IndexAccessor(const ast::IndexAccessorExpression*);
- sem::Expression* Binary(const ast::BinaryExpression*);
- sem::Expression* Bitcast(const ast::BitcastExpression*);
- sem::Call* Call(const ast::CallExpression*);
- sem::Expression* Expression(const ast::Expression*);
- sem::Function* Function(const ast::Function*);
- sem::Call* FunctionCall(const ast::CallExpression*,
- sem::Function* target,
- const std::vector<const sem::Expression*> args,
- sem::Behaviors arg_behaviors);
- sem::Expression* Identifier(const ast::IdentifierExpression*);
- sem::Call* BuiltinCall(const ast::CallExpression*,
- sem::BuiltinType,
- const std::vector<const sem::Expression*> args,
- const std::vector<const sem::Type*> arg_tys);
- sem::Expression* Literal(const ast::LiteralExpression*);
- sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
- sem::Call* TypeConversion(const ast::CallExpression* expr,
- const sem::Type* ty,
- const sem::Expression* arg,
- const sem::Type* arg_ty);
- sem::Call* TypeConstructor(const ast::CallExpression* expr,
- const sem::Type* ty,
- const std::vector<const sem::Expression*> args,
- const std::vector<const sem::Type*> arg_tys);
- sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
-
- // Statement resolving methods
- // Each return true on success, false on failure.
- sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
- sem::BlockStatement* BlockStatement(const ast::BlockStatement*);
- sem::Statement* BreakStatement(const ast::BreakStatement*);
- sem::Statement* CallStatement(const ast::CallStatement*);
- sem::CaseStatement* CaseStatement(const ast::CaseStatement*);
- sem::Statement* CompoundAssignmentStatement(
- const ast::CompoundAssignmentStatement*);
- sem::Statement* ContinueStatement(const ast::ContinueStatement*);
- sem::Statement* DiscardStatement(const ast::DiscardStatement*);
- sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
- sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
- sem::GlobalVariable* GlobalVariable(const ast::Variable*);
- sem::Statement* Parameter(const ast::Variable*);
- sem::IfStatement* IfStatement(const ast::IfStatement*);
- sem::Statement* IncrementDecrementStatement(
- const ast::IncrementDecrementStatement*);
- sem::LoopStatement* LoopStatement(const ast::LoopStatement*);
- sem::Statement* ReturnStatement(const ast::ReturnStatement*);
- sem::Statement* Statement(const ast::Statement*);
- sem::SwitchStatement* SwitchStatement(const ast::SwitchStatement* s);
- sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
- bool Statements(const ast::StatementList&);
-
- // Resolve the result type of a binary operator.
- // Returns nullptr if the types are not valid for this operator.
- const sem::Type* BinaryOpType(const sem::Type* lhs_ty,
- const sem::Type* rhs_ty,
- ast::BinaryOp op);
-
- /// Resolves the WorkgroupSize for the given function, assigning it to
- /// current_function_
- bool WorkgroupSize(const ast::Function*);
-
- /// @returns the sem::Type for the ast::Type `ty`, building it if it
- /// hasn't been constructed already. If an error is raised, nullptr is
- /// returned.
- /// @param ty the ast::Type
- sem::Type* Type(const ast::Type* ty);
-
- /// @param named_type the named type to resolve
- /// @returns the resolved semantic type
- sem::Type* TypeDecl(const ast::TypeDecl* named_type);
-
- /// Builds and returns the semantic information for the array `arr`.
- /// This method does not mark the ast::Array node, nor attach the generated
- /// semantic information to the AST node.
- /// @returns the semantic Array information, or nullptr if an error is
- /// raised.
- /// @param arr the Array to get semantic information for
- sem::Array* Array(const ast::Array* arr);
-
- /// Builds and returns the semantic information for the alias `alias`.
- /// This method does not mark the ast::Alias node, nor attach the generated
- /// semantic information to the AST node.
- /// @returns the aliased type, or nullptr if an error is raised.
- sem::Type* Alias(const ast::Alias* alias);
-
- /// Builds and returns the semantic information for the structure `str`.
- /// This method does not mark the ast::Struct node, nor attach the generated
- /// semantic information to the AST node.
- /// @returns the semantic Struct information, or nullptr if an error is
- /// raised.
- sem::Struct* Structure(const ast::Struct* str);
-
- /// @returns the semantic info for the variable `var`. If an error is
- /// raised, nullptr is returned.
- /// @note this method does not resolve the attributes as these are
- /// context-dependent (global, local, parameter)
- /// @param var the variable to create or return the `VariableInfo` for
- /// @param kind what kind of variable we are declaring
- /// @param index the index of the parameter, if this variable is a parameter
- sem::Variable* Variable(const ast::Variable* var,
- VariableKind kind,
- uint32_t index = 0);
-
- /// Records the storage class usage for the given type, and any transient
- /// dependencies of the type. Validates that the type can be used for the
- /// given storage class, erroring if it cannot.
- /// @param sc the storage class to apply to the type and transitent types
- /// @param ty the type to apply the storage class on
- /// @param usage the Source of the root variable declaration that uses the
- /// given type and storage class. Used for generating sensible error
- /// messages.
- /// @returns true on success, false on error
- bool ApplyStorageClassUsageToType(ast::StorageClass sc,
- sem::Type* ty,
- const Source& usage);
-
- /// @param storage_class the storage class
- /// @returns the default access control for the given storage class
- ast::Access DefaultAccessForStorageClass(ast::StorageClass storage_class);
-
- /// Allocate constant IDs for pipeline-overridable constants.
- void AllocateOverridableConstantIds();
-
- /// Set the shadowing information on variable declarations.
- /// @note this method must only be called after all semantic nodes are built.
- void SetShadows();
- /// StatementScope() does the following:
- /// * Creates the AST -> SEM mapping.
- /// * Assigns `sem` to #current_statement_
- /// * Assigns `sem` to #current_compound_statement_ if `sem` derives from
- /// sem::CompoundStatement.
- /// * Assigns `sem` to #current_block_ if `sem` derives from
- /// sem::BlockStatement.
- /// * Then calls `callback`.
- /// * Before returning #current_statement_, #current_compound_statement_, and
- /// #current_block_ are restored to their original values.
- /// @returns `sem` if `callback` returns true, otherwise `nullptr`.
- template <typename SEM, typename F>
- SEM* StatementScope(const ast::Statement* ast, SEM* sem, F&& callback);
-
- /// Mark records that the given AST node has been visited, and asserts that
- /// the given node has not already been seen. Diamonds in the AST are
- /// illegal.
- /// @param node the AST node.
- /// @returns true on success, false on error
- bool Mark(const ast::Node* node);
-
- /// Adds the given error message to the diagnostics
- void AddError(const std::string& msg, const Source& source) const;
-
- /// Adds the given warning message to the diagnostics
- void AddWarning(const std::string& msg, const Source& source) const;
-
- /// Adds the given note message to the diagnostics
- void AddNote(const std::string& msg, const Source& source) const;
-
- //////////////////////////////////////////////////////////////////////////////
- /// Constant value evaluation methods
- //////////////////////////////////////////////////////////////////////////////
- /// Cast `Value` to `target_type`
- /// @return the casted value
- sem::Constant ConstantCast(const sem::Constant& value,
- const sem::Type* target_elem_type);
-
- sem::Constant EvaluateConstantValue(const ast::Expression* expr,
- const sem::Type* type);
- sem::Constant EvaluateConstantValue(const ast::LiteralExpression* literal,
- const sem::Type* type);
- sem::Constant EvaluateConstantValue(const ast::CallExpression* call,
- const sem::Type* type);
-
- /// @returns true if the symbol is the name of a builtin function.
- bool IsBuiltin(Symbol) const;
-
- /// @returns true if `expr` is the current CallStatement's CallExpression
- bool IsCallStatement(const ast::Expression* expr) const;
-
- struct TypeConversionSig {
- const sem::Type* target;
- const sem::Type* source;
-
- bool operator==(const TypeConversionSig&) const;
-
- /// Hasher provides a hash function for the TypeConversionSig
- struct Hasher {
- /// @param sig the TypeConversionSig to create a hash for
- /// @return the hash value
- std::size_t operator()(const TypeConversionSig& sig) const;
+ // first_continue is set to the index of the first variable in decls
+ // declared after the first continue statement in a loop block, if any.
+ constexpr static size_t kNoContinue = size_t(~0);
+ size_t first_continue = kNoContinue;
};
- };
- struct TypeConstructorSig {
- const sem::Type* type;
- const std::vector<const sem::Type*> parameters;
-
- TypeConstructorSig(const sem::Type* ty,
- const std::vector<const sem::Type*> params);
- TypeConstructorSig(const TypeConstructorSig&);
- ~TypeConstructorSig();
- bool operator==(const TypeConstructorSig&) const;
-
- /// Hasher provides a hash function for the TypeConstructorSig
- struct Hasher {
- /// @param sig the TypeConstructorSig to create a hash for
- /// @return the hash value
- std::size_t operator()(const TypeConstructorSig& sig) const;
+ // Structure holding information for a TypeDecl
+ struct TypeDeclInfo {
+ ast::TypeDecl const* const ast;
+ sem::Type* const sem;
};
- };
- ProgramBuilder* const builder_;
- diag::List& diagnostics_;
- std::unique_ptr<BuiltinTable> const builtin_table_;
- DependencyGraph dependencies_;
- SemHelper sem_;
- Validator validator_;
- std::vector<sem::Function*> entry_points_;
- std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
- std::unordered_set<const ast::Node*> marked_;
- std::unordered_map<uint32_t, const sem::Variable*> constant_ids_;
- std::unordered_map<TypeConversionSig,
- sem::CallTarget*,
- TypeConversionSig::Hasher>
- type_conversions_;
- std::unordered_map<TypeConstructorSig,
- sem::CallTarget*,
- TypeConstructorSig::Hasher>
- type_ctors_;
+ /// Resolves the program, without creating final the semantic nodes.
+ /// @returns true on success, false on error
+ bool ResolveInternal();
- sem::Function* current_function_ = nullptr;
- sem::Statement* current_statement_ = nullptr;
- sem::CompoundStatement* current_compound_statement_ = nullptr;
- sem::BlockStatement* current_block_ = nullptr;
+ /// Creates the nodes and adds them to the sem::Info mappings of the
+ /// ProgramBuilder.
+ void CreateSemanticNodes() const;
+
+ /// Retrieves information for the requested import.
+ /// @param src the source of the import
+ /// @param path the import path
+ /// @param name the method name to get information on
+ /// @param params the parameters to the method call
+ /// @param id out parameter for the external call ID. Must not be a nullptr.
+ /// @returns the return type of `name` in `path` or nullptr on error.
+ sem::Type* GetImportData(const Source& src,
+ const std::string& path,
+ const std::string& name,
+ const ast::ExpressionList& params,
+ uint32_t* id);
+
+ //////////////////////////////////////////////////////////////////////////////
+ // AST and Type traversal methods
+ //////////////////////////////////////////////////////////////////////////////
+
+ // Expression resolving methods
+ // Returns the semantic node pointer on success, nullptr on failure.
+ sem::Expression* IndexAccessor(const ast::IndexAccessorExpression*);
+ sem::Expression* Binary(const ast::BinaryExpression*);
+ sem::Expression* Bitcast(const ast::BitcastExpression*);
+ sem::Call* Call(const ast::CallExpression*);
+ sem::Expression* Expression(const ast::Expression*);
+ sem::Function* Function(const ast::Function*);
+ sem::Call* FunctionCall(const ast::CallExpression*,
+ sem::Function* target,
+ const std::vector<const sem::Expression*> args,
+ sem::Behaviors arg_behaviors);
+ sem::Expression* Identifier(const ast::IdentifierExpression*);
+ sem::Call* BuiltinCall(const ast::CallExpression*,
+ sem::BuiltinType,
+ const std::vector<const sem::Expression*> args,
+ const std::vector<const sem::Type*> arg_tys);
+ sem::Expression* Literal(const ast::LiteralExpression*);
+ sem::Expression* MemberAccessor(const ast::MemberAccessorExpression*);
+ sem::Call* TypeConversion(const ast::CallExpression* expr,
+ const sem::Type* ty,
+ const sem::Expression* arg,
+ const sem::Type* arg_ty);
+ sem::Call* TypeConstructor(const ast::CallExpression* expr,
+ const sem::Type* ty,
+ const std::vector<const sem::Expression*> args,
+ const std::vector<const sem::Type*> arg_tys);
+ sem::Expression* UnaryOp(const ast::UnaryOpExpression*);
+
+ // Statement resolving methods
+ // Each return true on success, false on failure.
+ sem::Statement* AssignmentStatement(const ast::AssignmentStatement*);
+ sem::BlockStatement* BlockStatement(const ast::BlockStatement*);
+ sem::Statement* BreakStatement(const ast::BreakStatement*);
+ sem::Statement* CallStatement(const ast::CallStatement*);
+ sem::CaseStatement* CaseStatement(const ast::CaseStatement*);
+ sem::Statement* CompoundAssignmentStatement(const ast::CompoundAssignmentStatement*);
+ sem::Statement* ContinueStatement(const ast::ContinueStatement*);
+ sem::Statement* DiscardStatement(const ast::DiscardStatement*);
+ sem::Statement* FallthroughStatement(const ast::FallthroughStatement*);
+ sem::ForLoopStatement* ForLoopStatement(const ast::ForLoopStatement*);
+ sem::GlobalVariable* GlobalVariable(const ast::Variable*);
+ sem::Statement* Parameter(const ast::Variable*);
+ sem::IfStatement* IfStatement(const ast::IfStatement*);
+ sem::Statement* IncrementDecrementStatement(const ast::IncrementDecrementStatement*);
+ sem::LoopStatement* LoopStatement(const ast::LoopStatement*);
+ sem::Statement* ReturnStatement(const ast::ReturnStatement*);
+ sem::Statement* Statement(const ast::Statement*);
+ sem::SwitchStatement* SwitchStatement(const ast::SwitchStatement* s);
+ sem::Statement* VariableDeclStatement(const ast::VariableDeclStatement*);
+ bool Statements(const ast::StatementList&);
+
+ // Resolve the result type of a binary operator.
+ // Returns nullptr if the types are not valid for this operator.
+ const sem::Type* BinaryOpType(const sem::Type* lhs_ty,
+ const sem::Type* rhs_ty,
+ ast::BinaryOp op);
+
+ /// Resolves the WorkgroupSize for the given function, assigning it to
+ /// current_function_
+ bool WorkgroupSize(const ast::Function*);
+
+ /// @returns the sem::Type for the ast::Type `ty`, building it if it
+ /// hasn't been constructed already. If an error is raised, nullptr is
+ /// returned.
+ /// @param ty the ast::Type
+ sem::Type* Type(const ast::Type* ty);
+
+ /// @param named_type the named type to resolve
+ /// @returns the resolved semantic type
+ sem::Type* TypeDecl(const ast::TypeDecl* named_type);
+
+ /// Builds and returns the semantic information for the array `arr`.
+ /// This method does not mark the ast::Array node, nor attach the generated
+ /// semantic information to the AST node.
+ /// @returns the semantic Array information, or nullptr if an error is
+ /// raised.
+ /// @param arr the Array to get semantic information for
+ sem::Array* Array(const ast::Array* arr);
+
+ /// Builds and returns the semantic information for the alias `alias`.
+ /// This method does not mark the ast::Alias node, nor attach the generated
+ /// semantic information to the AST node.
+ /// @returns the aliased type, or nullptr if an error is raised.
+ sem::Type* Alias(const ast::Alias* alias);
+
+ /// Builds and returns the semantic information for the structure `str`.
+ /// This method does not mark the ast::Struct node, nor attach the generated
+ /// semantic information to the AST node.
+ /// @returns the semantic Struct information, or nullptr if an error is
+ /// raised.
+ sem::Struct* Structure(const ast::Struct* str);
+
+ /// @returns the semantic info for the variable `var`. If an error is
+ /// raised, nullptr is returned.
+ /// @note this method does not resolve the attributes as these are
+ /// context-dependent (global, local, parameter)
+ /// @param var the variable to create or return the `VariableInfo` for
+ /// @param kind what kind of variable we are declaring
+ /// @param index the index of the parameter, if this variable is a parameter
+ sem::Variable* Variable(const ast::Variable* var, VariableKind kind, uint32_t index = 0);
+
+ /// Records the storage class usage for the given type, and any transient
+ /// dependencies of the type. Validates that the type can be used for the
+ /// given storage class, erroring if it cannot.
+ /// @param sc the storage class to apply to the type and transitent types
+ /// @param ty the type to apply the storage class on
+ /// @param usage the Source of the root variable declaration that uses the
+ /// given type and storage class. Used for generating sensible error
+ /// messages.
+ /// @returns true on success, false on error
+ bool ApplyStorageClassUsageToType(ast::StorageClass sc, sem::Type* ty, const Source& usage);
+
+ /// @param storage_class the storage class
+ /// @returns the default access control for the given storage class
+ ast::Access DefaultAccessForStorageClass(ast::StorageClass storage_class);
+
+ /// Allocate constant IDs for pipeline-overridable constants.
+ void AllocateOverridableConstantIds();
+
+ /// Set the shadowing information on variable declarations.
+ /// @note this method must only be called after all semantic nodes are built.
+ void SetShadows();
+ /// StatementScope() does the following:
+ /// * Creates the AST -> SEM mapping.
+ /// * Assigns `sem` to #current_statement_
+ /// * Assigns `sem` to #current_compound_statement_ if `sem` derives from
+ /// sem::CompoundStatement.
+ /// * Assigns `sem` to #current_block_ if `sem` derives from
+ /// sem::BlockStatement.
+ /// * Then calls `callback`.
+ /// * Before returning #current_statement_, #current_compound_statement_, and
+ /// #current_block_ are restored to their original values.
+ /// @returns `sem` if `callback` returns true, otherwise `nullptr`.
+ template <typename SEM, typename F>
+ SEM* StatementScope(const ast::Statement* ast, SEM* sem, F&& callback);
+
+ /// Mark records that the given AST node has been visited, and asserts that
+ /// the given node has not already been seen. Diamonds in the AST are
+ /// illegal.
+ /// @param node the AST node.
+ /// @returns true on success, false on error
+ bool Mark(const ast::Node* node);
+
+ /// Adds the given error message to the diagnostics
+ void AddError(const std::string& msg, const Source& source) const;
+
+ /// Adds the given warning message to the diagnostics
+ void AddWarning(const std::string& msg, const Source& source) const;
+
+ /// Adds the given note message to the diagnostics
+ void AddNote(const std::string& msg, const Source& source) const;
+
+ //////////////////////////////////////////////////////////////////////////////
+ /// Constant value evaluation methods
+ //////////////////////////////////////////////////////////////////////////////
+ /// Cast `Value` to `target_type`
+ /// @return the casted value
+ sem::Constant ConstantCast(const sem::Constant& value, const sem::Type* target_elem_type);
+
+ sem::Constant EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type);
+ sem::Constant EvaluateConstantValue(const ast::LiteralExpression* literal,
+ const sem::Type* type);
+ sem::Constant EvaluateConstantValue(const ast::CallExpression* call, const sem::Type* type);
+
+ /// @returns true if the symbol is the name of a builtin function.
+ bool IsBuiltin(Symbol) const;
+
+ /// @returns true if `expr` is the current CallStatement's CallExpression
+ bool IsCallStatement(const ast::Expression* expr) const;
+
+ struct TypeConversionSig {
+ const sem::Type* target;
+ const sem::Type* source;
+
+ bool operator==(const TypeConversionSig&) const;
+
+ /// Hasher provides a hash function for the TypeConversionSig
+ struct Hasher {
+ /// @param sig the TypeConversionSig to create a hash for
+ /// @return the hash value
+ std::size_t operator()(const TypeConversionSig& sig) const;
+ };
+ };
+
+ struct TypeConstructorSig {
+ const sem::Type* type;
+ const std::vector<const sem::Type*> parameters;
+
+ TypeConstructorSig(const sem::Type* ty, const std::vector<const sem::Type*> params);
+ TypeConstructorSig(const TypeConstructorSig&);
+ ~TypeConstructorSig();
+ bool operator==(const TypeConstructorSig&) const;
+
+ /// Hasher provides a hash function for the TypeConstructorSig
+ struct Hasher {
+ /// @param sig the TypeConstructorSig to create a hash for
+ /// @return the hash value
+ std::size_t operator()(const TypeConstructorSig& sig) const;
+ };
+ };
+
+ ProgramBuilder* const builder_;
+ diag::List& diagnostics_;
+ std::unique_ptr<BuiltinTable> const builtin_table_;
+ DependencyGraph dependencies_;
+ SemHelper sem_;
+ Validator validator_;
+ std::vector<sem::Function*> entry_points_;
+ std::unordered_map<const sem::Type*, const Source&> atomic_composite_info_;
+ std::unordered_set<const ast::Node*> marked_;
+ std::unordered_map<uint32_t, const sem::Variable*> constant_ids_;
+ std::unordered_map<TypeConversionSig, sem::CallTarget*, TypeConversionSig::Hasher>
+ type_conversions_;
+ std::unordered_map<TypeConstructorSig, sem::CallTarget*, TypeConstructorSig::Hasher>
+ type_ctors_;
+
+ sem::Function* current_function_ = nullptr;
+ sem::Statement* current_statement_ = nullptr;
+ sem::CompoundStatement* current_compound_statement_ = nullptr;
+ sem::BlockStatement* current_block_ = nullptr;
};
} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_behavior_test.cc b/src/tint/resolver/resolver_behavior_test.cc
index 7fcf348..4bcd4ff 100644
--- a/src/tint/resolver/resolver_behavior_test.cc
+++ b/src/tint/resolver/resolver_behavior_test.cc
@@ -24,633 +24,600 @@
namespace {
class ResolverBehaviorTest : public ResolverTest {
- protected:
- void SetUp() override {
- // Create a function called 'DiscardOrNext' which returns an i32, and has
- // the behavior of {Discard, Return}, which when called, will have the
- // behavior {Discard, Next}.
- Func("DiscardOrNext", {}, ty.i32(),
- {
- If(true, Block(Discard())),
- Return(1),
- });
- }
+ protected:
+ void SetUp() override {
+ // Create a function called 'DiscardOrNext' which returns an i32, and has
+ // the behavior of {Discard, Return}, which when called, will have the
+ // behavior {Discard, Next}.
+ Func("DiscardOrNext", {}, ty.i32(),
+ {
+ If(true, Block(Discard())),
+ Return(1),
+ });
+ }
};
TEST_F(ResolverBehaviorTest, ExprBinaryOp_LHS) {
- auto* stmt = Decl(Var("lhs", ty.i32(), Add(Call("DiscardOrNext"), 1)));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Var("lhs", ty.i32(), Add(Call("DiscardOrNext"), 1)));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, ExprBinaryOp_RHS) {
- auto* stmt = Decl(Var("lhs", ty.i32(), Add(1, Call("DiscardOrNext"))));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Var("lhs", ty.i32(), Add(1, Call("DiscardOrNext"))));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, ExprBitcastOp) {
- auto* stmt = Decl(Var("lhs", ty.u32(), Bitcast<u32>(Call("DiscardOrNext"))));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Var("lhs", ty.u32(), Bitcast<u32>(Call("DiscardOrNext"))));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, ExprIndex_Arr) {
- Func("ArrayDiscardOrNext", {}, ty.array<i32, 4>(),
- {
- If(true, Block(Discard())),
- Return(Construct(ty.array<i32, 4>())),
- });
+ Func("ArrayDiscardOrNext", {}, ty.array<i32, 4>(),
+ {
+ If(true, Block(Discard())),
+ Return(Construct(ty.array<i32, 4>())),
+ });
- auto* stmt =
- Decl(Var("lhs", ty.i32(), IndexAccessor(Call("ArrayDiscardOrNext"), 1)));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Var("lhs", ty.i32(), IndexAccessor(Call("ArrayDiscardOrNext"), 1)));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, ExprIndex_Idx) {
- auto* stmt =
- Decl(Var("lhs", ty.i32(), IndexAccessor("arr", Call("DiscardOrNext"))));
- WrapInFunction(Decl(Var("arr", ty.array<i32, 4>())), //
- stmt);
+ auto* stmt = Decl(Var("lhs", ty.i32(), IndexAccessor("arr", Call("DiscardOrNext"))));
+ WrapInFunction(Decl(Var("arr", ty.array<i32, 4>())), //
+ stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, ExprUnaryOp) {
- auto* stmt = Decl(Var("lhs", ty.i32(),
- create<ast::UnaryOpExpression>(
- ast::UnaryOp::kComplement, Call("DiscardOrNext"))));
- WrapInFunction(stmt);
+ auto* stmt =
+ Decl(Var("lhs", ty.i32(),
+ create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Call("DiscardOrNext"))));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtAssign) {
- auto* stmt = Assign("lhs", "rhs");
- WrapInFunction(Decl(Var("lhs", ty.i32())), //
- Decl(Var("rhs", ty.i32())), //
- stmt);
+ auto* stmt = Assign("lhs", "rhs");
+ WrapInFunction(Decl(Var("lhs", ty.i32())), //
+ Decl(Var("rhs", ty.i32())), //
+ stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtAssign_LHSDiscardOrNext) {
- auto* stmt = Assign(IndexAccessor("lhs", Call("DiscardOrNext")), 1);
- WrapInFunction(Decl(Var("lhs", ty.array<i32, 4>())), //
- stmt);
+ auto* stmt = Assign(IndexAccessor("lhs", Call("DiscardOrNext")), 1);
+ WrapInFunction(Decl(Var("lhs", ty.array<i32, 4>())), //
+ stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtAssign_RHSDiscardOrNext) {
- auto* stmt = Assign("lhs", Call("DiscardOrNext"));
- WrapInFunction(Decl(Var("lhs", ty.i32())), //
- stmt);
+ auto* stmt = Assign("lhs", Call("DiscardOrNext"));
+ WrapInFunction(Decl(Var("lhs", ty.i32())), //
+ stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtBlockEmpty) {
- auto* stmt = Block();
- WrapInFunction(stmt);
+ auto* stmt = Block();
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtBlockSingleStmt) {
- auto* stmt = Block(Discard());
- WrapInFunction(stmt);
+ auto* stmt = Block(Discard());
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtCallReturn) {
- Func("f", {}, ty.void_(), {Return()});
- auto* stmt = CallStmt(Call("f"));
- WrapInFunction(stmt);
+ Func("f", {}, ty.void_(), {Return()});
+ auto* stmt = CallStmt(Call("f"));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtCallFuncDiscard) {
- Func("f", {}, ty.void_(), {Discard()});
- auto* stmt = CallStmt(Call("f"));
- WrapInFunction(stmt);
+ Func("f", {}, ty.void_(), {Discard()});
+ auto* stmt = CallStmt(Call("f"));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtCallFuncMayDiscard) {
- auto* stmt = For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr,
- nullptr, Block(Break()));
- WrapInFunction(stmt);
+ auto* stmt =
+ For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr, nullptr, Block(Break()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtBreak) {
- auto* stmt = Break();
- WrapInFunction(Loop(Block(stmt)));
+ auto* stmt = Break();
+ WrapInFunction(Loop(Block(stmt)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kBreak);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kBreak);
}
TEST_F(ResolverBehaviorTest, StmtContinue) {
- auto* stmt = Continue();
- WrapInFunction(Loop(Block(If(true, Block(Break())), //
- stmt)));
+ auto* stmt = Continue();
+ WrapInFunction(Loop(Block(If(true, Block(Break())), //
+ stmt)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kContinue);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kContinue);
}
TEST_F(ResolverBehaviorTest, StmtDiscard) {
- auto* stmt = Discard();
- WrapInFunction(stmt);
+ auto* stmt = Discard();
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_NoExit) {
- auto* stmt = For(Source{{12, 34}}, nullptr, nullptr, nullptr, Block());
- WrapInFunction(stmt);
+ auto* stmt = For(Source{{12, 34}}, nullptr, nullptr, nullptr, Block());
+ WrapInFunction(stmt);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: for-loop does not exit");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: for-loop does not exit");
}
TEST_F(ResolverBehaviorTest, StmtForLoopBreak) {
- auto* stmt = For(nullptr, nullptr, nullptr, Block(Break()));
- WrapInFunction(stmt);
+ auto* stmt = For(nullptr, nullptr, nullptr, Block(Break()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtForLoopContinue_NoExit) {
- auto* stmt =
- For(Source{{12, 34}}, nullptr, nullptr, nullptr, Block(Continue()));
- WrapInFunction(stmt);
+ auto* stmt = For(Source{{12, 34}}, nullptr, nullptr, nullptr, Block(Continue()));
+ WrapInFunction(stmt);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: for-loop does not exit");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: for-loop does not exit");
}
TEST_F(ResolverBehaviorTest, StmtForLoopDiscard) {
- auto* stmt = For(nullptr, nullptr, nullptr, Block(Discard()));
- WrapInFunction(stmt);
+ auto* stmt = For(nullptr, nullptr, nullptr, Block(Discard()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtForLoopReturn) {
- auto* stmt = For(nullptr, nullptr, nullptr, Block(Return()));
- WrapInFunction(stmt);
+ auto* stmt = For(nullptr, nullptr, nullptr, Block(Return()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
}
TEST_F(ResolverBehaviorTest, StmtForLoopBreak_InitCallFuncMayDiscard) {
- auto* stmt = For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr,
- nullptr, Block(Break()));
- WrapInFunction(stmt);
+ auto* stmt =
+ For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr, nullptr, Block(Break()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_InitCallFuncMayDiscard) {
- auto* stmt = For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr,
- nullptr, Block());
- WrapInFunction(stmt);
+ auto* stmt = For(Decl(Var("v", ty.i32(), Call("DiscardOrNext"))), nullptr, nullptr, Block());
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_CondTrue) {
- auto* stmt = For(nullptr, true, nullptr, Block());
- WrapInFunction(stmt);
+ auto* stmt = For(nullptr, true, nullptr, Block());
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtForLoopEmpty_CondCallFuncMayDiscard) {
- auto* stmt = For(nullptr, Equal(Call("DiscardOrNext"), 1), nullptr, Block());
- WrapInFunction(stmt);
+ auto* stmt = For(nullptr, Equal(Call("DiscardOrNext"), 1), nullptr, Block());
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock) {
- auto* stmt = If(true, Block());
- WrapInFunction(stmt);
+ auto* stmt = If(true, Block());
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenDiscard) {
- auto* stmt = If(true, Block(Discard()));
- WrapInFunction(stmt);
+ auto* stmt = If(true, Block(Discard()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseDiscard) {
- auto* stmt = If(true, Block(), Block(Discard()));
- WrapInFunction(stmt);
+ auto* stmt = If(true, Block(), Block(Discard()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenDiscard_ElseDiscard) {
- auto* stmt = If(true, Block(Discard()), Block(Discard()));
- WrapInFunction(stmt);
+ auto* stmt = If(true, Block(Discard()), Block(Discard()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtIfCallFuncMayDiscard_ThenEmptyBlock) {
- auto* stmt = If(Equal(Call("DiscardOrNext"), 1), Block());
- WrapInFunction(stmt);
+ auto* stmt = If(Equal(Call("DiscardOrNext"), 1), Block());
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtIfTrue_ThenEmptyBlock_ElseCallFuncMayDiscard) {
- auto* stmt = If(true, Block(), //
- If(Equal(Call("DiscardOrNext"), 1), Block()));
- WrapInFunction(stmt);
+ auto* stmt = If(true, Block(), //
+ If(Equal(Call("DiscardOrNext"), 1), Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtLetDecl) {
- auto* stmt = Decl(Let("v", ty.i32(), Expr(1)));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Let("v", ty.i32(), Expr(1)));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtLetDecl_RHSDiscardOrNext) {
- auto* stmt = Decl(Let("lhs", ty.i32(), Call("DiscardOrNext")));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Let("lhs", ty.i32(), Call("DiscardOrNext")));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtLoopEmpty_NoExit) {
- auto* stmt = Loop(Source{{12, 34}}, Block());
- WrapInFunction(stmt);
+ auto* stmt = Loop(Source{{12, 34}}, Block());
+ WrapInFunction(stmt);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
}
TEST_F(ResolverBehaviorTest, StmtLoopBreak) {
- auto* stmt = Loop(Block(Break()));
- WrapInFunction(stmt);
+ auto* stmt = Loop(Block(Break()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtLoopContinue_NoExit) {
- auto* stmt = Loop(Source{{12, 34}}, Block(Continue()));
- WrapInFunction(stmt);
+ auto* stmt = Loop(Source{{12, 34}}, Block(Continue()));
+ WrapInFunction(stmt);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
}
TEST_F(ResolverBehaviorTest, StmtLoopDiscard) {
- auto* stmt = Loop(Block(Discard()));
- WrapInFunction(stmt);
+ auto* stmt = Loop(Block(Discard()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtLoopReturn) {
- auto* stmt = Loop(Block(Return()));
- WrapInFunction(stmt);
+ auto* stmt = Loop(Block(Return()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
}
TEST_F(ResolverBehaviorTest, StmtLoopEmpty_ContEmpty_NoExit) {
- auto* stmt = Loop(Source{{12, 34}}, Block(), Block());
- WrapInFunction(stmt);
+ auto* stmt = Loop(Source{{12, 34}}, Block(), Block());
+ WrapInFunction(stmt);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: loop does not exit");
}
TEST_F(ResolverBehaviorTest, StmtLoopEmpty_ContIfTrueBreak) {
- auto* stmt = Loop(Block(), Block(If(true, Block(Break()))));
- WrapInFunction(stmt);
+ auto* stmt = Loop(Block(), Block(If(true, Block(Break()))));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtReturn) {
- auto* stmt = Return();
- WrapInFunction(stmt);
+ auto* stmt = Return();
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
}
TEST_F(ResolverBehaviorTest, StmtReturn_DiscardOrNext) {
- auto* stmt = Return(Call("DiscardOrNext"));
- Func("F", {}, ty.i32(), {stmt});
+ auto* stmt = Return(Call("DiscardOrNext"));
+ Func("F", {}, ty.i32(), {stmt});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kReturn, sem::Behavior::kDiscard));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kReturn, sem::Behavior::kDiscard));
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondTrue_DefaultEmpty) {
- auto* stmt = Switch(1, DefaultCase(Block()));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, DefaultCase(Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_DefaultEmpty) {
- auto* stmt = Switch(1, DefaultCase(Block()));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, DefaultCase(Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_DefaultDiscard) {
- auto* stmt = Switch(1, DefaultCase(Block(Discard())));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, DefaultCase(Block(Discard())));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_DefaultReturn) {
- auto* stmt = Switch(1, DefaultCase(Block(Return())));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, DefaultCase(Block(Return())));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kReturn);
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultEmpty) {
- auto* stmt = Switch(1, Case(Expr(0), Block()), DefaultCase(Block()));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, Case(Expr(0), Block()), DefaultCase(Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultDiscard) {
- auto* stmt = Switch(1, Case(Expr(0), Block()), DefaultCase(Block(Discard())));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, Case(Expr(0), Block()), DefaultCase(Block(Discard())));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kNext, sem::Behavior::kDiscard));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext, sem::Behavior::kDiscard));
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Empty_DefaultReturn) {
- auto* stmt = Switch(1, Case(Expr(0), Block()), DefaultCase(Block(Return())));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, Case(Expr(0), Block()), DefaultCase(Block(Return())));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kNext, sem::Behavior::kReturn));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kNext, sem::Behavior::kReturn));
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultEmpty) {
- auto* stmt = Switch(1, Case(Expr(0), Block(Discard())), DefaultCase(Block()));
- WrapInFunction(stmt);
+ auto* stmt = Switch(1, Case(Expr(0), Block(Discard())), DefaultCase(Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
-TEST_F(ResolverBehaviorTest,
- StmtSwitch_CondLiteral_Case0Discard_DefaultDiscard) {
- auto* stmt =
- Switch(1, Case(Expr(0), Block(Discard())), DefaultCase(Block(Discard())));
- WrapInFunction(stmt);
+TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultDiscard) {
+ auto* stmt = Switch(1, Case(Expr(0), Block(Discard())), DefaultCase(Block(Discard())));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kDiscard);
}
-TEST_F(ResolverBehaviorTest,
- StmtSwitch_CondLiteral_Case0Discard_DefaultReturn) {
- auto* stmt =
- Switch(1, Case(Expr(0), Block(Discard())), DefaultCase(Block(Return())));
- WrapInFunction(stmt);
+TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_DefaultReturn) {
+ auto* stmt = Switch(1, Case(Expr(0), Block(Discard())), DefaultCase(Block(Return())));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kReturn));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kReturn));
}
-TEST_F(ResolverBehaviorTest,
- StmtSwitch_CondLiteral_Case0Discard_Case1Return_DefaultEmpty) {
- auto* stmt = Switch(1, //
- Case(Expr(0), Block(Discard())), //
- Case(Expr(1), Block(Return())), //
- DefaultCase(Block()));
- WrapInFunction(stmt);
+TEST_F(ResolverBehaviorTest, StmtSwitch_CondLiteral_Case0Discard_Case1Return_DefaultEmpty) {
+ auto* stmt = Switch(1, //
+ Case(Expr(0), Block(Discard())), //
+ Case(Expr(1), Block(Return())), //
+ DefaultCase(Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext,
- sem::Behavior::kReturn));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext,
+ sem::Behavior::kReturn));
}
TEST_F(ResolverBehaviorTest, StmtSwitch_CondCallFuncMayDiscard_DefaultEmpty) {
- auto* stmt = Switch(Call("DiscardOrNext"), DefaultCase(Block()));
- WrapInFunction(stmt);
+ auto* stmt = Switch(Call("DiscardOrNext"), DefaultCase(Block()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
TEST_F(ResolverBehaviorTest, StmtVarDecl) {
- auto* stmt = Decl(Var("v", ty.i32()));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Var("v", ty.i32()));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behavior::kNext);
}
TEST_F(ResolverBehaviorTest, StmtVarDecl_RHSDiscardOrNext) {
- auto* stmt = Decl(Var("lhs", ty.i32(), Call("DiscardOrNext")));
- WrapInFunction(stmt);
+ auto* stmt = Decl(Var("lhs", ty.i32(), Call("DiscardOrNext")));
+ WrapInFunction(stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(stmt);
- EXPECT_EQ(sem->Behaviors(),
- sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
+ auto* sem = Sem().Get(stmt);
+ EXPECT_EQ(sem->Behaviors(), sem::Behaviors(sem::Behavior::kDiscard, sem::Behavior::kNext));
}
} // namespace
diff --git a/src/tint/resolver/resolver_constants.cc b/src/tint/resolver/resolver_constants.cc
index 85f0abd..a0f2711 100644
--- a/src/tint/resolver/resolver_constants.cc
+++ b/src/tint/resolver/resolver_constants.cc
@@ -27,116 +27,110 @@
} // namespace
-sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr,
- const sem::Type* type) {
- if (auto* e = expr->As<ast::LiteralExpression>()) {
- return EvaluateConstantValue(e, type);
- }
- if (auto* e = expr->As<ast::CallExpression>()) {
- return EvaluateConstantValue(e, type);
- }
- return {};
+sem::Constant Resolver::EvaluateConstantValue(const ast::Expression* expr, const sem::Type* type) {
+ if (auto* e = expr->As<ast::LiteralExpression>()) {
+ return EvaluateConstantValue(e, type);
+ }
+ if (auto* e = expr->As<ast::CallExpression>()) {
+ return EvaluateConstantValue(e, type);
+ }
+ return {};
}
-sem::Constant Resolver::EvaluateConstantValue(
- const ast::LiteralExpression* literal,
- const sem::Type* type) {
- if (auto* lit = literal->As<ast::SintLiteralExpression>()) {
- return {type, {lit->ValueAsI32()}};
- }
- if (auto* lit = literal->As<ast::UintLiteralExpression>()) {
- return {type, {lit->ValueAsU32()}};
- }
- if (auto* lit = literal->As<ast::FloatLiteralExpression>()) {
- return {type, {lit->value}};
- }
- if (auto* lit = literal->As<ast::BoolLiteralExpression>()) {
- return {type, {lit->value}};
- }
- TINT_UNREACHABLE(Resolver, builder_->Diagnostics());
- return {};
+sem::Constant Resolver::EvaluateConstantValue(const ast::LiteralExpression* literal,
+ const sem::Type* type) {
+ if (auto* lit = literal->As<ast::SintLiteralExpression>()) {
+ return {type, {lit->ValueAsI32()}};
+ }
+ if (auto* lit = literal->As<ast::UintLiteralExpression>()) {
+ return {type, {lit->ValueAsU32()}};
+ }
+ if (auto* lit = literal->As<ast::FloatLiteralExpression>()) {
+ return {type, {lit->value}};
+ }
+ if (auto* lit = literal->As<ast::BoolLiteralExpression>()) {
+ return {type, {lit->value}};
+ }
+ TINT_UNREACHABLE(Resolver, builder_->Diagnostics());
+ return {};
}
sem::Constant Resolver::EvaluateConstantValue(const ast::CallExpression* call,
const sem::Type* type) {
- auto* vec = type->As<sem::Vector>();
+ auto* vec = type->As<sem::Vector>();
- // For now, only fold scalars and vectors
- if (!type->is_scalar() && !vec) {
- return {};
- }
+ // For now, only fold scalars and vectors
+ if (!type->is_scalar() && !vec) {
+ return {};
+ }
- auto* elem_type = vec ? vec->type() : type;
- int result_size = vec ? static_cast<int>(vec->Width()) : 1;
+ auto* elem_type = vec ? vec->type() : type;
+ int result_size = vec ? static_cast<int>(vec->Width()) : 1;
- // For zero value init, return 0s
- if (call->args.empty()) {
- if (elem_type->Is<sem::I32>()) {
- return sem::Constant(type, sem::Constant::Scalars(result_size, 0));
+ // For zero value init, return 0s
+ if (call->args.empty()) {
+ if (elem_type->Is<sem::I32>()) {
+ return sem::Constant(type, sem::Constant::Scalars(result_size, 0));
+ }
+ if (elem_type->Is<sem::U32>()) {
+ return sem::Constant(type, sem::Constant::Scalars(result_size, 0u));
+ }
+ if (elem_type->Is<sem::F32>()) {
+ return sem::Constant(type, sem::Constant::Scalars(result_size, 0.f));
+ }
+ if (elem_type->Is<sem::Bool>()) {
+ return sem::Constant(type, sem::Constant::Scalars(result_size, false));
+ }
}
- if (elem_type->Is<sem::U32>()) {
- return sem::Constant(type, sem::Constant::Scalars(result_size, 0u));
- }
- if (elem_type->Is<sem::F32>()) {
- return sem::Constant(type, sem::Constant::Scalars(result_size, 0.f));
- }
- if (elem_type->Is<sem::Bool>()) {
- return sem::Constant(type, sem::Constant::Scalars(result_size, false));
- }
- }
- // Build value for type_ctor from each child value by casting to
- // type_ctor's type.
- sem::Constant::Scalars elems;
- for (auto* expr : call->args) {
- auto* arg = builder_->Sem().Get(expr);
- if (!arg || !arg->ConstantValue()) {
- return {};
+ // Build value for type_ctor from each child value by casting to
+ // type_ctor's type.
+ sem::Constant::Scalars elems;
+ for (auto* expr : call->args) {
+ auto* arg = builder_->Sem().Get(expr);
+ if (!arg || !arg->ConstantValue()) {
+ return {};
+ }
+ auto cast = ConstantCast(arg->ConstantValue(), elem_type);
+ elems.insert(elems.end(), cast.Elements().begin(), cast.Elements().end());
}
- auto cast = ConstantCast(arg->ConstantValue(), elem_type);
- elems.insert(elems.end(), cast.Elements().begin(), cast.Elements().end());
- }
- // Splat single-value initializers
- if (elems.size() == 1) {
- for (int i = 0; i < result_size - 1; ++i) {
- elems.emplace_back(elems[0]);
+ // Splat single-value initializers
+ if (elems.size() == 1) {
+ for (int i = 0; i < result_size - 1; ++i) {
+ elems.emplace_back(elems[0]);
+ }
}
- }
- return sem::Constant(type, std::move(elems));
+ return sem::Constant(type, std::move(elems));
}
sem::Constant Resolver::ConstantCast(const sem::Constant& value,
const sem::Type* target_elem_type) {
- if (value.ElementType() == target_elem_type) {
- return value;
- }
-
- sem::Constant::Scalars elems;
- for (size_t i = 0; i < value.Elements().size(); ++i) {
- if (target_elem_type->Is<sem::I32>()) {
- elems.emplace_back(
- value.WithScalarAt(i, [](auto&& s) { return static_cast<i32>(s); }));
- } else if (target_elem_type->Is<sem::U32>()) {
- elems.emplace_back(
- value.WithScalarAt(i, [](auto&& s) { return static_cast<u32>(s); }));
- } else if (target_elem_type->Is<sem::F32>()) {
- elems.emplace_back(
- value.WithScalarAt(i, [](auto&& s) { return static_cast<f32>(s); }));
- } else if (target_elem_type->Is<sem::Bool>()) {
- elems.emplace_back(
- value.WithScalarAt(i, [](auto&& s) { return static_cast<bool>(s); }));
+ if (value.ElementType() == target_elem_type) {
+ return value;
}
- }
- auto* target_type =
- value.Type()->Is<sem::Vector>()
- ? builder_->create<sem::Vector>(target_elem_type,
- static_cast<uint32_t>(elems.size()))
- : target_elem_type;
+ sem::Constant::Scalars elems;
+ for (size_t i = 0; i < value.Elements().size(); ++i) {
+ if (target_elem_type->Is<sem::I32>()) {
+ elems.emplace_back(value.WithScalarAt(i, [](auto&& s) { return static_cast<i32>(s); }));
+ } else if (target_elem_type->Is<sem::U32>()) {
+ elems.emplace_back(value.WithScalarAt(i, [](auto&& s) { return static_cast<u32>(s); }));
+ } else if (target_elem_type->Is<sem::F32>()) {
+ elems.emplace_back(value.WithScalarAt(i, [](auto&& s) { return static_cast<f32>(s); }));
+ } else if (target_elem_type->Is<sem::Bool>()) {
+ elems.emplace_back(
+ value.WithScalarAt(i, [](auto&& s) { return static_cast<bool>(s); }));
+ }
+ }
- return sem::Constant(target_type, elems);
+ auto* target_type =
+ value.Type()->Is<sem::Vector>()
+ ? builder_->create<sem::Vector>(target_elem_type, static_cast<uint32_t>(elems.size()))
+ : target_elem_type;
+
+ return sem::Constant(target_type, elems);
}
} // namespace tint::resolver
diff --git a/src/tint/resolver/resolver_constants_test.cc b/src/tint/resolver/resolver_constants_test.cc
index b3a1be7..ceb0822 100644
--- a/src/tint/resolver/resolver_constants_test.cc
+++ b/src/tint/resolver/resolver_constants_test.cc
@@ -26,405 +26,405 @@
using ResolverConstantsTest = ResolverTest;
TEST_F(ResolverConstantsTest, Scalar_i32) {
- auto* expr = Expr(99);
- WrapInFunction(expr);
+ auto* expr = Expr(99);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::I32>());
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::I32>());
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
}
TEST_F(ResolverConstantsTest, Scalar_u32) {
- auto* expr = Expr(99u);
- WrapInFunction(expr);
+ auto* expr = Expr(99u);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::U32>());
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::U32>());
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
}
TEST_F(ResolverConstantsTest, Scalar_f32) {
- auto* expr = Expr(9.9f);
- WrapInFunction(expr);
+ auto* expr = Expr(9.9f);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::F32>());
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::F32>());
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
}
TEST_F(ResolverConstantsTest, Scalar_bool) {
- auto* expr = Expr(true);
- WrapInFunction(expr);
+ auto* expr = Expr(true);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Type()->Is<sem::Bool>());
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_EQ(sem->ConstantValue().ElementType(), sem->Type());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 1u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_i32) {
- auto* expr = vec3<i32>();
- WrapInFunction(expr);
+ auto* expr = vec3<i32>();
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 0);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 0);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 0);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 0);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 0);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 0);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_u32) {
- auto* expr = vec3<u32>();
- WrapInFunction(expr);
+ auto* expr = vec3<u32>();
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 0u);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 0u);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 0u);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 0u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 0u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 0u);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) {
- auto* expr = vec3<f32>();
- WrapInFunction(expr);
+ auto* expr = vec3<f32>();
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 0u);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 0u);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 0u);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 0u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 0u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 0u);
}
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) {
- auto* expr = vec3<bool>();
- WrapInFunction(expr);
+ auto* expr = vec3<bool>();
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, false);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, false);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, false);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, false);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_i32) {
- auto* expr = vec3<i32>(99);
- WrapInFunction(expr);
+ auto* expr = vec3<i32>(99);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 99);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 99);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 99);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 99);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 99);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_u32) {
- auto* expr = vec3<u32>(99u);
- WrapInFunction(expr);
+ auto* expr = vec3<u32>(99u);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 99u);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 99u);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 99u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 99u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 99u);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_f32) {
- auto* expr = vec3<f32>(9.9f);
- WrapInFunction(expr);
+ auto* expr = vec3<f32>(9.9f);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 9.9f);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 9.9f);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 9.9f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 9.9f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 9.9f);
}
TEST_F(ResolverConstantsTest, Vec3_Splat_bool) {
- auto* expr = vec3<bool>(true);
- WrapInFunction(expr);
+ auto* expr = vec3<bool>(true);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, true);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, true);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_i32) {
- auto* expr = vec3<i32>(1, 2, 3);
- WrapInFunction(expr);
+ auto* expr = vec3<i32>(1, 2, 3);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_u32) {
- auto* expr = vec3<u32>(1u, 2u, 3u);
- WrapInFunction(expr);
+ auto* expr = vec3<u32>(1u, 2u, 3u);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 3u);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 3u);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_f32) {
- auto* expr = vec3<f32>(1.f, 2.f, 3.f);
- WrapInFunction(expr);
+ auto* expr = vec3<f32>(1.f, 2.f, 3.f);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
}
TEST_F(ResolverConstantsTest, Vec3_FullConstruct_bool) {
- auto* expr = vec3<bool>(true, false, true);
- WrapInFunction(expr);
+ auto* expr = vec3<bool>(true, false, true);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_i32) {
- auto* expr = vec3<i32>(1, vec2<i32>(2, 3));
- WrapInFunction(expr);
+ auto* expr = vec3<i32>(1, vec2<i32>(2, 3));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_u32) {
- auto* expr = vec3<u32>(vec2<u32>(1u, 2u), 3u);
- WrapInFunction(expr);
+ auto* expr = vec3<u32>(vec2<u32>(1u, 2u), 3u);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 3u);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::U32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].u32, 1u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].u32, 2u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].u32, 3u);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_f32) {
- auto* expr = vec3<f32>(1.f, vec2<f32>(2.f, 3.f));
- WrapInFunction(expr);
+ auto* expr = vec3<f32>(1.f, vec2<f32>(2.f, 3.f));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 1.f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 2.f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 3.f);
}
TEST_F(ResolverConstantsTest, Vec3_MixConstruct_bool) {
- auto* expr = vec3<bool>(vec2<bool>(true, false), true);
- WrapInFunction(expr);
+ auto* expr = vec3<bool>(vec2<bool>(true, false), true);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::Bool>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].bool_, true);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].bool_, false);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].bool_, true);
}
TEST_F(ResolverConstantsTest, Vec3_Cast_f32_to_32) {
- auto* expr = vec3<i32>(vec3<f32>(1.1f, 2.2f, 3.3f));
- WrapInFunction(expr);
+ auto* expr = vec3<i32>(vec3<f32>(1.1f, 2.2f, 3.3f));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::I32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].i32, 1);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].i32, 2);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].i32, 3);
}
TEST_F(ResolverConstantsTest, Vec3_Cast_u32_to_f32) {
- auto* expr = vec3<f32>(vec3<u32>(10u, 20u, 30u));
- WrapInFunction(expr);
+ auto* expr = vec3<f32>(vec3<u32>(10u, 20u, 30u));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_NE(sem, nullptr);
- ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
- EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
- EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
- ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
- EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 10.f);
- EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 20.f);
- EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 30.f);
+ auto* sem = Sem().Get(expr);
+ EXPECT_NE(sem, nullptr);
+ ASSERT_TRUE(sem->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(sem->Type()->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(sem->Type()->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
+ EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
+ ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
+ EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 10.f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 20.f);
+ EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 30.f);
}
} // namespace
diff --git a/src/tint/resolver/resolver_is_storeable_test.cc b/src/tint/resolver/resolver_is_storeable_test.cc
index 7c2de64..f4cde70 100644
--- a/src/tint/resolver/resolver_is_storeable_test.cc
+++ b/src/tint/resolver/resolver_is_storeable_test.cc
@@ -24,55 +24,54 @@
using ResolverIsStorableTest = ResolverTest;
TEST_F(ResolverIsStorableTest, Struct_AllMembersStorable) {
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.f32()),
- });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.f32()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIsStorableTest, Struct_SomeMembersNonStorable) {
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
- });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
}
TEST_F(ResolverIsStorableTest, Struct_NestedStorable) {
- auto* storable = Structure("Storable", {
- Member("a", ty.i32()),
- Member("b", ty.f32()),
- });
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.Of(storable)),
- });
+ auto* storable = Structure("Storable", {
+ Member("a", ty.i32()),
+ Member("b", ty.f32()),
+ });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.Of(storable)),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverIsStorableTest, Struct_NestedNonStorable) {
- auto* non_storable =
- Structure("nonstorable",
- {
- Member("a", ty.i32()),
- Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
- });
- Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.Of(non_storable)),
- });
+ auto* non_storable =
+ Structure("nonstorable", {
+ Member("a", ty.i32()),
+ Member("b", ty.pointer<i32>(ast::StorageClass::kPrivate)),
+ });
+ Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.Of(non_storable)),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(error: ptr<private, i32, read_write> cannot be used as the type of a structure member)");
}
} // namespace
diff --git a/src/tint/resolver/resolver_test.cc b/src/tint/resolver/resolver_test.cc
index c019e04..439d929 100644
--- a/src/tint/resolver/resolver_test.cc
+++ b/src/tint/resolver/resolver_test.cc
@@ -87,1162 +87,1145 @@
using Op = ast::BinaryOp;
TEST_F(ResolverTest, Stmt_Assign) {
- auto* v = Var("v", ty.f32());
- auto* lhs = Expr("v");
- auto* rhs = Expr(2.3f);
+ auto* v = Var("v", ty.f32());
+ auto* lhs = Expr("v");
+ auto* rhs = Expr(2.3f);
- auto* assign = Assign(lhs, rhs);
- WrapInFunction(v, assign);
+ auto* assign = Assign(lhs, rhs);
+ WrapInFunction(v, assign);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(lhs), nullptr);
- ASSERT_NE(TypeOf(rhs), nullptr);
+ ASSERT_NE(TypeOf(lhs), nullptr);
+ ASSERT_NE(TypeOf(rhs), nullptr);
- EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
- EXPECT_EQ(StmtOf(lhs), assign);
- EXPECT_EQ(StmtOf(rhs), assign);
+ EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(lhs), assign);
+ EXPECT_EQ(StmtOf(rhs), assign);
}
TEST_F(ResolverTest, Stmt_Case) {
- auto* v = Var("v", ty.f32());
- auto* lhs = Expr("v");
- auto* rhs = Expr(2.3f);
+ auto* v = Var("v", ty.f32());
+ auto* lhs = Expr("v");
+ auto* rhs = Expr(2.3f);
- auto* assign = Assign(lhs, rhs);
- auto* block = Block(assign);
- ast::CaseSelectorList lit;
- lit.push_back(create<ast::SintLiteralExpression>(3));
- auto* cse = create<ast::CaseStatement>(lit, block);
- auto* cond_var = Var("c", ty.i32());
- auto* sw = Switch(cond_var, cse, DefaultCase());
- WrapInFunction(v, cond_var, sw);
+ auto* assign = Assign(lhs, rhs);
+ auto* block = Block(assign);
+ ast::CaseSelectorList lit;
+ lit.push_back(create<ast::SintLiteralExpression>(3));
+ auto* cse = create<ast::CaseStatement>(lit, block);
+ auto* cond_var = Var("c", ty.i32());
+ auto* sw = Switch(cond_var, cse, DefaultCase());
+ WrapInFunction(v, cond_var, sw);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(lhs), nullptr);
- ASSERT_NE(TypeOf(rhs), nullptr);
- EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
- EXPECT_EQ(StmtOf(lhs), assign);
- EXPECT_EQ(StmtOf(rhs), assign);
- EXPECT_EQ(BlockOf(assign), block);
+ ASSERT_NE(TypeOf(lhs), nullptr);
+ ASSERT_NE(TypeOf(rhs), nullptr);
+ EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(lhs), assign);
+ EXPECT_EQ(StmtOf(rhs), assign);
+ EXPECT_EQ(BlockOf(assign), block);
}
TEST_F(ResolverTest, Stmt_Block) {
- auto* v = Var("v", ty.f32());
- auto* lhs = Expr("v");
- auto* rhs = Expr(2.3f);
+ auto* v = Var("v", ty.f32());
+ auto* lhs = Expr("v");
+ auto* rhs = Expr(2.3f);
- auto* assign = Assign(lhs, rhs);
- auto* block = Block(assign);
- WrapInFunction(v, block);
+ auto* assign = Assign(lhs, rhs);
+ auto* block = Block(assign);
+ WrapInFunction(v, block);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(lhs), nullptr);
- ASSERT_NE(TypeOf(rhs), nullptr);
- EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
- EXPECT_EQ(StmtOf(lhs), assign);
- EXPECT_EQ(StmtOf(rhs), assign);
- EXPECT_EQ(BlockOf(lhs), block);
- EXPECT_EQ(BlockOf(rhs), block);
- EXPECT_EQ(BlockOf(assign), block);
+ ASSERT_NE(TypeOf(lhs), nullptr);
+ ASSERT_NE(TypeOf(rhs), nullptr);
+ EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(lhs), assign);
+ EXPECT_EQ(StmtOf(rhs), assign);
+ EXPECT_EQ(BlockOf(lhs), block);
+ EXPECT_EQ(BlockOf(rhs), block);
+ EXPECT_EQ(BlockOf(assign), block);
}
TEST_F(ResolverTest, Stmt_If) {
- auto* v = Var("v", ty.f32());
- auto* else_lhs = Expr("v");
- auto* else_rhs = Expr(2.3f);
+ auto* v = Var("v", ty.f32());
+ auto* else_lhs = Expr("v");
+ auto* else_rhs = Expr(2.3f);
- auto* else_body = Block(Assign(else_lhs, else_rhs));
+ auto* else_body = Block(Assign(else_lhs, else_rhs));
- auto* else_cond = Expr(true);
- auto* else_stmt = If(else_cond, else_body);
+ auto* else_cond = Expr(true);
+ auto* else_stmt = If(else_cond, else_body);
- auto* lhs = Expr("v");
- auto* rhs = Expr(2.3f);
+ auto* lhs = Expr("v");
+ auto* rhs = Expr(2.3f);
- auto* assign = Assign(lhs, rhs);
- auto* body = Block(assign);
- auto* cond = Expr(true);
- auto* stmt = If(cond, body, else_stmt);
- WrapInFunction(v, stmt);
+ auto* assign = Assign(lhs, rhs);
+ auto* body = Block(assign);
+ auto* cond = Expr(true);
+ auto* stmt = If(cond, body, else_stmt);
+ WrapInFunction(v, stmt);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(stmt->condition), nullptr);
- ASSERT_NE(TypeOf(else_lhs), nullptr);
- ASSERT_NE(TypeOf(else_rhs), nullptr);
- ASSERT_NE(TypeOf(lhs), nullptr);
- ASSERT_NE(TypeOf(rhs), nullptr);
- EXPECT_TRUE(TypeOf(stmt->condition)->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(else_lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(else_rhs)->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
- EXPECT_EQ(StmtOf(lhs), assign);
- EXPECT_EQ(StmtOf(rhs), assign);
- EXPECT_EQ(StmtOf(cond), stmt);
- EXPECT_EQ(StmtOf(else_cond), else_stmt);
- EXPECT_EQ(BlockOf(lhs), body);
- EXPECT_EQ(BlockOf(rhs), body);
- EXPECT_EQ(BlockOf(else_lhs), else_body);
- EXPECT_EQ(BlockOf(else_rhs), else_body);
+ ASSERT_NE(TypeOf(stmt->condition), nullptr);
+ ASSERT_NE(TypeOf(else_lhs), nullptr);
+ ASSERT_NE(TypeOf(else_rhs), nullptr);
+ ASSERT_NE(TypeOf(lhs), nullptr);
+ ASSERT_NE(TypeOf(rhs), nullptr);
+ EXPECT_TRUE(TypeOf(stmt->condition)->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(else_lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(else_rhs)->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(lhs), assign);
+ EXPECT_EQ(StmtOf(rhs), assign);
+ EXPECT_EQ(StmtOf(cond), stmt);
+ EXPECT_EQ(StmtOf(else_cond), else_stmt);
+ EXPECT_EQ(BlockOf(lhs), body);
+ EXPECT_EQ(BlockOf(rhs), body);
+ EXPECT_EQ(BlockOf(else_lhs), else_body);
+ EXPECT_EQ(BlockOf(else_rhs), else_body);
}
TEST_F(ResolverTest, Stmt_Loop) {
- auto* v = Var("v", ty.f32());
- auto* body_lhs = Expr("v");
- auto* body_rhs = Expr(2.3f);
+ auto* v = Var("v", ty.f32());
+ auto* body_lhs = Expr("v");
+ auto* body_rhs = Expr(2.3f);
- auto* body = Block(Assign(body_lhs, body_rhs), Break());
- auto* continuing_lhs = Expr("v");
- auto* continuing_rhs = Expr(2.3f);
+ auto* body = Block(Assign(body_lhs, body_rhs), Break());
+ auto* continuing_lhs = Expr("v");
+ auto* continuing_rhs = Expr(2.3f);
- auto* continuing = Block(Assign(continuing_lhs, continuing_rhs));
- auto* stmt = Loop(body, continuing);
- WrapInFunction(v, stmt);
+ auto* continuing = Block(Assign(continuing_lhs, continuing_rhs));
+ auto* stmt = Loop(body, continuing);
+ WrapInFunction(v, stmt);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(body_lhs), nullptr);
- ASSERT_NE(TypeOf(body_rhs), nullptr);
- ASSERT_NE(TypeOf(continuing_lhs), nullptr);
- ASSERT_NE(TypeOf(continuing_rhs), nullptr);
- EXPECT_TRUE(TypeOf(body_lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(body_rhs)->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(continuing_lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(continuing_rhs)->Is<sem::F32>());
- EXPECT_EQ(BlockOf(body_lhs), body);
- EXPECT_EQ(BlockOf(body_rhs), body);
- EXPECT_EQ(BlockOf(continuing_lhs), continuing);
- EXPECT_EQ(BlockOf(continuing_rhs), continuing);
+ ASSERT_NE(TypeOf(body_lhs), nullptr);
+ ASSERT_NE(TypeOf(body_rhs), nullptr);
+ ASSERT_NE(TypeOf(continuing_lhs), nullptr);
+ ASSERT_NE(TypeOf(continuing_rhs), nullptr);
+ EXPECT_TRUE(TypeOf(body_lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(body_rhs)->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(continuing_lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(continuing_rhs)->Is<sem::F32>());
+ EXPECT_EQ(BlockOf(body_lhs), body);
+ EXPECT_EQ(BlockOf(body_rhs), body);
+ EXPECT_EQ(BlockOf(continuing_lhs), continuing);
+ EXPECT_EQ(BlockOf(continuing_rhs), continuing);
}
TEST_F(ResolverTest, Stmt_Return) {
- auto* cond = Expr(2);
+ auto* cond = Expr(2);
- auto* ret = Return(cond);
- Func("test", {}, ty.i32(), {ret}, {});
+ auto* ret = Return(cond);
+ Func("test", {}, ty.i32(), {ret}, {});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(cond), nullptr);
- EXPECT_TRUE(TypeOf(cond)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(cond), nullptr);
+ EXPECT_TRUE(TypeOf(cond)->Is<sem::I32>());
}
TEST_F(ResolverTest, Stmt_Return_WithoutValue) {
- auto* ret = Return();
- WrapInFunction(ret);
+ auto* ret = Return();
+ WrapInFunction(ret);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, Stmt_Switch) {
- auto* v = Var("v", ty.f32());
- auto* lhs = Expr("v");
- auto* rhs = Expr(2.3f);
- auto* case_block = Block(Assign(lhs, rhs));
- auto* stmt = Switch(Expr(2), Case(Expr(3), case_block), DefaultCase());
- WrapInFunction(v, stmt);
+ auto* v = Var("v", ty.f32());
+ auto* lhs = Expr("v");
+ auto* rhs = Expr(2.3f);
+ auto* case_block = Block(Assign(lhs, rhs));
+ auto* stmt = Switch(Expr(2), Case(Expr(3), case_block), DefaultCase());
+ WrapInFunction(v, stmt);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(stmt->condition), nullptr);
- ASSERT_NE(TypeOf(lhs), nullptr);
- ASSERT_NE(TypeOf(rhs), nullptr);
+ ASSERT_NE(TypeOf(stmt->condition), nullptr);
+ ASSERT_NE(TypeOf(lhs), nullptr);
+ ASSERT_NE(TypeOf(rhs), nullptr);
- EXPECT_TRUE(TypeOf(stmt->condition)->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
- EXPECT_EQ(BlockOf(lhs), case_block);
- EXPECT_EQ(BlockOf(rhs), case_block);
+ EXPECT_TRUE(TypeOf(stmt->condition)->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(lhs)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(rhs)->Is<sem::F32>());
+ EXPECT_EQ(BlockOf(lhs), case_block);
+ EXPECT_EQ(BlockOf(rhs), case_block);
}
TEST_F(ResolverTest, Stmt_Call) {
- ast::VariableList params;
- Func("my_func", params, ty.void_(), {Return()}, ast::AttributeList{});
+ ast::VariableList params;
+ Func("my_func", params, ty.void_(), {Return()}, ast::AttributeList{});
- auto* expr = Call("my_func");
+ auto* expr = Call("my_func");
- auto* call = CallStmt(expr);
- WrapInFunction(call);
+ auto* call = CallStmt(expr);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::Void>());
- EXPECT_EQ(StmtOf(expr), call);
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::Void>());
+ EXPECT_EQ(StmtOf(expr), call);
}
TEST_F(ResolverTest, Stmt_VariableDecl) {
- auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* init = var->constructor;
+ auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* init = var->constructor;
- auto* decl = Decl(var);
- WrapInFunction(decl);
+ auto* decl = Decl(var);
+ WrapInFunction(decl);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(init), nullptr);
- EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(init), nullptr);
+ EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
}
TEST_F(ResolverTest, Stmt_VariableDecl_Alias) {
- auto* my_int = Alias("MyInt", ty.i32());
- auto* var = Var("my_var", ty.Of(my_int), ast::StorageClass::kNone, Expr(2));
- auto* init = var->constructor;
+ auto* my_int = Alias("MyInt", ty.i32());
+ auto* var = Var("my_var", ty.Of(my_int), ast::StorageClass::kNone, Expr(2));
+ auto* init = var->constructor;
- auto* decl = Decl(var);
- WrapInFunction(decl);
+ auto* decl = Decl(var);
+ WrapInFunction(decl);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(init), nullptr);
- EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(init), nullptr);
+ EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
}
TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
- auto* init = Expr(2);
- Global("my_var", ty.i32(), ast::StorageClass::kPrivate, init);
+ auto* init = Expr(2);
+ Global("my_var", ty.i32(), ast::StorageClass::kPrivate, init);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(init), nullptr);
- EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
- EXPECT_EQ(StmtOf(init), nullptr);
+ ASSERT_NE(TypeOf(init), nullptr);
+ EXPECT_TRUE(TypeOf(init)->Is<sem::I32>());
+ EXPECT_EQ(StmtOf(init), nullptr);
}
TEST_F(ResolverTest, Stmt_VariableDecl_OuterScopeAfterInnerScope) {
- // fn func_i32() {
- // {
- // var foo : i32 = 2;
- // var bar : i32 = foo;
- // }
- // var foo : f32 = 2.0;
- // var bar : f32 = foo;
- // }
+ // fn func_i32() {
+ // {
+ // var foo : i32 = 2;
+ // var bar : i32 = foo;
+ // }
+ // var foo : f32 = 2.0;
+ // var bar : f32 = foo;
+ // }
- ast::VariableList params;
+ ast::VariableList params;
- // Declare i32 "foo" inside a block
- auto* foo_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* foo_i32_init = foo_i32->constructor;
- auto* foo_i32_decl = Decl(foo_i32);
+ // Declare i32 "foo" inside a block
+ auto* foo_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* foo_i32_init = foo_i32->constructor;
+ auto* foo_i32_decl = Decl(foo_i32);
- // Reference "foo" inside the block
- auto* bar_i32 = Var("bar", ty.i32(), ast::StorageClass::kNone, Expr("foo"));
- auto* bar_i32_init = bar_i32->constructor;
- auto* bar_i32_decl = Decl(bar_i32);
+ // Reference "foo" inside the block
+ auto* bar_i32 = Var("bar", ty.i32(), ast::StorageClass::kNone, Expr("foo"));
+ auto* bar_i32_init = bar_i32->constructor;
+ auto* bar_i32_decl = Decl(bar_i32);
- auto* inner = Block(foo_i32_decl, bar_i32_decl);
+ auto* inner = Block(foo_i32_decl, bar_i32_decl);
- // Declare f32 "foo" at function scope
- auto* foo_f32 = Var("foo", ty.f32(), ast::StorageClass::kNone, Expr(2.f));
- auto* foo_f32_init = foo_f32->constructor;
- auto* foo_f32_decl = Decl(foo_f32);
+ // Declare f32 "foo" at function scope
+ auto* foo_f32 = Var("foo", ty.f32(), ast::StorageClass::kNone, Expr(2.f));
+ auto* foo_f32_init = foo_f32->constructor;
+ auto* foo_f32_decl = Decl(foo_f32);
- // Reference "foo" at function scope
- auto* bar_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
- auto* bar_f32_init = bar_f32->constructor;
- auto* bar_f32_decl = Decl(bar_f32);
+ // Reference "foo" at function scope
+ auto* bar_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
+ auto* bar_f32_init = bar_f32->constructor;
+ auto* bar_f32_decl = Decl(bar_f32);
- Func("func", params, ty.void_(), {inner, foo_f32_decl, bar_f32_decl},
- ast::AttributeList{});
+ Func("func", params, ty.void_(), {inner, foo_f32_decl, bar_f32_decl}, ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(foo_i32_init), nullptr);
- EXPECT_TRUE(TypeOf(foo_i32_init)->Is<sem::I32>());
- ASSERT_NE(TypeOf(foo_f32_init), nullptr);
- EXPECT_TRUE(TypeOf(foo_f32_init)->Is<sem::F32>());
- ASSERT_NE(TypeOf(bar_i32_init), nullptr);
- EXPECT_TRUE(TypeOf(bar_i32_init)->UnwrapRef()->Is<sem::I32>());
- ASSERT_NE(TypeOf(bar_f32_init), nullptr);
- EXPECT_TRUE(TypeOf(bar_f32_init)->UnwrapRef()->Is<sem::F32>());
- EXPECT_EQ(StmtOf(foo_i32_init), foo_i32_decl);
- EXPECT_EQ(StmtOf(bar_i32_init), bar_i32_decl);
- EXPECT_EQ(StmtOf(foo_f32_init), foo_f32_decl);
- EXPECT_EQ(StmtOf(bar_f32_init), bar_f32_decl);
- EXPECT_TRUE(CheckVarUsers(foo_i32, {bar_i32->constructor}));
- EXPECT_TRUE(CheckVarUsers(foo_f32, {bar_f32->constructor}));
- ASSERT_NE(VarOf(bar_i32->constructor), nullptr);
- EXPECT_EQ(VarOf(bar_i32->constructor)->Declaration(), foo_i32);
- ASSERT_NE(VarOf(bar_f32->constructor), nullptr);
- EXPECT_EQ(VarOf(bar_f32->constructor)->Declaration(), foo_f32);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(foo_i32_init), nullptr);
+ EXPECT_TRUE(TypeOf(foo_i32_init)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(foo_f32_init), nullptr);
+ EXPECT_TRUE(TypeOf(foo_f32_init)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(bar_i32_init), nullptr);
+ EXPECT_TRUE(TypeOf(bar_i32_init)->UnwrapRef()->Is<sem::I32>());
+ ASSERT_NE(TypeOf(bar_f32_init), nullptr);
+ EXPECT_TRUE(TypeOf(bar_f32_init)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(foo_i32_init), foo_i32_decl);
+ EXPECT_EQ(StmtOf(bar_i32_init), bar_i32_decl);
+ EXPECT_EQ(StmtOf(foo_f32_init), foo_f32_decl);
+ EXPECT_EQ(StmtOf(bar_f32_init), bar_f32_decl);
+ EXPECT_TRUE(CheckVarUsers(foo_i32, {bar_i32->constructor}));
+ EXPECT_TRUE(CheckVarUsers(foo_f32, {bar_f32->constructor}));
+ ASSERT_NE(VarOf(bar_i32->constructor), nullptr);
+ EXPECT_EQ(VarOf(bar_i32->constructor)->Declaration(), foo_i32);
+ ASSERT_NE(VarOf(bar_f32->constructor), nullptr);
+ EXPECT_EQ(VarOf(bar_f32->constructor)->Declaration(), foo_f32);
}
TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScopeAfterFunctionScope) {
- // fn func_i32() {
- // var foo : i32 = 2;
- // }
- // var foo : f32 = 2.0;
- // fn func_f32() {
- // var bar : f32 = foo;
- // }
+ // fn func_i32() {
+ // var foo : i32 = 2;
+ // }
+ // var foo : f32 = 2.0;
+ // fn func_f32() {
+ // var bar : f32 = foo;
+ // }
- ast::VariableList params;
+ ast::VariableList params;
- // Declare i32 "foo" inside a function
- auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2));
- auto* fn_i32_init = fn_i32->constructor;
- auto* fn_i32_decl = Decl(fn_i32);
- Func("func_i32", params, ty.void_(), {fn_i32_decl}, ast::AttributeList{});
+ // Declare i32 "foo" inside a function
+ auto* fn_i32 = Var("foo", ty.i32(), ast::StorageClass::kNone, Expr(2));
+ auto* fn_i32_init = fn_i32->constructor;
+ auto* fn_i32_decl = Decl(fn_i32);
+ Func("func_i32", params, ty.void_(), {fn_i32_decl}, ast::AttributeList{});
- // Declare f32 "foo" at module scope
- auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kPrivate, Expr(2.f));
- auto* mod_init = mod_f32->constructor;
- AST().AddGlobalVariable(mod_f32);
+ // Declare f32 "foo" at module scope
+ auto* mod_f32 = Var("foo", ty.f32(), ast::StorageClass::kPrivate, Expr(2.f));
+ auto* mod_init = mod_f32->constructor;
+ AST().AddGlobalVariable(mod_f32);
- // Reference "foo" in another function
- auto* fn_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
- auto* fn_f32_init = fn_f32->constructor;
- auto* fn_f32_decl = Decl(fn_f32);
- Func("func_f32", params, ty.void_(), {fn_f32_decl}, ast::AttributeList{});
+ // Reference "foo" in another function
+ auto* fn_f32 = Var("bar", ty.f32(), ast::StorageClass::kNone, Expr("foo"));
+ auto* fn_f32_init = fn_f32->constructor;
+ auto* fn_f32_decl = Decl(fn_f32);
+ Func("func_f32", params, ty.void_(), {fn_f32_decl}, ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(mod_init), nullptr);
- EXPECT_TRUE(TypeOf(mod_init)->Is<sem::F32>());
- ASSERT_NE(TypeOf(fn_i32_init), nullptr);
- EXPECT_TRUE(TypeOf(fn_i32_init)->Is<sem::I32>());
- ASSERT_NE(TypeOf(fn_f32_init), nullptr);
- EXPECT_TRUE(TypeOf(fn_f32_init)->UnwrapRef()->Is<sem::F32>());
- EXPECT_EQ(StmtOf(fn_i32_init), fn_i32_decl);
- EXPECT_EQ(StmtOf(mod_init), nullptr);
- EXPECT_EQ(StmtOf(fn_f32_init), fn_f32_decl);
- EXPECT_TRUE(CheckVarUsers(fn_i32, {}));
- EXPECT_TRUE(CheckVarUsers(mod_f32, {fn_f32->constructor}));
- ASSERT_NE(VarOf(fn_f32->constructor), nullptr);
- EXPECT_EQ(VarOf(fn_f32->constructor)->Declaration(), mod_f32);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(mod_init), nullptr);
+ EXPECT_TRUE(TypeOf(mod_init)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(fn_i32_init), nullptr);
+ EXPECT_TRUE(TypeOf(fn_i32_init)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(fn_f32_init), nullptr);
+ EXPECT_TRUE(TypeOf(fn_f32_init)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(fn_i32_init), fn_i32_decl);
+ EXPECT_EQ(StmtOf(mod_init), nullptr);
+ EXPECT_EQ(StmtOf(fn_f32_init), fn_f32_decl);
+ EXPECT_TRUE(CheckVarUsers(fn_i32, {}));
+ EXPECT_TRUE(CheckVarUsers(mod_f32, {fn_f32->constructor}));
+ ASSERT_NE(VarOf(fn_f32->constructor), nullptr);
+ EXPECT_EQ(VarOf(fn_f32->constructor)->Declaration(), mod_f32);
}
TEST_F(ResolverTest, ArraySize_UnsignedLiteral) {
- // var<private> a : array<f32, 10u>;
- auto* a =
- Global("a", ty.array(ty.f32(), Expr(10u)), ast::StorageClass::kPrivate);
+ // var<private> a : array<f32, 10u>;
+ auto* a = Global("a", ty.array(ty.f32(), Expr(10u)), ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(a), nullptr);
- auto* ref = TypeOf(a)->As<sem::Reference>();
- ASSERT_NE(ref, nullptr);
- auto* ary = ref->StoreType()->As<sem::Array>();
- EXPECT_EQ(ary->Count(), 10u);
+ ASSERT_NE(TypeOf(a), nullptr);
+ auto* ref = TypeOf(a)->As<sem::Reference>();
+ ASSERT_NE(ref, nullptr);
+ auto* ary = ref->StoreType()->As<sem::Array>();
+ EXPECT_EQ(ary->Count(), 10u);
}
TEST_F(ResolverTest, ArraySize_SignedLiteral) {
- // var<private> a : array<f32, 10>;
- auto* a =
- Global("a", ty.array(ty.f32(), Expr(10)), ast::StorageClass::kPrivate);
+ // var<private> a : array<f32, 10>;
+ auto* a = Global("a", ty.array(ty.f32(), Expr(10)), ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(a), nullptr);
- auto* ref = TypeOf(a)->As<sem::Reference>();
- ASSERT_NE(ref, nullptr);
- auto* ary = ref->StoreType()->As<sem::Array>();
- EXPECT_EQ(ary->Count(), 10u);
+ ASSERT_NE(TypeOf(a), nullptr);
+ auto* ref = TypeOf(a)->As<sem::Reference>();
+ ASSERT_NE(ref, nullptr);
+ auto* ary = ref->StoreType()->As<sem::Array>();
+ EXPECT_EQ(ary->Count(), 10u);
}
TEST_F(ResolverTest, ArraySize_UnsignedConstant) {
- // let size = 0u;
- // var<private> a : array<f32, 10u>;
- GlobalConst("size", nullptr, Expr(10u));
- auto* a = Global("a", ty.array(ty.f32(), Expr("size")),
- ast::StorageClass::kPrivate);
+ // let size = 0u;
+ // var<private> a : array<f32, 10u>;
+ GlobalConst("size", nullptr, Expr(10u));
+ auto* a = Global("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(a), nullptr);
- auto* ref = TypeOf(a)->As<sem::Reference>();
- ASSERT_NE(ref, nullptr);
- auto* ary = ref->StoreType()->As<sem::Array>();
- EXPECT_EQ(ary->Count(), 10u);
+ ASSERT_NE(TypeOf(a), nullptr);
+ auto* ref = TypeOf(a)->As<sem::Reference>();
+ ASSERT_NE(ref, nullptr);
+ auto* ary = ref->StoreType()->As<sem::Array>();
+ EXPECT_EQ(ary->Count(), 10u);
}
TEST_F(ResolverTest, ArraySize_SignedConstant) {
- // let size = 0;
- // var<private> a : array<f32, 10>;
- GlobalConst("size", nullptr, Expr(10));
- auto* a = Global("a", ty.array(ty.f32(), Expr("size")),
- ast::StorageClass::kPrivate);
+ // let size = 0;
+ // var<private> a : array<f32, 10>;
+ GlobalConst("size", nullptr, Expr(10));
+ auto* a = Global("a", ty.array(ty.f32(), Expr("size")), ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(a), nullptr);
- auto* ref = TypeOf(a)->As<sem::Reference>();
- ASSERT_NE(ref, nullptr);
- auto* ary = ref->StoreType()->As<sem::Array>();
- EXPECT_EQ(ary->Count(), 10u);
+ ASSERT_NE(TypeOf(a), nullptr);
+ auto* ref = TypeOf(a)->As<sem::Reference>();
+ ASSERT_NE(ref, nullptr);
+ auto* ary = ref->StoreType()->As<sem::Array>();
+ EXPECT_EQ(ary->Count(), 10u);
}
TEST_F(ResolverTest, Expr_Bitcast) {
- Global("name", ty.f32(), ast::StorageClass::kPrivate);
+ Global("name", ty.f32(), ast::StorageClass::kPrivate);
- auto* bitcast = create<ast::BitcastExpression>(ty.f32(), Expr("name"));
- WrapInFunction(bitcast);
+ auto* bitcast = create<ast::BitcastExpression>(ty.f32(), Expr("name"));
+ WrapInFunction(bitcast);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(bitcast), nullptr);
- EXPECT_TRUE(TypeOf(bitcast)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(bitcast), nullptr);
+ EXPECT_TRUE(TypeOf(bitcast)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Call) {
- ast::VariableList params;
- Func("my_func", params, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
+ ast::VariableList params;
+ Func("my_func", params, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
- auto* call = Call("my_func");
- WrapInFunction(call);
+ auto* call = Call("my_func");
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Call_InBinaryOp) {
- ast::VariableList params;
- Func("func", params, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
+ ast::VariableList params;
+ Func("func", params, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
- auto* expr = Add(Call("func"), Call("func"));
- WrapInFunction(expr);
+ auto* expr = Add(Call("func"), Call("func"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Call_WithParams) {
- Func("my_func", {Param(Sym(), ty.f32())}, ty.f32(),
- {
- Return(1.2f),
- });
+ Func("my_func", {Param(Sym(), ty.f32())}, ty.f32(),
+ {
+ Return(1.2f),
+ });
- auto* param = Expr(2.4f);
+ auto* param = Expr(2.4f);
- auto* call = Call("my_func", param);
- WrapInFunction(call);
+ auto* call = Call("my_func", param);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(param), nullptr);
- EXPECT_TRUE(TypeOf(param)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(param), nullptr);
+ EXPECT_TRUE(TypeOf(param)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Call_Builtin) {
- auto* call = Call("round", 2.4f);
- WrapInFunction(call);
+ auto* call = Call("round", 2.4f);
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Cast) {
- Global("name", ty.f32(), ast::StorageClass::kPrivate);
+ Global("name", ty.f32(), ast::StorageClass::kPrivate);
- auto* cast = Construct(ty.f32(), "name");
- WrapInFunction(cast);
+ auto* cast = Construct(ty.f32(), "name");
+ WrapInFunction(cast);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(cast), nullptr);
- EXPECT_TRUE(TypeOf(cast)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(cast), nullptr);
+ EXPECT_TRUE(TypeOf(cast)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Constructor_Scalar) {
- auto* s = Expr(1.0f);
- WrapInFunction(s);
+ auto* s = Expr(1.0f);
+ WrapInFunction(s);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(s), nullptr);
- EXPECT_TRUE(TypeOf(s)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(s), nullptr);
+ EXPECT_TRUE(TypeOf(s)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Constructor_Type_Vec2) {
- auto* tc = vec2<f32>(1.0f, 1.0f);
- WrapInFunction(tc);
+ auto* tc = vec2<f32>(1.0f, 1.0f);
+ WrapInFunction(tc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
}
TEST_F(ResolverTest, Expr_Constructor_Type_Vec3) {
- auto* tc = vec3<f32>(1.0f, 1.0f, 1.0f);
- WrapInFunction(tc);
+ auto* tc = vec3<f32>(1.0f, 1.0f, 1.0f);
+ WrapInFunction(tc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
}
TEST_F(ResolverTest, Expr_Constructor_Type_Vec4) {
- auto* tc = vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f);
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f);
+ WrapInFunction(tc);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
TEST_F(ResolverTest, Expr_Identifier_GlobalVariable) {
- auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kPrivate);
+ auto* my_var = Global("my_var", ty.f32(), ast::StorageClass::kPrivate);
- auto* ident = Expr("my_var");
- WrapInFunction(ident);
+ auto* ident = Expr("my_var");
+ WrapInFunction(ident);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(ident), nullptr);
- ASSERT_TRUE(TypeOf(ident)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(ident)->UnwrapRef()->Is<sem::F32>());
- EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
- ASSERT_NE(VarOf(ident), nullptr);
- EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
+ ASSERT_NE(TypeOf(ident), nullptr);
+ ASSERT_TRUE(TypeOf(ident)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(ident)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
+ ASSERT_NE(VarOf(ident), nullptr);
+ EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
}
TEST_F(ResolverTest, Expr_Identifier_GlobalConstant) {
- auto* my_var = GlobalConst("my_var", ty.f32(), Construct(ty.f32()));
+ auto* my_var = GlobalConst("my_var", ty.f32(), Construct(ty.f32()));
- auto* ident = Expr("my_var");
- WrapInFunction(ident);
+ auto* ident = Expr("my_var");
+ WrapInFunction(ident);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(ident), nullptr);
- EXPECT_TRUE(TypeOf(ident)->Is<sem::F32>());
- EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
- ASSERT_NE(VarOf(ident), nullptr);
- EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
+ ASSERT_NE(TypeOf(ident), nullptr);
+ EXPECT_TRUE(TypeOf(ident)->Is<sem::F32>());
+ EXPECT_TRUE(CheckVarUsers(my_var, {ident}));
+ ASSERT_NE(VarOf(ident), nullptr);
+ EXPECT_EQ(VarOf(ident)->Declaration(), my_var);
}
TEST_F(ResolverTest, Expr_Identifier_FunctionVariable_Const) {
- auto* my_var_a = Expr("my_var");
- auto* var = Let("my_var", ty.f32(), Construct(ty.f32()));
- auto* decl = Decl(Var("b", ty.f32(), ast::StorageClass::kNone, my_var_a));
+ auto* my_var_a = Expr("my_var");
+ auto* var = Let("my_var", ty.f32(), Construct(ty.f32()));
+ auto* decl = Decl(Var("b", ty.f32(), ast::StorageClass::kNone, my_var_a));
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(var),
- decl,
- },
- ast::AttributeList{});
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(var),
+ decl,
+ },
+ ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(my_var_a), nullptr);
- EXPECT_TRUE(TypeOf(my_var_a)->Is<sem::F32>());
- EXPECT_EQ(StmtOf(my_var_a), decl);
- EXPECT_TRUE(CheckVarUsers(var, {my_var_a}));
- ASSERT_NE(VarOf(my_var_a), nullptr);
- EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
+ ASSERT_NE(TypeOf(my_var_a), nullptr);
+ EXPECT_TRUE(TypeOf(my_var_a)->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(my_var_a), decl);
+ EXPECT_TRUE(CheckVarUsers(var, {my_var_a}));
+ ASSERT_NE(VarOf(my_var_a), nullptr);
+ EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
}
TEST_F(ResolverTest, IndexAccessor_Dynamic_Ref_F32) {
- // var a : array<bool, 10> = 0;
- // var idx : f32 = f32();
- // var f : f32 = a[idx];
- auto* a = Var("a", ty.array<bool, 10>(), array<bool, 10>());
- auto* idx = Var("idx", ty.f32(), Construct(ty.f32()));
- auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(a),
- Decl(idx),
- Decl(f),
- },
- ast::AttributeList{});
+ // var a : array<bool, 10> = 0;
+ // var idx : f32 = f32();
+ // var f : f32 = a[idx];
+ auto* a = Var("a", ty.array<bool, 10>(), array<bool, 10>());
+ auto* idx = Var("idx", ty.f32(), Construct(ty.f32()));
+ auto* f = Var("f", ty.f32(), IndexAccessor("a", Expr(Source{{12, 34}}, idx)));
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(a),
+ Decl(idx),
+ Decl(f),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: index must be of type 'i32' or 'u32', found: 'f32'");
}
TEST_F(ResolverTest, Expr_Identifier_FunctionVariable) {
- auto* my_var_a = Expr("my_var");
- auto* my_var_b = Expr("my_var");
- auto* assign = Assign(my_var_a, my_var_b);
+ auto* my_var_a = Expr("my_var");
+ auto* my_var_b = Expr("my_var");
+ auto* assign = Assign(my_var_a, my_var_b);
- auto* var = Var("my_var", ty.f32());
+ auto* var = Var("my_var", ty.f32());
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(var),
- assign,
- },
- ast::AttributeList{});
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(var),
+ assign,
+ },
+ ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(my_var_a), nullptr);
- ASSERT_TRUE(TypeOf(my_var_a)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(my_var_a)->UnwrapRef()->Is<sem::F32>());
- EXPECT_EQ(StmtOf(my_var_a), assign);
- ASSERT_NE(TypeOf(my_var_b), nullptr);
- ASSERT_TRUE(TypeOf(my_var_b)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(my_var_b)->UnwrapRef()->Is<sem::F32>());
- EXPECT_EQ(StmtOf(my_var_b), assign);
- EXPECT_TRUE(CheckVarUsers(var, {my_var_a, my_var_b}));
- ASSERT_NE(VarOf(my_var_a), nullptr);
- EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
- ASSERT_NE(VarOf(my_var_b), nullptr);
- EXPECT_EQ(VarOf(my_var_b)->Declaration(), var);
+ ASSERT_NE(TypeOf(my_var_a), nullptr);
+ ASSERT_TRUE(TypeOf(my_var_a)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(my_var_a)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(my_var_a), assign);
+ ASSERT_NE(TypeOf(my_var_b), nullptr);
+ ASSERT_TRUE(TypeOf(my_var_b)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(my_var_b)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(my_var_b), assign);
+ EXPECT_TRUE(CheckVarUsers(var, {my_var_a, my_var_b}));
+ ASSERT_NE(VarOf(my_var_a), nullptr);
+ EXPECT_EQ(VarOf(my_var_a)->Declaration(), var);
+ ASSERT_NE(VarOf(my_var_b), nullptr);
+ EXPECT_EQ(VarOf(my_var_b)->Declaration(), var);
}
TEST_F(ResolverTest, Expr_Identifier_Function_Ptr) {
- auto* v = Expr("v");
- auto* p = Expr("p");
- auto* v_decl = Decl(Var("v", ty.f32()));
- auto* p_decl = Decl(
- Let("p", ty.pointer<f32>(ast::StorageClass::kFunction), AddressOf(v)));
- auto* assign = Assign(Deref(p), 1.23f);
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- v_decl,
- p_decl,
- assign,
- },
- ast::AttributeList{});
+ auto* v = Expr("v");
+ auto* p = Expr("p");
+ auto* v_decl = Decl(Var("v", ty.f32()));
+ auto* p_decl = Decl(Let("p", ty.pointer<f32>(ast::StorageClass::kFunction), AddressOf(v)));
+ auto* assign = Assign(Deref(p), 1.23f);
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ v_decl,
+ p_decl,
+ assign,
+ },
+ ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(v), nullptr);
- ASSERT_TRUE(TypeOf(v)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(v)->UnwrapRef()->Is<sem::F32>());
- EXPECT_EQ(StmtOf(v), p_decl);
- ASSERT_NE(TypeOf(p), nullptr);
- ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
- EXPECT_TRUE(TypeOf(p)->UnwrapPtr()->Is<sem::F32>());
- EXPECT_EQ(StmtOf(p), assign);
+ ASSERT_NE(TypeOf(v), nullptr);
+ ASSERT_TRUE(TypeOf(v)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(v)->UnwrapRef()->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(v), p_decl);
+ ASSERT_NE(TypeOf(p), nullptr);
+ ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
+ EXPECT_TRUE(TypeOf(p)->UnwrapPtr()->Is<sem::F32>());
+ EXPECT_EQ(StmtOf(p), assign);
}
TEST_F(ResolverTest, Expr_Call_Function) {
- Func("my_func", ast::VariableList{}, ty.f32(), {Return(0.0f)},
- ast::AttributeList{});
+ Func("my_func", ast::VariableList{}, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
- auto* call = Call("my_func");
- WrapInFunction(call);
+ auto* call = Call("my_func");
+ WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<sem::F32>());
}
TEST_F(ResolverTest, Expr_Identifier_Unknown) {
- auto* a = Expr("a");
- WrapInFunction(a);
+ auto* a = Expr("a");
+ WrapInFunction(a);
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
}
TEST_F(ResolverTest, Function_Parameters) {
- auto* param_a = Param("a", ty.f32());
- auto* param_b = Param("b", ty.i32());
- auto* param_c = Param("c", ty.u32());
+ auto* param_a = Param("a", ty.f32());
+ auto* param_b = Param("b", ty.i32());
+ auto* param_c = Param("c", ty.u32());
- auto* func = Func("my_func",
- ast::VariableList{
- param_a,
- param_b,
- param_c,
- },
- ty.void_(), {});
+ auto* func = Func("my_func",
+ ast::VariableList{
+ param_a,
+ param_b,
+ param_c,
+ },
+ ty.void_(), {});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->Parameters().size(), 3u);
- EXPECT_TRUE(func_sem->Parameters()[0]->Type()->Is<sem::F32>());
- EXPECT_TRUE(func_sem->Parameters()[1]->Type()->Is<sem::I32>());
- EXPECT_TRUE(func_sem->Parameters()[2]->Type()->Is<sem::U32>());
- EXPECT_EQ(func_sem->Parameters()[0]->Declaration(), param_a);
- EXPECT_EQ(func_sem->Parameters()[1]->Declaration(), param_b);
- EXPECT_EQ(func_sem->Parameters()[2]->Declaration(), param_c);
- EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
+ EXPECT_EQ(func_sem->Parameters().size(), 3u);
+ EXPECT_TRUE(func_sem->Parameters()[0]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(func_sem->Parameters()[1]->Type()->Is<sem::I32>());
+ EXPECT_TRUE(func_sem->Parameters()[2]->Type()->Is<sem::U32>());
+ EXPECT_EQ(func_sem->Parameters()[0]->Declaration(), param_a);
+ EXPECT_EQ(func_sem->Parameters()[1]->Declaration(), param_b);
+ EXPECT_EQ(func_sem->Parameters()[2]->Declaration(), param_c);
+ EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
}
TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
- auto* s = Structure("S", {Member("m", ty.u32())});
+ auto* s = Structure("S", {Member("m", ty.u32())});
- auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
+ auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
- auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Assign("wg_var", "wg_var"),
- Assign("sb_var", "sb_var"),
- Assign("priv_var", "priv_var"),
- });
+ auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Assign("wg_var", "wg_var"),
+ Assign("sb_var", "sb_var"),
+ Assign("priv_var", "priv_var"),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->Parameters().size(), 0u);
- EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
+ EXPECT_EQ(func_sem->Parameters().size(), 0u);
+ EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
- const auto& vars = func_sem->TransitivelyReferencedGlobals();
- ASSERT_EQ(vars.size(), 3u);
- EXPECT_EQ(vars[0]->Declaration(), wg_var);
- EXPECT_EQ(vars[1]->Declaration(), sb_var);
- EXPECT_EQ(vars[2]->Declaration(), priv_var);
+ const auto& vars = func_sem->TransitivelyReferencedGlobals();
+ ASSERT_EQ(vars.size(), 3u);
+ EXPECT_EQ(vars[0]->Declaration(), wg_var);
+ EXPECT_EQ(vars[1]->Declaration(), sb_var);
+ EXPECT_EQ(vars[2]->Declaration(), priv_var);
}
TEST_F(ResolverTest, Function_RegisterInputOutputVariables_SubFunction) {
- auto* s = Structure("S", {Member("m", ty.u32())});
+ auto* s = Structure("S", {Member("m", ty.u32())});
- auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
+ auto* sb_var = Global("sb_var", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ auto* wg_var = Global("wg_var", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* priv_var = Global("priv_var", ty.f32(), ast::StorageClass::kPrivate);
- Func("my_func", ast::VariableList{}, ty.f32(),
- {Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"),
- Assign("priv_var", "priv_var"), Return(0.0f)},
- ast::AttributeList{});
+ Func("my_func", ast::VariableList{}, ty.f32(),
+ {Assign("wg_var", "wg_var"), Assign("sb_var", "sb_var"), Assign("priv_var", "priv_var"),
+ Return(0.0f)},
+ ast::AttributeList{});
- auto* func2 = Func("func", ast::VariableList{}, ty.void_(),
- {
- WrapInStatement(Call("my_func")),
- },
- ast::AttributeList{});
+ auto* func2 = Func("func", ast::VariableList{}, ty.void_(),
+ {
+ WrapInStatement(Call("my_func")),
+ },
+ ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func2_sem = Sem().Get(func2);
- ASSERT_NE(func2_sem, nullptr);
- EXPECT_EQ(func2_sem->Parameters().size(), 0u);
+ auto* func2_sem = Sem().Get(func2);
+ ASSERT_NE(func2_sem, nullptr);
+ EXPECT_EQ(func2_sem->Parameters().size(), 0u);
- const auto& vars = func2_sem->TransitivelyReferencedGlobals();
- ASSERT_EQ(vars.size(), 3u);
- EXPECT_EQ(vars[0]->Declaration(), wg_var);
- EXPECT_EQ(vars[1]->Declaration(), sb_var);
- EXPECT_EQ(vars[2]->Declaration(), priv_var);
+ const auto& vars = func2_sem->TransitivelyReferencedGlobals();
+ ASSERT_EQ(vars.size(), 3u);
+ EXPECT_EQ(vars[0]->Declaration(), wg_var);
+ EXPECT_EQ(vars[1]->Declaration(), sb_var);
+ EXPECT_EQ(vars[2]->Declaration(), priv_var);
}
TEST_F(ResolverTest, Function_NotRegisterFunctionVariable) {
- auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(Var("var", ty.f32())),
- Assign("var", 1.f),
- });
+ auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(Var("var", ty.f32())),
+ Assign("var", 1.f),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->TransitivelyReferencedGlobals().size(), 0u);
- EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
+ EXPECT_EQ(func_sem->TransitivelyReferencedGlobals().size(), 0u);
+ EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
}
TEST_F(ResolverTest, Function_NotRegisterFunctionConstant) {
- auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Decl(Let("var", ty.f32(), Construct(ty.f32()))),
- });
+ auto* func = Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Decl(Let("var", ty.f32(), Construct(ty.f32()))),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->TransitivelyReferencedGlobals().size(), 0u);
- EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
+ EXPECT_EQ(func_sem->TransitivelyReferencedGlobals().size(), 0u);
+ EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
}
TEST_F(ResolverTest, Function_NotRegisterFunctionParams) {
- auto* func = Func("my_func", {Let("var", ty.f32(), Construct(ty.f32()))},
- ty.void_(), {});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* func = Func("my_func", {Let("var", ty.f32(), Construct(ty.f32()))}, ty.void_(), {});
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->TransitivelyReferencedGlobals().size(), 0u);
- EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
+ EXPECT_EQ(func_sem->TransitivelyReferencedGlobals().size(), 0u);
+ EXPECT_TRUE(func_sem->ReturnType()->Is<sem::Void>());
}
TEST_F(ResolverTest, Function_CallSites) {
- auto* foo = Func("foo", ast::VariableList{}, ty.void_(), {});
+ auto* foo = Func("foo", ast::VariableList{}, ty.void_(), {});
- auto* call_1 = Call("foo");
- auto* call_2 = Call("foo");
- auto* bar = Func("bar", ast::VariableList{}, ty.void_(),
- {
- CallStmt(call_1),
- CallStmt(call_2),
- });
+ auto* call_1 = Call("foo");
+ auto* call_2 = Call("foo");
+ auto* bar = Func("bar", ast::VariableList{}, ty.void_(),
+ {
+ CallStmt(call_1),
+ CallStmt(call_2),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* foo_sem = Sem().Get(foo);
- ASSERT_NE(foo_sem, nullptr);
- ASSERT_EQ(foo_sem->CallSites().size(), 2u);
- EXPECT_EQ(foo_sem->CallSites()[0]->Declaration(), call_1);
- EXPECT_EQ(foo_sem->CallSites()[1]->Declaration(), call_2);
+ auto* foo_sem = Sem().Get(foo);
+ ASSERT_NE(foo_sem, nullptr);
+ ASSERT_EQ(foo_sem->CallSites().size(), 2u);
+ EXPECT_EQ(foo_sem->CallSites()[0]->Declaration(), call_1);
+ EXPECT_EQ(foo_sem->CallSites()[1]->Declaration(), call_2);
- auto* bar_sem = Sem().Get(bar);
- ASSERT_NE(bar_sem, nullptr);
- EXPECT_EQ(bar_sem->CallSites().size(), 0u);
+ auto* bar_sem = Sem().Get(bar);
+ ASSERT_NE(bar_sem, nullptr);
+ EXPECT_EQ(bar_sem->CallSites().size(), 0u);
}
TEST_F(ResolverTest, Function_WorkgroupSize_NotSet) {
- // @stage(compute) @workgroup_size(1)
- // fn main() {}
- auto* func = Func("main", ast::VariableList{}, ty.void_(), {}, {});
+ // @stage(compute) @workgroup_size(1)
+ // fn main() {}
+ auto* func = Func("main", ast::VariableList{}, ty.void_(), {}, {});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 1u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 1u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 1u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 1u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 1u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 1u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
}
TEST_F(ResolverTest, Function_WorkgroupSize_Literals) {
- // @stage(compute) @workgroup_size(8, 2, 3)
- // fn main() {}
- auto* func =
- Func("main", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(8, 2, 3)});
+ // @stage(compute) @workgroup_size(8, 2, 3)
+ // fn main() {}
+ auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(8, 2, 3)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 8u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 2u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 3u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 8u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 2u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 3u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
}
TEST_F(ResolverTest, Function_WorkgroupSize_Consts) {
- // let width = 16;
- // let height = 8;
- // let depth = 2;
- // @stage(compute) @workgroup_size(width, height, depth)
- // fn main() {}
- GlobalConst("width", ty.i32(), Expr(16));
- GlobalConst("height", ty.i32(), Expr(8));
- GlobalConst("depth", ty.i32(), Expr(2));
- auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize("width", "height", "depth")});
+ // let width = 16;
+ // let height = 8;
+ // let depth = 2;
+ // @stage(compute) @workgroup_size(width, height, depth)
+ // fn main() {}
+ GlobalConst("width", ty.i32(), Expr(16));
+ GlobalConst("height", ty.i32(), Expr(8));
+ GlobalConst("depth", ty.i32(), Expr(2));
+ auto* func =
+ Func("main", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height", "depth")});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 16u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 8u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 2u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 16u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 8u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 2u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
}
TEST_F(ResolverTest, Function_WorkgroupSize_Consts_NestedInitializer) {
- // let width = i32(i32(i32(8)));
- // let height = i32(i32(i32(4)));
- // @stage(compute) @workgroup_size(width, height)
- // fn main() {}
- GlobalConst("width", ty.i32(),
- Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 8))));
- GlobalConst("height", ty.i32(),
- Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 4))));
- auto* func = Func(
- "main", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height")});
+ // let width = i32(i32(i32(8)));
+ // let height = i32(i32(i32(4)));
+ // @stage(compute) @workgroup_size(width, height)
+ // fn main() {}
+ GlobalConst("width", ty.i32(),
+ Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 8))));
+ GlobalConst("height", ty.i32(),
+ Construct(ty.i32(), Construct(ty.i32(), Construct(ty.i32(), 4))));
+ auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height")});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 8u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 4u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 1u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 8u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 4u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 1u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
}
TEST_F(ResolverTest, Function_WorkgroupSize_OverridableConsts) {
- // @id(0) override width = 16;
- // @id(1) override height = 8;
- // @id(2) override depth = 2;
- // @stage(compute) @workgroup_size(width, height, depth)
- // fn main() {}
- auto* width = Override("width", ty.i32(), Expr(16), {Id(0)});
- auto* height = Override("height", ty.i32(), Expr(8), {Id(1)});
- auto* depth = Override("depth", ty.i32(), Expr(2), {Id(2)});
- auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize("width", "height", "depth")});
+ // @id(0) override width = 16;
+ // @id(1) override height = 8;
+ // @id(2) override depth = 2;
+ // @stage(compute) @workgroup_size(width, height, depth)
+ // fn main() {}
+ auto* width = Override("width", ty.i32(), Expr(16), {Id(0)});
+ auto* height = Override("height", ty.i32(), Expr(8), {Id(1)});
+ auto* depth = Override("depth", ty.i32(), Expr(2), {Id(2)});
+ auto* func =
+ Func("main", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height", "depth")});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 16u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 8u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 2u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, width);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, height);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, depth);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 16u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 8u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 2u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, width);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, height);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, depth);
}
TEST_F(ResolverTest, Function_WorkgroupSize_OverridableConsts_NoInit) {
- // @id(0) override width : i32;
- // @id(1) override height : i32;
- // @id(2) override depth : i32;
- // @stage(compute) @workgroup_size(width, height, depth)
- // fn main() {}
- auto* width = Override("width", ty.i32(), nullptr, {Id(0)});
- auto* height = Override("height", ty.i32(), nullptr, {Id(1)});
- auto* depth = Override("depth", ty.i32(), nullptr, {Id(2)});
- auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize("width", "height", "depth")});
+ // @id(0) override width : i32;
+ // @id(1) override height : i32;
+ // @id(2) override depth : i32;
+ // @stage(compute) @workgroup_size(width, height, depth)
+ // fn main() {}
+ auto* width = Override("width", ty.i32(), nullptr, {Id(0)});
+ auto* height = Override("height", ty.i32(), nullptr, {Id(1)});
+ auto* depth = Override("depth", ty.i32(), nullptr, {Id(2)});
+ auto* func =
+ Func("main", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize("width", "height", "depth")});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 0u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 0u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 0u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, width);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, height);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, depth);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 0u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 0u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 0u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, width);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, height);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, depth);
}
TEST_F(ResolverTest, Function_WorkgroupSize_Mixed) {
- // @id(1) override height = 2;
- // let depth = 3;
- // @stage(compute) @workgroup_size(8, height, depth)
- // fn main() {}
- auto* height = Override("height", ty.i32(), Expr(2), {Id(0)});
- GlobalConst("depth", ty.i32(), Expr(3));
- auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(8, "height", "depth")});
+ // @id(1) override height = 2;
+ // let depth = 3;
+ // @stage(compute) @workgroup_size(8, height, depth)
+ // fn main() {}
+ auto* height = Override("height", ty.i32(), Expr(2), {Id(0)});
+ GlobalConst("depth", ty.i32(), Expr(3));
+ auto* func = Func("main", ast::VariableList{}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(8, "height", "depth")});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* func_sem = Sem().Get(func);
- ASSERT_NE(func_sem, nullptr);
+ auto* func_sem = Sem().Get(func);
+ ASSERT_NE(func_sem, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 8u);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 2u);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 3u);
- EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
- EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, height);
- EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].value, 8u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].value, 2u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].value, 3u);
+ EXPECT_EQ(func_sem->WorkgroupSize()[0].overridable_const, nullptr);
+ EXPECT_EQ(func_sem->WorkgroupSize()[1].overridable_const, height);
+ EXPECT_EQ(func_sem->WorkgroupSize()[2].overridable_const, nullptr);
}
TEST_F(ResolverTest, Expr_MemberAccessor_Struct) {
- auto* st = Structure("S", {Member("first_member", ty.i32()),
- Member("second_member", ty.f32())});
- Global("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
+ auto* st =
+ Structure("S", {Member("first_member", ty.i32()), Member("second_member", ty.f32())});
+ Global("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
- auto* mem = MemberAccessor("my_struct", "second_member");
- WrapInFunction(mem);
+ auto* mem = MemberAccessor("my_struct", "second_member");
+ WrapInFunction(mem);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(mem), nullptr);
- ASSERT_TRUE(TypeOf(mem)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(mem), nullptr);
+ ASSERT_TRUE(TypeOf(mem)->Is<sem::Reference>());
- auto* ref = TypeOf(mem)->As<sem::Reference>();
- EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
- auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
- ASSERT_NE(sma, nullptr);
- EXPECT_TRUE(sma->Member()->Type()->Is<sem::F32>());
- EXPECT_EQ(sma->Member()->Index(), 1u);
- EXPECT_EQ(sma->Member()->Declaration()->symbol,
- Symbols().Get("second_member"));
+ auto* ref = TypeOf(mem)->As<sem::Reference>();
+ EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+ auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
+ ASSERT_NE(sma, nullptr);
+ EXPECT_TRUE(sma->Member()->Type()->Is<sem::F32>());
+ EXPECT_EQ(sma->Member()->Index(), 1u);
+ EXPECT_EQ(sma->Member()->Declaration()->symbol, Symbols().Get("second_member"));
}
TEST_F(ResolverTest, Expr_MemberAccessor_Struct_Alias) {
- auto* st = Structure("S", {Member("first_member", ty.i32()),
- Member("second_member", ty.f32())});
- auto* alias = Alias("alias", ty.Of(st));
- Global("my_struct", ty.Of(alias), ast::StorageClass::kPrivate);
+ auto* st =
+ Structure("S", {Member("first_member", ty.i32()), Member("second_member", ty.f32())});
+ auto* alias = Alias("alias", ty.Of(st));
+ Global("my_struct", ty.Of(alias), ast::StorageClass::kPrivate);
- auto* mem = MemberAccessor("my_struct", "second_member");
- WrapInFunction(mem);
+ auto* mem = MemberAccessor("my_struct", "second_member");
+ WrapInFunction(mem);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(mem), nullptr);
- ASSERT_TRUE(TypeOf(mem)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(mem), nullptr);
+ ASSERT_TRUE(TypeOf(mem)->Is<sem::Reference>());
- auto* ref = TypeOf(mem)->As<sem::Reference>();
- EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
- auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
- ASSERT_NE(sma, nullptr);
- EXPECT_TRUE(sma->Member()->Type()->Is<sem::F32>());
- EXPECT_EQ(sma->Member()->Index(), 1u);
+ auto* ref = TypeOf(mem)->As<sem::Reference>();
+ EXPECT_TRUE(ref->StoreType()->Is<sem::F32>());
+ auto* sma = Sem().Get(mem)->As<sem::StructMemberAccess>();
+ ASSERT_NE(sma, nullptr);
+ EXPECT_TRUE(sma->Member()->Type()->Is<sem::F32>());
+ EXPECT_EQ(sma->Member()->Index(), 1u);
}
TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle) {
- Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* mem = MemberAccessor("my_vec", "xzyw");
- WrapInFunction(mem);
+ auto* mem = MemberAccessor("my_vec", "xzyw");
+ WrapInFunction(mem);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(mem), nullptr);
- ASSERT_TRUE(TypeOf(mem)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(mem)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(mem)->As<sem::Vector>()->Width(), 4u);
- ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
- EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(),
- ElementsAre(0, 2, 1, 3));
+ ASSERT_NE(TypeOf(mem), nullptr);
+ ASSERT_TRUE(TypeOf(mem)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(mem)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(mem)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
+ EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(0, 2, 1, 3));
}
TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_SingleElement) {
- Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
- auto* mem = MemberAccessor("my_vec", "b");
- WrapInFunction(mem);
+ auto* mem = MemberAccessor("my_vec", "b");
+ WrapInFunction(mem);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(mem), nullptr);
- ASSERT_TRUE(TypeOf(mem)->Is<sem::Reference>());
+ ASSERT_NE(TypeOf(mem), nullptr);
+ ASSERT_TRUE(TypeOf(mem)->Is<sem::Reference>());
- auto* ref = TypeOf(mem)->As<sem::Reference>();
- ASSERT_TRUE(ref->StoreType()->Is<sem::F32>());
- ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
- EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(2));
+ auto* ref = TypeOf(mem)->As<sem::Reference>();
+ ASSERT_TRUE(ref->StoreType()->Is<sem::F32>());
+ ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
+ EXPECT_THAT(Sem().Get(mem)->As<sem::Swizzle>()->Indices(), ElementsAre(2));
}
TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {
- // struct b {
- // vec4<f32> foo
- // }
- // struct A {
- // array<b, 3> mem
- // }
- // var c : A
- // c.mem[0].foo.yx
- // -> vec2<f32>
- //
- // fn f() {
- // c.mem[0].foo
- // }
- //
+ // struct b {
+ // vec4<f32> foo
+ // }
+ // struct A {
+ // array<b, 3> mem
+ // }
+ // var c : A
+ // c.mem[0].foo.yx
+ // -> vec2<f32>
+ //
+ // fn f() {
+ // c.mem[0].foo
+ // }
+ //
- auto* stB = Structure("B", {Member("foo", ty.vec4<f32>())});
- auto* stA = Structure("A", {Member("mem", ty.array(ty.Of(stB), 3))});
- Global("c", ty.Of(stA), ast::StorageClass::kPrivate);
+ auto* stB = Structure("B", {Member("foo", ty.vec4<f32>())});
+ auto* stA = Structure("A", {Member("mem", ty.array(ty.Of(stB), 3))});
+ Global("c", ty.Of(stA), ast::StorageClass::kPrivate);
- auto* mem = MemberAccessor(
- MemberAccessor(IndexAccessor(MemberAccessor("c", "mem"), 0), "foo"),
- "yx");
- WrapInFunction(mem);
+ auto* mem =
+ MemberAccessor(MemberAccessor(IndexAccessor(MemberAccessor("c", "mem"), 0), "foo"), "yx");
+ WrapInFunction(mem);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(mem), nullptr);
- ASSERT_TRUE(TypeOf(mem)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(mem)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(mem)->As<sem::Vector>()->Width(), 2u);
- ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
+ ASSERT_NE(TypeOf(mem), nullptr);
+ ASSERT_TRUE(TypeOf(mem)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(mem)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(mem)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_TRUE(Sem().Get(mem)->Is<sem::Swizzle>());
}
TEST_F(ResolverTest, Expr_MemberAccessor_InBinaryOp) {
- auto* st = Structure("S", {Member("first_member", ty.f32()),
- Member("second_member", ty.f32())});
- Global("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
+ auto* st =
+ Structure("S", {Member("first_member", ty.f32()), Member("second_member", ty.f32())});
+ Global("my_struct", ty.Of(st), ast::StorageClass::kPrivate);
- auto* expr = Add(MemberAccessor("my_struct", "first_member"),
- MemberAccessor("my_struct", "second_member"));
- WrapInFunction(expr);
+ auto* expr = Add(MemberAccessor("my_struct", "first_member"),
+ MemberAccessor("my_struct", "second_member"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<sem::F32>());
}
namespace ExprBinaryTest {
template <typename T, int ID>
struct Aliased {
- using type = alias<T, ID>;
+ using type = alias<T, ID>;
};
template <int N, typename T, int ID>
struct Aliased<vec<N, T>, ID> {
- using type = vec<N, alias<T, ID>>;
+ using type = vec<N, alias<T, ID>>;
};
template <int N, int M, typename T, int ID>
struct Aliased<mat<N, M, T>, ID> {
- using type = mat<N, M, alias<T, ID>>;
+ using type = mat<N, M, alias<T, ID>>;
};
struct Params {
- ast::BinaryOp op;
- builder::ast_type_func_ptr create_lhs_type;
- builder::ast_type_func_ptr create_rhs_type;
- builder::ast_type_func_ptr create_lhs_alias_type;
- builder::ast_type_func_ptr create_rhs_alias_type;
- builder::sem_type_func_ptr create_result_type;
+ ast::BinaryOp op;
+ builder::ast_type_func_ptr create_lhs_type;
+ builder::ast_type_func_ptr create_rhs_type;
+ builder::ast_type_func_ptr create_lhs_alias_type;
+ builder::ast_type_func_ptr create_rhs_alias_type;
+ builder::sem_type_func_ptr create_result_type;
};
template <typename LHS, typename RHS, typename RES>
constexpr Params ParamsFor(ast::BinaryOp op) {
- return Params{op,
- DataType<LHS>::AST,
- DataType<RHS>::AST,
- DataType<typename Aliased<LHS, 0>::type>::AST,
- DataType<typename Aliased<RHS, 1>::type>::AST,
- DataType<RES>::Sem};
+ return Params{op,
+ DataType<LHS>::AST,
+ DataType<RHS>::AST,
+ DataType<typename Aliased<LHS, 0>::type>::AST,
+ DataType<typename Aliased<RHS, 1>::type>::AST,
+ DataType<RES>::Sem};
}
static constexpr ast::BinaryOp all_ops[] = {
@@ -1490,174 +1473,158 @@
using Expr_Binary_Test_Valid = ResolverTestWithParam<Params>;
TEST_P(Expr_Binary_Test_Valid, All) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* lhs_type = params.create_lhs_type(*this);
- auto* rhs_type = params.create_rhs_type(*this);
- auto* result_type = params.create_result_type(*this);
+ auto* lhs_type = params.create_lhs_type(*this);
+ auto* rhs_type = params.create_rhs_type(*this);
+ auto* result_type = params.create_result_type(*this);
- std::stringstream ss;
- ss << FriendlyName(lhs_type) << " " << params.op << " "
- << FriendlyName(rhs_type);
- SCOPED_TRACE(ss.str());
+ std::stringstream ss;
+ ss << FriendlyName(lhs_type) << " " << params.op << " " << FriendlyName(rhs_type);
+ SCOPED_TRACE(ss.str());
- Global("lhs", lhs_type, ast::StorageClass::kPrivate);
- Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+ Global("lhs", lhs_type, ast::StorageClass::kPrivate);
+ Global("rhs", rhs_type, ast::StorageClass::kPrivate);
- auto* expr =
- create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
- WrapInFunction(expr);
+ auto* expr = create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr) == result_type);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr) == result_type);
}
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
- Expr_Binary_Test_Valid,
- testing::ValuesIn(all_valid_cases));
+INSTANTIATE_TEST_SUITE_P(ResolverTest, Expr_Binary_Test_Valid, testing::ValuesIn(all_valid_cases));
enum class BinaryExprSide { Left, Right, Both };
-using Expr_Binary_Test_WithAlias_Valid =
- ResolverTestWithParam<std::tuple<Params, BinaryExprSide>>;
+using Expr_Binary_Test_WithAlias_Valid = ResolverTestWithParam<std::tuple<Params, BinaryExprSide>>;
TEST_P(Expr_Binary_Test_WithAlias_Valid, All) {
- const Params& params = std::get<0>(GetParam());
- BinaryExprSide side = std::get<1>(GetParam());
+ const Params& params = std::get<0>(GetParam());
+ BinaryExprSide side = std::get<1>(GetParam());
- auto* create_lhs_type =
- (side == BinaryExprSide::Left || side == BinaryExprSide::Both)
- ? params.create_lhs_alias_type
- : params.create_lhs_type;
- auto* create_rhs_type =
- (side == BinaryExprSide::Right || side == BinaryExprSide::Both)
- ? params.create_rhs_alias_type
- : params.create_rhs_type;
+ auto* create_lhs_type = (side == BinaryExprSide::Left || side == BinaryExprSide::Both)
+ ? params.create_lhs_alias_type
+ : params.create_lhs_type;
+ auto* create_rhs_type = (side == BinaryExprSide::Right || side == BinaryExprSide::Both)
+ ? params.create_rhs_alias_type
+ : params.create_rhs_type;
- auto* lhs_type = create_lhs_type(*this);
- auto* rhs_type = create_rhs_type(*this);
+ auto* lhs_type = create_lhs_type(*this);
+ auto* rhs_type = create_rhs_type(*this);
- std::stringstream ss;
- ss << FriendlyName(lhs_type) << " " << params.op << " "
- << FriendlyName(rhs_type);
+ std::stringstream ss;
+ ss << FriendlyName(lhs_type) << " " << params.op << " " << FriendlyName(rhs_type);
- ss << ", After aliasing: " << FriendlyName(lhs_type) << " " << params.op
- << " " << FriendlyName(rhs_type);
- SCOPED_TRACE(ss.str());
+ ss << ", After aliasing: " << FriendlyName(lhs_type) << " " << params.op << " "
+ << FriendlyName(rhs_type);
+ SCOPED_TRACE(ss.str());
- Global("lhs", lhs_type, ast::StorageClass::kPrivate);
- Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+ Global("lhs", lhs_type, ast::StorageClass::kPrivate);
+ Global("rhs", rhs_type, ast::StorageClass::kPrivate);
- auto* expr =
- create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
- WrapInFunction(expr);
+ auto* expr = create<ast::BinaryExpression>(params.op, Expr("lhs"), Expr("rhs"));
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- // TODO(amaiorano): Bring this back once we have a way to get the canonical
- // type
- // auto* *result_type = params.create_result_type(*this);
- // ASSERT_TRUE(TypeOf(expr) == result_type);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(expr), nullptr);
+ // TODO(amaiorano): Bring this back once we have a way to get the canonical
+ // type
+ // auto* *result_type = params.create_result_type(*this);
+ // ASSERT_TRUE(TypeOf(expr) == result_type);
}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Expr_Binary_Test_WithAlias_Valid,
- testing::Combine(testing::ValuesIn(all_valid_cases),
- testing::Values(BinaryExprSide::Left,
- BinaryExprSide::Right,
- BinaryExprSide::Both)));
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ Expr_Binary_Test_WithAlias_Valid,
+ testing::Combine(testing::ValuesIn(all_valid_cases),
+ testing::Values(BinaryExprSide::Left,
+ BinaryExprSide::Right,
+ BinaryExprSide::Both)));
// This test works by taking the cartesian product of all possible
// (type * type * op), and processing only the triplets that are not found in
// the `all_valid_cases` table.
-using Expr_Binary_Test_Invalid =
- ResolverTestWithParam<std::tuple<builder::ast_type_func_ptr,
- builder::ast_type_func_ptr,
- ast::BinaryOp>>;
+using Expr_Binary_Test_Invalid = ResolverTestWithParam<
+ std::tuple<builder::ast_type_func_ptr, builder::ast_type_func_ptr, ast::BinaryOp>>;
TEST_P(Expr_Binary_Test_Invalid, All) {
- const builder::ast_type_func_ptr& lhs_create_type_func =
- std::get<0>(GetParam());
- const builder::ast_type_func_ptr& rhs_create_type_func =
- std::get<1>(GetParam());
- const ast::BinaryOp op = std::get<2>(GetParam());
+ const builder::ast_type_func_ptr& lhs_create_type_func = std::get<0>(GetParam());
+ const builder::ast_type_func_ptr& rhs_create_type_func = std::get<1>(GetParam());
+ const ast::BinaryOp op = std::get<2>(GetParam());
- // Skip if valid case
- // TODO(amaiorano): replace linear lookup with O(1) if too slow
- for (auto& c : all_valid_cases) {
- if (c.create_lhs_type == lhs_create_type_func &&
- c.create_rhs_type == rhs_create_type_func && c.op == op) {
- return;
+ // Skip if valid case
+ // TODO(amaiorano): replace linear lookup with O(1) if too slow
+ for (auto& c : all_valid_cases) {
+ if (c.create_lhs_type == lhs_create_type_func &&
+ c.create_rhs_type == rhs_create_type_func && c.op == op) {
+ return;
+ }
}
- }
- auto* lhs_type = lhs_create_type_func(*this);
- auto* rhs_type = rhs_create_type_func(*this);
+ auto* lhs_type = lhs_create_type_func(*this);
+ auto* rhs_type = rhs_create_type_func(*this);
- std::stringstream ss;
- ss << FriendlyName(lhs_type) << " " << op << " " << FriendlyName(rhs_type);
- SCOPED_TRACE(ss.str());
+ std::stringstream ss;
+ ss << FriendlyName(lhs_type) << " " << op << " " << FriendlyName(rhs_type);
+ SCOPED_TRACE(ss.str());
- Global("lhs", lhs_type, ast::StorageClass::kPrivate);
- Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+ Global("lhs", lhs_type, ast::StorageClass::kPrivate);
+ Global("rhs", rhs_type, ast::StorageClass::kPrivate);
- auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, op, Expr("lhs"),
- Expr("rhs"));
- WrapInFunction(expr);
+ auto* expr = create<ast::BinaryExpression>(Source{{12, 34}}, op, Expr("lhs"), Expr("rhs"));
+ WrapInFunction(expr);
- ASSERT_FALSE(r()->Resolve());
- ASSERT_EQ(r()->error(),
- "12:34 error: Binary expression operand types are invalid for "
- "this operation: " +
- FriendlyName(lhs_type) + " " + ast::FriendlyName(expr->op) +
- " " + FriendlyName(rhs_type));
-}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Expr_Binary_Test_Invalid,
- testing::Combine(testing::ValuesIn(all_create_type_funcs),
- testing::ValuesIn(all_create_type_funcs),
- testing::ValuesIn(all_ops)));
-
-using Expr_Binary_Test_Invalid_VectorMatrixMultiply =
- ResolverTestWithParam<std::tuple<bool, uint32_t, uint32_t, uint32_t>>;
-TEST_P(Expr_Binary_Test_Invalid_VectorMatrixMultiply, All) {
- bool vec_by_mat = std::get<0>(GetParam());
- uint32_t vec_size = std::get<1>(GetParam());
- uint32_t mat_rows = std::get<2>(GetParam());
- uint32_t mat_cols = std::get<3>(GetParam());
-
- const ast::Type* lhs_type = nullptr;
- const ast::Type* rhs_type = nullptr;
- const sem::Type* result_type = nullptr;
- bool is_valid_expr;
-
- if (vec_by_mat) {
- lhs_type = ty.vec<f32>(vec_size);
- rhs_type = ty.mat<f32>(mat_cols, mat_rows);
- result_type = create<sem::Vector>(create<sem::F32>(), mat_cols);
- is_valid_expr = vec_size == mat_rows;
- } else {
- lhs_type = ty.mat<f32>(mat_cols, mat_rows);
- rhs_type = ty.vec<f32>(vec_size);
- result_type = create<sem::Vector>(create<sem::F32>(), mat_rows);
- is_valid_expr = vec_size == mat_cols;
- }
-
- Global("lhs", lhs_type, ast::StorageClass::kPrivate);
- Global("rhs", rhs_type, ast::StorageClass::kPrivate);
-
- auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
- WrapInFunction(expr);
-
- if (is_valid_expr) {
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(expr) == result_type);
- } else {
ASSERT_FALSE(r()->Resolve());
ASSERT_EQ(r()->error(),
"12:34 error: Binary expression operand types are invalid for "
"this operation: " +
- FriendlyName(lhs_type) + " " + ast::FriendlyName(expr->op) +
- " " + FriendlyName(rhs_type));
- }
+ FriendlyName(lhs_type) + " " + ast::FriendlyName(expr->op) + " " +
+ FriendlyName(rhs_type));
+}
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ Expr_Binary_Test_Invalid,
+ testing::Combine(testing::ValuesIn(all_create_type_funcs),
+ testing::ValuesIn(all_create_type_funcs),
+ testing::ValuesIn(all_ops)));
+
+using Expr_Binary_Test_Invalid_VectorMatrixMultiply =
+ ResolverTestWithParam<std::tuple<bool, uint32_t, uint32_t, uint32_t>>;
+TEST_P(Expr_Binary_Test_Invalid_VectorMatrixMultiply, All) {
+ bool vec_by_mat = std::get<0>(GetParam());
+ uint32_t vec_size = std::get<1>(GetParam());
+ uint32_t mat_rows = std::get<2>(GetParam());
+ uint32_t mat_cols = std::get<3>(GetParam());
+
+ const ast::Type* lhs_type = nullptr;
+ const ast::Type* rhs_type = nullptr;
+ const sem::Type* result_type = nullptr;
+ bool is_valid_expr;
+
+ if (vec_by_mat) {
+ lhs_type = ty.vec<f32>(vec_size);
+ rhs_type = ty.mat<f32>(mat_cols, mat_rows);
+ result_type = create<sem::Vector>(create<sem::F32>(), mat_cols);
+ is_valid_expr = vec_size == mat_rows;
+ } else {
+ lhs_type = ty.mat<f32>(mat_cols, mat_rows);
+ rhs_type = ty.vec<f32>(vec_size);
+ result_type = create<sem::Vector>(create<sem::F32>(), mat_rows);
+ is_valid_expr = vec_size == mat_cols;
+ }
+
+ Global("lhs", lhs_type, ast::StorageClass::kPrivate);
+ Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+
+ auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
+ WrapInFunction(expr);
+
+ if (is_valid_expr) {
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(TypeOf(expr) == result_type);
+ } else {
+ ASSERT_FALSE(r()->Resolve());
+ ASSERT_EQ(r()->error(),
+ "12:34 error: Binary expression operand types are invalid for "
+ "this operation: " +
+ FriendlyName(lhs_type) + " " + ast::FriendlyName(expr->op) + " " +
+ FriendlyName(rhs_type));
+ }
}
auto all_dimension_values = testing::Values(2u, 3u, 4u);
INSTANTIATE_TEST_SUITE_P(ResolverTest,
@@ -1670,36 +1637,36 @@
using Expr_Binary_Test_Invalid_MatrixMatrixMultiply =
ResolverTestWithParam<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>>;
TEST_P(Expr_Binary_Test_Invalid_MatrixMatrixMultiply, All) {
- uint32_t lhs_mat_rows = std::get<0>(GetParam());
- uint32_t lhs_mat_cols = std::get<1>(GetParam());
- uint32_t rhs_mat_rows = std::get<2>(GetParam());
- uint32_t rhs_mat_cols = std::get<3>(GetParam());
+ uint32_t lhs_mat_rows = std::get<0>(GetParam());
+ uint32_t lhs_mat_cols = std::get<1>(GetParam());
+ uint32_t rhs_mat_rows = std::get<2>(GetParam());
+ uint32_t rhs_mat_cols = std::get<3>(GetParam());
- auto* lhs_type = ty.mat<f32>(lhs_mat_cols, lhs_mat_rows);
- auto* rhs_type = ty.mat<f32>(rhs_mat_cols, rhs_mat_rows);
+ auto* lhs_type = ty.mat<f32>(lhs_mat_cols, lhs_mat_rows);
+ auto* rhs_type = ty.mat<f32>(rhs_mat_cols, rhs_mat_rows);
- auto* f32 = create<sem::F32>();
- auto* col = create<sem::Vector>(f32, lhs_mat_rows);
- auto* result_type = create<sem::Matrix>(col, rhs_mat_cols);
+ auto* f32 = create<sem::F32>();
+ auto* col = create<sem::Vector>(f32, lhs_mat_rows);
+ auto* result_type = create<sem::Matrix>(col, rhs_mat_cols);
- Global("lhs", lhs_type, ast::StorageClass::kPrivate);
- Global("rhs", rhs_type, ast::StorageClass::kPrivate);
+ Global("lhs", lhs_type, ast::StorageClass::kPrivate);
+ Global("rhs", rhs_type, ast::StorageClass::kPrivate);
- auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
- WrapInFunction(expr);
+ auto* expr = Mul(Source{{12, 34}}, Expr("lhs"), Expr("rhs"));
+ WrapInFunction(expr);
- bool is_valid_expr = lhs_mat_cols == rhs_mat_rows;
- if (is_valid_expr) {
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(expr) == result_type);
- } else {
- ASSERT_FALSE(r()->Resolve());
- ASSERT_EQ(r()->error(),
- "12:34 error: Binary expression operand types are invalid for "
- "this operation: " +
- FriendlyName(lhs_type) + " " + ast::FriendlyName(expr->op) +
- " " + FriendlyName(rhs_type));
- }
+ bool is_valid_expr = lhs_mat_cols == rhs_mat_rows;
+ if (is_valid_expr) {
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(TypeOf(expr) == result_type);
+ } else {
+ ASSERT_FALSE(r()->Resolve());
+ ASSERT_EQ(r()->error(),
+ "12:34 error: Binary expression operand types are invalid for "
+ "this operation: " +
+ FriendlyName(lhs_type) + " " + ast::FriendlyName(expr->op) + " " +
+ FriendlyName(rhs_type));
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
Expr_Binary_Test_Invalid_MatrixMatrixMultiply,
@@ -1712,30 +1679,30 @@
using UnaryOpExpressionTest = ResolverTestWithParam<ast::UnaryOp>;
TEST_P(UnaryOpExpressionTest, Expr_UnaryOp) {
- auto op = GetParam();
+ auto op = GetParam();
- if (op == ast::UnaryOp::kNot) {
- Global("ident", ty.vec4<bool>(), ast::StorageClass::kPrivate);
- } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
- Global("ident", ty.vec4<i32>(), ast::StorageClass::kPrivate);
- } else {
- Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- }
- auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
- WrapInFunction(der);
+ if (op == ast::UnaryOp::kNot) {
+ Global("ident", ty.vec4<bool>(), ast::StorageClass::kPrivate);
+ } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
+ Global("ident", ty.vec4<i32>(), ast::StorageClass::kPrivate);
+ } else {
+ Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ }
+ auto* der = create<ast::UnaryOpExpression>(op, Expr("ident"));
+ WrapInFunction(der);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(der), nullptr);
- ASSERT_TRUE(TypeOf(der)->Is<sem::Vector>());
- if (op == ast::UnaryOp::kNot) {
- EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::Bool>());
- } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
- EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::I32>());
- } else {
- EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::F32>());
- }
- EXPECT_EQ(TypeOf(der)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(der), nullptr);
+ ASSERT_TRUE(TypeOf(der)->Is<sem::Vector>());
+ if (op == ast::UnaryOp::kNot) {
+ EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ } else if (op == ast::UnaryOp::kNegation || op == ast::UnaryOp::kComplement) {
+ EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::I32>());
+ } else {
+ EXPECT_TRUE(TypeOf(der)->As<sem::Vector>()->type()->Is<sem::F32>());
+ }
+ EXPECT_EQ(TypeOf(der)->As<sem::Vector>()->Width(), 4u);
}
INSTANTIATE_TEST_SUITE_P(ResolverTest,
UnaryOpExpressionTest,
@@ -1744,434 +1711,404 @@
ast::UnaryOp::kNot));
TEST_F(ResolverTest, StorageClass_SetsIfMissing) {
- auto* var = Var("var", ty.i32());
+ auto* var = Var("var", ty.i32());
- auto* stmt = Decl(var);
- Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+ auto* stmt = Decl(var);
+ Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kFunction);
+ EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kFunction);
}
TEST_F(ResolverTest, StorageClass_SetForSampler) {
- auto* t = ty.sampler(ast::SamplerKind::kSampler);
- auto* var = Global("var", t,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* t = ty.sampler(ast::SamplerKind::kSampler);
+ auto* var = Global("var", t,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kHandle);
+ EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kHandle);
}
TEST_F(ResolverTest, StorageClass_SetForTexture) {
- auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
- auto* var = Global("var", t,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
+ auto* var = Global("var", t,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kHandle);
+ EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kHandle);
}
TEST_F(ResolverTest, StorageClass_DoesNotSetOnConst) {
- auto* var = Let("var", ty.i32(), Construct(ty.i32()));
- auto* stmt = Decl(var);
- Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+ auto* var = Let("var", ty.i32(), Construct(ty.i32()));
+ auto* stmt = Decl(var);
+ Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kNone);
+ EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kNone);
}
TEST_F(ResolverTest, Access_SetForStorageBuffer) {
- // struct S { x : i32 };
- // var<storage> g : S;
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
- auto* var =
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S { x : i32 };
+ // var<storage> g : S;
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+ auto* var = Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(var)->Access(), ast::Access::kRead);
+ EXPECT_EQ(Sem().Get(var)->Access(), ast::Access::kRead);
}
TEST_F(ResolverTest, BindingPoint_SetForResources) {
- // @group(1) @binding(2) var s1 : sampler;
- // @group(3) @binding(4) var s2 : sampler;
- auto* s1 = Global(Sym(), ty.sampler(ast::SamplerKind::kSampler),
- ast::AttributeList{create<ast::GroupAttribute>(1),
- create<ast::BindingAttribute>(2)});
- auto* s2 = Global(Sym(), ty.sampler(ast::SamplerKind::kSampler),
- ast::AttributeList{create<ast::GroupAttribute>(3),
- create<ast::BindingAttribute>(4)});
+ // @group(1) @binding(2) var s1 : sampler;
+ // @group(3) @binding(4) var s2 : sampler;
+ auto* s1 = Global(
+ Sym(), ty.sampler(ast::SamplerKind::kSampler),
+ ast::AttributeList{create<ast::GroupAttribute>(1), create<ast::BindingAttribute>(2)});
+ auto* s2 = Global(
+ Sym(), ty.sampler(ast::SamplerKind::kSampler),
+ ast::AttributeList{create<ast::GroupAttribute>(3), create<ast::BindingAttribute>(4)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get<sem::GlobalVariable>(s1)->BindingPoint(),
- (sem::BindingPoint{1u, 2u}));
- EXPECT_EQ(Sem().Get<sem::GlobalVariable>(s2)->BindingPoint(),
- (sem::BindingPoint{3u, 4u}));
+ EXPECT_EQ(Sem().Get<sem::GlobalVariable>(s1)->BindingPoint(), (sem::BindingPoint{1u, 2u}));
+ EXPECT_EQ(Sem().Get<sem::GlobalVariable>(s2)->BindingPoint(), (sem::BindingPoint{3u, 4u}));
}
TEST_F(ResolverTest, Function_EntryPoints_StageAttribute) {
- // fn b() {}
- // fn c() { b(); }
- // fn a() { c(); }
- // fn ep_1() { a(); b(); }
- // fn ep_2() { c();}
- //
- // c -> {ep_1, ep_2}
- // a -> {ep_1}
- // b -> {ep_1, ep_2}
- // ep_1 -> {}
- // ep_2 -> {}
+ // fn b() {}
+ // fn c() { b(); }
+ // fn a() { c(); }
+ // fn ep_1() { a(); b(); }
+ // fn ep_2() { c();}
+ //
+ // c -> {ep_1, ep_2}
+ // a -> {ep_1}
+ // b -> {ep_1, ep_2}
+ // ep_1 -> {}
+ // ep_2 -> {}
- Global("first", ty.f32(), ast::StorageClass::kPrivate);
- Global("second", ty.f32(), ast::StorageClass::kPrivate);
- Global("call_a", ty.f32(), ast::StorageClass::kPrivate);
- Global("call_b", ty.f32(), ast::StorageClass::kPrivate);
- Global("call_c", ty.f32(), ast::StorageClass::kPrivate);
+ Global("first", ty.f32(), ast::StorageClass::kPrivate);
+ Global("second", ty.f32(), ast::StorageClass::kPrivate);
+ Global("call_a", ty.f32(), ast::StorageClass::kPrivate);
+ Global("call_b", ty.f32(), ast::StorageClass::kPrivate);
+ Global("call_c", ty.f32(), ast::StorageClass::kPrivate);
- ast::VariableList params;
- auto* func_b =
- Func("b", params, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
- auto* func_c =
- Func("c", params, ty.f32(), {Assign("second", Call("b")), Return(0.0f)},
- ast::AttributeList{});
+ ast::VariableList params;
+ auto* func_b = Func("b", params, ty.f32(), {Return(0.0f)}, ast::AttributeList{});
+ auto* func_c = Func("c", params, ty.f32(), {Assign("second", Call("b")), Return(0.0f)},
+ ast::AttributeList{});
- auto* func_a =
- Func("a", params, ty.f32(), {Assign("first", Call("c")), Return(0.0f)},
- ast::AttributeList{});
+ auto* func_a = Func("a", params, ty.f32(), {Assign("first", Call("c")), Return(0.0f)},
+ ast::AttributeList{});
- auto* ep_1 = Func("ep_1", params, ty.void_(),
- {
- Assign("call_a", Call("a")),
- Assign("call_b", Call("b")),
- },
- ast::AttributeList{Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(1)});
+ auto* ep_1 = Func("ep_1", params, ty.void_(),
+ {
+ Assign("call_a", Call("a")),
+ Assign("call_b", Call("b")),
+ },
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- auto* ep_2 = Func("ep_2", params, ty.void_(),
- {
- Assign("call_c", Call("c")),
- },
- ast::AttributeList{Stage(ast::PipelineStage::kCompute),
- WorkgroupSize(1)});
+ auto* ep_2 = Func("ep_2", params, ty.void_(),
+ {
+ Assign("call_c", Call("c")),
+ },
+ ast::AttributeList{Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* func_b_sem = Sem().Get(func_b);
- auto* func_a_sem = Sem().Get(func_a);
- auto* func_c_sem = Sem().Get(func_c);
- auto* ep_1_sem = Sem().Get(ep_1);
- auto* ep_2_sem = Sem().Get(ep_2);
- ASSERT_NE(func_b_sem, nullptr);
- ASSERT_NE(func_a_sem, nullptr);
- ASSERT_NE(func_c_sem, nullptr);
- ASSERT_NE(ep_1_sem, nullptr);
- ASSERT_NE(ep_2_sem, nullptr);
+ auto* func_b_sem = Sem().Get(func_b);
+ auto* func_a_sem = Sem().Get(func_a);
+ auto* func_c_sem = Sem().Get(func_c);
+ auto* ep_1_sem = Sem().Get(ep_1);
+ auto* ep_2_sem = Sem().Get(ep_2);
+ ASSERT_NE(func_b_sem, nullptr);
+ ASSERT_NE(func_a_sem, nullptr);
+ ASSERT_NE(func_c_sem, nullptr);
+ ASSERT_NE(ep_1_sem, nullptr);
+ ASSERT_NE(ep_2_sem, nullptr);
- EXPECT_EQ(func_b_sem->Parameters().size(), 0u);
- EXPECT_EQ(func_a_sem->Parameters().size(), 0u);
- EXPECT_EQ(func_c_sem->Parameters().size(), 0u);
+ EXPECT_EQ(func_b_sem->Parameters().size(), 0u);
+ EXPECT_EQ(func_a_sem->Parameters().size(), 0u);
+ EXPECT_EQ(func_c_sem->Parameters().size(), 0u);
- const auto& b_eps = func_b_sem->AncestorEntryPoints();
- ASSERT_EQ(2u, b_eps.size());
- EXPECT_EQ(Symbols().Register("ep_1"), b_eps[0]->Declaration()->symbol);
- EXPECT_EQ(Symbols().Register("ep_2"), b_eps[1]->Declaration()->symbol);
+ const auto& b_eps = func_b_sem->AncestorEntryPoints();
+ ASSERT_EQ(2u, b_eps.size());
+ EXPECT_EQ(Symbols().Register("ep_1"), b_eps[0]->Declaration()->symbol);
+ EXPECT_EQ(Symbols().Register("ep_2"), b_eps[1]->Declaration()->symbol);
- const auto& a_eps = func_a_sem->AncestorEntryPoints();
- ASSERT_EQ(1u, a_eps.size());
- EXPECT_EQ(Symbols().Register("ep_1"), a_eps[0]->Declaration()->symbol);
+ const auto& a_eps = func_a_sem->AncestorEntryPoints();
+ ASSERT_EQ(1u, a_eps.size());
+ EXPECT_EQ(Symbols().Register("ep_1"), a_eps[0]->Declaration()->symbol);
- const auto& c_eps = func_c_sem->AncestorEntryPoints();
- ASSERT_EQ(2u, c_eps.size());
- EXPECT_EQ(Symbols().Register("ep_1"), c_eps[0]->Declaration()->symbol);
- EXPECT_EQ(Symbols().Register("ep_2"), c_eps[1]->Declaration()->symbol);
+ const auto& c_eps = func_c_sem->AncestorEntryPoints();
+ ASSERT_EQ(2u, c_eps.size());
+ EXPECT_EQ(Symbols().Register("ep_1"), c_eps[0]->Declaration()->symbol);
+ EXPECT_EQ(Symbols().Register("ep_2"), c_eps[1]->Declaration()->symbol);
- EXPECT_TRUE(ep_1_sem->AncestorEntryPoints().empty());
- EXPECT_TRUE(ep_2_sem->AncestorEntryPoints().empty());
+ EXPECT_TRUE(ep_1_sem->AncestorEntryPoints().empty());
+ EXPECT_TRUE(ep_2_sem->AncestorEntryPoints().empty());
}
// Check for linear-time traversal of functions reachable from entry points.
// See: crbug.com/tint/245
TEST_F(ResolverTest, Function_EntryPoints_LinearTime) {
- // fn lNa() { }
- // fn lNb() { }
- // ...
- // fn l2a() { l3a(); l3b(); }
- // fn l2b() { l3a(); l3b(); }
- // fn l1a() { l2a(); l2b(); }
- // fn l1b() { l2a(); l2b(); }
- // fn main() { l1a(); l1b(); }
+ // fn lNa() { }
+ // fn lNb() { }
+ // ...
+ // fn l2a() { l3a(); l3b(); }
+ // fn l2b() { l3a(); l3b(); }
+ // fn l1a() { l2a(); l2b(); }
+ // fn l1b() { l2a(); l2b(); }
+ // fn main() { l1a(); l1b(); }
- static constexpr int levels = 64;
+ static constexpr int levels = 64;
- auto fn_a = [](int level) { return "l" + std::to_string(level + 1) + "a"; };
- auto fn_b = [](int level) { return "l" + std::to_string(level + 1) + "b"; };
+ auto fn_a = [](int level) { return "l" + std::to_string(level + 1) + "a"; };
+ auto fn_b = [](int level) { return "l" + std::to_string(level + 1) + "b"; };
- Func(fn_a(levels), {}, ty.void_(), {}, {});
- Func(fn_b(levels), {}, ty.void_(), {}, {});
+ Func(fn_a(levels), {}, ty.void_(), {}, {});
+ Func(fn_b(levels), {}, ty.void_(), {}, {});
- for (int i = levels - 1; i >= 0; i--) {
- Func(fn_a(i), {}, ty.void_(),
+ for (int i = levels - 1; i >= 0; i--) {
+ Func(fn_a(i), {}, ty.void_(),
+ {
+ CallStmt(Call(fn_a(i + 1))),
+ CallStmt(Call(fn_b(i + 1))),
+ },
+ {});
+ Func(fn_b(i), {}, ty.void_(),
+ {
+ CallStmt(Call(fn_a(i + 1))),
+ CallStmt(Call(fn_b(i + 1))),
+ },
+ {});
+ }
+
+ Func("main", {}, ty.void_(),
{
- CallStmt(Call(fn_a(i + 1))),
- CallStmt(Call(fn_b(i + 1))),
+ CallStmt(Call(fn_a(0))),
+ CallStmt(Call(fn_b(0))),
},
- {});
- Func(fn_b(i), {}, ty.void_(),
- {
- CallStmt(Call(fn_a(i + 1))),
- CallStmt(Call(fn_b(i + 1))),
- },
- {});
- }
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- Func("main", {}, ty.void_(),
- {
- CallStmt(Call(fn_a(0))),
- CallStmt(Call(fn_b(0))),
- },
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
-
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
// Test for crbug.com/tint/728
TEST_F(ResolverTest, ASTNodesAreReached) {
- Structure("A", {Member("x", ty.array<f32, 4>(4))});
- Structure("B", {Member("x", ty.array<f32, 4>(4))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ Structure("A", {Member("x", ty.array<f32, 4>(4))});
+ Structure("B", {Member("x", ty.array<f32, 4>(4))});
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, ASTNodeNotReached) {
- EXPECT_FATAL_FAILURE(
- {
- ProgramBuilder b;
- b.Expr("expr");
- Resolver(&b).Resolve();
- },
- "internal compiler error: AST node 'tint::ast::IdentifierExpression' was "
- "not reached by the resolver");
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b;
+ b.Expr("expr");
+ Resolver(&b).Resolve();
+ },
+ "internal compiler error: AST node 'tint::ast::IdentifierExpression' was "
+ "not reached by the resolver");
}
TEST_F(ResolverTest, ASTNodeReachedTwice) {
- EXPECT_FATAL_FAILURE(
- {
- ProgramBuilder b;
- auto* expr = b.Expr(1);
- b.Global("a", b.ty.i32(), ast::StorageClass::kPrivate, expr);
- b.Global("b", b.ty.i32(), ast::StorageClass::kPrivate, expr);
- Resolver(&b).Resolve();
- },
- "internal compiler error: AST node 'tint::ast::SintLiteralExpression' "
- "was encountered twice in the same AST of a Program");
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b;
+ auto* expr = b.Expr(1);
+ b.Global("a", b.ty.i32(), ast::StorageClass::kPrivate, expr);
+ b.Global("b", b.ty.i32(), ast::StorageClass::kPrivate, expr);
+ Resolver(&b).Resolve();
+ },
+ "internal compiler error: AST node 'tint::ast::SintLiteralExpression' "
+ "was encountered twice in the same AST of a Program");
}
TEST_F(ResolverTest, UnaryOp_Not) {
- Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot,
- Expr(Source{{12, 34}}, "ident"));
- WrapInFunction(der);
+ Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNot, Expr(Source{{12, 34}}, "ident"));
+ WrapInFunction(der);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot logical negate expression of type 'vec4<f32>");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot logical negate expression of type 'vec4<f32>");
}
TEST_F(ResolverTest, UnaryOp_Complement) {
- Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement,
- Expr(Source{{12, 34}}, "ident"));
- WrapInFunction(der);
+ Global("ident", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ auto* der =
+ create<ast::UnaryOpExpression>(ast::UnaryOp::kComplement, Expr(Source{{12, 34}}, "ident"));
+ WrapInFunction(der);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: cannot bitwise complement expression of type 'vec4<f32>");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot bitwise complement expression of type 'vec4<f32>");
}
TEST_F(ResolverTest, UnaryOp_Negation) {
- Global("ident", ty.u32(), ast::StorageClass::kPrivate);
- auto* der = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation,
- Expr(Source{{12, 34}}, "ident"));
- WrapInFunction(der);
+ Global("ident", ty.u32(), ast::StorageClass::kPrivate);
+ auto* der =
+ create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr(Source{{12, 34}}, "ident"));
+ WrapInFunction(der);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: cannot negate expression of type 'u32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: cannot negate expression of type 'u32");
}
TEST_F(ResolverTest, TextureSampler_TextureSample) {
- Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(1, 1));
- Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
+ Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+ Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
- auto* call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
- const ast::Function* f = Func("test_function", {}, ty.void_(), {call},
- {Stage(ast::PipelineStage::kFragment)});
+ auto* call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
+ const ast::Function* f =
+ Func("test_function", {}, ty.void_(), {call}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- const sem::Function* sf = Sem().Get(f);
- auto pairs = sf->TextureSamplerPairs();
- ASSERT_EQ(pairs.size(), 1u);
- EXPECT_TRUE(pairs[0].first != nullptr);
- EXPECT_TRUE(pairs[0].second != nullptr);
+ const sem::Function* sf = Sem().Get(f);
+ auto pairs = sf->TextureSamplerPairs();
+ ASSERT_EQ(pairs.size(), 1u);
+ EXPECT_TRUE(pairs[0].first != nullptr);
+ EXPECT_TRUE(pairs[0].second != nullptr);
}
TEST_F(ResolverTest, TextureSampler_TextureSampleInFunction) {
- Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(1, 1));
- Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
+ Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+ Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
- auto* inner_call =
- CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
- const ast::Function* inner_func =
- Func("inner_func", {}, ty.void_(), {inner_call});
- auto* outer_call = CallStmt(Call("inner_func"));
- const ast::Function* outer_func =
- Func("outer_func", {}, ty.void_(), {outer_call},
- {Stage(ast::PipelineStage::kFragment)});
+ auto* inner_call = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
+ const ast::Function* inner_func = Func("inner_func", {}, ty.void_(), {inner_call});
+ auto* outer_call = CallStmt(Call("inner_func"));
+ const ast::Function* outer_func =
+ Func("outer_func", {}, ty.void_(), {outer_call}, {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto inner_pairs = Sem().Get(inner_func)->TextureSamplerPairs();
- ASSERT_EQ(inner_pairs.size(), 1u);
- EXPECT_TRUE(inner_pairs[0].first != nullptr);
- EXPECT_TRUE(inner_pairs[0].second != nullptr);
+ auto inner_pairs = Sem().Get(inner_func)->TextureSamplerPairs();
+ ASSERT_EQ(inner_pairs.size(), 1u);
+ EXPECT_TRUE(inner_pairs[0].first != nullptr);
+ EXPECT_TRUE(inner_pairs[0].second != nullptr);
- auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
- ASSERT_EQ(outer_pairs.size(), 1u);
- EXPECT_TRUE(outer_pairs[0].first != nullptr);
- EXPECT_TRUE(outer_pairs[0].second != nullptr);
+ auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
+ ASSERT_EQ(outer_pairs.size(), 1u);
+ EXPECT_TRUE(outer_pairs[0].first != nullptr);
+ EXPECT_TRUE(outer_pairs[0].second != nullptr);
}
TEST_F(ResolverTest, TextureSampler_TextureSampleFunctionDiamondSameVariables) {
- Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(1, 1));
- Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
+ Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+ Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 2));
- auto* inner_call_1 =
- CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
- const ast::Function* inner_func_1 =
- Func("inner_func_1", {}, ty.void_(), {inner_call_1});
- auto* inner_call_2 =
- CallStmt(Call("textureSample", "t", "s", vec2<f32>(3.0f, 4.0f)));
- const ast::Function* inner_func_2 =
- Func("inner_func_2", {}, ty.void_(), {inner_call_2});
- auto* outer_call_1 = CallStmt(Call("inner_func_1"));
- auto* outer_call_2 = CallStmt(Call("inner_func_2"));
- const ast::Function* outer_func =
- Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
- {Stage(ast::PipelineStage::kFragment)});
+ auto* inner_call_1 = CallStmt(Call("textureSample", "t", "s", vec2<f32>(1.0f, 2.0f)));
+ const ast::Function* inner_func_1 = Func("inner_func_1", {}, ty.void_(), {inner_call_1});
+ auto* inner_call_2 = CallStmt(Call("textureSample", "t", "s", vec2<f32>(3.0f, 4.0f)));
+ const ast::Function* inner_func_2 = Func("inner_func_2", {}, ty.void_(), {inner_call_2});
+ auto* outer_call_1 = CallStmt(Call("inner_func_1"));
+ auto* outer_call_2 = CallStmt(Call("inner_func_2"));
+ const ast::Function* outer_func =
+ Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto inner_pairs_1 = Sem().Get(inner_func_1)->TextureSamplerPairs();
- ASSERT_EQ(inner_pairs_1.size(), 1u);
- EXPECT_TRUE(inner_pairs_1[0].first != nullptr);
- EXPECT_TRUE(inner_pairs_1[0].second != nullptr);
+ auto inner_pairs_1 = Sem().Get(inner_func_1)->TextureSamplerPairs();
+ ASSERT_EQ(inner_pairs_1.size(), 1u);
+ EXPECT_TRUE(inner_pairs_1[0].first != nullptr);
+ EXPECT_TRUE(inner_pairs_1[0].second != nullptr);
- auto inner_pairs_2 = Sem().Get(inner_func_2)->TextureSamplerPairs();
- ASSERT_EQ(inner_pairs_1.size(), 1u);
- EXPECT_TRUE(inner_pairs_2[0].first != nullptr);
- EXPECT_TRUE(inner_pairs_2[0].second != nullptr);
+ auto inner_pairs_2 = Sem().Get(inner_func_2)->TextureSamplerPairs();
+ ASSERT_EQ(inner_pairs_1.size(), 1u);
+ EXPECT_TRUE(inner_pairs_2[0].first != nullptr);
+ EXPECT_TRUE(inner_pairs_2[0].second != nullptr);
- auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
- ASSERT_EQ(outer_pairs.size(), 1u);
- EXPECT_TRUE(outer_pairs[0].first != nullptr);
- EXPECT_TRUE(outer_pairs[0].second != nullptr);
+ auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
+ ASSERT_EQ(outer_pairs.size(), 1u);
+ EXPECT_TRUE(outer_pairs[0].first != nullptr);
+ EXPECT_TRUE(outer_pairs[0].second != nullptr);
}
-TEST_F(ResolverTest,
- TextureSampler_TextureSampleFunctionDiamondDifferentVariables) {
- Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(1, 1));
- Global("t2", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(1, 2));
- Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 3));
+TEST_F(ResolverTest, TextureSampler_TextureSampleFunctionDiamondDifferentVariables) {
+ Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 1));
+ Global("t2", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 2));
+ Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(1, 3));
- auto* inner_call_1 =
- CallStmt(Call("textureSample", "t1", "s", vec2<f32>(1.0f, 2.0f)));
- const ast::Function* inner_func_1 =
- Func("inner_func_1", {}, ty.void_(), {inner_call_1});
- auto* inner_call_2 =
- CallStmt(Call("textureSample", "t2", "s", vec2<f32>(3.0f, 4.0f)));
- const ast::Function* inner_func_2 =
- Func("inner_func_2", {}, ty.void_(), {inner_call_2});
- auto* outer_call_1 = CallStmt(Call("inner_func_1"));
- auto* outer_call_2 = CallStmt(Call("inner_func_2"));
- const ast::Function* outer_func =
- Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
- {Stage(ast::PipelineStage::kFragment)});
+ auto* inner_call_1 = CallStmt(Call("textureSample", "t1", "s", vec2<f32>(1.0f, 2.0f)));
+ const ast::Function* inner_func_1 = Func("inner_func_1", {}, ty.void_(), {inner_call_1});
+ auto* inner_call_2 = CallStmt(Call("textureSample", "t2", "s", vec2<f32>(3.0f, 4.0f)));
+ const ast::Function* inner_func_2 = Func("inner_func_2", {}, ty.void_(), {inner_call_2});
+ auto* outer_call_1 = CallStmt(Call("inner_func_1"));
+ auto* outer_call_2 = CallStmt(Call("inner_func_2"));
+ const ast::Function* outer_func =
+ Func("outer_func", {}, ty.void_(), {outer_call_1, outer_call_2},
+ {Stage(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto inner_pairs_1 = Sem().Get(inner_func_1)->TextureSamplerPairs();
- ASSERT_EQ(inner_pairs_1.size(), 1u);
- EXPECT_TRUE(inner_pairs_1[0].first != nullptr);
- EXPECT_TRUE(inner_pairs_1[0].second != nullptr);
+ auto inner_pairs_1 = Sem().Get(inner_func_1)->TextureSamplerPairs();
+ ASSERT_EQ(inner_pairs_1.size(), 1u);
+ EXPECT_TRUE(inner_pairs_1[0].first != nullptr);
+ EXPECT_TRUE(inner_pairs_1[0].second != nullptr);
- auto inner_pairs_2 = Sem().Get(inner_func_2)->TextureSamplerPairs();
- ASSERT_EQ(inner_pairs_2.size(), 1u);
- EXPECT_TRUE(inner_pairs_2[0].first != nullptr);
- EXPECT_TRUE(inner_pairs_2[0].second != nullptr);
+ auto inner_pairs_2 = Sem().Get(inner_func_2)->TextureSamplerPairs();
+ ASSERT_EQ(inner_pairs_2.size(), 1u);
+ EXPECT_TRUE(inner_pairs_2[0].first != nullptr);
+ EXPECT_TRUE(inner_pairs_2[0].second != nullptr);
- auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
- ASSERT_EQ(outer_pairs.size(), 2u);
- EXPECT_TRUE(outer_pairs[0].first == inner_pairs_1[0].first);
- EXPECT_TRUE(outer_pairs[0].second == inner_pairs_1[0].second);
- EXPECT_TRUE(outer_pairs[1].first == inner_pairs_2[0].first);
- EXPECT_TRUE(outer_pairs[1].second == inner_pairs_2[0].second);
+ auto outer_pairs = Sem().Get(outer_func)->TextureSamplerPairs();
+ ASSERT_EQ(outer_pairs.size(), 2u);
+ EXPECT_TRUE(outer_pairs[0].first == inner_pairs_1[0].first);
+ EXPECT_TRUE(outer_pairs[0].second == inner_pairs_1[0].second);
+ EXPECT_TRUE(outer_pairs[1].first == inner_pairs_2[0].first);
+ EXPECT_TRUE(outer_pairs[1].second == inner_pairs_2[0].second);
}
TEST_F(ResolverTest, TextureSampler_TextureDimensions) {
- Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(1, 2));
+ Global("t", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()), GroupAndBinding(1, 2));
- auto* call = Call("textureDimensions", "t");
- const ast::Function* f = WrapInFunction(call);
+ auto* call = Call("textureDimensions", "t");
+ const ast::Function* f = WrapInFunction(call);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- const sem::Function* sf = Sem().Get(f);
- auto pairs = sf->TextureSamplerPairs();
- ASSERT_EQ(pairs.size(), 1u);
- EXPECT_TRUE(pairs[0].first != nullptr);
- EXPECT_TRUE(pairs[0].second == nullptr);
+ const sem::Function* sf = Sem().Get(f);
+ auto pairs = sf->TextureSamplerPairs();
+ ASSERT_EQ(pairs.size(), 1u);
+ EXPECT_TRUE(pairs[0].first != nullptr);
+ EXPECT_TRUE(pairs[0].second == nullptr);
}
TEST_F(ResolverTest, ModuleDependencyOrderedDeclarations) {
- auto* f0 = Func("f0", {}, ty.void_(), {});
- auto* v0 = Global("v0", ty.i32(), ast::StorageClass::kPrivate);
- auto* a0 = Alias("a0", ty.i32());
- auto* s0 = Structure("s0", {Member("m", ty.i32())});
- auto* f1 = Func("f1", {}, ty.void_(), {});
- auto* v1 = Global("v1", ty.i32(), ast::StorageClass::kPrivate);
- auto* a1 = Alias("a1", ty.i32());
- auto* s1 = Structure("s1", {Member("m", ty.i32())});
- auto* f2 = Func("f2", {}, ty.void_(), {});
- auto* v2 = Global("v2", ty.i32(), ast::StorageClass::kPrivate);
- auto* a2 = Alias("a2", ty.i32());
- auto* s2 = Structure("s2", {Member("m", ty.i32())});
+ auto* f0 = Func("f0", {}, ty.void_(), {});
+ auto* v0 = Global("v0", ty.i32(), ast::StorageClass::kPrivate);
+ auto* a0 = Alias("a0", ty.i32());
+ auto* s0 = Structure("s0", {Member("m", ty.i32())});
+ auto* f1 = Func("f1", {}, ty.void_(), {});
+ auto* v1 = Global("v1", ty.i32(), ast::StorageClass::kPrivate);
+ auto* a1 = Alias("a1", ty.i32());
+ auto* s1 = Structure("s1", {Member("m", ty.i32())});
+ auto* f2 = Func("f2", {}, ty.void_(), {});
+ auto* v2 = Global("v2", ty.i32(), ast::StorageClass::kPrivate);
+ auto* a2 = Alias("a2", ty.i32());
+ auto* s2 = Structure("s2", {Member("m", ty.i32())});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(Sem().Module(), nullptr);
- EXPECT_THAT(Sem().Module()->DependencyOrderedDeclarations(),
- ElementsAre(f0, v0, a0, s0, f1, v1, a1, s1, f2, v2, a2, s2));
+ ASSERT_NE(Sem().Module(), nullptr);
+ EXPECT_THAT(Sem().Module()->DependencyOrderedDeclarations(),
+ ElementsAre(f0, v0, a0, s0, f1, v1, a1, s1, f2, v2, a2, s2));
}
} // namespace
diff --git a/src/tint/resolver/resolver_test_helper.h b/src/tint/resolver/resolver_test_helper.h
index bd036ae..e6dcf96 100644
--- a/src/tint/resolver/resolver_test_helper.h
+++ b/src/tint/resolver/resolver_test_helper.h
@@ -30,97 +30,92 @@
/// Helper class for testing
class TestHelper : public ProgramBuilder {
- public:
- /// Constructor
- TestHelper();
+ public:
+ /// Constructor
+ TestHelper();
- /// Destructor
- ~TestHelper() override;
+ /// Destructor
+ ~TestHelper() override;
- /// @return a pointer to the Resolver
- Resolver* r() const { return resolver_.get(); }
+ /// @return a pointer to the Resolver
+ Resolver* r() const { return resolver_.get(); }
- /// @return a pointer to the validator
- const Validator* v() const { return resolver_->GetValidatorForTesting(); }
+ /// @return a pointer to the validator
+ const Validator* v() const { return resolver_->GetValidatorForTesting(); }
- /// Returns the statement that holds the given expression.
- /// @param expr the ast::Expression
- /// @return the ast::Statement of the ast::Expression, or nullptr if the
- /// expression is not owned by a statement.
- const ast::Statement* StmtOf(const ast::Expression* expr) {
- auto* sem_stmt = Sem().Get(expr)->Stmt();
- return sem_stmt ? sem_stmt->Declaration() : nullptr;
- }
-
- /// Returns the BlockStatement that holds the given statement.
- /// @param stmt the ast::Statement
- /// @return the ast::BlockStatement that holds the ast::Statement, or nullptr
- /// if the statement is not owned by a BlockStatement.
- const ast::BlockStatement* BlockOf(const ast::Statement* stmt) {
- auto* sem_stmt = Sem().Get(stmt);
- return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr;
- }
-
- /// Returns the BlockStatement that holds the given expression.
- /// @param expr the ast::Expression
- /// @return the ast::Statement of the ast::Expression, or nullptr if the
- /// expression is not indirectly owned by a BlockStatement.
- const ast::BlockStatement* BlockOf(const ast::Expression* expr) {
- auto* sem_stmt = Sem().Get(expr)->Stmt();
- return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr;
- }
-
- /// Returns the semantic variable for the given identifier expression.
- /// @param expr the identifier expression
- /// @return the resolved sem::Variable of the identifier, or nullptr if
- /// the expression did not resolve to a variable.
- const sem::Variable* VarOf(const ast::Expression* expr) {
- auto* sem_ident = Sem().Get(expr);
- auto* var_user = sem_ident ? sem_ident->As<sem::VariableUser>() : nullptr;
- return var_user ? var_user->Variable() : nullptr;
- }
-
- /// Checks that all the users of the given variable are as expected
- /// @param var the variable to check
- /// @param expected_users the expected users of the variable
- /// @return true if all users are as expected
- bool CheckVarUsers(const ast::Variable* var,
- std::vector<const ast::Expression*>&& expected_users) {
- auto& var_users = Sem().Get(var)->Users();
- if (var_users.size() != expected_users.size()) {
- return false;
+ /// Returns the statement that holds the given expression.
+ /// @param expr the ast::Expression
+ /// @return the ast::Statement of the ast::Expression, or nullptr if the
+ /// expression is not owned by a statement.
+ const ast::Statement* StmtOf(const ast::Expression* expr) {
+ auto* sem_stmt = Sem().Get(expr)->Stmt();
+ return sem_stmt ? sem_stmt->Declaration() : nullptr;
}
- for (size_t i = 0; i < var_users.size(); i++) {
- if (var_users[i]->Declaration() != expected_users[i]) {
- return false;
- }
+
+ /// Returns the BlockStatement that holds the given statement.
+ /// @param stmt the ast::Statement
+ /// @return the ast::BlockStatement that holds the ast::Statement, or nullptr
+ /// if the statement is not owned by a BlockStatement.
+ const ast::BlockStatement* BlockOf(const ast::Statement* stmt) {
+ auto* sem_stmt = Sem().Get(stmt);
+ return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr;
}
- return true;
- }
- /// @param type a type
- /// @returns the name for `type` that closely resembles how it would be
- /// declared in WGSL.
- std::string FriendlyName(const ast::Type* type) {
- return type->FriendlyName(Symbols());
- }
+ /// Returns the BlockStatement that holds the given expression.
+ /// @param expr the ast::Expression
+ /// @return the ast::Statement of the ast::Expression, or nullptr if the
+ /// expression is not indirectly owned by a BlockStatement.
+ const ast::BlockStatement* BlockOf(const ast::Expression* expr) {
+ auto* sem_stmt = Sem().Get(expr)->Stmt();
+ return sem_stmt ? sem_stmt->Block()->Declaration() : nullptr;
+ }
- /// @param type a type
- /// @returns the name for `type` that closely resembles how it would be
- /// declared in WGSL.
- std::string FriendlyName(const sem::Type* type) {
- return type->FriendlyName(Symbols());
- }
+ /// Returns the semantic variable for the given identifier expression.
+ /// @param expr the identifier expression
+ /// @return the resolved sem::Variable of the identifier, or nullptr if
+ /// the expression did not resolve to a variable.
+ const sem::Variable* VarOf(const ast::Expression* expr) {
+ auto* sem_ident = Sem().Get(expr);
+ auto* var_user = sem_ident ? sem_ident->As<sem::VariableUser>() : nullptr;
+ return var_user ? var_user->Variable() : nullptr;
+ }
- private:
- std::unique_ptr<Resolver> resolver_;
+ /// Checks that all the users of the given variable are as expected
+ /// @param var the variable to check
+ /// @param expected_users the expected users of the variable
+ /// @return true if all users are as expected
+ bool CheckVarUsers(const ast::Variable* var,
+ std::vector<const ast::Expression*>&& expected_users) {
+ auto& var_users = Sem().Get(var)->Users();
+ if (var_users.size() != expected_users.size()) {
+ return false;
+ }
+ for (size_t i = 0; i < var_users.size(); i++) {
+ if (var_users[i]->Declaration() != expected_users[i]) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /// @param type a type
+ /// @returns the name for `type` that closely resembles how it would be
+ /// declared in WGSL.
+ std::string FriendlyName(const ast::Type* type) { return type->FriendlyName(Symbols()); }
+
+ /// @param type a type
+ /// @returns the name for `type` that closely resembles how it would be
+ /// declared in WGSL.
+ std::string FriendlyName(const sem::Type* type) { return type->FriendlyName(Symbols()); }
+
+ private:
+ std::unique_ptr<Resolver> resolver_;
};
class ResolverTest : public TestHelper, public testing::Test {};
template <typename T>
-class ResolverTestWithParam : public TestHelper,
- public testing::TestWithParam<T> {};
+class ResolverTestWithParam : public TestHelper, public testing::TestWithParam<T> {};
namespace builder {
@@ -177,8 +172,7 @@
struct ptr {};
using ast_type_func_ptr = const ast::Type* (*)(ProgramBuilder& b);
-using ast_expr_func_ptr = const ast::Expression* (*)(ProgramBuilder& b,
- int elem_value);
+using ast_expr_func_ptr = const ast::Expression* (*)(ProgramBuilder& b, int elem_value);
using sem_type_func_ptr = const sem::Type* (*)(ProgramBuilder& b);
template <typename T>
@@ -187,300 +181,283 @@
/// Helper for building bool types and expressions
template <>
struct DataType<bool> {
- /// false as bool is not a composite type
- static constexpr bool is_composite = false;
+ /// false as bool is not a composite type
+ static constexpr bool is_composite = false;
- /// @param b the ProgramBuilder
- /// @return a new AST bool type
- static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.bool_(); }
- /// @param b the ProgramBuilder
- /// @return the semantic bool type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return b.create<sem::Bool>();
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the b
- /// @return a new AST expression of the bool type
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Expr(elem_value == 0);
- }
+ /// @param b the ProgramBuilder
+ /// @return a new AST bool type
+ static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.bool_(); }
+ /// @param b the ProgramBuilder
+ /// @return the semantic bool type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::Bool>(); }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the b
+ /// @return a new AST expression of the bool type
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Expr(elem_value == 0);
+ }
};
/// Helper for building i32 types and expressions
template <>
struct DataType<i32> {
- /// false as i32 is not a composite type
- static constexpr bool is_composite = false;
+ /// false as i32 is not a composite type
+ static constexpr bool is_composite = false;
- /// @param b the ProgramBuilder
- /// @return a new AST i32 type
- static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.i32(); }
- /// @param b the ProgramBuilder
- /// @return the semantic i32 type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return b.create<sem::I32>();
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the value i32 will be initialized with
- /// @return a new AST i32 literal value expression
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Expr(static_cast<i32>(elem_value));
- }
+ /// @param b the ProgramBuilder
+ /// @return a new AST i32 type
+ static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.i32(); }
+ /// @param b the ProgramBuilder
+ /// @return the semantic i32 type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::I32>(); }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value i32 will be initialized with
+ /// @return a new AST i32 literal value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Expr(static_cast<i32>(elem_value));
+ }
};
/// Helper for building u32 types and expressions
template <>
struct DataType<u32> {
- /// false as u32 is not a composite type
- static constexpr bool is_composite = false;
+ /// false as u32 is not a composite type
+ static constexpr bool is_composite = false;
- /// @param b the ProgramBuilder
- /// @return a new AST u32 type
- static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.u32(); }
- /// @param b the ProgramBuilder
- /// @return the semantic u32 type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return b.create<sem::U32>();
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the value u32 will be initialized with
- /// @return a new AST u32 literal value expression
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Expr(static_cast<u32>(elem_value));
- }
+ /// @param b the ProgramBuilder
+ /// @return a new AST u32 type
+ static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.u32(); }
+ /// @param b the ProgramBuilder
+ /// @return the semantic u32 type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::U32>(); }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value u32 will be initialized with
+ /// @return a new AST u32 literal value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Expr(static_cast<u32>(elem_value));
+ }
};
/// Helper for building f32 types and expressions
template <>
struct DataType<f32> {
- /// false as f32 is not a composite type
- static constexpr bool is_composite = false;
+ /// false as f32 is not a composite type
+ static constexpr bool is_composite = false;
- /// @param b the ProgramBuilder
- /// @return a new AST f32 type
- static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.f32(); }
- /// @param b the ProgramBuilder
- /// @return the semantic f32 type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return b.create<sem::F32>();
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the value f32 will be initialized with
- /// @return a new AST f32 literal value expression
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Expr(static_cast<f32>(elem_value));
- }
+ /// @param b the ProgramBuilder
+ /// @return a new AST f32 type
+ static inline const ast::Type* AST(ProgramBuilder& b) { return b.ty.f32(); }
+ /// @param b the ProgramBuilder
+ /// @return the semantic f32 type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return b.create<sem::F32>(); }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value f32 will be initialized with
+ /// @return a new AST f32 literal value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Expr(static_cast<f32>(elem_value));
+ }
};
/// Helper for building vector types and expressions
template <uint32_t N, typename T>
struct DataType<vec<N, T>> {
- /// true as vectors are a composite type
- static constexpr bool is_composite = true;
+ /// true as vectors are a composite type
+ static constexpr bool is_composite = true;
- /// @param b the ProgramBuilder
- /// @return a new AST vector type
- static inline const ast::Type* AST(ProgramBuilder& b) {
- return b.ty.vec(DataType<T>::AST(b), N);
- }
- /// @param b the ProgramBuilder
- /// @return the semantic vector type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return b.create<sem::Vector>(DataType<T>::Sem(b), N);
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the value each element in the vector will be initialized
- /// with
- /// @return a new AST vector value expression
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Construct(AST(b), ExprArgs(b, elem_value));
- }
-
- /// @param b the ProgramBuilder
- /// @param elem_value the value each element will be initialized with
- /// @return the list of expressions that are used to construct the vector
- static inline ast::ExpressionList ExprArgs(ProgramBuilder& b,
- int elem_value) {
- ast::ExpressionList args;
- for (uint32_t i = 0; i < N; i++) {
- args.emplace_back(DataType<T>::Expr(b, elem_value));
+ /// @param b the ProgramBuilder
+ /// @return a new AST vector type
+ static inline const ast::Type* AST(ProgramBuilder& b) {
+ return b.ty.vec(DataType<T>::AST(b), N);
}
- return args;
- }
+ /// @param b the ProgramBuilder
+ /// @return the semantic vector type
+ static inline const sem::Type* Sem(ProgramBuilder& b) {
+ return b.create<sem::Vector>(DataType<T>::Sem(b), N);
+ }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value each element in the vector will be initialized
+ /// with
+ /// @return a new AST vector value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Construct(AST(b), ExprArgs(b, elem_value));
+ }
+
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value each element will be initialized with
+ /// @return the list of expressions that are used to construct the vector
+ static inline ast::ExpressionList ExprArgs(ProgramBuilder& b, int elem_value) {
+ ast::ExpressionList args;
+ for (uint32_t i = 0; i < N; i++) {
+ args.emplace_back(DataType<T>::Expr(b, elem_value));
+ }
+ return args;
+ }
};
/// Helper for building matrix types and expressions
template <uint32_t N, uint32_t M, typename T>
struct DataType<mat<N, M, T>> {
- /// true as matrices are a composite type
- static constexpr bool is_composite = true;
+ /// true as matrices are a composite type
+ static constexpr bool is_composite = true;
- /// @param b the ProgramBuilder
- /// @return a new AST matrix type
- static inline const ast::Type* AST(ProgramBuilder& b) {
- return b.ty.mat(DataType<T>::AST(b), N, M);
- }
- /// @param b the ProgramBuilder
- /// @return the semantic matrix type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- auto* column_type = b.create<sem::Vector>(DataType<T>::Sem(b), M);
- return b.create<sem::Matrix>(column_type, N);
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the value each element in the matrix will be initialized
- /// with
- /// @return a new AST matrix value expression
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Construct(AST(b), ExprArgs(b, elem_value));
- }
-
- /// @param b the ProgramBuilder
- /// @param elem_value the value each element will be initialized with
- /// @return the list of expressions that are used to construct the matrix
- static inline ast::ExpressionList ExprArgs(ProgramBuilder& b,
- int elem_value) {
- ast::ExpressionList args;
- for (uint32_t i = 0; i < N; i++) {
- args.emplace_back(DataType<vec<M, T>>::Expr(b, elem_value));
+ /// @param b the ProgramBuilder
+ /// @return a new AST matrix type
+ static inline const ast::Type* AST(ProgramBuilder& b) {
+ return b.ty.mat(DataType<T>::AST(b), N, M);
}
- return args;
- }
+ /// @param b the ProgramBuilder
+ /// @return the semantic matrix type
+ static inline const sem::Type* Sem(ProgramBuilder& b) {
+ auto* column_type = b.create<sem::Vector>(DataType<T>::Sem(b), M);
+ return b.create<sem::Matrix>(column_type, N);
+ }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value each element in the matrix will be initialized
+ /// with
+ /// @return a new AST matrix value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Construct(AST(b), ExprArgs(b, elem_value));
+ }
+
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value each element will be initialized with
+ /// @return the list of expressions that are used to construct the matrix
+ static inline ast::ExpressionList ExprArgs(ProgramBuilder& b, int elem_value) {
+ ast::ExpressionList args;
+ for (uint32_t i = 0; i < N; i++) {
+ args.emplace_back(DataType<vec<M, T>>::Expr(b, elem_value));
+ }
+ return args;
+ }
};
/// Helper for building alias types and expressions
template <typename T, int ID>
struct DataType<alias<T, ID>> {
- /// true if the aliased type is a composite type
- static constexpr bool is_composite = DataType<T>::is_composite;
+ /// true if the aliased type is a composite type
+ static constexpr bool is_composite = DataType<T>::is_composite;
- /// @param b the ProgramBuilder
- /// @return a new AST alias type
- static inline const ast::Type* AST(ProgramBuilder& b) {
- auto name = b.Symbols().Register("alias_" + std::to_string(ID));
- if (!b.AST().LookupType(name)) {
- auto* type = DataType<T>::AST(b);
- b.AST().AddTypeDecl(b.ty.alias(name, type));
+ /// @param b the ProgramBuilder
+ /// @return a new AST alias type
+ static inline const ast::Type* AST(ProgramBuilder& b) {
+ auto name = b.Symbols().Register("alias_" + std::to_string(ID));
+ if (!b.AST().LookupType(name)) {
+ auto* type = DataType<T>::AST(b);
+ b.AST().AddTypeDecl(b.ty.alias(name, type));
+ }
+ return b.create<ast::TypeName>(name);
}
- return b.create<ast::TypeName>(name);
- }
- /// @param b the ProgramBuilder
- /// @return the semantic aliased type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return DataType<T>::Sem(b);
- }
+ /// @param b the ProgramBuilder
+ /// @return the semantic aliased type
+ static inline const sem::Type* Sem(ProgramBuilder& b) { return DataType<T>::Sem(b); }
- /// @param b the ProgramBuilder
- /// @param elem_value the value nested elements will be initialized with
- /// @return a new AST expression of the alias type
- template <bool IS_COMPOSITE = is_composite>
- static inline traits::EnableIf<!IS_COMPOSITE, const ast::Expression*> Expr(
- ProgramBuilder& b,
- int elem_value) {
- // Cast
- return b.Construct(AST(b), DataType<T>::Expr(b, elem_value));
- }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value nested elements will be initialized with
+ /// @return a new AST expression of the alias type
+ template <bool IS_COMPOSITE = is_composite>
+ static inline traits::EnableIf<!IS_COMPOSITE, const ast::Expression*> Expr(ProgramBuilder& b,
+ int elem_value) {
+ // Cast
+ return b.Construct(AST(b), DataType<T>::Expr(b, elem_value));
+ }
- /// @param b the ProgramBuilder
- /// @param elem_value the value nested elements will be initialized with
- /// @return a new AST expression of the alias type
- template <bool IS_COMPOSITE = is_composite>
- static inline traits::EnableIf<IS_COMPOSITE, const ast::Expression*> Expr(
- ProgramBuilder& b,
- int elem_value) {
- // Construct
- return b.Construct(AST(b), DataType<T>::ExprArgs(b, elem_value));
- }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value nested elements will be initialized with
+ /// @return a new AST expression of the alias type
+ template <bool IS_COMPOSITE = is_composite>
+ static inline traits::EnableIf<IS_COMPOSITE, const ast::Expression*> Expr(ProgramBuilder& b,
+ int elem_value) {
+ // Construct
+ return b.Construct(AST(b), DataType<T>::ExprArgs(b, elem_value));
+ }
};
/// Helper for building pointer types and expressions
template <typename T>
struct DataType<ptr<T>> {
- /// true if the pointer type is a composite type
- static constexpr bool is_composite = false;
+ /// true if the pointer type is a composite type
+ static constexpr bool is_composite = false;
- /// @param b the ProgramBuilder
- /// @return a new AST alias type
- static inline const ast::Type* AST(ProgramBuilder& b) {
- return b.create<ast::Pointer>(DataType<T>::AST(b),
- ast::StorageClass::kPrivate,
- ast::Access::kReadWrite);
- }
- /// @param b the ProgramBuilder
- /// @return the semantic aliased type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- return b.create<sem::Pointer>(DataType<T>::Sem(b),
- ast::StorageClass::kPrivate,
- ast::Access::kReadWrite);
- }
+ /// @param b the ProgramBuilder
+ /// @return a new AST alias type
+ static inline const ast::Type* AST(ProgramBuilder& b) {
+ return b.create<ast::Pointer>(DataType<T>::AST(b), ast::StorageClass::kPrivate,
+ ast::Access::kReadWrite);
+ }
+ /// @param b the ProgramBuilder
+ /// @return the semantic aliased type
+ static inline const sem::Type* Sem(ProgramBuilder& b) {
+ return b.create<sem::Pointer>(DataType<T>::Sem(b), ast::StorageClass::kPrivate,
+ ast::Access::kReadWrite);
+ }
- /// @param b the ProgramBuilder
- /// @return a new AST expression of the alias type
- static inline const ast::Expression* Expr(ProgramBuilder& b, int /*unused*/) {
- auto sym = b.Symbols().New("global_for_ptr");
- b.Global(sym, DataType<T>::AST(b), ast::StorageClass::kPrivate);
- return b.AddressOf(sym);
- }
+ /// @param b the ProgramBuilder
+ /// @return a new AST expression of the alias type
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int /*unused*/) {
+ auto sym = b.Symbols().New("global_for_ptr");
+ b.Global(sym, DataType<T>::AST(b), ast::StorageClass::kPrivate);
+ return b.AddressOf(sym);
+ }
};
/// Helper for building array types and expressions
template <uint32_t N, typename T>
struct DataType<array<N, T>> {
- /// true as arrays are a composite type
- static constexpr bool is_composite = true;
+ /// true as arrays are a composite type
+ static constexpr bool is_composite = true;
- /// @param b the ProgramBuilder
- /// @return a new AST array type
- static inline const ast::Type* AST(ProgramBuilder& b) {
- return b.ty.array(DataType<T>::AST(b), N);
- }
- /// @param b the ProgramBuilder
- /// @return the semantic array type
- static inline const sem::Type* Sem(ProgramBuilder& b) {
- auto* el = DataType<T>::Sem(b);
- return b.create<sem::Array>(
- /* element */ el,
- /* count */ N,
- /* align */ el->Align(),
- /* size */ el->Size(),
- /* stride */ el->Align(),
- /* implicit_stride */ el->Align());
- }
- /// @param b the ProgramBuilder
- /// @param elem_value the value each element in the array will be initialized
- /// with
- /// @return a new AST array value expression
- static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
- return b.Construct(AST(b), ExprArgs(b, elem_value));
- }
-
- /// @param b the ProgramBuilder
- /// @param elem_value the value each element will be initialized with
- /// @return the list of expressions that are used to construct the array
- static inline ast::ExpressionList ExprArgs(ProgramBuilder& b,
- int elem_value) {
- ast::ExpressionList args;
- for (uint32_t i = 0; i < N; i++) {
- args.emplace_back(DataType<T>::Expr(b, elem_value));
+ /// @param b the ProgramBuilder
+ /// @return a new AST array type
+ static inline const ast::Type* AST(ProgramBuilder& b) {
+ return b.ty.array(DataType<T>::AST(b), N);
}
- return args;
- }
+ /// @param b the ProgramBuilder
+ /// @return the semantic array type
+ static inline const sem::Type* Sem(ProgramBuilder& b) {
+ auto* el = DataType<T>::Sem(b);
+ return b.create<sem::Array>(
+ /* element */ el,
+ /* count */ N,
+ /* align */ el->Align(),
+ /* size */ el->Size(),
+ /* stride */ el->Align(),
+ /* implicit_stride */ el->Align());
+ }
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value each element in the array will be initialized
+ /// with
+ /// @return a new AST array value expression
+ static inline const ast::Expression* Expr(ProgramBuilder& b, int elem_value) {
+ return b.Construct(AST(b), ExprArgs(b, elem_value));
+ }
+
+ /// @param b the ProgramBuilder
+ /// @param elem_value the value each element will be initialized with
+ /// @return the list of expressions that are used to construct the array
+ static inline ast::ExpressionList ExprArgs(ProgramBuilder& b, int elem_value) {
+ ast::ExpressionList args;
+ for (uint32_t i = 0; i < N; i++) {
+ args.emplace_back(DataType<T>::Expr(b, elem_value));
+ }
+ return args;
+ }
};
/// Struct of all creation pointer types
struct CreatePtrs {
- /// ast node type create function
- ast_type_func_ptr ast;
- /// ast expression type create function
- ast_expr_func_ptr expr;
- /// sem type create function
- sem_type_func_ptr sem;
+ /// ast node type create function
+ ast_type_func_ptr ast;
+ /// ast expression type create function
+ ast_expr_func_ptr expr;
+ /// sem type create function
+ sem_type_func_ptr sem;
};
/// Returns a CreatePtrs struct instance with all creation pointer types for
/// type `T`
template <typename T>
constexpr CreatePtrs CreatePtrsFor() {
- return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::Sem};
+ return {DataType<T>::AST, DataType<T>::Expr, DataType<T>::Sem};
}
} // namespace builder
diff --git a/src/tint/resolver/sem_helper.cc b/src/tint/resolver/sem_helper.cc
index 57fff2d..53c2817 100644
--- a/src/tint/resolver/sem_helper.cc
+++ b/src/tint/resolver/sem_helper.cc
@@ -24,38 +24,29 @@
SemHelper::~SemHelper() = default;
std::string SemHelper::TypeNameOf(const sem::Type* ty) const {
- return RawTypeNameOf(ty->UnwrapRef());
+ return RawTypeNameOf(ty->UnwrapRef());
}
std::string SemHelper::RawTypeNameOf(const sem::Type* ty) const {
- return ty->FriendlyName(builder_->Symbols());
+ return ty->FriendlyName(builder_->Symbols());
}
sem::Type* SemHelper::TypeOf(const ast::Expression* expr) const {
- auto* sem = Get(expr);
- return sem ? const_cast<sem::Type*>(sem->Type()) : nullptr;
+ auto* sem = Get(expr);
+ return sem ? const_cast<sem::Type*>(sem->Type()) : nullptr;
}
sem::Type* SemHelper::TypeOf(const ast::LiteralExpression* lit) {
- return Switch(
- lit,
- [&](const ast::SintLiteralExpression*) {
- return builder_->create<sem::I32>();
- },
- [&](const ast::UintLiteralExpression*) {
- return builder_->create<sem::U32>();
- },
- [&](const ast::FloatLiteralExpression*) {
- return builder_->create<sem::F32>();
- },
- [&](const ast::BoolLiteralExpression*) {
- return builder_->create<sem::Bool>();
- },
- [&](Default) {
- TINT_UNREACHABLE(Resolver, builder_->Diagnostics())
- << "Unhandled literal type: " << lit->TypeInfo().name;
- return nullptr;
- });
+ return Switch(
+ lit, [&](const ast::SintLiteralExpression*) { return builder_->create<sem::I32>(); },
+ [&](const ast::UintLiteralExpression*) { return builder_->create<sem::U32>(); },
+ [&](const ast::FloatLiteralExpression*) { return builder_->create<sem::F32>(); },
+ [&](const ast::BoolLiteralExpression*) { return builder_->create<sem::Bool>(); },
+ [&](Default) {
+ TINT_UNREACHABLE(Resolver, builder_->Diagnostics())
+ << "Unhandled literal type: " << lit->TypeInfo().name;
+ return nullptr;
+ });
}
} // namespace tint::resolver
diff --git a/src/tint/resolver/sem_helper.h b/src/tint/resolver/sem_helper.h
index 58c2d57..5ab14eb 100644
--- a/src/tint/resolver/sem_helper.h
+++ b/src/tint/resolver/sem_helper.h
@@ -26,61 +26,59 @@
/// Helper class to retrieve sem information.
class SemHelper {
- public:
- /// Constructor
- /// @param builder the program builder
- /// @param dependencies the program dependency graph
- explicit SemHelper(ProgramBuilder* builder, DependencyGraph& dependencies);
- ~SemHelper();
+ public:
+ /// Constructor
+ /// @param builder the program builder
+ /// @param dependencies the program dependency graph
+ explicit SemHelper(ProgramBuilder* builder, DependencyGraph& dependencies);
+ ~SemHelper();
- /// Get is a helper for obtaining the semantic node for the given AST node.
- /// @param ast the ast node to get the sem for
- /// @returns the sem node for the provided |ast|
- template <typename SEM = sem::Info::InferFromAST,
- typename AST_OR_TYPE = CastableBase>
- auto* Get(const AST_OR_TYPE* ast) const {
- using T = sem::Info::GetResultType<SEM, AST_OR_TYPE>;
- auto* sem = builder_->Sem().Get(ast);
- if (!sem) {
- TINT_ICE(Resolver, builder_->Diagnostics())
- << "AST node '" << ast->TypeInfo().name << "' had no semantic info\n"
- << "At: " << ast->source << "\n"
- << "Pointer: " << ast;
+ /// Get is a helper for obtaining the semantic node for the given AST node.
+ /// @param ast the ast node to get the sem for
+ /// @returns the sem node for the provided |ast|
+ template <typename SEM = sem::Info::InferFromAST, typename AST_OR_TYPE = CastableBase>
+ auto* Get(const AST_OR_TYPE* ast) const {
+ using T = sem::Info::GetResultType<SEM, AST_OR_TYPE>;
+ auto* sem = builder_->Sem().Get(ast);
+ if (!sem) {
+ TINT_ICE(Resolver, builder_->Diagnostics())
+ << "AST node '" << ast->TypeInfo().name << "' had no semantic info\n"
+ << "At: " << ast->source << "\n"
+ << "Pointer: " << ast;
+ }
+ return const_cast<T*>(As<T>(sem));
}
- return const_cast<T*>(As<T>(sem));
- }
- /// @returns the resolved symbol (function, type or variable) for the given
- /// ast::Identifier or ast::TypeName cast to the given semantic type.
- /// @param node the node to retrieve
- template <typename SEM = sem::Node>
- SEM* ResolvedSymbol(const ast::Node* node) const {
- auto* resolved = utils::Lookup(dependencies_.resolved_symbols, node);
- return resolved ? const_cast<SEM*>(builder_->Sem().Get<SEM>(resolved))
- : nullptr;
- }
+ /// @returns the resolved symbol (function, type or variable) for the given
+ /// ast::Identifier or ast::TypeName cast to the given semantic type.
+ /// @param node the node to retrieve
+ template <typename SEM = sem::Node>
+ SEM* ResolvedSymbol(const ast::Node* node) const {
+ auto* resolved = utils::Lookup(dependencies_.resolved_symbols, node);
+ return resolved ? const_cast<SEM*>(builder_->Sem().Get<SEM>(resolved)) : nullptr;
+ }
- /// @returns the resolved type of the ast::Expression `expr`
- /// @param expr the expression
- sem::Type* TypeOf(const ast::Expression* expr) const;
+ /// @returns the resolved type of the ast::Expression `expr`
+ /// @param expr the expression
+ sem::Type* TypeOf(const ast::Expression* expr) const;
- /// @returns the semantic type of the AST literal `lit`
- /// @param lit the literal
- sem::Type* TypeOf(const ast::LiteralExpression* lit);
+ /// @returns the semantic type of the AST literal `lit`
+ /// @param lit the literal
+ sem::Type* TypeOf(const ast::LiteralExpression* lit);
- /// @returns the type name of the given semantic type, unwrapping
- /// references.
- /// @param ty the type to look up
- std::string TypeNameOf(const sem::Type* ty) const;
+ /// @returns the type name of the given semantic type, unwrapping
+ /// references.
+ /// @param ty the type to look up
+ std::string TypeNameOf(const sem::Type* ty) const;
- /// @returns the type name of the given semantic type, without unwrapping
- /// references.
- /// @param ty the type to look up
- std::string RawTypeNameOf(const sem::Type* ty) const;
+ /// @returns the type name of the given semantic type, without unwrapping
+ /// references.
+ /// @param ty the type to look up
+ std::string RawTypeNameOf(const sem::Type* ty) const;
- private:
- ProgramBuilder* builder_;
- DependencyGraph& dependencies_;
+ private:
+ ProgramBuilder* builder_;
+ DependencyGraph& dependencies_;
};
} // namespace tint::resolver
diff --git a/src/tint/resolver/side_effects_test.cc b/src/tint/resolver/side_effects_test.cc
index 50f4e99..4057e6a 100644
--- a/src/tint/resolver/side_effects_test.cc
+++ b/src/tint/resolver/side_effects_test.cc
@@ -23,360 +23,360 @@
namespace {
struct SideEffectsTest : ResolverTest {
- template <typename T>
- void MakeSideEffectFunc(const char* name) {
- auto global = Sym();
- Global(global, ty.Of<T>(), ast::StorageClass::kPrivate);
- auto local = Sym();
- Func(name, {}, ty.Of<T>(),
- {
- Decl(Var(local, ty.Of<T>())),
- Assign(global, local),
- Return(global),
- });
- }
+ template <typename T>
+ void MakeSideEffectFunc(const char* name) {
+ auto global = Sym();
+ Global(global, ty.Of<T>(), ast::StorageClass::kPrivate);
+ auto local = Sym();
+ Func(name, {}, ty.Of<T>(),
+ {
+ Decl(Var(local, ty.Of<T>())),
+ Assign(global, local),
+ Return(global),
+ });
+ }
- template <typename MAKE_TYPE_FUNC>
- void MakeSideEffectFunc(const char* name, MAKE_TYPE_FUNC make_type) {
- auto global = Sym();
- Global(global, make_type(), ast::StorageClass::kPrivate);
- auto local = Sym();
- Func(name, {}, make_type(),
- {
- Decl(Var(local, make_type())),
- Assign(global, local),
- Return(global),
- });
- }
+ template <typename MAKE_TYPE_FUNC>
+ void MakeSideEffectFunc(const char* name, MAKE_TYPE_FUNC make_type) {
+ auto global = Sym();
+ Global(global, make_type(), ast::StorageClass::kPrivate);
+ auto local = Sym();
+ Func(name, {}, make_type(),
+ {
+ Decl(Var(local, make_type())),
+ Assign(global, local),
+ Return(global),
+ });
+ }
};
TEST_F(SideEffectsTest, Phony) {
- auto* expr = Phony();
- auto* body = Assign(expr, 1);
- WrapInFunction(body);
+ auto* expr = Phony();
+ auto* body = Assign(expr, 1);
+ WrapInFunction(body);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Literal) {
- auto* expr = Expr(1);
- WrapInFunction(expr);
+ auto* expr = Expr(1);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, VariableUser) {
- auto* var = Decl(Var("a", ty.i32()));
- auto* expr = Expr("a");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.i32()));
+ auto* expr = Expr("a");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::VariableUser>());
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::VariableUser>());
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_Builtin_NoSE) {
- Global("a", ty.f32(), ast::StorageClass::kPrivate);
- auto* expr = Call("dpdx", "a");
- Func("f", {}, ty.void_(), {Ignore(expr)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ Global("a", ty.f32(), ast::StorageClass::kPrivate);
+ auto* expr = Call("dpdx", "a");
+ Func("f", {}, ty.void_(), {Ignore(expr)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_Builtin_NoSE_WithSEArg) {
- MakeSideEffectFunc<f32>("se");
- auto* expr = Call("dpdx", Call("se"));
- Func("f", {}, ty.void_(), {Ignore(expr)},
- {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
+ MakeSideEffectFunc<f32>("se");
+ auto* expr = Call("dpdx", Call("se"));
+ Func("f", {}, ty.void_(), {Ignore(expr)},
+ {create<ast::StageAttribute>(ast::PipelineStage::kFragment)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_Builtin_SE) {
- Global("a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
- auto* expr = Call("atomicAdd", AddressOf("a"), 1);
- WrapInFunction(expr);
+ Global("a", ty.atomic(ty.i32()), ast::StorageClass::kWorkgroup);
+ auto* expr = Call("atomicAdd", AddressOf("a"), 1);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_Function) {
- Func("f", {}, ty.i32(), {Return(1)});
- auto* expr = Call("f");
- WrapInFunction(expr);
+ Func("f", {}, ty.i32(), {Return(1)});
+ auto* expr = Call("f");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_TypeConversion_NoSE) {
- auto* var = Decl(Var("a", ty.i32()));
- auto* expr = Construct(ty.f32(), "a");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.i32()));
+ auto* expr = Construct(ty.f32(), "a");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_TypeConversion_SE) {
- MakeSideEffectFunc<i32>("se");
- auto* expr = Construct(ty.f32(), Call("se"));
- WrapInFunction(expr);
+ MakeSideEffectFunc<i32>("se");
+ auto* expr = Construct(ty.f32(), Call("se"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_TypeConstructor_NoSE) {
- auto* var = Decl(Var("a", ty.f32()));
- auto* expr = Construct(ty.f32(), "a");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.f32()));
+ auto* expr = Construct(ty.f32(), "a");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Call_TypeConstructor_SE) {
- MakeSideEffectFunc<f32>("se");
- auto* expr = Construct(ty.f32(), Call("se"));
- WrapInFunction(expr);
+ MakeSideEffectFunc<f32>("se");
+ auto* expr = Construct(ty.f32(), Call("se"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->Is<sem::Call>());
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->Is<sem::Call>());
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, MemberAccessor_Struct_NoSE) {
- auto* s = Structure("S", {Member("m", ty.i32())});
- auto* var = Decl(Var("a", ty.Of(s)));
- auto* expr = MemberAccessor("a", "m");
- WrapInFunction(var, expr);
+ auto* s = Structure("S", {Member("m", ty.i32())});
+ auto* var = Decl(Var("a", ty.Of(s)));
+ auto* expr = MemberAccessor("a", "m");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, MemberAccessor_Struct_SE) {
- auto* s = Structure("S", {Member("m", ty.i32())});
- MakeSideEffectFunc("se", [&] { return ty.Of(s); });
- auto* expr = MemberAccessor(Call("se"), "m");
- WrapInFunction(expr);
+ auto* s = Structure("S", {Member("m", ty.i32())});
+ MakeSideEffectFunc("se", [&] { return ty.Of(s); });
+ auto* expr = MemberAccessor(Call("se"), "m");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, MemberAccessor_Vector) {
- auto* var = Decl(Var("a", ty.vec4<f32>()));
- auto* expr = MemberAccessor("a", "x");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.vec4<f32>()));
+ auto* expr = MemberAccessor("a", "x");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_TRUE(sem->Is<sem::MemberAccessorExpression>());
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ EXPECT_TRUE(sem->Is<sem::MemberAccessorExpression>());
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, MemberAccessor_VectorSwizzleNoSE) {
- auto* var = Decl(Var("a", ty.vec4<f32>()));
- auto* expr = MemberAccessor("a", "xzyw");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.vec4<f32>()));
+ auto* expr = MemberAccessor("a", "xzyw");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_TRUE(sem->Is<sem::Swizzle>());
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ EXPECT_TRUE(sem->Is<sem::Swizzle>());
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, MemberAccessor_VectorSwizzleSE) {
- MakeSideEffectFunc("se", [&] { return ty.vec4<f32>(); });
- auto* expr = MemberAccessor(Call("se"), "xzyw");
- WrapInFunction(expr);
+ MakeSideEffectFunc("se", [&] { return ty.vec4<f32>(); });
+ auto* expr = MemberAccessor(Call("se"), "xzyw");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- EXPECT_TRUE(sem->Is<sem::Swizzle>());
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ EXPECT_TRUE(sem->Is<sem::Swizzle>());
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Binary_NoSE) {
- auto* a = Decl(Var("a", ty.i32()));
- auto* b = Decl(Var("b", ty.i32()));
- auto* expr = Add("a", "b");
- WrapInFunction(a, b, expr);
+ auto* a = Decl(Var("a", ty.i32()));
+ auto* b = Decl(Var("b", ty.i32()));
+ auto* expr = Add("a", "b");
+ WrapInFunction(a, b, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Binary_LeftSE) {
- MakeSideEffectFunc<i32>("se");
- auto* b = Decl(Var("b", ty.i32()));
- auto* expr = Add(Call("se"), "b");
- WrapInFunction(b, expr);
+ MakeSideEffectFunc<i32>("se");
+ auto* b = Decl(Var("b", ty.i32()));
+ auto* expr = Add(Call("se"), "b");
+ WrapInFunction(b, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Binary_RightSE) {
- MakeSideEffectFunc<i32>("se");
- auto* a = Decl(Var("a", ty.i32()));
- auto* expr = Add("a", Call("se"));
- WrapInFunction(a, expr);
+ MakeSideEffectFunc<i32>("se");
+ auto* a = Decl(Var("a", ty.i32()));
+ auto* expr = Add("a", Call("se"));
+ WrapInFunction(a, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Binary_BothSE) {
- MakeSideEffectFunc<i32>("se1");
- MakeSideEffectFunc<i32>("se2");
- auto* expr = Add(Call("se1"), Call("se2"));
- WrapInFunction(expr);
+ MakeSideEffectFunc<i32>("se1");
+ MakeSideEffectFunc<i32>("se2");
+ auto* expr = Add(Call("se1"), Call("se2"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Unary_NoSE) {
- auto* var = Decl(Var("a", ty.bool_()));
- auto* expr = Not("a");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.bool_()));
+ auto* expr = Not("a");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Unary_SE) {
- MakeSideEffectFunc<bool>("se");
- auto* expr = Not(Call("se"));
- WrapInFunction(expr);
+ MakeSideEffectFunc<bool>("se");
+ auto* expr = Not(Call("se"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, IndexAccessor_NoSE) {
- auto* var = Decl(Var("a", ty.array<i32, 10>()));
- auto* expr = IndexAccessor("a", 0);
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.array<i32, 10>()));
+ auto* expr = IndexAccessor("a", 0);
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, IndexAccessor_ObjSE) {
- MakeSideEffectFunc("se", [&] { return ty.array<i32, 10>(); });
- auto* expr = IndexAccessor(Call("se"), 0);
- WrapInFunction(expr);
+ MakeSideEffectFunc("se", [&] { return ty.array<i32, 10>(); });
+ auto* expr = IndexAccessor(Call("se"), 0);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, IndexAccessor_IndexSE) {
- MakeSideEffectFunc<i32>("se");
- auto* var = Decl(Var("a", ty.array<i32, 10>()));
- auto* expr = IndexAccessor("a", Call("se"));
- WrapInFunction(var, expr);
+ MakeSideEffectFunc<i32>("se");
+ auto* var = Decl(Var("a", ty.array<i32, 10>()));
+ auto* expr = IndexAccessor("a", Call("se"));
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, IndexAccessor_BothSE) {
- MakeSideEffectFunc("se1", [&] { return ty.array<i32, 10>(); });
- MakeSideEffectFunc<i32>("se2");
- auto* expr = IndexAccessor(Call("se1"), Call("se2"));
- WrapInFunction(expr);
+ MakeSideEffectFunc("se1", [&] { return ty.array<i32, 10>(); });
+ MakeSideEffectFunc<i32>("se2");
+ auto* expr = IndexAccessor(Call("se1"), Call("se2"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Bitcast_NoSE) {
- auto* var = Decl(Var("a", ty.i32()));
- auto* expr = Bitcast<f32>("a");
- WrapInFunction(var, expr);
+ auto* var = Decl(Var("a", ty.i32()));
+ auto* expr = Bitcast<f32>("a");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_FALSE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_FALSE(sem->HasSideEffects());
}
TEST_F(SideEffectsTest, Bitcast_SE) {
- MakeSideEffectFunc<i32>("se");
- auto* expr = Bitcast<f32>(Call("se"));
- WrapInFunction(expr);
+ MakeSideEffectFunc<i32>("se");
+ auto* expr = Bitcast<f32>(Call("se"));
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = Sem().Get(expr);
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->HasSideEffects());
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* sem = Sem().Get(expr);
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->HasSideEffects());
}
} // namespace
diff --git a/src/tint/resolver/source_variable_test.cc b/src/tint/resolver/source_variable_test.cc
index d5aa1df..6d10f84 100644
--- a/src/tint/resolver/source_variable_test.cc
+++ b/src/tint/resolver/source_variable_test.cc
@@ -23,270 +23,266 @@
class ResolverSourceVariableTest : public ResolverTest {};
TEST_F(ResolverSourceVariableTest, GlobalPrivateVar) {
- auto* a = Global("a", ty.f32(), ast::StorageClass::kPrivate);
- auto* expr = Expr(a);
- WrapInFunction(expr);
+ auto* a = Global("a", ty.f32(), ast::StorageClass::kPrivate);
+ auto* expr = Expr(a);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, GlobalWorkgroupVar) {
- auto* a = Global("a", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* expr = Expr(a);
- WrapInFunction(expr);
+ auto* a = Global("a", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* expr = Expr(a);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, GlobalStorageVar) {
- auto* a =
- Global("a", ty.f32(), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
- auto* expr = Expr(a);
- WrapInFunction(expr);
+ auto* a = Global("a", ty.f32(), ast::StorageClass::kStorage, GroupAndBinding(0, 0));
+ auto* expr = Expr(a);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, GlobalUniformVar) {
- auto* a =
- Global("a", ty.f32(), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
- auto* expr = Expr(a);
- WrapInFunction(expr);
+ auto* a = Global("a", ty.f32(), ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ auto* expr = Expr(a);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, GlobalTextureVar) {
- auto* a =
- Global("a", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- ast::StorageClass::kNone, GroupAndBinding(0, 0));
- auto* expr = Expr(a);
- WrapInFunction(Call("textureDimensions", expr));
+ auto* a = Global("a", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ ast::StorageClass::kNone, GroupAndBinding(0, 0));
+ auto* expr = Expr(a);
+ WrapInFunction(Call("textureDimensions", expr));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, GlobalOverride) {
- auto* a = Override("a", ty.f32(), Expr(1.f));
- auto* expr = Expr(a);
- WrapInFunction(expr);
+ auto* a = Override("a", ty.f32(), Expr(1.f));
+ auto* expr = Expr(a);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, GlobalConst) {
- auto* a = GlobalConst("a", ty.f32(), Expr(1.f));
- auto* expr = Expr(a);
- WrapInFunction(expr);
+ auto* a = GlobalConst("a", ty.f32(), Expr(1.f));
+ auto* expr = Expr(a);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, FunctionVar) {
- auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* expr = Expr(a);
- WrapInFunction(a, expr);
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* expr = Expr(a);
+ WrapInFunction(a, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, FunctionLet) {
- auto* a = Let("a", ty.f32(), Expr(1.f));
- auto* expr = Expr(a);
- WrapInFunction(a, expr);
+ auto* a = Let("a", ty.f32(), Expr(1.f));
+ auto* expr = Expr(a);
+ WrapInFunction(a, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, Parameter) {
- auto* a = Param("a", ty.f32());
- auto* expr = Expr(a);
- Func("foo", {a}, ty.void_(), {WrapInStatement(expr)});
+ auto* a = Param("a", ty.f32());
+ auto* expr = Expr(a);
+ Func("foo", {a}, ty.void_(), {WrapInStatement(expr)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, PointerParameter) {
- // fn foo(a : ptr<function, f32>)
- // {
- // let b = a;
- // }
- auto* param = Param("a", ty.pointer(ty.f32(), ast::StorageClass::kFunction));
- auto* expr_param = Expr(param);
- auto* let = Let("b", nullptr, expr_param);
- auto* expr_let = Expr("b");
- Func("foo", {param}, ty.void_(),
- {WrapInStatement(let), WrapInStatement(expr_let)});
+ // fn foo(a : ptr<function, f32>)
+ // {
+ // let b = a;
+ // }
+ auto* param = Param("a", ty.pointer(ty.f32(), ast::StorageClass::kFunction));
+ auto* expr_param = Expr(param);
+ auto* let = Let("b", nullptr, expr_param);
+ auto* expr_let = Expr("b");
+ Func("foo", {param}, ty.void_(), {WrapInStatement(let), WrapInStatement(expr_let)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_param = Sem().Get(param);
- EXPECT_EQ(Sem().Get(expr_param)->SourceVariable(), sem_param);
- EXPECT_EQ(Sem().Get(expr_let)->SourceVariable(), sem_param);
+ auto* sem_param = Sem().Get(param);
+ EXPECT_EQ(Sem().Get(expr_param)->SourceVariable(), sem_param);
+ EXPECT_EQ(Sem().Get(expr_let)->SourceVariable(), sem_param);
}
TEST_F(ResolverSourceVariableTest, VarCopyVar) {
- // {
- // var a : f32;
- // var b = a;
- // }
- auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* expr_a = Expr(a);
- auto* b = Var("b", ty.f32(), ast::StorageClass::kNone, expr_a);
- auto* expr_b = Expr(b);
- WrapInFunction(a, b, expr_b);
+ // {
+ // var a : f32;
+ // var b = a;
+ // }
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* expr_a = Expr(a);
+ auto* b = Var("b", ty.f32(), ast::StorageClass::kNone, expr_a);
+ auto* expr_b = Expr(b);
+ WrapInFunction(a, b, expr_b);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- auto* sem_b = Sem().Get(b);
- EXPECT_EQ(Sem().Get(expr_a)->SourceVariable(), sem_a);
- EXPECT_EQ(Sem().Get(expr_b)->SourceVariable(), sem_b);
+ auto* sem_a = Sem().Get(a);
+ auto* sem_b = Sem().Get(b);
+ EXPECT_EQ(Sem().Get(expr_a)->SourceVariable(), sem_a);
+ EXPECT_EQ(Sem().Get(expr_b)->SourceVariable(), sem_b);
}
TEST_F(ResolverSourceVariableTest, LetCopyVar) {
- // {
- // var a : f32;
- // let b = a;
- // }
- auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* expr_a = Expr(a);
- auto* b = Let("b", ty.f32(), expr_a);
- auto* expr_b = Expr(b);
- WrapInFunction(a, b, expr_b);
+ // {
+ // var a : f32;
+ // let b = a;
+ // }
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* expr_a = Expr(a);
+ auto* b = Let("b", ty.f32(), expr_a);
+ auto* expr_b = Expr(b);
+ WrapInFunction(a, b, expr_b);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- auto* sem_b = Sem().Get(b);
- EXPECT_EQ(Sem().Get(expr_a)->SourceVariable(), sem_a);
- EXPECT_EQ(Sem().Get(expr_b)->SourceVariable(), sem_b);
+ auto* sem_a = Sem().Get(a);
+ auto* sem_b = Sem().Get(b);
+ EXPECT_EQ(Sem().Get(expr_a)->SourceVariable(), sem_a);
+ EXPECT_EQ(Sem().Get(expr_b)->SourceVariable(), sem_b);
}
TEST_F(ResolverSourceVariableTest, ThroughIndexAccessor) {
- // var<private> a : array<f32, 4>;
- // {
- // a[2]
- // }
- auto* a = Global("a", ty.array(ty.f32(), 4), ast::StorageClass::kPrivate);
- auto* expr = IndexAccessor(a, 2);
- WrapInFunction(expr);
+ // var<private> a : array<f32, 4>;
+ // {
+ // a[2]
+ // }
+ auto* a = Global("a", ty.array(ty.f32(), 4), ast::StorageClass::kPrivate);
+ auto* expr = IndexAccessor(a, 2);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, ThroughMemberAccessor) {
- // struct S { f : f32 }
- // var<private> a : S;
- // {
- // a.f
- // }
- auto* S = Structure("S", {Member("f", ty.f32())});
- auto* a = Global("a", ty.Of(S), ast::StorageClass::kPrivate);
- auto* expr = MemberAccessor(a, "f");
- WrapInFunction(expr);
+ // struct S { f : f32 }
+ // var<private> a : S;
+ // {
+ // a.f
+ // }
+ auto* S = Structure("S", {Member("f", ty.f32())});
+ auto* a = Global("a", ty.Of(S), ast::StorageClass::kPrivate);
+ auto* expr = MemberAccessor(a, "f");
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, ThroughPointers) {
- // var<private> a : f32;
- // {
- // let a_ptr1 = &*&a;
- // let a_ptr2 = &*a_ptr1;
- // }
- auto* a = Global("a", ty.f32(), ast::StorageClass::kPrivate);
- auto* address_of_1 = AddressOf(a);
- auto* deref_1 = Deref(address_of_1);
- auto* address_of_2 = AddressOf(deref_1);
- auto* a_ptr1 = Let("a_ptr1", nullptr, address_of_2);
- auto* deref_2 = Deref(a_ptr1);
- auto* address_of_3 = AddressOf(deref_2);
- auto* a_ptr2 = Let("a_ptr2", nullptr, address_of_3);
- WrapInFunction(a_ptr1, a_ptr2);
+ // var<private> a : f32;
+ // {
+ // let a_ptr1 = &*&a;
+ // let a_ptr2 = &*a_ptr1;
+ // }
+ auto* a = Global("a", ty.f32(), ast::StorageClass::kPrivate);
+ auto* address_of_1 = AddressOf(a);
+ auto* deref_1 = Deref(address_of_1);
+ auto* address_of_2 = AddressOf(deref_1);
+ auto* a_ptr1 = Let("a_ptr1", nullptr, address_of_2);
+ auto* deref_2 = Deref(a_ptr1);
+ auto* address_of_3 = AddressOf(deref_2);
+ auto* a_ptr2 = Let("a_ptr2", nullptr, address_of_3);
+ WrapInFunction(a_ptr1, a_ptr2);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* sem_a = Sem().Get(a);
- EXPECT_EQ(Sem().Get(address_of_1)->SourceVariable(), sem_a);
- EXPECT_EQ(Sem().Get(address_of_2)->SourceVariable(), sem_a);
- EXPECT_EQ(Sem().Get(address_of_3)->SourceVariable(), sem_a);
- EXPECT_EQ(Sem().Get(deref_1)->SourceVariable(), sem_a);
- EXPECT_EQ(Sem().Get(deref_2)->SourceVariable(), sem_a);
+ auto* sem_a = Sem().Get(a);
+ EXPECT_EQ(Sem().Get(address_of_1)->SourceVariable(), sem_a);
+ EXPECT_EQ(Sem().Get(address_of_2)->SourceVariable(), sem_a);
+ EXPECT_EQ(Sem().Get(address_of_3)->SourceVariable(), sem_a);
+ EXPECT_EQ(Sem().Get(deref_1)->SourceVariable(), sem_a);
+ EXPECT_EQ(Sem().Get(deref_2)->SourceVariable(), sem_a);
}
TEST_F(ResolverSourceVariableTest, Literal) {
- auto* expr = Expr(1.f);
- WrapInFunction(expr);
+ auto* expr = Expr(1.f);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
}
TEST_F(ResolverSourceVariableTest, FunctionReturnValue) {
- auto* expr = Call("min", 1.f, 2.f);
- WrapInFunction(expr);
+ auto* expr = Call("min", 1.f, 2.f);
+ WrapInFunction(expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
}
TEST_F(ResolverSourceVariableTest, BinaryExpression) {
- auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* expr = Add(a, Expr(1.f));
- WrapInFunction(a, expr);
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* expr = Add(a, Expr(1.f));
+ WrapInFunction(a, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
}
TEST_F(ResolverSourceVariableTest, UnaryExpression) {
- auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* expr = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr(a));
- WrapInFunction(a, expr);
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* expr = create<ast::UnaryOpExpression>(ast::UnaryOp::kNegation, Expr(a));
+ WrapInFunction(a, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
+ EXPECT_EQ(Sem().Get(expr)->SourceVariable(), nullptr);
}
} // namespace
diff --git a/src/tint/resolver/storage_class_layout_validation_test.cc b/src/tint/resolver/storage_class_layout_validation_test.cc
index 23d4c87..e5f8a40 100644
--- a/src/tint/resolver/storage_class_layout_validation_test.cc
+++ b/src/tint/resolver/storage_class_layout_validation_test.cc
@@ -23,26 +23,25 @@
using ResolverStorageClassLayoutValidationTest = ResolverTest;
// Detect unaligned member for storage buffers
-TEST_F(ResolverStorageClassLayoutValidationTest,
- StorageBuffer_UnalignedMember) {
- // struct S {
- // @size(5) a : f32;
- // @align(1) b : f32;
- // };
- // @group(0) @binding(0)
- // var<storage> a : S;
+TEST_F(ResolverStorageClassLayoutValidationTest, StorageBuffer_UnalignedMember) {
+ // struct S {
+ // @size(5) a : f32;
+ // @align(1) b : f32;
+ // };
+ // @group(0) @binding(0)
+ // var<storage> a : S;
- Structure(Source{{12, 34}}, "S",
- {Member("a", ty.f32(), {MemberSize(5)}),
- Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(1)})});
+ Structure(Source{{12, 34}}, "S",
+ {Member("a", ty.f32(), {MemberSize(5)}),
+ Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(1)})});
- Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
- GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(34:56 error: the offset of a struct member of type 'f32' in storage class 'storage' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting @align(4) on this member
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(34:56 error: the offset of a struct member of type 'f32' in storage class 'storage' must be a multiple of 4 bytes, but 'b' is currently at offset 5. Consider setting @align(4) on this member
12:34 note: see layout of struct:
/* align(4) size(12) */ struct S {
/* offset(0) align(4) size( 5) */ a : f32;
@@ -52,55 +51,53 @@
78:90 note: see declaration of variable)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- StorageBuffer_UnalignedMember_SuggestedFix) {
- // struct S {
- // @size(5) a : f32;
- // @align(4) b : f32;
- // };
- // @group(0) @binding(0)
- // var<storage> a : S;
+TEST_F(ResolverStorageClassLayoutValidationTest, StorageBuffer_UnalignedMember_SuggestedFix) {
+ // struct S {
+ // @size(5) a : f32;
+ // @align(4) b : f32;
+ // };
+ // @group(0) @binding(0)
+ // var<storage> a : S;
- Structure(Source{{12, 34}}, "S",
- {Member("a", ty.f32(), {MemberSize(5)}),
- Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(4)})});
+ Structure(Source{{12, 34}}, "S",
+ {Member("a", ty.f32(), {MemberSize(5)}),
+ Member(Source{{34, 56}}, "b", ty.f32(), {MemberAlign(4)})});
- Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
- GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("S"), ast::StorageClass::kStorage,
+ GroupAndBinding(0, 0));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
// Detect unaligned struct member for uniform buffers
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_UnalignedMember_Struct) {
- // struct Inner {
- // scalar : i32;
- // };
- //
- // struct Outer {
- // scalar : f32;
- // inner : Inner;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_UnalignedMember_Struct) {
+ // struct Inner {
+ // scalar : i32;
+ // };
+ //
+ // struct Outer {
+ // scalar : f32;
+ // inner : Inner;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Structure(Source{{12, 34}}, "Inner", {Member("scalar", ty.i32())});
+ Structure(Source{{12, 34}}, "Inner", {Member("scalar", ty.i32())});
- Structure(Source{{34, 56}}, "Outer",
- {
- Member("scalar", ty.f32()),
- Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
- });
+ Structure(Source{{34, 56}}, "Outer",
+ {
+ Member("scalar", ty.f32()),
+ Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: the offset of a struct member of type 'Inner' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: the offset of a struct member of type 'Inner' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
34:56 note: see layout of struct:
/* align(4) size(8) */ struct Outer {
/* offset(0) align(4) size(4) */ scalar : f32;
@@ -115,60 +112,58 @@
TEST_F(ResolverStorageClassLayoutValidationTest,
UniformBuffer_UnalignedMember_Struct_SuggestedFix) {
- // struct Inner {
- // scalar : i32;
- // };
- //
- // struct Outer {
- // scalar : f32;
- // @align(16) inner : Inner;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+ // struct Inner {
+ // scalar : i32;
+ // };
+ //
+ // struct Outer {
+ // scalar : f32;
+ // @align(16) inner : Inner;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Structure(Source{{12, 34}}, "Inner", {Member("scalar", ty.i32())});
+ Structure(Source{{12, 34}}, "Inner", {Member("scalar", ty.i32())});
- Structure(Source{{34, 56}}, "Outer",
- {
- Member("scalar", ty.f32()),
- Member(Source{{56, 78}}, "inner", ty.type_name("Inner"),
- {MemberAlign(16)}),
- });
+ Structure(Source{{34, 56}}, "Outer",
+ {
+ Member("scalar", ty.f32()),
+ Member(Source{{56, 78}}, "inner", ty.type_name("Inner"), {MemberAlign(16)}),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
// Detect unaligned array member for uniform buffers
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_UnalignedMember_Array) {
- // type Inner = @stride(16) array<f32, 10>;
- //
- // struct Outer {
- // scalar : f32;
- // inner : Inner;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
- Alias("Inner", ty.array(ty.f32(), 10, 16));
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_UnalignedMember_Array) {
+ // type Inner = @stride(16) array<f32, 10>;
+ //
+ // struct Outer {
+ // scalar : f32;
+ // inner : Inner;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
+ Alias("Inner", ty.array(ty.f32(), 10, 16));
- Structure(Source{{12, 34}}, "Outer",
- {
- Member("scalar", ty.f32()),
- Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("scalar", ty.f32()),
+ Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: the offset of a struct member of type '@stride(16) array<f32, 10>' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: the offset of a struct member of type '@stride(16) array<f32, 10>' in storage class 'uniform' must be a multiple of 16 bytes, but 'inner' is currently at offset 4. Consider setting @align(16) on this member
12:34 note: see layout of struct:
/* align(4) size(164) */ struct Outer {
/* offset( 0) align(4) size( 4) */ scalar : f32;
@@ -177,64 +172,61 @@
78:90 note: see declaration of variable)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_UnalignedMember_Array_SuggestedFix) {
- // type Inner = @stride(16) array<f32, 10>;
- //
- // struct Outer {
- // scalar : f32;
- // @align(16) inner : Inner;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
- Alias("Inner", ty.array(ty.f32(), 10, 16));
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_UnalignedMember_Array_SuggestedFix) {
+ // type Inner = @stride(16) array<f32, 10>;
+ //
+ // struct Outer {
+ // scalar : f32;
+ // @align(16) inner : Inner;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
+ Alias("Inner", ty.array(ty.f32(), 10, 16));
- Structure(Source{{12, 34}}, "Outer",
- {
- Member("scalar", ty.f32()),
- Member(Source{{34, 56}}, "inner", ty.type_name("Inner"),
- {MemberAlign(16)}),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("scalar", ty.f32()),
+ Member(Source{{34, 56}}, "inner", ty.type_name("Inner"), {MemberAlign(16)}),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
// Detect uniform buffers with byte offset between 2 members that is not a
// multiple of 16 bytes
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_MembersOffsetNotMultipleOf16) {
- // struct Inner {
- // @align(1) @size(5) scalar : i32;
- // };
- //
- // struct Outer {
- // inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_MembersOffsetNotMultipleOf16) {
+ // struct Inner {
+ // @align(1) @size(5) scalar : i32;
+ // };
+ //
+ // struct Outer {
+ // inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Structure(Source{{12, 34}}, "Inner",
- {Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)})});
+ Structure(Source{{12, 34}}, "Inner",
+ {Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)})});
- Structure(Source{{34, 56}}, "Outer",
- {
- Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
- Member(Source{{78, 90}}, "scalar", ty.i32()),
- });
+ Structure(Source{{34, 56}}, "Outer",
+ {
+ Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
+ Member(Source{{78, 90}}, "scalar", ty.i32()),
+ });
- Global(Source{{22, 24}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(78:90 error: uniform storage requires that the number of bytes between the start of the previous member of type struct and the current member be a multiple of 16 bytes, but there are currently 8 bytes between 'inner' and 'scalar'. Consider setting @align(16) on this member
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(78:90 error: uniform storage requires that the number of bytes between the start of the previous member of type struct and the current member be a multiple of 16 bytes, but there are currently 8 bytes between 'inner' and 'scalar'. Consider setting @align(16) on this member
34:56 note: see layout of struct:
/* align(4) size(12) */ struct Outer {
/* offset( 0) align(1) size( 5) */ inner : Inner;
@@ -251,42 +243,42 @@
// See https://crbug.com/tint/1344
TEST_F(ResolverStorageClassLayoutValidationTest,
UniformBuffer_MembersOffsetNotMultipleOf16_InnerMoreMembersThanOuter) {
- // struct Inner {
- // a : i32;
- // b : i32;
- // c : i32;
- // @align(1) @size(5) scalar : i32;
- // };
- //
- // struct Outer {
- // inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+ // struct Inner {
+ // a : i32;
+ // b : i32;
+ // c : i32;
+ // @align(1) @size(5) scalar : i32;
+ // };
+ //
+ // struct Outer {
+ // inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Structure(Source{{12, 34}}, "Inner",
- {
- Member("a", ty.i32()),
- Member("b", ty.i32()),
- Member("c", ty.i32()),
- Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)}),
- });
+ Structure(Source{{12, 34}}, "Inner",
+ {
+ Member("a", ty.i32()),
+ Member("b", ty.i32()),
+ Member("c", ty.i32()),
+ Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)}),
+ });
- Structure(Source{{34, 56}}, "Outer",
- {
- Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
- Member(Source{{78, 90}}, "scalar", ty.i32()),
- });
+ Structure(Source{{34, 56}}, "Outer",
+ {
+ Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
+ Member(Source{{78, 90}}, "scalar", ty.i32()),
+ });
- Global(Source{{22, 24}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{22, 24}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(78:90 error: uniform storage requires that the number of bytes between the start of the previous member of type struct and the current member be a multiple of 16 bytes, but there are currently 20 bytes between 'inner' and 'scalar'. Consider setting @align(16) on this member
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(78:90 error: uniform storage requires that the number of bytes between the start of the previous member of type struct and the current member be a multiple of 16 bytes, but there are currently 20 bytes between 'inner' and 'scalar'. Consider setting @align(16) on this member
34:56 note: see layout of struct:
/* align(4) size(24) */ struct Outer {
/* offset( 0) align(4) size(20) */ inner : Inner;
@@ -305,83 +297,81 @@
TEST_F(ResolverStorageClassLayoutValidationTest,
UniformBuffer_MembersOffsetNotMultipleOf16_SuggestedFix) {
- // struct Inner {
- // @align(1) @size(5) scalar : i32;
- // };
- //
- // struct Outer {
- // @align(16) inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+ // struct Inner {
+ // @align(1) @size(5) scalar : i32;
+ // };
+ //
+ // struct Outer {
+ // @align(16) inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Structure(Source{{12, 34}}, "Inner",
- {Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)})});
+ Structure(Source{{12, 34}}, "Inner",
+ {Member("scalar", ty.i32(), {MemberAlign(1), MemberSize(5)})});
- Structure(Source{{34, 56}}, "Outer",
- {
- Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
- Member(Source{{78, 90}}, "scalar", ty.i32(), {MemberAlign(16)}),
- });
+ Structure(Source{{34, 56}}, "Outer",
+ {
+ Member(Source{{56, 78}}, "inner", ty.type_name("Inner")),
+ Member(Source{{78, 90}}, "scalar", ty.i32(), {MemberAlign(16)}),
+ });
- Global(Source{{22, 34}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{22, 34}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
// Make sure that this doesn't fail validation because vec3's align is 16, but
// size is 12. 's' should be at offset 12, which is okay here.
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_Vec3MemberOffset_NoFail) {
- // struct ScalarPackedAtEndOfVec3 {
- // v : vec3<f32>;
- // s : f32;
- // };
- // @group(0) @binding(0)
- // var<uniform> a : ScalarPackedAtEndOfVec3;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_Vec3MemberOffset_NoFail) {
+ // struct ScalarPackedAtEndOfVec3 {
+ // v : vec3<f32>;
+ // s : f32;
+ // };
+ // @group(0) @binding(0)
+ // var<uniform> a : ScalarPackedAtEndOfVec3;
- Structure("ScalarPackedAtEndOfVec3", {
- Member("v", ty.vec3(ty.f32())),
- Member("s", ty.f32()),
- });
+ Structure("ScalarPackedAtEndOfVec3", {
+ Member("v", ty.vec3(ty.f32())),
+ Member("s", ty.f32()),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("ScalarPackedAtEndOfVec3"),
+ ast::StorageClass::kUniform, GroupAndBinding(0, 0));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
// Detect array stride must be a multiple of 16 bytes for uniform buffers
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_InvalidArrayStride_Scalar) {
- // type Inner = array<f32, 10>;
- //
- // struct Outer {
- // inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_Scalar) {
+ // type Inner = array<f32, 10>;
+ //
+ // struct Outer {
+ // inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Alias("Inner", ty.array(ty.f32(), 10));
+ Alias("Inner", ty.array(ty.f32(), 10));
- Structure(Source{{12, 34}}, "Outer",
- {
- Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
- Member("scalar", ty.i32()),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
+ Member("scalar", ty.i32()),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.
12:34 note: see layout of struct:
/* align(4) size(44) */ struct Outer {
/* offset( 0) align(4) size(40) */ inner : array<f32, 10>;
@@ -390,33 +380,32 @@
78:90 note: see declaration of variable)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_InvalidArrayStride_Vector) {
- // type Inner = array<vec2<f32>, 10>;
- //
- // struct Outer {
- // inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_Vector) {
+ // type Inner = array<vec2<f32>, 10>;
+ //
+ // struct Outer {
+ // inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Alias("Inner", ty.array(ty.vec2<f32>(), 10));
+ Alias("Inner", ty.array(ty.vec2<f32>(), 10));
- Structure(Source{{12, 34}}, "Outer",
- {
- Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
- Member("scalar", ty.i32()),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
+ Member("scalar", ty.i32()),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 8. Consider using a vec4 instead.
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 8. Consider using a vec4 instead.
12:34 note: see layout of struct:
/* align(8) size(88) */ struct Outer {
/* offset( 0) align(8) size(80) */ inner : array<vec2<f32>, 10>;
@@ -426,41 +415,40 @@
78:90 note: see declaration of variable)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_InvalidArrayStride_Struct) {
- // struct ArrayElem {
- // a : f32;
- // b : i32;
- // }
- // type Inner = array<ArrayElem, 10>;
- //
- // struct Outer {
- // inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_Struct) {
+ // struct ArrayElem {
+ // a : f32;
+ // b : i32;
+ // }
+ // type Inner = array<ArrayElem, 10>;
+ //
+ // struct Outer {
+ // inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- auto* array_elem = Structure("ArrayElem", {
- Member("a", ty.f32()),
- Member("b", ty.i32()),
- });
- Alias("Inner", ty.array(ty.Of(array_elem), 10));
+ auto* array_elem = Structure("ArrayElem", {
+ Member("a", ty.f32()),
+ Member("b", ty.i32()),
+ });
+ Alias("Inner", ty.array(ty.Of(array_elem), 10));
- Structure(Source{{12, 34}}, "Outer",
- {
- Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
- Member("scalar", ty.i32()),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
+ Member("scalar", ty.i32()),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 8. Consider using the @size attribute on the last struct member.
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 8. Consider using the @size attribute on the last struct member.
12:34 note: see layout of struct:
/* align(4) size(84) */ struct Outer {
/* offset( 0) align(4) size(80) */ inner : array<ArrayElem, 10>;
@@ -469,41 +457,38 @@
78:90 note: see declaration of variable)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_InvalidArrayStride_TopLevelArray) {
- // @group(0) @binding(0)
- // var<uniform> a : array<f32, 4>;
- Global(Source{{78, 90}}, "a", ty.array(Source{{34, 56}}, ty.f32(), 4),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_TopLevelArray) {
+ // @group(0) @binding(0)
+ // var<uniform> a : array<f32, 4>;
+ Global(Source{{78, 90}}, "a", ty.array(Source{{34, 56}}, ty.f32(), 4),
+ ast::StorageClass::kUniform, GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_InvalidArrayStride_NestedArray) {
- // struct Outer {
- // inner : array<array<f32, 4>, 4>
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : array<Outer, 4>;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_NestedArray) {
+ // struct Outer {
+ // inner : array<array<f32, 4>, 4>
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : array<Outer, 4>;
- Structure(
- Source{{12, 34}}, "Outer",
- {
- Member("inner", ty.array(Source{{34, 56}}, ty.array(ty.f32(), 4), 4)),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("inner", ty.array(Source{{34, 56}}, ty.array(ty.f32(), 4), 4)),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(34:56 error: uniform storage requires that array elements be aligned to 16 bytes, but array element alignment is currently 4. Consider using a vector or struct as the element type instead.
12:34 note: see layout of struct:
/* align(4) size(64) */ struct Outer {
/* offset( 0) align(4) size(64) */ inner : array<array<f32, 4>, 4>;
@@ -511,30 +496,29 @@
78:90 note: see declaration of variable)");
}
-TEST_F(ResolverStorageClassLayoutValidationTest,
- UniformBuffer_InvalidArrayStride_SuggestedFix) {
- // type Inner = @stride(16) array<f32, 10>;
- //
- // struct Outer {
- // inner : Inner;
- // scalar : i32;
- // };
- //
- // @group(0) @binding(0)
- // var<uniform> a : Outer;
+TEST_F(ResolverStorageClassLayoutValidationTest, UniformBuffer_InvalidArrayStride_SuggestedFix) {
+ // type Inner = @stride(16) array<f32, 10>;
+ //
+ // struct Outer {
+ // inner : Inner;
+ // scalar : i32;
+ // };
+ //
+ // @group(0) @binding(0)
+ // var<uniform> a : Outer;
- Alias("Inner", ty.array(ty.f32(), 10, 16));
+ Alias("Inner", ty.array(ty.f32(), 10, 16));
- Structure(Source{{12, 34}}, "Outer",
- {
- Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
- Member("scalar", ty.i32()),
- });
+ Structure(Source{{12, 34}}, "Outer",
+ {
+ Member("inner", ty.type_name(Source{{34, 56}}, "Inner")),
+ Member("scalar", ty.i32()),
+ });
- Global(Source{{78, 90}}, "a", ty.type_name("Outer"),
- ast::StorageClass::kUniform, GroupAndBinding(0, 0));
+ Global(Source{{78, 90}}, "a", ty.type_name("Outer"), ast::StorageClass::kUniform,
+ GroupAndBinding(0, 0));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/storage_class_validation_test.cc b/src/tint/resolver/storage_class_validation_test.cc
index 1173578..ed4e6b8 100644
--- a/src/tint/resolver/storage_class_validation_test.cc
+++ b/src/tint/resolver/storage_class_validation_test.cc
@@ -24,338 +24,323 @@
using ResolverStorageClassValidationTest = ResolverTest;
TEST_F(ResolverStorageClassValidationTest, GlobalVariableNoStorageClass_Fail) {
- // var g : f32;
- Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
+ // var g : f32;
+ Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kNone);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: global variables must have a storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: global variables must have a storage class");
}
-TEST_F(ResolverStorageClassValidationTest,
- GlobalVariableFunctionStorageClass_Fail) {
- // var<function> g : f32;
- Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kFunction);
+TEST_F(ResolverStorageClassValidationTest, GlobalVariableFunctionStorageClass_Fail) {
+ // var<function> g : f32;
+ Global(Source{{12, 34}}, "g", ty.f32(), ast::StorageClass::kFunction);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: variables declared at module scope must not be in "
- "the function storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: variables declared at module scope must not be in "
+ "the function storage class");
}
TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArray) {
- Global(Source{{12, 34}}, "v", ty.array(ty.i32()),
- ast::StorageClass::kPrivate);
+ Global(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
12:34 note: while instantiating variable v)");
}
TEST_F(ResolverStorageClassValidationTest, Private_RuntimeArrayInStruct) {
- auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
- Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
+ auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
+ Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
note: while analysing structure member S.m
12:34 note: while instantiating variable v)");
}
TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArray) {
- Global(Source{{12, 34}}, "v", ty.array(ty.i32()),
- ast::StorageClass::kWorkgroup);
+ Global(Source{{12, 34}}, "v", ty.array(ty.i32()), ast::StorageClass::kWorkgroup);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
12:34 note: while instantiating variable v)");
}
TEST_F(ResolverStorageClassValidationTest, Workgroup_RuntimeArrayInStruct) {
- auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
- Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
+ auto* s = Structure("S", {Member("m", ty.array(ty.i32()))});
+ Global(Source{{12, 34}}, "v", ty.Of(s), ast::StorageClass::kWorkgroup);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
note: while analysing structure member S.m
12:34 note: while instantiating variable v)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferBool) {
- // var<storage> g : bool;
- Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<storage> g : bool;
+ Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferPointer) {
- // var<storage> g : ptr<private, f32>;
- Global(Source{{56, 78}}, "g",
- ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
- ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<storage> g : ptr<private, f32>;
+ Global(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
+ ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'storage' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferIntScalar) {
- // var<storage> g : i32;
- Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<storage> g : i32;
+ Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferVector) {
- // var<storage> g : vec4<f32>;
- Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<storage> g : vec4<f32>;
+ Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferArray) {
- // var<storage, read> g : array<S, 3>;
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* a = ty.array(ty.Of(s), 3);
- Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<storage, read> g : array<S, 3>;
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* a = ty.array(ty.Of(s), 3);
+ Global(Source{{56, 78}}, "g", a, ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferBoolAlias) {
- // type a = bool;
- // var<storage, read> g : a;
- auto* a = Alias("a", ty.bool_());
- Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // type a = bool;
+ // var<storage, read> g : a;
+ auto* a = Alias("a", ty.bool_());
+ Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'bool' cannot be used in storage class 'storage' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, NotStorage_AccessMode) {
- // var<private, read> g : a;
- Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate,
- ast::Access::kRead);
+ // var<private, read> g : a;
+ Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kPrivate, ast::Access::kRead);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: only variables in <storage> storage class may declare an access mode)");
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: only variables in <storage> storage class may declare an access mode)");
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Basic) {
- // struct S { x : i32 };
- // var<storage, read> g : S;
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S { x : i32 };
+ // var<storage, read> g : S;
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+ Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve());
+ ASSERT_TRUE(r()->Resolve());
}
TEST_F(ResolverStorageClassValidationTest, StorageBufferNoError_Aliases) {
- // struct S { x : i32 };
- // type a1 = S;
- // var<storage, read> g : a1;
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
- auto* a1 = Alias("a1", ty.Of(s));
- auto* a2 = Alias("a2", ty.Of(a1));
- Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage,
- ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S { x : i32 };
+ // type a1 = S;
+ // var<storage, read> g : a1;
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+ auto* a1 = Alias("a1", ty.Of(s));
+ auto* a2 = Alias("a2", ty.Of(a1));
+ Global(Source{{56, 78}}, "g", ty.Of(a2), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve());
+ ASSERT_TRUE(r()->Resolve());
}
TEST_F(ResolverStorageClassValidationTest, UniformBuffer_Struct_Runtime) {
- // struct S { m: array<f32>; };
- // @group(0) @binding(0) var<uniform, > svar : S;
+ // struct S { m: array<f32>; };
+ // @group(0) @binding(0) var<uniform, > svar : S;
- auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())});
+ auto* s = Structure(Source{{12, 34}}, "S", {Member("m", ty.array<i32>())});
- Global(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ Global(Source{{56, 78}}, "svar", ty.Of(s), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
+ ASSERT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
note: while analysing structure member S.m
56:78 note: while instantiating variable svar)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferBool) {
- // var<uniform> g : bool;
- Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<uniform> g : bool;
+ Global(Source{{56, 78}}, "g", ty.bool_(), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferPointer) {
- // var<uniform> g : ptr<private, f32>;
- Global(Source{{56, 78}}, "g",
- ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
- ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<uniform> g : ptr<private, f32>;
+ Global(Source{{56, 78}}, "g", ty.pointer(ty.f32(), ast::StorageClass::kPrivate),
+ ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'uniform' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'ptr<private, f32, read_write>' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferIntScalar) {
- // var<uniform> g : i32;
- Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<uniform> g : i32;
+ Global(Source{{56, 78}}, "g", ty.i32(), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferVector) {
- // var<uniform> g : vec4<f32>;
- Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // var<uniform> g : vec4<f32>;
+ Global(Source{{56, 78}}, "g", ty.vec4<f32>(), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferArray) {
- // struct S {
- // @size(16) f : f32;
- // }
- // var<uniform> g : array<S, 3>;
- auto* s = Structure("S", {Member("a", ty.f32(), {MemberSize(16)})});
- auto* a = ty.array(ty.Of(s), 3);
- Global(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S {
+ // @size(16) f : f32;
+ // }
+ // var<uniform> g : array<S, 3>;
+ auto* s = Structure("S", {Member("a", ty.f32(), {MemberSize(16)})});
+ auto* a = ty.array(ty.Of(s), 3);
+ Global(Source{{56, 78}}, "g", a, ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferBoolAlias) {
- // type a = bool;
- // var<uniform> g : a;
- auto* a = Alias("a", ty.bool_());
- Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // type a = bool;
+ // var<uniform> g : a;
+ auto* a = Alias("a", ty.bool_());
+ Global(Source{{56, 78}}, "g", ty.Of(a), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
+ EXPECT_EQ(
+ r()->error(),
+ R"(56:78 error: Type 'bool' cannot be used in storage class 'uniform' as it is non-host-shareable
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Basic) {
- // struct S { x : i32 };
- // var<uniform> g : S;
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
- Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S { x : i32 };
+ // var<uniform> g : S;
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+ Global(Source{{56, 78}}, "g", ty.Of(s), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverStorageClassValidationTest, UniformBufferNoError_Aliases) {
- // struct S { x : i32 };
- // type a1 = S;
- // var<uniform> g : a1;
- auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
- auto* a1 = Alias("a1", ty.Of(s));
- Global(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct S { x : i32 };
+ // type a1 = S;
+ // var<uniform> g : a1;
+ auto* s = Structure("S", {Member(Source{{12, 34}}, "x", ty.i32())});
+ auto* a1 = Alias("a1", ty.Of(s));
+ Global(Source{{56, 78}}, "g", ty.Of(a1), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace
diff --git a/src/tint/resolver/struct_layout_test.cc b/src/tint/resolver/struct_layout_test.cc
index 7c2f450..04b1389 100644
--- a/src/tint/resolver/struct_layout_test.cc
+++ b/src/tint/resolver/struct_layout_test.cc
@@ -24,379 +24,379 @@
using ResolverStructLayoutTest = ResolverTest;
TEST_F(ResolverStructLayoutTest, Scalars) {
- auto* s = Structure("S", {
- Member("a", ty.f32()),
- Member("b", ty.u32()),
- Member("c", ty.i32()),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.f32()),
+ Member("b", ty.u32()),
+ Member("c", ty.i32()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 12u);
- EXPECT_EQ(sem->SizeNoPadding(), 12u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 3u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
- EXPECT_EQ(sem->Members()[1]->Align(), 4u);
- EXPECT_EQ(sem->Members()[1]->Size(), 4u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 8u);
- EXPECT_EQ(sem->Members()[2]->Align(), 4u);
- EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 12u);
+ EXPECT_EQ(sem->SizeNoPadding(), 12u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 3u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 8u);
+ EXPECT_EQ(sem->Members()[2]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 4u);
}
TEST_F(ResolverStructLayoutTest, Alias) {
- auto* alias_a = Alias("a", ty.f32());
- auto* alias_b = Alias("b", ty.f32());
+ auto* alias_a = Alias("a", ty.f32());
+ auto* alias_b = Alias("b", ty.f32());
- auto* s = Structure("S", {
- Member("a", ty.Of(alias_a)),
- Member("b", ty.Of(alias_b)),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.Of(alias_a)),
+ Member("b", ty.Of(alias_b)),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 8u);
- EXPECT_EQ(sem->SizeNoPadding(), 8u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 2u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
- EXPECT_EQ(sem->Members()[1]->Align(), 4u);
- EXPECT_EQ(sem->Members()[1]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 8u);
+ EXPECT_EQ(sem->SizeNoPadding(), 8u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 2u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 4u);
}
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayStaticSize) {
- auto* s = Structure("S", {
- Member("a", ty.array<i32, 3>()),
- Member("b", ty.array<f32, 5>()),
- Member("c", ty.array<f32, 1>()),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.array<i32, 3>()),
+ Member("b", ty.array<f32, 5>()),
+ Member("c", ty.array<f32, 1>()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 36u);
- EXPECT_EQ(sem->SizeNoPadding(), 36u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 3u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 12u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 12u);
- EXPECT_EQ(sem->Members()[1]->Align(), 4u);
- EXPECT_EQ(sem->Members()[1]->Size(), 20u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 32u);
- EXPECT_EQ(sem->Members()[2]->Align(), 4u);
- EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 36u);
+ EXPECT_EQ(sem->SizeNoPadding(), 36u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 3u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 12u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 12u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 20u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 32u);
+ EXPECT_EQ(sem->Members()[2]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 4u);
}
TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayStaticSize) {
- auto* s = Structure("S", {
- Member("a", ty.array<i32, 3>(/*stride*/ 8)),
- Member("b", ty.array<f32, 5>(/*stride*/ 16)),
- Member("c", ty.array<f32, 1>(/*stride*/ 32)),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.array<i32, 3>(/*stride*/ 8)),
+ Member("b", ty.array<f32, 5>(/*stride*/ 16)),
+ Member("c", ty.array<f32, 1>(/*stride*/ 32)),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 136u);
- EXPECT_EQ(sem->SizeNoPadding(), 136u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 3u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 24u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 24u);
- EXPECT_EQ(sem->Members()[1]->Align(), 4u);
- EXPECT_EQ(sem->Members()[1]->Size(), 80u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 104u);
- EXPECT_EQ(sem->Members()[2]->Align(), 4u);
- EXPECT_EQ(sem->Members()[2]->Size(), 32u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 136u);
+ EXPECT_EQ(sem->SizeNoPadding(), 136u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 3u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 24u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 24u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 80u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 104u);
+ EXPECT_EQ(sem->Members()[2]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 32u);
}
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayRuntimeSized) {
- auto* s = Structure("S", {
- Member("c", ty.array<f32>()),
- });
+ auto* s = Structure("S", {
+ Member("c", ty.array<f32>()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 4u);
- EXPECT_EQ(sem->SizeNoPadding(), 4u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 1u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 4u);
+ EXPECT_EQ(sem->SizeNoPadding(), 4u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 1u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
}
TEST_F(ResolverStructLayoutTest, ExplicitStrideArrayRuntimeSized) {
- auto* s = Structure("S", {
- Member("c", ty.array<f32>(/*stride*/ 32)),
- });
+ auto* s = Structure("S", {
+ Member("c", ty.array<f32>(/*stride*/ 32)),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 32u);
- EXPECT_EQ(sem->SizeNoPadding(), 32u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 1u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 32u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 32u);
+ EXPECT_EQ(sem->SizeNoPadding(), 32u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 1u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 32u);
}
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfExplicitStrideArray) {
- auto* inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32
- auto* outer = ty.array(inner, 12); // size: 12 * 32
- auto* s = Structure("S", {
- Member("c", outer),
- });
+ auto* inner = ty.array<i32, 2>(/*stride*/ 16); // size: 32
+ auto* outer = ty.array(inner, 12); // size: 12 * 32
+ auto* s = Structure("S", {
+ Member("c", outer),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 384u);
- EXPECT_EQ(sem->SizeNoPadding(), 384u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 1u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 384u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 384u);
+ EXPECT_EQ(sem->SizeNoPadding(), 384u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 1u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 384u);
}
TEST_F(ResolverStructLayoutTest, ImplicitStrideArrayOfStructure) {
- auto* inner = Structure("Inner", {
- Member("a", ty.vec2<i32>()),
- Member("b", ty.vec3<i32>()),
- Member("c", ty.vec4<i32>()),
- }); // size: 48
- auto* outer = ty.array(ty.Of(inner), 12); // size: 12 * 48
- auto* s = Structure("S", {
- Member("c", outer),
- });
+ auto* inner = Structure("Inner", {
+ Member("a", ty.vec2<i32>()),
+ Member("b", ty.vec3<i32>()),
+ Member("c", ty.vec4<i32>()),
+ }); // size: 48
+ auto* outer = ty.array(ty.Of(inner), 12); // size: 12 * 48
+ auto* s = Structure("S", {
+ Member("c", outer),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 576u);
- EXPECT_EQ(sem->SizeNoPadding(), 576u);
- EXPECT_EQ(sem->Align(), 16u);
- ASSERT_EQ(sem->Members().size(), 1u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 16u);
- EXPECT_EQ(sem->Members()[0]->Size(), 576u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 576u);
+ EXPECT_EQ(sem->SizeNoPadding(), 576u);
+ EXPECT_EQ(sem->Align(), 16u);
+ ASSERT_EQ(sem->Members().size(), 1u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 576u);
}
TEST_F(ResolverStructLayoutTest, Vector) {
- auto* s = Structure("S", {
- Member("a", ty.vec2<i32>()),
- Member("b", ty.vec3<i32>()),
- Member("c", ty.vec4<i32>()),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.vec2<i32>()),
+ Member("b", ty.vec3<i32>()),
+ Member("c", ty.vec4<i32>()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 48u);
- EXPECT_EQ(sem->SizeNoPadding(), 48u);
- EXPECT_EQ(sem->Align(), 16u);
- ASSERT_EQ(sem->Members().size(), 3u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // vec2
- EXPECT_EQ(sem->Members()[0]->Align(), 8u);
- EXPECT_EQ(sem->Members()[0]->Size(), 8u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // vec3
- EXPECT_EQ(sem->Members()[1]->Align(), 16u);
- EXPECT_EQ(sem->Members()[1]->Size(), 12u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 32u); // vec4
- EXPECT_EQ(sem->Members()[2]->Align(), 16u);
- EXPECT_EQ(sem->Members()[2]->Size(), 16u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 48u);
+ EXPECT_EQ(sem->SizeNoPadding(), 48u);
+ EXPECT_EQ(sem->Align(), 16u);
+ ASSERT_EQ(sem->Members().size(), 3u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // vec2
+ EXPECT_EQ(sem->Members()[0]->Align(), 8u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 8u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // vec3
+ EXPECT_EQ(sem->Members()[1]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 12u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 32u); // vec4
+ EXPECT_EQ(sem->Members()[2]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 16u);
}
TEST_F(ResolverStructLayoutTest, Matrix) {
- auto* s = Structure("S", {
- Member("a", ty.mat2x2<f32>()),
- Member("b", ty.mat2x3<f32>()),
- Member("c", ty.mat2x4<f32>()),
- Member("d", ty.mat3x2<f32>()),
- Member("e", ty.mat3x3<f32>()),
- Member("f", ty.mat3x4<f32>()),
- Member("g", ty.mat4x2<f32>()),
- Member("h", ty.mat4x3<f32>()),
- Member("i", ty.mat4x4<f32>()),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.mat2x2<f32>()),
+ Member("b", ty.mat2x3<f32>()),
+ Member("c", ty.mat2x4<f32>()),
+ Member("d", ty.mat3x2<f32>()),
+ Member("e", ty.mat3x3<f32>()),
+ Member("f", ty.mat3x4<f32>()),
+ Member("g", ty.mat4x2<f32>()),
+ Member("h", ty.mat4x3<f32>()),
+ Member("i", ty.mat4x4<f32>()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 368u);
- EXPECT_EQ(sem->SizeNoPadding(), 368u);
- EXPECT_EQ(sem->Align(), 16u);
- ASSERT_EQ(sem->Members().size(), 9u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // mat2x2
- EXPECT_EQ(sem->Members()[0]->Align(), 8u);
- EXPECT_EQ(sem->Members()[0]->Size(), 16u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // mat2x3
- EXPECT_EQ(sem->Members()[1]->Align(), 16u);
- EXPECT_EQ(sem->Members()[1]->Size(), 32u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 48u); // mat2x4
- EXPECT_EQ(sem->Members()[2]->Align(), 16u);
- EXPECT_EQ(sem->Members()[2]->Size(), 32u);
- EXPECT_EQ(sem->Members()[3]->Offset(), 80u); // mat3x2
- EXPECT_EQ(sem->Members()[3]->Align(), 8u);
- EXPECT_EQ(sem->Members()[3]->Size(), 24u);
- EXPECT_EQ(sem->Members()[4]->Offset(), 112u); // mat3x3
- EXPECT_EQ(sem->Members()[4]->Align(), 16u);
- EXPECT_EQ(sem->Members()[4]->Size(), 48u);
- EXPECT_EQ(sem->Members()[5]->Offset(), 160u); // mat3x4
- EXPECT_EQ(sem->Members()[5]->Align(), 16u);
- EXPECT_EQ(sem->Members()[5]->Size(), 48u);
- EXPECT_EQ(sem->Members()[6]->Offset(), 208u); // mat4x2
- EXPECT_EQ(sem->Members()[6]->Align(), 8u);
- EXPECT_EQ(sem->Members()[6]->Size(), 32u);
- EXPECT_EQ(sem->Members()[7]->Offset(), 240u); // mat4x3
- EXPECT_EQ(sem->Members()[7]->Align(), 16u);
- EXPECT_EQ(sem->Members()[7]->Size(), 64u);
- EXPECT_EQ(sem->Members()[8]->Offset(), 304u); // mat4x4
- EXPECT_EQ(sem->Members()[8]->Align(), 16u);
- EXPECT_EQ(sem->Members()[8]->Size(), 64u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 368u);
+ EXPECT_EQ(sem->SizeNoPadding(), 368u);
+ EXPECT_EQ(sem->Align(), 16u);
+ ASSERT_EQ(sem->Members().size(), 9u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u); // mat2x2
+ EXPECT_EQ(sem->Members()[0]->Align(), 8u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 16u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 16u); // mat2x3
+ EXPECT_EQ(sem->Members()[1]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 32u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 48u); // mat2x4
+ EXPECT_EQ(sem->Members()[2]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 32u);
+ EXPECT_EQ(sem->Members()[3]->Offset(), 80u); // mat3x2
+ EXPECT_EQ(sem->Members()[3]->Align(), 8u);
+ EXPECT_EQ(sem->Members()[3]->Size(), 24u);
+ EXPECT_EQ(sem->Members()[4]->Offset(), 112u); // mat3x3
+ EXPECT_EQ(sem->Members()[4]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[4]->Size(), 48u);
+ EXPECT_EQ(sem->Members()[5]->Offset(), 160u); // mat3x4
+ EXPECT_EQ(sem->Members()[5]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[5]->Size(), 48u);
+ EXPECT_EQ(sem->Members()[6]->Offset(), 208u); // mat4x2
+ EXPECT_EQ(sem->Members()[6]->Align(), 8u);
+ EXPECT_EQ(sem->Members()[6]->Size(), 32u);
+ EXPECT_EQ(sem->Members()[7]->Offset(), 240u); // mat4x3
+ EXPECT_EQ(sem->Members()[7]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[7]->Size(), 64u);
+ EXPECT_EQ(sem->Members()[8]->Offset(), 304u); // mat4x4
+ EXPECT_EQ(sem->Members()[8]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[8]->Size(), 64u);
}
TEST_F(ResolverStructLayoutTest, NestedStruct) {
- auto* inner = Structure("Inner", {
- Member("a", ty.mat3x3<f32>()),
- });
- auto* s = Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.Of(inner)),
- Member("c", ty.i32()),
- });
+ auto* inner = Structure("Inner", {
+ Member("a", ty.mat3x3<f32>()),
+ });
+ auto* s = Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.Of(inner)),
+ Member("c", ty.i32()),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 80u);
- EXPECT_EQ(sem->SizeNoPadding(), 68u);
- EXPECT_EQ(sem->Align(), 16u);
- ASSERT_EQ(sem->Members().size(), 3u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 16u);
- EXPECT_EQ(sem->Members()[1]->Align(), 16u);
- EXPECT_EQ(sem->Members()[1]->Size(), 48u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 64u);
- EXPECT_EQ(sem->Members()[2]->Align(), 4u);
- EXPECT_EQ(sem->Members()[2]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 80u);
+ EXPECT_EQ(sem->SizeNoPadding(), 68u);
+ EXPECT_EQ(sem->Align(), 16u);
+ ASSERT_EQ(sem->Members().size(), 3u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 16u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 48u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 64u);
+ EXPECT_EQ(sem->Members()[2]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 4u);
}
TEST_F(ResolverStructLayoutTest, SizeAttributes) {
- auto* inner = Structure("Inner", {
- Member("a", ty.f32(), {MemberSize(8)}),
- Member("b", ty.f32(), {MemberSize(16)}),
- Member("c", ty.f32(), {MemberSize(8)}),
- });
- auto* s = Structure("S", {
- Member("a", ty.f32(), {MemberSize(4)}),
- Member("b", ty.u32(), {MemberSize(8)}),
- Member("c", ty.Of(inner)),
- Member("d", ty.i32(), {MemberSize(32)}),
- });
+ auto* inner = Structure("Inner", {
+ Member("a", ty.f32(), {MemberSize(8)}),
+ Member("b", ty.f32(), {MemberSize(16)}),
+ Member("c", ty.f32(), {MemberSize(8)}),
+ });
+ auto* s = Structure("S", {
+ Member("a", ty.f32(), {MemberSize(4)}),
+ Member("b", ty.u32(), {MemberSize(8)}),
+ Member("c", ty.Of(inner)),
+ Member("d", ty.i32(), {MemberSize(32)}),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 76u);
- EXPECT_EQ(sem->SizeNoPadding(), 76u);
- EXPECT_EQ(sem->Align(), 4u);
- ASSERT_EQ(sem->Members().size(), 4u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
- EXPECT_EQ(sem->Members()[1]->Align(), 4u);
- EXPECT_EQ(sem->Members()[1]->Size(), 8u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 12u);
- EXPECT_EQ(sem->Members()[2]->Align(), 4u);
- EXPECT_EQ(sem->Members()[2]->Size(), 32u);
- EXPECT_EQ(sem->Members()[3]->Offset(), 44u);
- EXPECT_EQ(sem->Members()[3]->Align(), 4u);
- EXPECT_EQ(sem->Members()[3]->Size(), 32u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 76u);
+ EXPECT_EQ(sem->SizeNoPadding(), 76u);
+ EXPECT_EQ(sem->Align(), 4u);
+ ASSERT_EQ(sem->Members().size(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 8u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 12u);
+ EXPECT_EQ(sem->Members()[2]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 32u);
+ EXPECT_EQ(sem->Members()[3]->Offset(), 44u);
+ EXPECT_EQ(sem->Members()[3]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[3]->Size(), 32u);
}
TEST_F(ResolverStructLayoutTest, AlignAttributes) {
- auto* inner = Structure("Inner", {
- Member("a", ty.f32(), {MemberAlign(8)}),
- Member("b", ty.f32(), {MemberAlign(16)}),
- Member("c", ty.f32(), {MemberAlign(4)}),
- });
- auto* s = Structure("S", {
- Member("a", ty.f32(), {MemberAlign(4)}),
- Member("b", ty.u32(), {MemberAlign(8)}),
- Member("c", ty.Of(inner)),
- Member("d", ty.i32(), {MemberAlign(32)}),
- });
+ auto* inner = Structure("Inner", {
+ Member("a", ty.f32(), {MemberAlign(8)}),
+ Member("b", ty.f32(), {MemberAlign(16)}),
+ Member("c", ty.f32(), {MemberAlign(4)}),
+ });
+ auto* s = Structure("S", {
+ Member("a", ty.f32(), {MemberAlign(4)}),
+ Member("b", ty.u32(), {MemberAlign(8)}),
+ Member("c", ty.Of(inner)),
+ Member("d", ty.i32(), {MemberAlign(32)}),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 96u);
- EXPECT_EQ(sem->SizeNoPadding(), 68u);
- EXPECT_EQ(sem->Align(), 32u);
- ASSERT_EQ(sem->Members().size(), 4u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 4u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
- EXPECT_EQ(sem->Members()[1]->Offset(), 8u);
- EXPECT_EQ(sem->Members()[1]->Align(), 8u);
- EXPECT_EQ(sem->Members()[1]->Size(), 4u);
- EXPECT_EQ(sem->Members()[2]->Offset(), 16u);
- EXPECT_EQ(sem->Members()[2]->Align(), 16u);
- EXPECT_EQ(sem->Members()[2]->Size(), 32u);
- EXPECT_EQ(sem->Members()[3]->Offset(), 64u);
- EXPECT_EQ(sem->Members()[3]->Align(), 32u);
- EXPECT_EQ(sem->Members()[3]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 96u);
+ EXPECT_EQ(sem->SizeNoPadding(), 68u);
+ EXPECT_EQ(sem->Align(), 32u);
+ ASSERT_EQ(sem->Members().size(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 4u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[1]->Offset(), 8u);
+ EXPECT_EQ(sem->Members()[1]->Align(), 8u);
+ EXPECT_EQ(sem->Members()[1]->Size(), 4u);
+ EXPECT_EQ(sem->Members()[2]->Offset(), 16u);
+ EXPECT_EQ(sem->Members()[2]->Align(), 16u);
+ EXPECT_EQ(sem->Members()[2]->Size(), 32u);
+ EXPECT_EQ(sem->Members()[3]->Offset(), 64u);
+ EXPECT_EQ(sem->Members()[3]->Align(), 32u);
+ EXPECT_EQ(sem->Members()[3]->Size(), 4u);
}
TEST_F(ResolverStructLayoutTest, StructWithLotsOfPadding) {
- auto* s = Structure("S", {
- Member("a", ty.i32(), {MemberAlign(1024)}),
- });
+ auto* s = Structure("S", {
+ Member("a", ty.i32(), {MemberAlign(1024)}),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_EQ(sem->Size(), 1024u);
- EXPECT_EQ(sem->SizeNoPadding(), 4u);
- EXPECT_EQ(sem->Align(), 1024u);
- ASSERT_EQ(sem->Members().size(), 1u);
- EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
- EXPECT_EQ(sem->Members()[0]->Align(), 1024u);
- EXPECT_EQ(sem->Members()[0]->Size(), 4u);
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_EQ(sem->Size(), 1024u);
+ EXPECT_EQ(sem->SizeNoPadding(), 4u);
+ EXPECT_EQ(sem->Align(), 1024u);
+ ASSERT_EQ(sem->Members().size(), 1u);
+ EXPECT_EQ(sem->Members()[0]->Offset(), 0u);
+ EXPECT_EQ(sem->Members()[0]->Align(), 1024u);
+ EXPECT_EQ(sem->Members()[0]->Size(), 4u);
}
} // namespace
diff --git a/src/tint/resolver/struct_pipeline_stage_use_test.cc b/src/tint/resolver/struct_pipeline_stage_use_test.cc
index b843cea..2857ec8 100644
--- a/src/tint/resolver/struct_pipeline_stage_use_test.cc
+++ b/src/tint/resolver/struct_pipeline_stage_use_test.cc
@@ -27,162 +27,155 @@
using ResolverPipelineStageUseTest = ResolverTest;
TEST_F(ResolverPipelineStageUseTest, UnusedStruct) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->PipelineStageUses().empty());
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->PipelineStageUses().empty());
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointParam) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- Func("foo", {Param("param", ty.Of(s))}, ty.void_(), {}, {});
+ Func("foo", {Param("param", ty.Of(s))}, ty.void_(), {}, {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->PipelineStageUses().empty());
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->PipelineStageUses().empty());
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsNonEntryPointReturnType) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- Func("foo", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0.f)))}, {});
+ Func("foo", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0.f)))}, {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->PipelineStageUses().empty());
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->PipelineStageUses().empty());
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderParam) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- Func("main", {Param("param", ty.Of(s))}, ty.vec4<f32>(),
- {Return(Construct(ty.vec4<f32>()))},
- {Stage(ast::PipelineStage::kVertex)},
- {Builtin(ast::Builtin::kPosition)});
+ Func("main", {Param("param", ty.Of(s))}, ty.vec4<f32>(), {Return(Construct(ty.vec4<f32>()))},
+ {Stage(ast::PipelineStage::kVertex)}, {Builtin(ast::Builtin::kPosition)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kVertexInput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kVertexInput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsVertexShaderReturnType) {
- auto* s = Structure(
- "S", {Member("a", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
+ auto* s = Structure("S", {Member("a", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
- Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
- {Stage(ast::PipelineStage::kVertex)});
+ Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))}, {Stage(ast::PipelineStage::kVertex)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderParam) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- Func("main", {Param("param", ty.Of(s))}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ Func("main", {Param("param", ty.Of(s))}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsFragmentShaderReturnType) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0.f)))},
- {Stage(ast::PipelineStage::kFragment)});
+ Func("main", {}, ty.Of(s), {Return(Construct(ty.Of(s), Expr(0.f)))},
+ {Stage(ast::PipelineStage::kFragment)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsComputeShaderParam) {
- auto* s = Structure(
- "S",
- {Member("a", ty.u32(), {Builtin(ast::Builtin::kLocalInvocationIndex)})});
+ auto* s =
+ Structure("S", {Member("a", ty.u32(), {Builtin(ast::Builtin::kLocalInvocationIndex)})});
- Func("main", {Param("param", ty.Of(s))}, ty.void_(), {},
- {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
+ Func("main", {Param("param", ty.Of(s))}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kCompute), WorkgroupSize(1)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kComputeInput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kComputeInput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedMultipleStages) {
- auto* s = Structure(
- "S", {Member("a", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
+ auto* s = Structure("S", {Member("a", ty.vec4<f32>(), {Builtin(ast::Builtin::kPosition)})});
- Func("vert_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
- {Stage(ast::PipelineStage::kVertex)});
+ Func("vert_main", {}, ty.Of(s), {Return(Construct(ty.Of(s)))},
+ {Stage(ast::PipelineStage::kVertex)});
- Func("frag_main", {Param("param", ty.Of(s))}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ Func("frag_main", {Param("param", ty.Of(s))}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput,
- sem::PipelineStageUsage::kFragmentInput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kVertexOutput,
+ sem::PipelineStageUsage::kFragmentInput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderParamViaAlias) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- auto* s_alias = Alias("S_alias", ty.Of(s));
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s_alias = Alias("S_alias", ty.Of(s));
- Func("main", {Param("param", ty.Of(s_alias))}, ty.void_(), {},
- {Stage(ast::PipelineStage::kFragment)});
+ Func("main", {Param("param", ty.Of(s_alias))}, ty.void_(), {},
+ {Stage(ast::PipelineStage::kFragment)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kFragmentInput));
}
TEST_F(ResolverPipelineStageUseTest, StructUsedAsShaderReturnTypeViaAlias) {
- auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
- auto* s_alias = Alias("S_alias", ty.Of(s));
+ auto* s = Structure("S", {Member("a", ty.f32(), {Location(0)})});
+ auto* s_alias = Alias("S_alias", ty.Of(s));
- Func("main", {}, ty.Of(s_alias),
- {Return(Construct(ty.Of(s_alias), Expr(0.f)))},
- {Stage(ast::PipelineStage::kFragment)});
+ Func("main", {}, ty.Of(s_alias), {Return(Construct(ty.Of(s_alias), Expr(0.f)))},
+ {Stage(ast::PipelineStage::kFragment)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->PipelineStageUses(),
- UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->PipelineStageUses(),
+ UnorderedElementsAre(sem::PipelineStageUsage::kFragmentOutput));
}
} // namespace
diff --git a/src/tint/resolver/struct_storage_class_use_test.cc b/src/tint/resolver/struct_storage_class_use_test.cc
index bc7e7a0..8971bae 100644
--- a/src/tint/resolver/struct_storage_class_use_test.cc
+++ b/src/tint/resolver/struct_storage_class_use_test.cc
@@ -26,167 +26,156 @@
using ResolverStorageClassUseTest = ResolverTest;
TEST_F(ResolverStorageClassUseTest, UnreachableStruct) {
- auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* s = Structure("S", {Member("a", ty.f32())});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_TRUE(sem->StorageClassUsage().empty());
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_TRUE(sem->StorageClassUsage().empty());
}
TEST_F(ResolverStorageClassUseTest, StructReachableFromParameter) {
- auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* s = Structure("S", {Member("a", ty.f32())});
- Func("f", {Param("param", ty.Of(s))}, ty.void_(), {}, {});
+ Func("f", {Param("param", ty.Of(s))}, ty.void_(), {}, {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kNone));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kNone));
}
TEST_F(ResolverStorageClassUseTest, StructReachableFromReturnType) {
- auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* s = Structure("S", {Member("a", ty.f32())});
- Func("f", {}, ty.Of(s), {Return(Construct(ty.Of(s)))}, {});
+ Func("f", {}, ty.Of(s), {Return(Construct(ty.Of(s)))}, {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kNone));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kNone));
}
TEST_F(ResolverStorageClassUseTest, StructReachableFromGlobal) {
- auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* s = Structure("S", {Member("a", ty.f32())});
- Global("g", ty.Of(s), ast::StorageClass::kPrivate);
+ Global("g", ty.Of(s), ast::StorageClass::kPrivate);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kPrivate));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
}
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalAlias) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* a = Alias("A", ty.Of(s));
- Global("g", ty.Of(a), ast::StorageClass::kPrivate);
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* a = Alias("A", ty.Of(s));
+ Global("g", ty.Of(a), ast::StorageClass::kPrivate);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kPrivate));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
}
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalStruct) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* o = Structure("O", {Member("a", ty.Of(s))});
- Global("g", ty.Of(o), ast::StorageClass::kPrivate);
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* o = Structure("O", {Member("a", ty.Of(s))});
+ Global("g", ty.Of(o), ast::StorageClass::kPrivate);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kPrivate));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
}
TEST_F(ResolverStorageClassUseTest, StructReachableViaGlobalArray) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* a = ty.array(ty.Of(s), 3);
- Global("g", a, ast::StorageClass::kPrivate);
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* a = ty.array(ty.Of(s), 3);
+ Global("g", a, ast::StorageClass::kPrivate);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kPrivate));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kPrivate));
}
TEST_F(ResolverStorageClassUseTest, StructReachableFromLocal) {
- auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* s = Structure("S", {Member("a", ty.f32())});
- WrapInFunction(Var("g", ty.Of(s)));
+ WrapInFunction(Var("g", ty.Of(s)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kFunction));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
}
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalAlias) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* a = Alias("A", ty.Of(s));
- WrapInFunction(Var("g", ty.Of(a)));
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* a = Alias("A", ty.Of(s));
+ WrapInFunction(Var("g", ty.Of(a)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kFunction));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
}
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalStruct) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* o = Structure("O", {Member("a", ty.Of(s))});
- WrapInFunction(Var("g", ty.Of(o)));
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* o = Structure("O", {Member("a", ty.Of(s))});
+ WrapInFunction(Var("g", ty.Of(o)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kFunction));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
}
TEST_F(ResolverStorageClassUseTest, StructReachableViaLocalArray) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- auto* a = ty.array(ty.Of(s), 3);
- WrapInFunction(Var("g", a));
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ auto* a = ty.array(ty.Of(s), 3);
+ WrapInFunction(Var("g", a));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kFunction));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(), UnorderedElementsAre(ast::StorageClass::kFunction));
}
TEST_F(ResolverStorageClassUseTest, StructMultipleStorageClassUses) {
- auto* s = Structure("S", {Member("a", ty.f32())});
- Global("x", ty.Of(s), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- Global("y", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(0),
- });
- WrapInFunction(Var("g", ty.Of(s)));
+ auto* s = Structure("S", {Member("a", ty.f32())});
+ Global("x", ty.Of(s), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ Global("y", ty.Of(s), ast::StorageClass::kStorage, ast::Access::kRead,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(0),
+ });
+ WrapInFunction(Var("g", ty.Of(s)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* sem = TypeOf(s)->As<sem::Struct>();
- ASSERT_NE(sem, nullptr);
- EXPECT_THAT(sem->StorageClassUsage(),
- UnorderedElementsAre(ast::StorageClass::kUniform,
- ast::StorageClass::kStorage,
- ast::StorageClass::kFunction));
+ auto* sem = TypeOf(s)->As<sem::Struct>();
+ ASSERT_NE(sem, nullptr);
+ EXPECT_THAT(sem->StorageClassUsage(),
+ UnorderedElementsAre(ast::StorageClass::kUniform, ast::StorageClass::kStorage,
+ ast::StorageClass::kFunction));
}
} // namespace
diff --git a/src/tint/resolver/type_constructor_validation_test.cc b/src/tint/resolver/type_constructor_validation_test.cc
index 4782147..d886b2e 100644
--- a/src/tint/resolver/type_constructor_validation_test.cc
+++ b/src/tint/resolver/type_constructor_validation_test.cc
@@ -43,67 +43,61 @@
using builder::vec3;
using builder::vec4;
-class ResolverTypeConstructorValidationTest : public resolver::TestHelper,
- public testing::Test {};
+class ResolverTypeConstructorValidationTest : public resolver::TestHelper, public testing::Test {};
namespace InferTypeTest {
struct Params {
- builder::ast_type_func_ptr create_rhs_ast_type;
- builder::ast_expr_func_ptr create_rhs_ast_value;
- builder::sem_type_func_ptr create_rhs_sem_type;
+ builder::ast_type_func_ptr create_rhs_ast_type;
+ builder::ast_expr_func_ptr create_rhs_ast_value;
+ builder::sem_type_func_ptr create_rhs_sem_type;
};
template <typename T>
constexpr Params ParamsFor() {
- return Params{DataType<T>::AST, DataType<T>::Expr, DataType<T>::Sem};
+ return Params{DataType<T>::AST, DataType<T>::Expr, DataType<T>::Sem};
}
TEST_F(ResolverTypeConstructorValidationTest, InferTypeTest_Simple) {
- // var a = 1;
- // var b = a;
- auto* a = Var("a", nullptr, ast::StorageClass::kNone, Expr(1));
- auto* b = Var("b", nullptr, ast::StorageClass::kNone, Expr("a"));
- auto* a_ident = Expr("a");
- auto* b_ident = Expr("b");
+ // var a = 1;
+ // var b = a;
+ auto* a = Var("a", nullptr, ast::StorageClass::kNone, Expr(1));
+ auto* b = Var("b", nullptr, ast::StorageClass::kNone, Expr("a"));
+ auto* a_ident = Expr("a");
+ auto* b_ident = Expr("b");
- WrapInFunction(a, b, Assign(a_ident, "a"), Assign(b_ident, "b"));
+ WrapInFunction(a, b, Assign(a_ident, "a"), Assign(b_ident, "b"));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(a_ident)->Is<sem::Reference>());
- EXPECT_TRUE(
- TypeOf(a_ident)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
- EXPECT_EQ(TypeOf(a_ident)->As<sem::Reference>()->StorageClass(),
- ast::StorageClass::kFunction);
- ASSERT_TRUE(TypeOf(b_ident)->Is<sem::Reference>());
- EXPECT_TRUE(
- TypeOf(b_ident)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
- EXPECT_EQ(TypeOf(b_ident)->As<sem::Reference>()->StorageClass(),
- ast::StorageClass::kFunction);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(TypeOf(a_ident)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(a_ident)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+ EXPECT_EQ(TypeOf(a_ident)->As<sem::Reference>()->StorageClass(), ast::StorageClass::kFunction);
+ ASSERT_TRUE(TypeOf(b_ident)->Is<sem::Reference>());
+ EXPECT_TRUE(TypeOf(b_ident)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+ EXPECT_EQ(TypeOf(b_ident)->As<sem::Reference>()->StorageClass(), ast::StorageClass::kFunction);
}
using InferTypeTest_FromConstructorExpression = ResolverTestWithParam<Params>;
TEST_P(InferTypeTest_FromConstructorExpression, All) {
- // e.g. for vec3<f32>
- // {
- // var a = vec3<f32>(0.0, 0.0, 0.0)
- // }
- auto& params = GetParam();
+ // e.g. for vec3<f32>
+ // {
+ // var a = vec3<f32>(0.0, 0.0, 0.0)
+ // }
+ auto& params = GetParam();
- auto* constructor_expr = params.create_rhs_ast_value(*this, 0);
+ auto* constructor_expr = params.create_rhs_ast_value(*this, 0);
- auto* a = Var("a", nullptr, ast::StorageClass::kNone, constructor_expr);
- // Self-assign 'a' to force the expression to be resolved so we can test its
- // type below
- auto* a_ident = Expr("a");
- WrapInFunction(Decl(a), Assign(a_ident, "a"));
+ auto* a = Var("a", nullptr, ast::StorageClass::kNone, constructor_expr);
+ // Self-assign 'a' to force the expression to be resolved so we can test its
+ // type below
+ auto* a_ident = Expr("a");
+ WrapInFunction(Decl(a), Assign(a_ident, "a"));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* got = TypeOf(a_ident);
- auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
- ast::StorageClass::kFunction,
- ast::Access::kReadWrite);
- ASSERT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
- << "expected: " << FriendlyName(expected) << "\n";
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* got = TypeOf(a_ident);
+ auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
+ ast::StorageClass::kFunction, ast::Access::kReadWrite);
+ ASSERT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
+ << "expected: " << FriendlyName(expected) << "\n";
}
static constexpr Params from_constructor_expression_cases[] = {
@@ -130,29 +124,28 @@
using InferTypeTest_FromArithmeticExpression = ResolverTestWithParam<Params>;
TEST_P(InferTypeTest_FromArithmeticExpression, All) {
- // e.g. for vec3<f32>
- // {
- // var a = vec3<f32>(2.0, 2.0, 2.0) * 3.0;
- // }
- auto& params = GetParam();
+ // e.g. for vec3<f32>
+ // {
+ // var a = vec3<f32>(2.0, 2.0, 2.0) * 3.0;
+ // }
+ auto& params = GetParam();
- auto* arith_lhs_expr = params.create_rhs_ast_value(*this, 2);
- auto* arith_rhs_expr = params.create_rhs_ast_value(*this, 3);
- auto* constructor_expr = Mul(arith_lhs_expr, arith_rhs_expr);
+ auto* arith_lhs_expr = params.create_rhs_ast_value(*this, 2);
+ auto* arith_rhs_expr = params.create_rhs_ast_value(*this, 3);
+ auto* constructor_expr = Mul(arith_lhs_expr, arith_rhs_expr);
- auto* a = Var("a", nullptr, constructor_expr);
- // Self-assign 'a' to force the expression to be resolved so we can test its
- // type below
- auto* a_ident = Expr("a");
- WrapInFunction(Decl(a), Assign(a_ident, "a"));
+ auto* a = Var("a", nullptr, constructor_expr);
+ // Self-assign 'a' to force the expression to be resolved so we can test its
+ // type below
+ auto* a_ident = Expr("a");
+ WrapInFunction(Decl(a), Assign(a_ident, "a"));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* got = TypeOf(a_ident);
- auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
- ast::StorageClass::kFunction,
- ast::Access::kReadWrite);
- ASSERT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
- << "expected: " << FriendlyName(expected) << "\n";
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* got = TypeOf(a_ident);
+ auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
+ ast::StorageClass::kFunction, ast::Access::kReadWrite);
+ ASSERT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
+ << "expected: " << FriendlyName(expected) << "\n";
}
static constexpr Params from_arithmetic_expression_cases[] = {
ParamsFor<i32>(), ParamsFor<u32>(), ParamsFor<f32>(),
@@ -171,34 +164,33 @@
using InferTypeTest_FromCallExpression = ResolverTestWithParam<Params>;
TEST_P(InferTypeTest_FromCallExpression, All) {
- // e.g. for vec3<f32>
- //
- // fn foo() -> vec3<f32> {
- // return vec3<f32>();
- // }
- //
- // fn bar()
- // {
- // var a = foo();
- // }
- auto& params = GetParam();
+ // e.g. for vec3<f32>
+ //
+ // fn foo() -> vec3<f32> {
+ // return vec3<f32>();
+ // }
+ //
+ // fn bar()
+ // {
+ // var a = foo();
+ // }
+ auto& params = GetParam();
- Func("foo", {}, params.create_rhs_ast_type(*this),
- {Return(Construct(params.create_rhs_ast_type(*this)))}, {});
+ Func("foo", {}, params.create_rhs_ast_type(*this),
+ {Return(Construct(params.create_rhs_ast_type(*this)))}, {});
- auto* a = Var("a", nullptr, Call("foo"));
- // Self-assign 'a' to force the expression to be resolved so we can test its
- // type below
- auto* a_ident = Expr("a");
- WrapInFunction(Decl(a), Assign(a_ident, "a"));
+ auto* a = Var("a", nullptr, Call("foo"));
+ // Self-assign 'a' to force the expression to be resolved so we can test its
+ // type below
+ auto* a_ident = Expr("a");
+ WrapInFunction(Decl(a), Assign(a_ident, "a"));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* got = TypeOf(a_ident);
- auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
- ast::StorageClass::kFunction,
- ast::Access::kReadWrite);
- ASSERT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
- << "expected: " << FriendlyName(expected) << "\n";
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* got = TypeOf(a_ident);
+ auto* expected = create<sem::Reference>(params.create_rhs_sem_type(*this),
+ ast::StorageClass::kFunction, ast::Access::kReadWrite);
+ ASSERT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
+ << "expected: " << FriendlyName(expected) << "\n";
}
static constexpr Params from_call_expression_cases[] = {
ParamsFor<bool>(),
@@ -226,21 +218,20 @@
namespace ConversionConstructTest {
enum class Kind {
- Construct,
- Conversion,
+ Construct,
+ Conversion,
};
struct Params {
- Kind kind;
- builder::ast_type_func_ptr lhs_type;
- builder::ast_type_func_ptr rhs_type;
- builder::ast_expr_func_ptr rhs_value_expr;
+ Kind kind;
+ builder::ast_type_func_ptr lhs_type;
+ builder::ast_type_func_ptr rhs_type;
+ builder::ast_expr_func_ptr rhs_value_expr;
};
template <typename LhsType, typename RhsType>
constexpr Params ParamsFor(Kind kind) {
- return Params{kind, DataType<LhsType>::AST, DataType<RhsType>::AST,
- DataType<RhsType>::Expr};
+ return Params{kind, DataType<LhsType>::AST, DataType<RhsType>::AST, DataType<RhsType>::Expr};
}
static constexpr Params valid_cases[] = {
@@ -296,50 +287,50 @@
using ConversionConstructorValidTest = ResolverTestWithParam<Params>;
TEST_P(ConversionConstructorValidTest, All) {
- auto& params = GetParam();
+ auto& params = GetParam();
- // var a : <lhs_type1> = <lhs_type2>(<rhs_type>(<rhs_value_expr>));
- auto* lhs_type1 = params.lhs_type(*this);
- auto* lhs_type2 = params.lhs_type(*this);
- auto* rhs_type = params.rhs_type(*this);
- auto* rhs_value_expr = params.rhs_value_expr(*this, 0);
+ // var a : <lhs_type1> = <lhs_type2>(<rhs_type>(<rhs_value_expr>));
+ auto* lhs_type1 = params.lhs_type(*this);
+ auto* lhs_type2 = params.lhs_type(*this);
+ auto* rhs_type = params.rhs_type(*this);
+ auto* rhs_value_expr = params.rhs_value_expr(*this, 0);
- std::stringstream ss;
- ss << FriendlyName(lhs_type1) << " = " << FriendlyName(lhs_type2) << "("
- << FriendlyName(rhs_type) << "(<rhs value expr>))";
- SCOPED_TRACE(ss.str());
+ std::stringstream ss;
+ ss << FriendlyName(lhs_type1) << " = " << FriendlyName(lhs_type2) << "("
+ << FriendlyName(rhs_type) << "(<rhs value expr>))";
+ SCOPED_TRACE(ss.str());
- auto* arg = Construct(rhs_type, rhs_value_expr);
- auto* tc = Construct(lhs_type2, arg);
- auto* a = Var("a", lhs_type1, ast::StorageClass::kNone, tc);
+ auto* arg = Construct(rhs_type, rhs_value_expr);
+ auto* tc = Construct(lhs_type2, arg);
+ auto* a = Var("a", lhs_type1, ast::StorageClass::kNone, tc);
- // Self-assign 'a' to force the expression to be resolved so we can test its
- // type below
- auto* a_ident = Expr("a");
- WrapInFunction(Decl(a), Assign(a_ident, "a"));
+ // Self-assign 'a' to force the expression to be resolved so we can test its
+ // type below
+ auto* a_ident = Expr("a");
+ WrapInFunction(Decl(a), Assign(a_ident, "a"));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- switch (params.kind) {
- case Kind::Construct: {
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_EQ(ctor->Parameters()[0]->Type(), TypeOf(arg));
- break;
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ switch (params.kind) {
+ case Kind::Construct: {
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_EQ(ctor->Parameters()[0]->Type(), TypeOf(arg));
+ break;
+ }
+ case Kind::Conversion: {
+ auto* conv = call->Target()->As<sem::TypeConversion>();
+ ASSERT_NE(conv, nullptr);
+ EXPECT_EQ(call->Type(), conv->ReturnType());
+ ASSERT_EQ(conv->Parameters().size(), 1u);
+ EXPECT_EQ(conv->Parameters()[0]->Type(), TypeOf(arg));
+ break;
+ }
}
- case Kind::Conversion: {
- auto* conv = call->Target()->As<sem::TypeConversion>();
- ASSERT_NE(conv, nullptr);
- EXPECT_EQ(call->Type(), conv->ReturnType());
- ASSERT_EQ(conv->Parameters().size(), 1u);
- EXPECT_EQ(conv->Parameters()[0]->Type(), TypeOf(arg));
- break;
- }
- }
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
ConversionConstructorValidTest,
@@ -365,272 +356,257 @@
CreatePtrsFor<mat3x2<f32>>() //
};
-using ConversionConstructorInvalidTest =
- ResolverTestWithParam<std::tuple<CreatePtrs, // lhs
- CreatePtrs // rhs
- >>;
+using ConversionConstructorInvalidTest = ResolverTestWithParam<std::tuple<CreatePtrs, // lhs
+ CreatePtrs // rhs
+ >>;
TEST_P(ConversionConstructorInvalidTest, All) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto& lhs_params = std::get<0>(params);
- auto& rhs_params = std::get<1>(params);
+ auto& lhs_params = std::get<0>(params);
+ auto& rhs_params = std::get<1>(params);
- // Skip test for valid cases
- for (auto& v : valid_cases) {
- if (v.lhs_type == lhs_params.ast && v.rhs_type == rhs_params.ast &&
- v.rhs_value_expr == rhs_params.expr) {
- return;
+ // Skip test for valid cases
+ for (auto& v : valid_cases) {
+ if (v.lhs_type == lhs_params.ast && v.rhs_type == rhs_params.ast &&
+ v.rhs_value_expr == rhs_params.expr) {
+ return;
+ }
}
- }
- // Skip non-conversions
- if (lhs_params.ast == rhs_params.ast) {
- return;
- }
+ // Skip non-conversions
+ if (lhs_params.ast == rhs_params.ast) {
+ return;
+ }
- // var a : <lhs_type1> = <lhs_type2>(<rhs_type>(<rhs_value_expr>));
- auto* lhs_type1 = lhs_params.ast(*this);
- auto* lhs_type2 = lhs_params.ast(*this);
- auto* rhs_type = rhs_params.ast(*this);
- auto* rhs_value_expr = rhs_params.expr(*this, 0);
+ // var a : <lhs_type1> = <lhs_type2>(<rhs_type>(<rhs_value_expr>));
+ auto* lhs_type1 = lhs_params.ast(*this);
+ auto* lhs_type2 = lhs_params.ast(*this);
+ auto* rhs_type = rhs_params.ast(*this);
+ auto* rhs_value_expr = rhs_params.expr(*this, 0);
- std::stringstream ss;
- ss << FriendlyName(lhs_type1) << " = " << FriendlyName(lhs_type2) << "("
- << FriendlyName(rhs_type) << "(<rhs value expr>))";
- SCOPED_TRACE(ss.str());
+ std::stringstream ss;
+ ss << FriendlyName(lhs_type1) << " = " << FriendlyName(lhs_type2) << "("
+ << FriendlyName(rhs_type) << "(<rhs value expr>))";
+ SCOPED_TRACE(ss.str());
- auto* a = Var("a", lhs_type1, ast::StorageClass::kNone,
- Construct(lhs_type2, Construct(rhs_type, rhs_value_expr)));
+ auto* a = Var("a", lhs_type1, ast::StorageClass::kNone,
+ Construct(lhs_type2, Construct(rhs_type, rhs_value_expr)));
- // Self-assign 'a' to force the expression to be resolved so we can test its
- // type below
- auto* a_ident = Expr("a");
- WrapInFunction(Decl(a), Assign(a_ident, "a"));
+ // Self-assign 'a' to force the expression to be resolved so we can test its
+ // type below
+ auto* a_ident = Expr("a");
+ WrapInFunction(Decl(a), Assign(a_ident, "a"));
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
ConversionConstructorInvalidTest,
testing::Combine(testing::ValuesIn(all_types),
testing::ValuesIn(all_types)));
-TEST_F(ResolverTypeConstructorValidationTest,
- ConversionConstructorInvalid_TooManyInitializers) {
- auto* a = Var("a", ty.f32(), ast::StorageClass::kNone,
- Construct(Source{{12, 34}}, ty.f32(), Expr(1.0f), Expr(2.0f)));
- WrapInFunction(a);
+TEST_F(ResolverTypeConstructorValidationTest, ConversionConstructorInvalid_TooManyInitializers) {
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone,
+ Construct(Source{{12, 34}}, ty.f32(), Expr(1.0f), Expr(2.0f)));
+ WrapInFunction(a);
- ASSERT_FALSE(r()->Resolve());
- ASSERT_EQ(r()->error(),
- "12:34 error: expected zero or one value in constructor, got 2");
+ ASSERT_FALSE(r()->Resolve());
+ ASSERT_EQ(r()->error(), "12:34 error: expected zero or one value in constructor, got 2");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- ConversionConstructorInvalid_InvalidInitializer) {
- auto* a =
- Var("a", ty.f32(), ast::StorageClass::kNone,
- Construct(Source{{12, 34}}, ty.f32(), Construct(ty.array<f32, 4>())));
- WrapInFunction(a);
+TEST_F(ResolverTypeConstructorValidationTest, ConversionConstructorInvalid_InvalidInitializer) {
+ auto* a = Var("a", ty.f32(), ast::StorageClass::kNone,
+ Construct(Source{{12, 34}}, ty.f32(), Construct(ty.array<f32, 4>())));
+ WrapInFunction(a);
- ASSERT_FALSE(r()->Resolve());
- ASSERT_EQ(r()->error(),
- "12:34 error: cannot construct 'f32' with a value of type "
- "'array<f32, 4>'");
+ ASSERT_FALSE(r()->Resolve());
+ ASSERT_EQ(r()->error(),
+ "12:34 error: cannot construct 'f32' with a value of type "
+ "'array<f32, 4>'");
}
} // namespace ConversionConstructTest
namespace ArrayConstructor {
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Array_ZeroValue_Pass) {
- // array<u32, 10>();
- auto* tc = array<u32, 10>();
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_ZeroValue_Pass) {
+ // array<u32, 10>();
+ auto* tc = array<u32, 10>();
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- EXPECT_TRUE(call->Type()->Is<sem::Array>());
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 0u);
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ EXPECT_TRUE(call->Type()->Is<sem::Array>());
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 0u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Array_type_match) {
- // array<u32, 3>(0u, 10u. 20u);
- auto* tc = array<u32, 3>(Expr(0u), Expr(10u), Expr(20u));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_type_match) {
+ // array<u32, 3>(0u, 10u. 20u);
+ auto* tc = array<u32, 3>(Expr(0u), Expr(10u), Expr(20u));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- EXPECT_TRUE(call->Type()->Is<sem::Array>());
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
- EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ EXPECT_TRUE(call->Type()->Is<sem::Array>());
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 3u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
+ EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Array_type_Mismatch_U32F32) {
- // array<u32, 3>(0u, 1.0f, 20u);
- auto* tc = array<u32, 3>(Expr(0u), Expr(Source{{12, 34}}, 1.0f), Expr(20u));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_type_Mismatch_U32F32) {
+ // array<u32, 3>(0u, 1.0f, 20u);
+ auto* tc = array<u32, 3>(Expr(0u), Expr(Source{{12, 34}}, 1.0f), Expr(20u));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'u32', found 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'u32', found 'f32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Array_ScalarArgumentTypeMismatch_F32I32) {
- // array<f32, 1>(1);
- auto* tc = array<f32, 1>(Expr(Source{{12, 34}}, 1));
- WrapInFunction(tc);
+ // array<f32, 1>(1);
+ auto* tc = array<f32, 1>(Expr(Source{{12, 34}}, 1));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'f32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'f32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Array_ScalarArgumentTypeMismatch_U32I32) {
- // array<u32, 6>(1, 0u, 0u, 0u, 0u, 0u);
- auto* tc = array<u32, 1>(Expr(Source{{12, 34}}, 1), Expr(0u), Expr(0u),
- Expr(0u), Expr(0u));
- WrapInFunction(tc);
+ // array<u32, 6>(1, 0u, 0u, 0u, 0u, 0u);
+ auto* tc = array<u32, 1>(Expr(Source{{12, 34}}, 1), Expr(0u), Expr(0u), Expr(0u), Expr(0u));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'u32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'u32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Array_ScalarArgumentTypeMismatch_Vec2) {
- // array<i32, 3>(1, vec2<i32>());
- auto* tc =
- array<i32, 3>(Expr(1), Construct(Source{{12, 34}}, ty.vec2<i32>()));
- WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'i32', found 'vec2<i32>'");
+ // array<i32, 3>(1, vec2<i32>());
+ auto* tc = array<i32, 3>(Expr(1), Construct(Source{{12, 34}}, ty.vec2<i32>()));
+ WrapInFunction(tc);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'i32', found 'vec2<i32>'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_ArrayOfVector_SubElemTypeMismatch_I32U32) {
- // array<vec3<i32>, 2>(vec3<i32>(), vec3<u32>());
- auto* e0 = vec3<i32>();
- SetSource(Source::Location({12, 34}));
- auto* e1 = vec3<u32>();
- auto* t = Construct(ty.array(ty.vec3<i32>(), 2), e0, e1);
- WrapInFunction(t);
+ // array<vec3<i32>, 2>(vec3<i32>(), vec3<u32>());
+ auto* e0 = vec3<i32>();
+ SetSource(Source::Location({12, 34}));
+ auto* e1 = vec3<u32>();
+ auto* t = Construct(ty.array(ty.vec3<i32>(), 2), e0, e1);
+ WrapInFunction(t);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'vec3<i32>', found 'vec3<u32>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'vec3<i32>', found 'vec3<u32>'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_ArrayOfVector_SubElemTypeMismatch_I32Bool) {
- // array<vec3<i32>, 2>(vec3<i32>(), vec3<bool>(true, true, false));
- SetSource(Source::Location({12, 34}));
- auto* e0 = vec3<bool>(true, true, false);
- auto* e1 = vec3<i32>();
- auto* t = Construct(ty.array(ty.vec3<i32>(), 2), e0, e1);
- WrapInFunction(t);
+ // array<vec3<i32>, 2>(vec3<i32>(), vec3<bool>(true, true, false));
+ SetSource(Source::Location({12, 34}));
+ auto* e0 = vec3<bool>(true, true, false);
+ auto* e1 = vec3<i32>();
+ auto* t = Construct(ty.array(ty.vec3<i32>(), 2), e0, e1);
+ WrapInFunction(t);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'vec3<i32>', found 'vec3<bool>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'vec3<i32>', found 'vec3<bool>'");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_ArrayOfArray_SubElemSizeMismatch) {
- // array<array<i32, 2>, 2>(array<i32, 3>(), array<i32, 2>());
- SetSource(Source::Location({12, 34}));
- auto* e0 = array<i32, 3>();
- auto* e1 = array<i32, 2>();
- auto* t = Construct(ty.array(ty.array<i32, 2>(), 2), e0, e1);
- WrapInFunction(t);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_ArrayOfArray_SubElemSizeMismatch) {
+ // array<array<i32, 2>, 2>(array<i32, 3>(), array<i32, 2>());
+ SetSource(Source::Location({12, 34}));
+ auto* e0 = array<i32, 3>();
+ auto* e1 = array<i32, 2>();
+ auto* t = Construct(ty.array(ty.array<i32, 2>(), 2), e0, e1);
+ WrapInFunction(t);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'array<i32, 2>', found 'array<i32, 3>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'array<i32, 2>', found 'array<i32, 3>'");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_ArrayOfArray_SubElemTypeMismatch) {
- // array<array<i32, 2>, 2>(array<i32, 2>(), array<u32, 2>());
- auto* e0 = array<i32, 2>();
- SetSource(Source::Location({12, 34}));
- auto* e1 = array<u32, 2>();
- auto* t = Construct(ty.array(ty.array<i32, 2>(), 2), e0, e1);
- WrapInFunction(t);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_ArrayOfArray_SubElemTypeMismatch) {
+ // array<array<i32, 2>, 2>(array<i32, 2>(), array<u32, 2>());
+ auto* e0 = array<i32, 2>();
+ SetSource(Source::Location({12, 34}));
+ auto* e1 = array<u32, 2>();
+ auto* t = Construct(ty.array(ty.array<i32, 2>(), 2), e0, e1);
+ WrapInFunction(t);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in array constructor does not match array type: "
- "expected 'array<i32, 2>', found 'array<u32, 2>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in array constructor does not match array type: "
+ "expected 'array<i32, 2>', found 'array<u32, 2>'");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Array_TooFewElements) {
- // array<i32, 4>(1, 2, 3);
- SetSource(Source::Location({12, 34}));
- auto* tc = array<i32, 4>(Expr(1), Expr(2), Expr(3));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_TooFewElements) {
+ // array<i32, 4>(1, 2, 3);
+ SetSource(Source::Location({12, 34}));
+ auto* tc = array<i32, 4>(Expr(1), Expr(2), Expr(3));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: array constructor has too few elements: expected 4, "
- "found 3");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: array constructor has too few elements: expected 4, "
+ "found 3");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Array_TooManyElements) {
- // array<i32, 4>(1, 2, 3, 4, 5);
- SetSource(Source::Location({12, 34}));
- auto* tc = array<i32, 4>(Expr(1), Expr(2), Expr(3), Expr(4), Expr(5));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_TooManyElements) {
+ // array<i32, 4>(1, 2, 3, 4, 5);
+ SetSource(Source::Location({12, 34}));
+ auto* tc = array<i32, 4>(Expr(1), Expr(2), Expr(3), Expr(4), Expr(5));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: array constructor has too many "
- "elements: expected 4, "
- "found 5");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: array constructor has too many "
+ "elements: expected 4, "
+ "found 5");
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_Runtime) {
- // array<i32>(1);
- auto* tc = array(ty.i32(), nullptr, Expr(Source{{12, 34}}, 1));
- WrapInFunction(tc);
+ // array<i32>(1);
+ auto* tc = array(ty.i32(), nullptr, Expr(Source{{12, 34}}, 1));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: cannot init a runtime-sized array");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "error: cannot init a runtime-sized array");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Array_RuntimeZeroValue) {
- // array<i32>();
- auto* tc = array(ty.i32(), nullptr);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Array_RuntimeZeroValue) {
+ // array<i32>();
+ auto* tc = array(ty.i32(), nullptr);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: cannot init a runtime-sized array");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "error: cannot init a runtime-sized array");
}
} // namespace ArrayConstructor
@@ -638,111 +614,111 @@
namespace ScalarConstructor {
TEST_F(ResolverTypeConstructorValidationTest, Expr_Construct_i32_Success) {
- auto* expr = Construct<i32>(Expr(123));
- WrapInFunction(expr);
+ auto* expr = Construct<i32>(Expr(123));
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::I32>());
- auto* call = Sem().Get(expr);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
+ auto* call = Sem().Get(expr);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Construct_u32_Success) {
- auto* expr = Construct<u32>(Expr(123u));
- WrapInFunction(expr);
+ auto* expr = Construct<u32>(Expr(123u));
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::U32>());
- auto* call = Sem().Get(expr);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+ auto* call = Sem().Get(expr);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Construct_f32_Success) {
- auto* expr = Construct<f32>(Expr(1.23f));
- WrapInFunction(expr);
+ auto* expr = Construct<f32>(Expr(1.23f));
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
- auto* call = Sem().Get(expr);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
+ auto* call = Sem().Get(expr);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_f32_to_i32_Success) {
- auto* expr = Construct<i32>(1.23f);
- WrapInFunction(expr);
+ auto* expr = Construct<i32>(1.23f);
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::I32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::I32>());
- auto* call = Sem().Get(expr);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConversion>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
+ auto* call = Sem().Get(expr);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConversion>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_i32_to_u32_Success) {
- auto* expr = Construct<u32>(123);
- WrapInFunction(expr);
+ auto* expr = Construct<u32>(123);
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::U32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::U32>());
- auto* call = Sem().Get(expr);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConversion>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
+ auto* call = Sem().Get(expr);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConversion>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Convert_u32_to_f32_Success) {
- auto* expr = Construct<f32>(123u);
- WrapInFunction(expr);
+ auto* expr = Construct<f32>(123u);
+ WrapInFunction(expr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::F32>());
- auto* call = Sem().Get(expr);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConversion>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+ auto* call = Sem().Get(expr);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConversion>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
}
} // namespace ScalarConstructor
@@ -751,1451 +727,1308 @@
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2F32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec2<f32>(Expr(Source{{12, 34}}, 1), 1.0f);
- WrapInFunction(tc);
+ auto* tc = vec2<f32>(Expr(Source{{12, 34}}, 1), 1.0f);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'f32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'f32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2U32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec2<u32>(1u, Expr(Source{{12, 34}}, 1));
- WrapInFunction(tc);
+ auto* tc = vec2<u32>(1u, Expr(Source{{12, 34}}, 1));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'u32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'u32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2I32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec2<i32>(Expr(Source{{12, 34}}, 1u), 1);
- WrapInFunction(tc);
+ auto* tc = vec2<i32>(Expr(Source{{12, 34}}, 1u), 1);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'i32', found 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'i32', found 'u32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2Bool_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec2<bool>(true, Expr(Source{{12, 34}}, 1));
- WrapInFunction(tc);
+ auto* tc = vec2<bool>(true, Expr(Source{{12, 34}}, 1));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'bool', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'bool', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2_Error_Vec3ArgumentCardinalityTooLarge) {
- auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2_Error_Vec4ArgumentCardinalityTooLarge) {
- auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec4<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec4<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2_Error_TooManyArgumentsScalar) {
- auto* tc =
- vec2<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
- Expr(Source{{12, 46}}, 1.0f));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Error_TooManyArgumentsScalar) {
+ auto* tc = vec2<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2_Error_TooManyArgumentsVector) {
- auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Construct(Source{{12, 40}}, ty.vec2<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Error_TooManyArgumentsVector) {
+ auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
+ Construct(Source{{12, 40}}, ty.vec2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 4 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec2_Error_TooManyArgumentsVectorAndScalar) {
- auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Expr(Source{{12, 40}}, 1.0f));
- WrapInFunction(tc);
+ auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec2<f32>' with 3 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2_Error_InvalidArgumentType) {
- auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Error_InvalidArgumentType) {
+ auto* tc = vec2<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: expected vector or scalar type in vector "
- "constructor; found: mat2x2<f32>");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: expected vector or scalar type in vector "
+ "constructor; found: mat2x2<f32>");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2_Success_ZeroValue) {
- auto* tc = vec2<f32>();
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Success_ZeroValue) {
+ auto* tc = vec2<f32>();
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 0u);
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 0u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2F32_Success_Scalar) {
- auto* tc = vec2<f32>(1.0f, 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2F32_Success_Scalar) {
+ auto* tc = vec2<f32>(1.0f, 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 2u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2U32_Success_Scalar) {
- auto* tc = vec2<u32>(1u, 1u);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2U32_Success_Scalar) {
+ auto* tc = vec2<u32>(1u, 1u);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 2u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2I32_Success_Scalar) {
- auto* tc = vec2<i32>(1, 1);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2I32_Success_Scalar) {
+ auto* tc = vec2<i32>(1, 1);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 2u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2Bool_Success_Scalar) {
- auto* tc = vec2<bool>(true, false);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2Bool_Success_Scalar) {
+ auto* tc = vec2<bool>(true, false);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Bool>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 2u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Bool>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2_Success_Identity) {
- auto* tc = vec2<f32>(vec2<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Success_Identity) {
+ auto* tc = vec2<f32>(vec2<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec2_Success_Vec2TypeConversion) {
- auto* tc = vec2<f32>(vec2<i32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec2_Success_Vec2TypeConversion) {
+ auto* tc = vec2<f32>(vec2<i32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 2u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConversion>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConversion>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec3F32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec3<f32>(1.0f, 1.0f, Expr(Source{{12, 34}}, 1));
- WrapInFunction(tc);
+ auto* tc = vec3<f32>(1.0f, 1.0f, Expr(Source{{12, 34}}, 1));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'f32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'f32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec3U32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec3<u32>(1u, Expr(Source{{12, 34}}, 1), 1u);
- WrapInFunction(tc);
+ auto* tc = vec3<u32>(1u, Expr(Source{{12, 34}}, 1), 1u);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'u32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'u32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec3I32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec3<i32>(1, Expr(Source{{12, 34}}, 1u), 1);
- WrapInFunction(tc);
+ auto* tc = vec3<i32>(1, Expr(Source{{12, 34}}, 1u), 1);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'i32', found 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'i32', found 'u32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec3Bool_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec3<bool>(true, Expr(Source{{12, 34}}, 1), false);
- WrapInFunction(tc);
+ auto* tc = vec3<bool>(true, Expr(Source{{12, 34}}, 1), false);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'bool', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'bool', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec3_Error_Vec4ArgumentCardinalityTooLarge) {
- auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec4<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec4<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Error_TooFewArgumentsScalar) {
- auto* tc =
- vec3<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooFewArgumentsScalar) {
+ auto* tc = vec3<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Error_TooManyArgumentsScalar) {
- auto* tc =
- vec3<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
- Expr(Source{{12, 46}}, 1.0f), Expr(Source{{12, 52}}, 1.0f));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooManyArgumentsScalar) {
+ auto* tc = vec3<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f), Expr(Source{{12, 52}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Error_TooFewArgumentsVec2) {
- auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooFewArgumentsVec2) {
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Error_TooManyArgumentsVec2) {
- auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Construct(Source{{12, 40}}, ty.vec2<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooManyArgumentsVec2) {
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
+ Construct(Source{{12, 40}}, ty.vec2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec3_Error_TooManyArgumentsVec2AndScalar) {
- auto* tc =
- vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Expr(Source{{12, 40}}, 1.0f), Expr(Source{{12, 46}}, 1.0f));
- WrapInFunction(tc);
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Error_TooManyArgumentsVec3) {
- auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
- Expr(Source{{12, 40}}, 1.0f));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_TooManyArgumentsVec3) {
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()), Expr(Source{{12, 40}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 4 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Error_InvalidArgumentType) {
- auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Error_InvalidArgumentType) {
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: expected vector or scalar type in vector "
- "constructor; found: mat2x2<f32>");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: expected vector or scalar type in vector "
+ "constructor; found: mat2x2<f32>");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Success_ZeroValue) {
- auto* tc = vec3<f32>();
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Success_ZeroValue) {
+ auto* tc = vec3<f32>();
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 0u);
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 0u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3F32_Success_Scalar) {
- auto* tc = vec3<f32>(1.0f, 1.0f, 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3F32_Success_Scalar) {
+ auto* tc = vec3<f32>(1.0f, 1.0f, 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
- EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::F32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 3u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::F32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3U32_Success_Scalar) {
- auto* tc = vec3<u32>(1u, 1u, 1u);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3U32_Success_Scalar) {
+ auto* tc = vec3<u32>(1u, 1u, 1u);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
- EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 3u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::U32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::U32>());
+ EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::U32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3I32_Success_Scalar) {
- auto* tc = vec3<i32>(1, 1, 1);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3I32_Success_Scalar) {
+ auto* tc = vec3<i32>(1, 1, 1);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
- EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 3u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::I32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::I32>());
+ EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::I32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3Bool_Success_Scalar) {
- auto* tc = vec3<bool>(true, false, true);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3Bool_Success_Scalar) {
+ auto* tc = vec3<bool>(true, false, true);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 3u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Bool>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
- EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::Bool>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 3u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Bool>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Bool>());
+ EXPECT_TRUE(ctor->Parameters()[2]->Type()->Is<sem::Bool>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Success_Vec2AndScalar) {
- auto* tc = vec3<f32>(vec2<f32>(), 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Success_Vec2AndScalar) {
+ auto* tc = vec3<f32>(vec2<f32>(), 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 2u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::F32>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Success_ScalarAndVec2) {
- auto* tc = vec3<f32>(1.0f, vec2<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Success_ScalarAndVec2) {
+ auto* tc = vec3<f32>(1.0f, vec2<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 2u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
- EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Vector>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 2u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::F32>());
+ EXPECT_TRUE(ctor->Parameters()[1]->Type()->Is<sem::Vector>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Success_Identity) {
- auto* tc = vec3<f32>(vec3<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Success_Identity) {
+ auto* tc = vec3<f32>(vec3<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConstructor>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConstructor>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec3_Success_Vec3TypeConversion) {
- auto* tc = vec3<f32>(vec3<i32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec3_Success_Vec3TypeConversion) {
+ auto* tc = vec3<f32>(vec3<i32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 3u);
- auto* call = Sem().Get(tc);
- ASSERT_NE(call, nullptr);
- auto* ctor = call->Target()->As<sem::TypeConversion>();
- ASSERT_NE(ctor, nullptr);
- EXPECT_EQ(call->Type(), ctor->ReturnType());
- ASSERT_EQ(ctor->Parameters().size(), 1u);
- EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
+ auto* call = Sem().Get(tc);
+ ASSERT_NE(call, nullptr);
+ auto* ctor = call->Target()->As<sem::TypeConversion>();
+ ASSERT_NE(ctor, nullptr);
+ EXPECT_EQ(call->Type(), ctor->ReturnType());
+ ASSERT_EQ(ctor->Parameters().size(), 1u);
+ EXPECT_TRUE(ctor->Parameters()[0]->Type()->Is<sem::Vector>());
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4F32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec4<f32>(1.0f, 1.0f, Expr(Source{{12, 34}}, 1), 1.0f);
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(1.0f, 1.0f, Expr(Source{{12, 34}}, 1), 1.0f);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'f32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'f32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4U32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec4<u32>(1u, 1u, Expr(Source{{12, 34}}, 1), 1u);
- WrapInFunction(tc);
+ auto* tc = vec4<u32>(1u, 1u, Expr(Source{{12, 34}}, 1), 1u);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'u32', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'u32', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4I32_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec4<i32>(1, 1, Expr(Source{{12, 34}}, 1u), 1);
- WrapInFunction(tc);
+ auto* tc = vec4<i32>(1, 1, Expr(Source{{12, 34}}, 1u), 1);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'i32', found 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'i32', found 'u32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4Bool_Error_ScalarArgumentTypeMismatch) {
- auto* tc = vec4<bool>(true, false, Expr(Source{{12, 34}}, 1), true);
- WrapInFunction(tc);
+ auto* tc = vec4<bool>(true, false, Expr(Source{{12, 34}}, 1), true);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'bool', found 'i32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'bool', found 'i32'");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Error_TooFewArgumentsScalar) {
- auto* tc =
- vec4<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
- Expr(Source{{12, 46}}, 1.0f));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_TooFewArgumentsScalar) {
+ auto* tc = vec4<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Error_TooManyArgumentsScalar) {
- auto* tc =
- vec4<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
- Expr(Source{{12, 46}}, 1.0f), Expr(Source{{12, 52}}, 1.0f),
- Expr(Source{{12, 58}}, 1.0f));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_TooManyArgumentsScalar) {
+ auto* tc = vec4<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f), Expr(Source{{12, 52}}, 1.0f),
+ Expr(Source{{12, 58}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooFewArgumentsVec2AndScalar) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Expr(Source{{12, 40}}, 1.0f));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec2AndScalars) {
- auto* tc = vec4<f32>(
- Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1.0f),
- Expr(Source{{12, 46}}, 1.0f), Expr(Source{{12, 52}}, 1.0f));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f), Expr(Source{{12, 52}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec2Vec2Scalar) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Construct(Source{{12, 40}}, ty.vec2<f32>()),
- Expr(Source{{12, 46}}, 1.0f));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
+ Construct(Source{{12, 40}}, ty.vec2<f32>()), Expr(Source{{12, 46}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec2Vec2Vec2) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Construct(Source{{12, 40}}, ty.vec2<f32>()),
- Construct(Source{{12, 40}}, ty.vec2<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
+ Construct(Source{{12, 40}}, ty.vec2<f32>()),
+ Construct(Source{{12, 40}}, ty.vec2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Error_TooFewArgumentsVec3) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_TooFewArgumentsVec3) {
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 3 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndScalars) {
- auto* tc =
- vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
- Expr(Source{{12, 40}}, 1.0f), Expr(Source{{12, 46}}, 1.0f));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()), Expr(Source{{12, 40}}, 1.0f),
+ Expr(Source{{12, 46}}, 1.0f));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndVec2) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
- Construct(Source{{12, 40}}, ty.vec2<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
+ Construct(Source{{12, 40}}, ty.vec2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec2AndVec3) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
- Construct(Source{{12, 40}}, ty.vec3<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec2<f32>()),
+ Construct(Source{{12, 40}}, ty.vec3<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 5 component(s)");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vec4_Error_TooManyArgumentsVec3AndVec3) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
- Construct(Source{{12, 40}}, ty.vec3<f32>()));
- WrapInFunction(tc);
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.vec3<f32>()),
+ Construct(Source{{12, 40}}, ty.vec3<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec4<f32>' with 6 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Error_InvalidArgumentType) {
- auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Error_InvalidArgumentType) {
+ auto* tc = vec4<f32>(Construct(Source{{12, 34}}, ty.mat2x2<f32>()));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: expected vector or scalar type in vector "
- "constructor; found: mat2x2<f32>");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: expected vector or scalar type in vector "
+ "constructor; found: mat2x2<f32>");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_ZeroValue) {
- auto* tc = vec4<f32>();
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_ZeroValue) {
+ auto* tc = vec4<f32>();
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4F32_Success_Scalar) {
- auto* tc = vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4F32_Success_Scalar) {
+ auto* tc = vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4U32_Success_Scalar) {
- auto* tc = vec4<u32>(1u, 1u, 1u, 1u);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4U32_Success_Scalar) {
+ auto* tc = vec4<u32>(1u, 1u, 1u, 1u);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4I32_Success_Scalar) {
- auto* tc = vec4<i32>(1, 1, 1, 1);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4I32_Success_Scalar) {
+ auto* tc = vec4<i32>(1, 1, 1, 1);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4Bool_Success_Scalar) {
- auto* tc = vec4<bool>(true, false, true, false);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4Bool_Success_Scalar) {
+ auto* tc = vec4<bool>(true, false, true, false);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_Vec2ScalarScalar) {
- auto* tc = vec4<f32>(vec2<f32>(), 1.0f, 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_Vec2ScalarScalar) {
+ auto* tc = vec4<f32>(vec2<f32>(), 1.0f, 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_ScalarVec2Scalar) {
- auto* tc = vec4<f32>(1.0f, vec2<f32>(), 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_ScalarVec2Scalar) {
+ auto* tc = vec4<f32>(1.0f, vec2<f32>(), 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_ScalarScalarVec2) {
- auto* tc = vec4<f32>(1.0f, 1.0f, vec2<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_ScalarScalarVec2) {
+ auto* tc = vec4<f32>(1.0f, 1.0f, vec2<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_Vec2AndVec2) {
- auto* tc = vec4<f32>(vec2<f32>(), vec2<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_Vec2AndVec2) {
+ auto* tc = vec4<f32>(vec2<f32>(), vec2<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_Vec3AndScalar) {
- auto* tc = vec4<f32>(vec3<f32>(), 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_Vec3AndScalar) {
+ auto* tc = vec4<f32>(vec3<f32>(), 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_ScalarAndVec3) {
- auto* tc = vec4<f32>(1.0f, vec3<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_ScalarAndVec3) {
+ auto* tc = vec4<f32>(1.0f, vec3<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_Identity) {
- auto* tc = vec4<f32>(vec4<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_Identity) {
+ auto* tc = vec4<f32>(vec4<f32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vec4_Success_Vec4TypeConversion) {
- auto* tc = vec4<f32>(vec4<i32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vec4_Success_Vec4TypeConversion) {
+ auto* tc = vec4<f32>(vec4<i32>());
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_NestedVectorConstructors_InnerError) {
- auto* tc = vec4<f32>(vec4<f32>(1.0f, 1.0f,
- vec3<f32>(Expr(Source{{12, 34}}, 1.0f),
- Expr(Source{{12, 34}}, 1.0f))),
- 1.0f);
- WrapInFunction(tc);
+ auto* tc =
+ vec4<f32>(vec4<f32>(1.0f, 1.0f,
+ vec3<f32>(Expr(Source{{12, 34}}, 1.0f), Expr(Source{{12, 34}}, 1.0f))),
+ 1.0f);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: attempted to construct 'vec3<f32>' with 2 component(s)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_NestedVectorConstructors_Success) {
- auto* tc = vec4<f32>(vec3<f32>(vec2<f32>(1.0f, 1.0f), 1.0f), 1.0f);
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_NestedVectorConstructors_Success) {
+ auto* tc = vec4<f32>(vec3<f32>(vec2<f32>(1.0f, 1.0f), 1.0f), 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(tc), nullptr);
- ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
+ ASSERT_NE(TypeOf(tc), nullptr);
+ ASSERT_TRUE(TypeOf(tc)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(tc)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(tc)->As<sem::Vector>()->Width(), 4u);
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vector_Alias_Argument_Error) {
- auto* alias = Alias("UnsignedInt", ty.u32());
- Global("uint_var", ty.Of(alias), ast::StorageClass::kPrivate);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_Alias_Argument_Error) {
+ auto* alias = Alias("UnsignedInt", ty.u32());
+ Global("uint_var", ty.Of(alias), ast::StorageClass::kPrivate);
- auto* tc = vec2<f32>(Expr(Source{{12, 34}}, "uint_var"));
- WrapInFunction(tc);
+ auto* tc = vec2<f32>(Expr(Source{{12, 34}}, "uint_var"));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'f32', found 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'f32', found 'u32'");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vector_Alias_Argument_Success) {
- auto* f32_alias = Alias("Float32", ty.f32());
- auto* vec2_alias = Alias("VectorFloat2", ty.vec2<f32>());
- Global("my_f32", ty.Of(f32_alias), ast::StorageClass::kPrivate);
- Global("my_vec2", ty.Of(vec2_alias), ast::StorageClass::kPrivate);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_Alias_Argument_Success) {
+ auto* f32_alias = Alias("Float32", ty.f32());
+ auto* vec2_alias = Alias("VectorFloat2", ty.vec2<f32>());
+ Global("my_f32", ty.Of(f32_alias), ast::StorageClass::kPrivate);
+ Global("my_vec2", ty.Of(vec2_alias), ast::StorageClass::kPrivate);
- auto* tc = vec3<f32>("my_vec2", "my_f32");
- WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* tc = vec3<f32>("my_vec2", "my_f32");
+ WrapInFunction(tc);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vector_ElementTypeAlias_Error) {
- auto* f32_alias = Alias("Float32", ty.f32());
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_ElementTypeAlias_Error) {
+ auto* f32_alias = Alias("Float32", ty.f32());
- // vec2<Float32>(1.0f, 1u)
- auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
- auto* tc =
- Construct(Source{{12, 34}}, vec_type, 1.0f, Expr(Source{{12, 40}}, 1u));
- WrapInFunction(tc);
+ // vec2<Float32>(1.0f, 1u)
+ auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
+ auto* tc = Construct(Source{{12, 34}}, vec_type, 1.0f, Expr(Source{{12, 40}}, 1u));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:40 error: type in vector constructor does not match vector "
- "type: expected 'f32', found 'u32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:40 error: type in vector constructor does not match vector "
+ "type: expected 'f32', found 'u32'");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_Constructor_Vector_ElementTypeAlias_Success) {
- auto* f32_alias = Alias("Float32", ty.f32());
+TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Vector_ElementTypeAlias_Success) {
+ auto* f32_alias = Alias("Float32", ty.f32());
- // vec2<Float32>(1.0f, 1.0f)
- auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
- auto* tc = Construct(Source{{12, 34}}, vec_type, 1.0f, 1.0f);
- WrapInFunction(tc);
+ // vec2<Float32>(1.0f, 1.0f)
+ auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
+ auto* tc = Construct(Source{{12, 34}}, vec_type, 1.0f, 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vector_ArgumentElementTypeAlias_Error) {
- auto* f32_alias = Alias("Float32", ty.f32());
+ auto* f32_alias = Alias("Float32", ty.f32());
- // vec3<u32>(vec<Float32>(), 1.0f)
- auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
- auto* tc = vec3<u32>(Construct(Source{{12, 34}}, vec_type), 1.0f);
- WrapInFunction(tc);
+ // vec3<u32>(vec<Float32>(), 1.0f)
+ auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
+ auto* tc = vec3<u32>(Construct(Source{{12, 34}}, vec_type), 1.0f);
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type in vector constructor does not match vector "
- "type: expected 'u32', found 'f32'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: type in vector constructor does not match vector "
+ "type: expected 'u32', found 'f32'");
}
TEST_F(ResolverTypeConstructorValidationTest,
Expr_Constructor_Vector_ArgumentElementTypeAlias_Success) {
- auto* f32_alias = Alias("Float32", ty.f32());
+ auto* f32_alias = Alias("Float32", ty.f32());
- // vec3<f32>(vec<Float32>(), 1.0f)
- auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
- auto* tc = vec3<f32>(Construct(Source{{12, 34}}, vec_type), 1.0f);
- WrapInFunction(tc);
+ // vec3<f32>(vec<Float32>(), 1.0f)
+ auto* vec_type = ty.vec(ty.Of(f32_alias), 2);
+ auto* tc = vec3<f32>(Construct(Source{{12, 34}}, vec_type), 1.0f);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeConstructorValidationTest, InferVec2ElementTypeFromScalars) {
- auto* vec2_bool =
- Construct(create<ast::Vector>(nullptr, 2), Expr(true), Expr(false));
- auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1), Expr(2));
- auto* vec2_u32 =
- Construct(create<ast::Vector>(nullptr, 2), Expr(1u), Expr(2u));
- auto* vec2_f32 =
- Construct(create<ast::Vector>(nullptr, 2), Expr(1.0f), Expr(2.0f));
- WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32);
+ auto* vec2_bool = Construct(create<ast::Vector>(nullptr, 2), Expr(true), Expr(false));
+ auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1), Expr(2));
+ auto* vec2_u32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1u), Expr(2u));
+ auto* vec2_f32 = Construct(create<ast::Vector>(nullptr, 2), Expr(1.0f), Expr(2.0f));
+ WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec2_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec2_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec2_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec2_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec2_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec2_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec2_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec2_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec2_bool)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_i32)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_u32)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_f32)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_bool), TypeOf(vec2_bool->target.type));
- EXPECT_EQ(TypeOf(vec2_i32), TypeOf(vec2_i32->target.type));
- EXPECT_EQ(TypeOf(vec2_u32), TypeOf(vec2_u32->target.type));
- EXPECT_EQ(TypeOf(vec2_f32), TypeOf(vec2_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec2_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec2_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec2_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec2_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec2_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec2_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec2_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec2_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec2_bool)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_i32)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_u32)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_f32)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_bool), TypeOf(vec2_bool->target.type));
+ EXPECT_EQ(TypeOf(vec2_i32), TypeOf(vec2_i32->target.type));
+ EXPECT_EQ(TypeOf(vec2_u32), TypeOf(vec2_u32->target.type));
+ EXPECT_EQ(TypeOf(vec2_f32), TypeOf(vec2_f32->target.type));
}
TEST_F(ResolverTypeConstructorValidationTest, InferVec2ElementTypeFromVec2) {
- auto* vec2_bool =
- Construct(create<ast::Vector>(nullptr, 2), vec2<bool>(true, false));
- auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2), vec2<i32>(1, 2));
- auto* vec2_u32 =
- Construct(create<ast::Vector>(nullptr, 2), vec2<u32>(1u, 2u));
- auto* vec2_f32 =
- Construct(create<ast::Vector>(nullptr, 2), vec2<f32>(1.0f, 2.0f));
- WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32);
+ auto* vec2_bool = Construct(create<ast::Vector>(nullptr, 2), vec2<bool>(true, false));
+ auto* vec2_i32 = Construct(create<ast::Vector>(nullptr, 2), vec2<i32>(1, 2));
+ auto* vec2_u32 = Construct(create<ast::Vector>(nullptr, 2), vec2<u32>(1u, 2u));
+ auto* vec2_f32 = Construct(create<ast::Vector>(nullptr, 2), vec2<f32>(1.0f, 2.0f));
+ WrapInFunction(vec2_bool, vec2_i32, vec2_u32, vec2_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec2_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec2_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec2_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec2_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec2_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec2_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec2_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec2_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec2_bool)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_i32)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_u32)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_f32)->As<sem::Vector>()->Width(), 2u);
- EXPECT_EQ(TypeOf(vec2_bool), TypeOf(vec2_bool->target.type));
- EXPECT_EQ(TypeOf(vec2_i32), TypeOf(vec2_i32->target.type));
- EXPECT_EQ(TypeOf(vec2_u32), TypeOf(vec2_u32->target.type));
- EXPECT_EQ(TypeOf(vec2_f32), TypeOf(vec2_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec2_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec2_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec2_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec2_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec2_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec2_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec2_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec2_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec2_bool)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_i32)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_u32)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_f32)->As<sem::Vector>()->Width(), 2u);
+ EXPECT_EQ(TypeOf(vec2_bool), TypeOf(vec2_bool->target.type));
+ EXPECT_EQ(TypeOf(vec2_i32), TypeOf(vec2_i32->target.type));
+ EXPECT_EQ(TypeOf(vec2_u32), TypeOf(vec2_u32->target.type));
+ EXPECT_EQ(TypeOf(vec2_f32), TypeOf(vec2_f32->target.type));
}
TEST_F(ResolverTypeConstructorValidationTest, InferVec3ElementTypeFromScalars) {
- auto* vec3_bool = Construct(create<ast::Vector>(nullptr, 3), Expr(true),
- Expr(false), Expr(true));
- auto* vec3_i32 =
- Construct(create<ast::Vector>(nullptr, 3), Expr(1), Expr(2), Expr(3));
- auto* vec3_u32 =
- Construct(create<ast::Vector>(nullptr, 3), Expr(1u), Expr(2u), Expr(3u));
- auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1.0f),
- Expr(2.0f), Expr(3.0f));
- WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
+ auto* vec3_bool =
+ Construct(create<ast::Vector>(nullptr, 3), Expr(true), Expr(false), Expr(true));
+ auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1), Expr(2), Expr(3));
+ auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1u), Expr(2u), Expr(3u));
+ auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1.0f), Expr(2.0f), Expr(3.0f));
+ WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec3_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
- EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
- EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
- EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec3_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
+ EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
+ EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
+ EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
}
TEST_F(ResolverTypeConstructorValidationTest, InferVec3ElementTypeFromVec3) {
- auto* vec3_bool =
- Construct(create<ast::Vector>(nullptr, 3), vec3<bool>(true, false, true));
- auto* vec3_i32 =
- Construct(create<ast::Vector>(nullptr, 3), vec3<i32>(1, 2, 3));
- auto* vec3_u32 =
- Construct(create<ast::Vector>(nullptr, 3), vec3<u32>(1u, 2u, 3u));
- auto* vec3_f32 =
- Construct(create<ast::Vector>(nullptr, 3), vec3<f32>(1.0f, 2.0f, 3.0f));
- WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
+ auto* vec3_bool = Construct(create<ast::Vector>(nullptr, 3), vec3<bool>(true, false, true));
+ auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3), vec3<i32>(1, 2, 3));
+ auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3), vec3<u32>(1u, 2u, 3u));
+ auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), vec3<f32>(1.0f, 2.0f, 3.0f));
+ WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec3_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
- EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
- EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
- EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec3_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
+ EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
+ EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
+ EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
}
-TEST_F(ResolverTypeConstructorValidationTest,
- InferVec3ElementTypeFromScalarAndVec2) {
- auto* vec3_bool = Construct(create<ast::Vector>(nullptr, 3), Expr(true),
- vec2<bool>(false, true));
- auto* vec3_i32 =
- Construct(create<ast::Vector>(nullptr, 3), Expr(1), vec2<i32>(2, 3));
- auto* vec3_u32 =
- Construct(create<ast::Vector>(nullptr, 3), Expr(1u), vec2<u32>(2u, 3u));
- auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1.0f),
- vec2<f32>(2.0f, 3.0f));
- WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
+TEST_F(ResolverTypeConstructorValidationTest, InferVec3ElementTypeFromScalarAndVec2) {
+ auto* vec3_bool =
+ Construct(create<ast::Vector>(nullptr, 3), Expr(true), vec2<bool>(false, true));
+ auto* vec3_i32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1), vec2<i32>(2, 3));
+ auto* vec3_u32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1u), vec2<u32>(2u, 3u));
+ auto* vec3_f32 = Construct(create<ast::Vector>(nullptr, 3), Expr(1.0f), vec2<f32>(2.0f, 3.0f));
+ WrapInFunction(vec3_bool, vec3_i32, vec3_u32, vec3_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec3_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
- EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
- EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
- EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
- EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec3_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec3_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec3_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec3_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec3_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec3_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec3_bool)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_i32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_u32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_f32)->As<sem::Vector>()->Width(), 3u);
+ EXPECT_EQ(TypeOf(vec3_bool), TypeOf(vec3_bool->target.type));
+ EXPECT_EQ(TypeOf(vec3_i32), TypeOf(vec3_i32->target.type));
+ EXPECT_EQ(TypeOf(vec3_u32), TypeOf(vec3_u32->target.type));
+ EXPECT_EQ(TypeOf(vec3_f32), TypeOf(vec3_f32->target.type));
}
TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromScalars) {
- auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4), Expr(true),
- Expr(false), Expr(true), Expr(false));
- auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1), Expr(2),
- Expr(3), Expr(4));
- auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1u),
- Expr(2u), Expr(3u), Expr(4u));
- auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1.0f),
- Expr(2.0f), Expr(3.0f), Expr(4.0f));
- WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+ auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4), Expr(true), Expr(false),
+ Expr(true), Expr(false));
+ auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1), Expr(2), Expr(3), Expr(4));
+ auto* vec4_u32 =
+ Construct(create<ast::Vector>(nullptr, 4), Expr(1u), Expr(2u), Expr(3u), Expr(4u));
+ auto* vec4_f32 =
+ Construct(create<ast::Vector>(nullptr, 4), Expr(1.0f), Expr(2.0f), Expr(3.0f), Expr(4.0f));
+ WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
- EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
- EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
- EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
+ EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
+ EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
+ EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
}
TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromVec4) {
- auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4),
- vec4<bool>(true, false, true, false));
- auto* vec4_i32 =
- Construct(create<ast::Vector>(nullptr, 4), vec4<i32>(1, 2, 3, 4));
- auto* vec4_u32 =
- Construct(create<ast::Vector>(nullptr, 4), vec4<u32>(1u, 2u, 3u, 4u));
- auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4),
- vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
- WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+ auto* vec4_bool =
+ Construct(create<ast::Vector>(nullptr, 4), vec4<bool>(true, false, true, false));
+ auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), vec4<i32>(1, 2, 3, 4));
+ auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4), vec4<u32>(1u, 2u, 3u, 4u));
+ auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4), vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
+ WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
- EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
- EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
- EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
+ EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
+ EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
+ EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
}
-TEST_F(ResolverTypeConstructorValidationTest,
- InferVec4ElementTypeFromScalarAndVec3) {
- auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4), Expr(true),
- vec3<bool>(false, true, false));
- auto* vec4_i32 =
- Construct(create<ast::Vector>(nullptr, 4), Expr(1), vec3<i32>(2, 3, 4));
- auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1u),
- vec3<u32>(2u, 3u, 4u));
- auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1.0f),
- vec3<f32>(2.0f, 3.0f, 4.0f));
- WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromScalarAndVec3) {
+ auto* vec4_bool =
+ Construct(create<ast::Vector>(nullptr, 4), Expr(true), vec3<bool>(false, true, false));
+ auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1), vec3<i32>(2, 3, 4));
+ auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4), Expr(1u), vec3<u32>(2u, 3u, 4u));
+ auto* vec4_f32 =
+ Construct(create<ast::Vector>(nullptr, 4), Expr(1.0f), vec3<f32>(2.0f, 3.0f, 4.0f));
+ WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
- EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
- EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
- EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
+ EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
+ EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
+ EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
}
-TEST_F(ResolverTypeConstructorValidationTest,
- InferVec4ElementTypeFromVec2AndVec2) {
- auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4),
- vec2<bool>(true, false), vec2<bool>(true, false));
- auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), vec2<i32>(1, 2),
- vec2<i32>(3, 4));
- auto* vec4_u32 = Construct(create<ast::Vector>(nullptr, 4), vec2<u32>(1u, 2u),
- vec2<u32>(3u, 4u));
- auto* vec4_f32 = Construct(create<ast::Vector>(nullptr, 4),
- vec2<f32>(1.0f, 2.0f), vec2<f32>(3.0f, 4.0f));
- WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
+TEST_F(ResolverTypeConstructorValidationTest, InferVec4ElementTypeFromVec2AndVec2) {
+ auto* vec4_bool = Construct(create<ast::Vector>(nullptr, 4), vec2<bool>(true, false),
+ vec2<bool>(true, false));
+ auto* vec4_i32 = Construct(create<ast::Vector>(nullptr, 4), vec2<i32>(1, 2), vec2<i32>(3, 4));
+ auto* vec4_u32 =
+ Construct(create<ast::Vector>(nullptr, 4), vec2<u32>(1u, 2u), vec2<u32>(3u, 4u));
+ auto* vec4_f32 =
+ Construct(create<ast::Vector>(nullptr, 4), vec2<f32>(1.0f, 2.0f), vec2<f32>(3.0f, 4.0f));
+ WrapInFunction(vec4_bool, vec4_i32, vec4_u32, vec4_f32);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
- ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
- EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
- EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
- EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
- EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
- EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
- EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
+ ASSERT_TRUE(TypeOf(vec4_bool)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_i32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_u32)->Is<sem::Vector>());
+ ASSERT_TRUE(TypeOf(vec4_f32)->Is<sem::Vector>());
+ EXPECT_TRUE(TypeOf(vec4_bool)->As<sem::Vector>()->type()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(vec4_i32)->As<sem::Vector>()->type()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(vec4_u32)->As<sem::Vector>()->type()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(vec4_f32)->As<sem::Vector>()->type()->Is<sem::F32>());
+ EXPECT_EQ(TypeOf(vec4_bool)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_i32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_u32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_f32)->As<sem::Vector>()->Width(), 4u);
+ EXPECT_EQ(TypeOf(vec4_bool), TypeOf(vec4_bool->target.type));
+ EXPECT_EQ(TypeOf(vec4_i32), TypeOf(vec4_i32->target.type));
+ EXPECT_EQ(TypeOf(vec4_u32), TypeOf(vec4_u32->target.type));
+ EXPECT_EQ(TypeOf(vec4_f32), TypeOf(vec4_f32->target.type));
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVectorElementTypeWithoutArgs) {
- WrapInFunction(Construct(create<ast::Vector>(Source{{12, 34}}, nullptr, 3)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVectorElementTypeWithoutArgs) {
+ WrapInFunction(Construct(create<ast::Vector>(Source{{12, 34}}, nullptr, 3)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVec2ElementTypeFromScalarsMismatch) {
- WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 2),
- Expr(Source{{1, 2}}, 1), //
- Expr(Source{{1, 3}}, 2u)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec2ElementTypeFromScalarsMismatch) {
+ WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 2),
+ Expr(Source{{1, 2}}, 1), //
+ Expr(Source{{1, 3}}, 2u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
1:2 note: argument 0 has type i32
1:3 note: argument 1 has type u32)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVec3ElementTypeFromScalarsMismatch) {
- WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3),
- Expr(Source{{1, 2}}, 1), //
- Expr(Source{{1, 3}}, 2u), //
- Expr(Source{{1, 4}}, 3)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec3ElementTypeFromScalarsMismatch) {
+ WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3),
+ Expr(Source{{1, 2}}, 1), //
+ Expr(Source{{1, 3}}, 2u), //
+ Expr(Source{{1, 4}}, 3)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
1:2 note: argument 0 has type i32
1:3 note: argument 1 has type u32
1:4 note: argument 2 has type i32)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVec3ElementTypeFromScalarAndVec2Mismatch) {
- WrapInFunction(
- Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3),
- Expr(Source{{1, 2}}, 1), //
- Construct(Source{{1, 3}}, ty.vec2<f32>(), 2.0f, 3.0f)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec3ElementTypeFromScalarAndVec2Mismatch) {
+ WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 3),
+ Expr(Source{{1, 2}}, 1), //
+ Construct(Source{{1, 3}}, ty.vec2<f32>(), 2.0f, 3.0f)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
1:2 note: argument 0 has type i32
1:3 note: argument 1 has type vec2<f32>)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVec4ElementTypeFromScalarsMismatch) {
- WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
- Expr(Source{{1, 2}}, 1), //
- Expr(Source{{1, 3}}, 2), //
- Expr(Source{{1, 4}}, 3.0f), //
- Expr(Source{{1, 5}}, 4)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromScalarsMismatch) {
+ WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
+ Expr(Source{{1, 2}}, 1), //
+ Expr(Source{{1, 3}}, 2), //
+ Expr(Source{{1, 4}}, 3.0f), //
+ Expr(Source{{1, 5}}, 4)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
1:2 note: argument 0 has type i32
1:3 note: argument 1 has type i32
1:4 note: argument 2 has type f32
1:5 note: argument 3 has type i32)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVec4ElementTypeFromScalarAndVec3Mismatch) {
- WrapInFunction(
- Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
- Expr(Source{{1, 2}}, 1), //
- Construct(Source{{1, 3}}, ty.vec3<u32>(), 2u, 3u, 4u)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromScalarAndVec3Mismatch) {
+ WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
+ Expr(Source{{1, 2}}, 1), //
+ Construct(Source{{1, 3}}, ty.vec3<u32>(), 2u, 3u, 4u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
1:2 note: argument 0 has type i32
1:3 note: argument 1 has type vec3<u32>)");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- CannotInferVec4ElementTypeFromVec2AndVec2Mismatch) {
- WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
- Construct(Source{{1, 2}}, ty.vec2<i32>(), 3, 4), //
- Construct(Source{{1, 3}}, ty.vec2<u32>(), 3u, 4u)));
+TEST_F(ResolverTypeConstructorValidationTest, CannotInferVec4ElementTypeFromVec2AndVec2Mismatch) {
+ WrapInFunction(Construct(Source{{1, 1}}, create<ast::Vector>(nullptr, 4),
+ Construct(Source{{1, 2}}, ty.vec2<i32>(), 3, 4), //
+ Construct(Source{{1, 3}}, ty.vec2<u32>(), 3u, 4u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(1:1 error: cannot infer vector element type, as constructor arguments have different types
1:2 note: argument 0 has type vec2<i32>
1:3 note: argument 1 has type vec2<u32>)");
}
@@ -2204,347 +2037,331 @@
namespace MatrixConstructor {
struct MatrixDimensions {
- uint32_t rows;
- uint32_t columns;
+ uint32_t rows;
+ uint32_t columns;
};
static std::string MatrixStr(const MatrixDimensions& dimensions) {
- return "mat" + std::to_string(dimensions.columns) + "x" +
- std::to_string(dimensions.rows) + "<f32>";
+ return "mat" + std::to_string(dimensions.columns) + "x" + std::to_string(dimensions.rows) +
+ "<f32>";
}
using MatrixConstructorTest = ResolverTestWithParam<MatrixDimensions>;
TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooFewArguments) {
- // matNxM<f32>(vecM<f32>(), ...); with N - 1 arguments
+ // matNxM<f32>(vecM<f32>(), ...); with N - 1 arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns - 1; i++) {
- auto* vec_type = ty.vec<f32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns - 1; i++) {
+ auto* vec_type = ty.vec<f32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<f32>";
}
- args_tys << "vec" << param.rows << "<f32>";
- }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_TooFewArguments) {
- // matNxM<f32>(f32,...,f32); with N*M - 1 arguments
+ // matNxM<f32>(f32,...,f32); with N*M - 1 arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns * param.rows - 1; i++) {
- args.push_back(Construct(Source{{12, i}}, ty.f32()));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns * param.rows - 1; i++) {
+ args.push_back(Construct(Source{{12, i}}, ty.f32()));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "f32";
}
- args_tys << "f32";
- }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooManyArguments) {
- // matNxM<f32>(vecM<f32>(), ...); with N + 1 arguments
+ // matNxM<f32>(vecM<f32>(), ...); with N + 1 arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns + 1; i++) {
- auto* vec_type = ty.vec<f32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns + 1; i++) {
+ auto* vec_type = ty.vec<f32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<f32>";
}
- args_tys << "vec" << param.rows << "<f32>";
- }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_TooManyArguments) {
- // matNxM<f32>(f32,...,f32); with N*M + 1 arguments
+ // matNxM<f32>(f32,...,f32); with N*M + 1 arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns * param.rows + 1; i++) {
- args.push_back(Construct(Source{{12, i}}, ty.f32()));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns * param.rows + 1; i++) {
+ args.push_back(Construct(Source{{12, i}}, ty.f32()));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "f32";
}
- args_tys << "f32";
- }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
-TEST_P(MatrixConstructorTest,
- Expr_ColumnConstructor_Error_InvalidArgumentType) {
- // matNxM<f32>(vec<u32>, vec<u32>, ...); N arguments
+TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_InvalidArgumentType) {
+ // matNxM<f32>(vec<u32>, vec<u32>, ...); N arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- auto* vec_type = ty.vec<u32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ auto* vec_type = ty.vec<u32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<u32>";
}
- args_tys << "vec" << param.rows << "<u32>";
- }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
-TEST_P(MatrixConstructorTest,
- Expr_ElementConstructor_Error_InvalidArgumentType) {
- // matNxM<f32>(u32, u32, ...); N*M arguments
+TEST_P(MatrixConstructorTest, Expr_ElementConstructor_Error_InvalidArgumentType) {
+ // matNxM<f32>(u32, u32, ...); N*M arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- args.push_back(Expr(Source{{12, i}}, 1u));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ args.push_back(Expr(Source{{12, i}}, 1u));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "u32";
}
- args_tys << "u32";
- }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
-TEST_P(MatrixConstructorTest,
- Expr_ColumnConstructor_Error_TooFewRowsInVectorArgument) {
- // matNxM<f32>(vecM<f32>(),...,vecM-1<f32>());
+TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooFewRowsInVectorArgument) {
+ // matNxM<f32>(vecM<f32>(),...,vecM-1<f32>());
- const auto param = GetParam();
+ const auto param = GetParam();
- // Skip the test if parameters would have resulted in an invalid vec1 type.
- if (param.rows == 2) {
- return;
- }
-
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns - 1; i++) {
- auto* valid_vec_type = ty.vec<f32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, valid_vec_type));
- if (i > 1) {
- args_tys << ", ";
+ // Skip the test if parameters would have resulted in an invalid vec1 type.
+ if (param.rows == 2) {
+ return;
}
- args_tys << "vec" << param.rows << "<f32>";
- }
- const size_t kInvalidLoc = 2 * (param.columns - 1);
- auto* invalid_vec_type = ty.vec<f32>(param.rows - 1);
- args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
- args_tys << ", vec" << (param.rows - 1) << "<f32>";
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns - 1; i++) {
+ auto* valid_vec_type = ty.vec<f32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, valid_vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<f32>";
+ }
+ const size_t kInvalidLoc = 2 * (param.columns - 1);
+ auto* invalid_vec_type = ty.vec<f32>(param.rows - 1);
+ args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
+ args_tys << ", vec" << (param.rows - 1) << "<f32>";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
-TEST_P(MatrixConstructorTest,
- Expr_ColumnConstructor_Error_TooManyRowsInVectorArgument) {
- // matNxM<f32>(vecM<f32>(),...,vecM+1<f32>());
+TEST_P(MatrixConstructorTest, Expr_ColumnConstructor_Error_TooManyRowsInVectorArgument) {
+ // matNxM<f32>(vecM<f32>(),...,vecM+1<f32>());
- const auto param = GetParam();
+ const auto param = GetParam();
- // Skip the test if parameters would have resulted in an invalid vec5 type.
- if (param.rows == 4) {
- return;
- }
-
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns - 1; i++) {
- auto* valid_vec_type = ty.vec<f32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, valid_vec_type));
- if (i > 1) {
- args_tys << ", ";
+ // Skip the test if parameters would have resulted in an invalid vec5 type.
+ if (param.rows == 4) {
+ return;
}
- args_tys << "vec" << param.rows << "<f32>";
- }
- const size_t kInvalidLoc = 2 * (param.columns - 1);
- auto* invalid_vec_type = ty.vec<f32>(param.rows + 1);
- args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
- args_tys << ", vec" << (param.rows + 1) << "<f32>";
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns - 1; i++) {
+ auto* valid_vec_type = ty.vec<f32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, valid_vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<f32>";
+ }
+ const size_t kInvalidLoc = 2 * (param.columns - 1);
+ auto* invalid_vec_type = ty.vec<f32>(param.rows + 1);
+ args.push_back(Construct(Source{{12, kInvalidLoc}}, invalid_vec_type));
+ args_tys << ", vec" << (param.rows + 1) << "<f32>";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
TEST_P(MatrixConstructorTest, Expr_Constructor_ZeroValue_Success) {
- // matNxM<f32>();
+ // matNxM<f32>();
- const auto param = GetParam();
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{{12, 40}}, matrix_type);
- WrapInFunction(tc);
+ const auto param = GetParam();
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{{12, 40}}, matrix_type);
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, Expr_Constructor_WithColumns_Success) {
- // matNxM<f32>(vecM<f32>(), ...); with N arguments
+ // matNxM<f32>(vecM<f32>(), ...); with N arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- auto* vec_type = ty.vec<f32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ auto* vec_type = ty.vec<f32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, Expr_Constructor_WithElements_Success) {
- // matNxM<f32>(f32,...,f32); with N*M arguments
+ // matNxM<f32>(f32,...,f32); with N*M arguments
- const auto param = GetParam();
+ const auto param = GetParam();
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns * param.rows; i++) {
- args.push_back(Construct(Source{{12, i}}, ty.f32()));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns * param.rows; i++) {
+ args.push_back(Construct(Source{{12, i}}, ty.f32()));
+ }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Error) {
- // matNxM<Float32>(vecM<u32>(), ...); with N arguments
+ // matNxM<Float32>(vecM<u32>(), ...); with N arguments
- const auto param = GetParam();
- auto* f32_alias = Alias("Float32", ty.f32());
+ const auto param = GetParam();
+ auto* f32_alias = Alias("Float32", ty.f32());
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- auto* vec_type = ty.vec(ty.u32(), param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ auto* vec_type = ty.vec(ty.u32(), param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<u32>";
}
- args_tys << "vec" << param.rows << "<u32>";
- }
- auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
TEST_P(MatrixConstructorTest, Expr_Constructor_ElementTypeAlias_Success) {
- // matNxM<Float32>(vecM<f32>(), ...); with N arguments
+ // matNxM<Float32>(vecM<f32>(), ...); with N arguments
- const auto param = GetParam();
- auto* f32_alias = Alias("Float32", ty.f32());
+ const auto param = GetParam();
+ auto* f32_alias = Alias("Float32", ty.f32());
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- auto* vec_type = ty.vec<f32>(param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ auto* vec_type = ty.vec<f32>(param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ }
- auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat(ty.Of(f32_alias), param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverTypeConstructorValidationTest,
- Expr_MatrixConstructor_ArgumentTypeAlias_Error) {
- auto* alias = Alias("VectorUnsigned2", ty.vec2<u32>());
- auto* tc =
- mat2x2<f32>(Construct(Source{{12, 34}}, ty.Of(alias)), vec2<f32>());
- WrapInFunction(tc);
+TEST_F(ResolverTypeConstructorValidationTest, Expr_MatrixConstructor_ArgumentTypeAlias_Error) {
+ auto* alias = Alias("VectorUnsigned2", ty.vec2<u32>());
+ auto* tc = mat2x2<f32>(Construct(Source{{12, 34}}, ty.Of(alias)), vec2<f32>());
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: no matching constructor mat2x2<f32>(vec2<u32>, vec2<f32>)
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: no matching constructor mat2x2<f32>(vec2<u32>, vec2<f32>)
3 candidates available:
mat2x2<f32>()
@@ -2554,148 +2371,144 @@
}
TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentTypeAlias_Success) {
- const auto param = GetParam();
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* vec_type = ty.vec<f32>(param.rows);
- auto* vec_alias = Alias("VectorFloat2", vec_type);
+ const auto param = GetParam();
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* vec_type = ty.vec<f32>(param.rows);
+ auto* vec_alias = Alias("VectorFloat2", vec_type);
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- args.push_back(Construct(Source{{12, i}}, ty.Of(vec_alias)));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ args.push_back(Construct(Source{{12, i}}, ty.Of(vec_alias)));
+ }
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentElementTypeAlias_Error) {
- const auto param = GetParam();
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* f32_alias = Alias("UnsignedInt", ty.u32());
+ const auto param = GetParam();
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* f32_alias = Alias("UnsignedInt", ty.u32());
- std::stringstream args_tys;
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- if (i > 1) {
- args_tys << ", ";
+ std::stringstream args_tys;
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ if (i > 1) {
+ args_tys << ", ";
+ }
+ args_tys << "vec" << param.rows << "<u32>";
}
- args_tys << "vec" << param.rows << "<u32>";
- }
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " +
- MatrixStr(param) + "(" + args_tys.str() +
- ")\n\n3 candidates available:"));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), HasSubstr("12:1 error: no matching constructor " + MatrixStr(param) +
+ "(" + args_tys.str() + ")\n\n3 candidates available:"));
}
-TEST_P(MatrixConstructorTest,
- Expr_Constructor_ArgumentElementTypeAlias_Success) {
- const auto param = GetParam();
- auto* f32_alias = Alias("Float32", ty.f32());
+TEST_P(MatrixConstructorTest, Expr_Constructor_ArgumentElementTypeAlias_Success) {
+ const auto param = GetParam();
+ auto* f32_alias = Alias("Float32", ty.f32());
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
- args.push_back(Construct(Source{{12, i}}, vec_type));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ auto* vec_type = ty.vec(ty.Of(f32_alias), param.rows);
+ args.push_back(Construct(Source{{12, i}}, vec_type));
+ }
- auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = ty.mat<f32>(param.columns, param.rows);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, InferElementTypeFromVectors) {
- const auto param = GetParam();
+ const auto param = GetParam();
- ast::ExpressionList args;
- for (uint32_t i = 1; i <= param.columns; i++) {
- args.push_back(Construct(ty.vec<f32>(param.rows)));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 1; i <= param.columns; i++) {
+ args.push_back(Construct(ty.vec<f32>(param.rows)));
+ }
- auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
- auto* tc = Construct(Source{}, matrix_type, std::move(args));
- WrapInFunction(tc);
+ auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
+ auto* tc = Construct(Source{}, matrix_type, std::move(args));
+ WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, InferElementTypeFromScalars) {
- const auto param = GetParam();
+ const auto param = GetParam();
- ast::ExpressionList args;
- for (uint32_t i = 0; i < param.rows * param.columns; i++) {
- args.push_back(Expr(static_cast<f32>(i)));
- }
+ ast::ExpressionList args;
+ for (uint32_t i = 0; i < param.rows * param.columns; i++) {
+ args.push_back(Expr(static_cast<f32>(i)));
+ }
- auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
- WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
+ auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
+ WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_P(MatrixConstructorTest, CannotInferElementTypeFromVectors_Mismatch) {
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream err;
- err << "12:34 error: cannot infer matrix element type, as constructor "
- "arguments have different types";
+ std::stringstream err;
+ err << "12:34 error: cannot infer matrix element type, as constructor "
+ "arguments have different types";
- ast::ExpressionList args;
- for (uint32_t i = 0; i < param.columns; i++) {
- err << "\n";
- auto src = Source{{1, 10 + i}};
- if (i == 1) {
- // Odd one out
- args.push_back(Construct(src, ty.vec<i32>(param.rows)));
- err << src << " note: argument " << i << " has type vec" << param.rows
- << "<i32>";
- } else {
- args.push_back(Construct(src, ty.vec<f32>(param.rows)));
- err << src << " note: argument " << i << " has type vec" << param.rows
- << "<f32>";
+ ast::ExpressionList args;
+ for (uint32_t i = 0; i < param.columns; i++) {
+ err << "\n";
+ auto src = Source{{1, 10 + i}};
+ if (i == 1) {
+ // Odd one out
+ args.push_back(Construct(src, ty.vec<i32>(param.rows)));
+ err << src << " note: argument " << i << " has type vec" << param.rows << "<i32>";
+ } else {
+ args.push_back(Construct(src, ty.vec<f32>(param.rows)));
+ err << src << " note: argument " << i << " has type vec" << param.rows << "<f32>";
+ }
}
- }
- auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
- WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
+ auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
+ WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), err.str());
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), err.str());
}
TEST_P(MatrixConstructorTest, CannotInferElementTypeFromScalars_Mismatch) {
- const auto param = GetParam();
+ const auto param = GetParam();
- std::stringstream err;
- err << "12:34 error: cannot infer matrix element type, as constructor "
- "arguments have different types";
- ast::ExpressionList args;
- for (uint32_t i = 0; i < param.rows * param.columns; i++) {
- err << "\n";
- auto src = Source{{1, 10 + i}};
- if (i == 3) {
- args.push_back(Expr(src, static_cast<i32>(i))); // The odd one out
- err << src << " note: argument " << i << " has type i32";
- } else {
- args.push_back(Expr(src, static_cast<f32>(i)));
- err << src << " note: argument " << i << " has type f32";
+ std::stringstream err;
+ err << "12:34 error: cannot infer matrix element type, as constructor "
+ "arguments have different types";
+ ast::ExpressionList args;
+ for (uint32_t i = 0; i < param.rows * param.columns; i++) {
+ err << "\n";
+ auto src = Source{{1, 10 + i}};
+ if (i == 3) {
+ args.push_back(Expr(src, static_cast<i32>(i))); // The odd one out
+ err << src << " note: argument " << i << " has type i32";
+ } else {
+ args.push_back(Expr(src, static_cast<f32>(i)));
+ err << src << " note: argument " << i << " has type f32";
+ }
}
- }
- auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
- WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
+ auto* matrix_type = create<ast::Matrix>(nullptr, param.rows, param.columns);
+ WrapInFunction(Construct(Source{{12, 34}}, matrix_type, std::move(args)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_THAT(r()->error(), err.str());
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_THAT(r()->error(), err.str());
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
@@ -2744,94 +2557,91 @@
ResolverTestWithParam<std::tuple<CreatePtrs, // struct member type
uint32_t>>; // number of struct members
TEST_P(StructConstructorInputsTest, TooFew) {
- auto& param = GetParam();
- auto& str_params = std::get<0>(param);
- uint32_t N = std::get<1>(param);
+ auto& param = GetParam();
+ auto& str_params = std::get<0>(param);
+ uint32_t N = std::get<1>(param);
- ast::StructMemberList members;
- ast::ExpressionList values;
- for (uint32_t i = 0; i < N; i++) {
- auto* struct_type = str_params.ast(*this);
- members.push_back(Member("member_" + std::to_string(i), struct_type));
- if (i < N - 1) {
- auto* ctor_value_expr = str_params.expr(*this, 0);
- values.push_back(ctor_value_expr);
+ ast::StructMemberList members;
+ ast::ExpressionList values;
+ for (uint32_t i = 0; i < N; i++) {
+ auto* struct_type = str_params.ast(*this);
+ members.push_back(Member("member_" + std::to_string(i), struct_type));
+ if (i < N - 1) {
+ auto* ctor_value_expr = str_params.expr(*this, 0);
+ values.push_back(ctor_value_expr);
+ }
}
- }
- auto* s = Structure("s", members);
- auto* tc = Construct(Source{{12, 34}}, ty.Of(s), values);
- WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: struct constructor has too few inputs: expected " +
- std::to_string(N) + ", found " + std::to_string(N - 1));
+ auto* s = Structure("s", members);
+ auto* tc = Construct(Source{{12, 34}}, ty.Of(s), values);
+ WrapInFunction(tc);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: struct constructor has too few inputs: expected " +
+ std::to_string(N) + ", found " + std::to_string(N - 1));
}
TEST_P(StructConstructorInputsTest, TooMany) {
- auto& param = GetParam();
- auto& str_params = std::get<0>(param);
- uint32_t N = std::get<1>(param);
+ auto& param = GetParam();
+ auto& str_params = std::get<0>(param);
+ uint32_t N = std::get<1>(param);
- ast::StructMemberList members;
- ast::ExpressionList values;
- for (uint32_t i = 0; i < N + 1; i++) {
- if (i < N) {
- auto* struct_type = str_params.ast(*this);
- members.push_back(Member("member_" + std::to_string(i), struct_type));
+ ast::StructMemberList members;
+ ast::ExpressionList values;
+ for (uint32_t i = 0; i < N + 1; i++) {
+ if (i < N) {
+ auto* struct_type = str_params.ast(*this);
+ members.push_back(Member("member_" + std::to_string(i), struct_type));
+ }
+ auto* ctor_value_expr = str_params.expr(*this, 0);
+ values.push_back(ctor_value_expr);
}
- auto* ctor_value_expr = str_params.expr(*this, 0);
- values.push_back(ctor_value_expr);
- }
- auto* s = Structure("s", members);
- auto* tc = Construct(Source{{12, 34}}, ty.Of(s), values);
- WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: struct constructor has too many inputs: expected " +
- std::to_string(N) + ", found " + std::to_string(N + 1));
+ auto* s = Structure("s", members);
+ auto* tc = Construct(Source{{12, 34}}, ty.Of(s), values);
+ WrapInFunction(tc);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: struct constructor has too many inputs: expected " +
+ std::to_string(N) + ", found " + std::to_string(N + 1));
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
StructConstructorInputsTest,
- testing::Combine(testing::ValuesIn(all_types),
- number_of_members));
+ testing::Combine(testing::ValuesIn(all_types), number_of_members));
using StructConstructorTypeTest =
ResolverTestWithParam<std::tuple<CreatePtrs, // struct member type
CreatePtrs, // constructor value type
uint32_t>>; // number of struct members
TEST_P(StructConstructorTypeTest, AllTypes) {
- auto& param = GetParam();
- auto& str_params = std::get<0>(param);
- auto& ctor_params = std::get<1>(param);
- uint32_t N = std::get<2>(param);
+ auto& param = GetParam();
+ auto& str_params = std::get<0>(param);
+ auto& ctor_params = std::get<1>(param);
+ uint32_t N = std::get<2>(param);
- if (str_params.ast == ctor_params.ast) {
- return;
- }
+ if (str_params.ast == ctor_params.ast) {
+ return;
+ }
- ast::StructMemberList members;
- ast::ExpressionList values;
- // make the last value of the constructor to have a different type
- uint32_t constructor_value_with_different_type = N - 1;
- for (uint32_t i = 0; i < N; i++) {
- auto* struct_type = str_params.ast(*this);
- members.push_back(Member("member_" + std::to_string(i), struct_type));
- auto* ctor_value_expr = (i == constructor_value_with_different_type)
- ? ctor_params.expr(*this, 0)
- : str_params.expr(*this, 0);
- values.push_back(ctor_value_expr);
- }
- auto* s = Structure("s", members);
- auto* tc = Construct(ty.Of(s), values);
- WrapInFunction(tc);
+ ast::StructMemberList members;
+ ast::ExpressionList values;
+ // make the last value of the constructor to have a different type
+ uint32_t constructor_value_with_different_type = N - 1;
+ for (uint32_t i = 0; i < N; i++) {
+ auto* struct_type = str_params.ast(*this);
+ members.push_back(Member("member_" + std::to_string(i), struct_type));
+ auto* ctor_value_expr = (i == constructor_value_with_different_type)
+ ? ctor_params.expr(*this, 0)
+ : str_params.expr(*this, 0);
+ values.push_back(ctor_value_expr);
+ }
+ auto* s = Structure("s", members);
+ auto* tc = Construct(ty.Of(s), values);
+ WrapInFunction(tc);
- std::string found = FriendlyName(ctor_params.ast(*this));
- std::string expected = FriendlyName(str_params.ast(*this));
- std::stringstream err;
- err << "error: type in struct constructor does not match struct member ";
- err << "type: expected '" << expected << "', found '" << found << "'";
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), err.str());
+ std::string found = FriendlyName(ctor_params.ast(*this));
+ std::string expected = FriendlyName(str_params.ast(*this));
+ std::stringstream err;
+ err << "error: type in struct constructor does not match struct member ";
+ err << "type: expected '" << expected << "', found '" << found << "'";
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), err.str());
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeConstructorValidationTest,
@@ -2841,94 +2651,84 @@
number_of_members));
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Struct_Nested) {
- auto* inner_m = Member("m", ty.i32());
- auto* inner_s = Structure("inner_s", {inner_m});
+ auto* inner_m = Member("m", ty.i32());
+ auto* inner_s = Structure("inner_s", {inner_m});
- auto* m0 = Member("m0", ty.i32());
- auto* m1 = Member("m1", ty.Of(inner_s));
- auto* m2 = Member("m2", ty.i32());
- auto* s = Structure("s", {m0, m1, m2});
+ auto* m0 = Member("m0", ty.i32());
+ auto* m1 = Member("m1", ty.Of(inner_s));
+ auto* m2 = Member("m2", ty.i32());
+ auto* s = Structure("s", {m0, m1, m2});
- auto* tc = Construct(Source{{12, 34}}, ty.Of(s), 1, 1, 1);
- WrapInFunction(tc);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: type in struct constructor does not match struct member "
- "type: expected 'inner_s', found 'i32'");
+ auto* tc = Construct(Source{{12, 34}}, ty.Of(s), 1, 1, 1);
+ WrapInFunction(tc);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: type in struct constructor does not match struct member "
+ "type: expected 'inner_s', found 'i32'");
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Struct) {
- auto* m = Member("m", ty.i32());
- auto* s = Structure("MyInputs", {m});
- auto* tc = Construct(Source{{12, 34}}, ty.Of(s));
- WrapInFunction(tc);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ auto* m = Member("m", ty.i32());
+ auto* s = Structure("MyInputs", {m});
+ auto* tc = Construct(Source{{12, 34}}, ty.Of(s));
+ WrapInFunction(tc);
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeConstructorValidationTest, Expr_Constructor_Struct_Empty) {
- auto* str = Structure("S", {
- Member("a", ty.i32()),
- Member("b", ty.f32()),
- Member("c", ty.vec3<i32>()),
- });
+ auto* str = Structure("S", {
+ Member("a", ty.i32()),
+ Member("b", ty.f32()),
+ Member("c", ty.vec3<i32>()),
+ });
- WrapInFunction(Construct(ty.Of(str)));
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ WrapInFunction(Construct(ty.Of(str)));
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace StructConstructor
TEST_F(ResolverTypeConstructorValidationTest, NonConstructibleType_Atomic) {
- WrapInFunction(
- Assign(Phony(), Construct(Source{{12, 34}}, ty.atomic(ty.i32()))));
+ WrapInFunction(Assign(Phony(), Construct(Source{{12, 34}}, ty.atomic(ty.i32()))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- NonConstructibleType_AtomicArray) {
- WrapInFunction(Assign(
- Phony(), Construct(Source{{12, 34}}, ty.array(ty.atomic(ty.i32()), 4))));
+TEST_F(ResolverTypeConstructorValidationTest, NonConstructibleType_AtomicArray) {
+ WrapInFunction(Assign(Phony(), Construct(Source{{12, 34}}, ty.array(ty.atomic(ty.i32()), 4))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: array constructor has non-constructible element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array constructor has non-constructible element type");
}
-TEST_F(ResolverTypeConstructorValidationTest,
- NonConstructibleType_AtomicStructMember) {
- auto* str = Structure("S", {Member("a", ty.atomic(ty.i32()))});
- WrapInFunction(Assign(Phony(), Construct(Source{{12, 34}}, ty.Of(str))));
+TEST_F(ResolverTypeConstructorValidationTest, NonConstructibleType_AtomicStructMember) {
+ auto* str = Structure("S", {Member("a", ty.atomic(ty.i32()))});
+ WrapInFunction(Assign(Phony(), Construct(Source{{12, 34}}, ty.Of(str))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: struct constructor has non-constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: struct constructor has non-constructible type");
}
TEST_F(ResolverTypeConstructorValidationTest, NonConstructibleType_Sampler) {
- WrapInFunction(Assign(
- Phony(),
- Construct(Source{{12, 34}}, ty.sampler(ast::SamplerKind::kSampler))));
+ WrapInFunction(
+ Assign(Phony(), Construct(Source{{12, 34}}, ty.sampler(ast::SamplerKind::kSampler))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
}
TEST_F(ResolverTypeConstructorValidationTest, TypeConstructorAsStatement) {
- WrapInFunction(
- CallStmt(Construct(Source{{12, 34}}, ty.vec2<f32>(), 1.f, 2.f)));
+ WrapInFunction(CallStmt(Construct(Source{{12, 34}}, ty.vec2<f32>(), 1.f, 2.f)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: type constructor evaluated but not used");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: type constructor evaluated but not used");
}
TEST_F(ResolverTypeConstructorValidationTest, TypeConversionAsStatement) {
- WrapInFunction(CallStmt(Construct(Source{{12, 34}}, ty.f32(), 1)));
+ WrapInFunction(CallStmt(Construct(Source{{12, 34}}, ty.f32(), 1)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: type cast evaluated but not used");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: type cast evaluated but not used");
}
} // namespace
diff --git a/src/tint/resolver/type_validation_test.cc b/src/tint/resolver/type_validation_test.cc
index 1d1ac38..bfd4249 100644
--- a/src/tint/resolver/type_validation_test.cc
+++ b/src/tint/resolver/type_validation_test.cc
@@ -54,671 +54,625 @@
using i32 = builder::i32;
using u32 = builder::u32;
-class ResolverTypeValidationTest : public resolver::TestHelper,
- public testing::Test {};
+class ResolverTypeValidationTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverTypeValidationTest, VariableDeclNoConstructor_Pass) {
- // {
- // var a :i32;
- // a = 2;
- // }
- auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, nullptr);
- auto* lhs = Expr("a");
- auto* rhs = Expr(2);
+ // {
+ // var a :i32;
+ // a = 2;
+ // }
+ auto* var = Var("a", ty.i32(), ast::StorageClass::kNone, nullptr);
+ auto* lhs = Expr("a");
+ auto* rhs = Expr(2);
- auto* body =
- Block(Decl(var), Assign(Source{Source::Location{12, 34}}, lhs, rhs));
+ auto* body = Block(Decl(var), Assign(Source{Source::Location{12, 34}}, lhs, rhs));
- WrapInFunction(body);
+ WrapInFunction(body);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(lhs), nullptr);
- ASSERT_NE(TypeOf(rhs), nullptr);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(lhs), nullptr);
+ ASSERT_NE(TypeOf(rhs), nullptr);
}
TEST_F(ResolverTypeValidationTest, GlobalConstantNoConstructor_Pass) {
- // @id(0) override a :i32;
- Override(Source{{12, 34}}, "a", ty.i32(), nullptr, ast::AttributeList{Id(0)});
+ // @id(0) override a :i32;
+ Override(Source{{12, 34}}, "a", ty.i32(), nullptr, ast::AttributeList{Id(0)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, GlobalVariableWithStorageClass_Pass) {
- // var<private> global_var: f32;
- Global(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kPrivate);
+ // var<private> global_var: f32;
+ Global(Source{{12, 34}}, "global_var", ty.f32(), ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, GlobalConstantWithStorageClass_Fail) {
- // const<private> global_var: f32;
- AST().AddGlobalVariable(create<ast::Variable>(
- Source{{12, 34}}, Symbols().Register("global_var"),
- ast::StorageClass::kPrivate, ast::Access::kUndefined, ty.f32(), true,
- false, Expr(1.23f), ast::AttributeList{}));
+ // const<private> global_var: f32;
+ AST().AddGlobalVariable(create<ast::Variable>(
+ Source{{12, 34}}, Symbols().Register("global_var"), ast::StorageClass::kPrivate,
+ ast::Access::kUndefined, ty.f32(), true, false, Expr(1.23f), ast::AttributeList{}));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: global constants shouldn't have a storage class");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: global constants shouldn't have a storage class");
}
TEST_F(ResolverTypeValidationTest, GlobalConstNoStorageClass_Pass) {
- // let global_var: f32;
- GlobalConst(Source{{12, 34}}, "global_var", ty.f32(), Construct(ty.f32()));
+ // let global_var: f32;
+ GlobalConst(Source{{12, 34}}, "global_var", ty.f32(), Construct(ty.f32()));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, GlobalVariableUnique_Pass) {
- // var global_var0 : f32 = 0.1;
- // var global_var1 : i32 = 0;
+ // var global_var0 : f32 = 0.1;
+ // var global_var1 : i32 = 0;
- Global("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1f));
+ Global("global_var0", ty.f32(), ast::StorageClass::kPrivate, Expr(0.1f));
- Global(Source{{12, 34}}, "global_var1", ty.f32(), ast::StorageClass::kPrivate,
- Expr(1.0f));
+ Global(Source{{12, 34}}, "global_var1", ty.f32(), ast::StorageClass::kPrivate, Expr(1.0f));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverTypeValidationTest,
- GlobalVariableFunctionVariableNotUnique_Pass) {
- // fn my_func() {
- // var a: f32 = 2.0;
- // }
- // var a: f32 = 2.1;
+TEST_F(ResolverTypeValidationTest, GlobalVariableFunctionVariableNotUnique_Pass) {
+ // fn my_func() {
+ // var a: f32 = 2.0;
+ // }
+ // var a: f32 = 2.1;
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- Func("my_func", ast::VariableList{}, ty.void_(), {Decl(var)});
+ Func("my_func", ast::VariableList{}, ty.void_(), {Decl(var)});
- Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
+ Global("a", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, RedeclaredIdentifierInnerScope_Pass) {
- // {
- // if (true) { var a : f32 = 2.0; }
- // var a : f32 = 3.14;
- // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+ // {
+ // if (true) { var a : f32 = 2.0; }
+ // var a : f32 = 3.14;
+ // }
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- auto* cond = Expr(true);
- auto* body = Block(Decl(var));
+ auto* cond = Expr(true);
+ auto* body = Block(Decl(var));
- auto* var_a_float = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(3.1f));
+ auto* var_a_float = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(3.1f));
- auto* outer_body = Block(If(cond, body), Decl(Source{{12, 34}}, var_a_float));
+ auto* outer_body = Block(If(cond, body), Decl(Source{{12, 34}}, var_a_float));
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_TRUE(r()->Resolve());
+ EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverTypeValidationTest, RedeclaredIdentifierInnerScopeBlock_Pass) {
- // {
- // { var a : f32; }
- // var a : f32;
- // }
- auto* var_inner = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* inner = Block(Decl(Source{{12, 34}}, var_inner));
+ // {
+ // { var a : f32; }
+ // var a : f32;
+ // }
+ auto* var_inner = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* inner = Block(Decl(Source{{12, 34}}, var_inner));
- auto* var_outer = Var("a", ty.f32(), ast::StorageClass::kNone);
- auto* outer_body = Block(inner, Decl(var_outer));
+ auto* var_outer = Var("a", ty.f32(), ast::StorageClass::kNone);
+ auto* outer_body = Block(inner, Decl(var_outer));
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverTypeValidationTest,
- RedeclaredIdentifierDifferentFunctions_Pass) {
- // func0 { var a : f32 = 2.0; return; }
- // func1 { var a : f32 = 3.0; return; }
- auto* var0 = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+TEST_F(ResolverTypeValidationTest, RedeclaredIdentifierDifferentFunctions_Pass) {
+ // func0 { var a : f32 = 2.0; return; }
+ // func1 { var a : f32 = 3.0; return; }
+ auto* var0 = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- auto* var1 = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(1.0f));
+ auto* var1 = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(1.0f));
- Func("func0", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Decl(Source{{12, 34}}, var0),
- Return(),
- },
- ast::AttributeList{});
+ Func("func0", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Decl(Source{{12, 34}}, var0),
+ Return(),
+ },
+ ast::AttributeList{});
- Func("func1", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Decl(Source{{13, 34}}, var1),
- Return(),
- });
+ Func("func1", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Decl(Source{{13, 34}}, var1),
+ Return(),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLiteral_Pass) {
- // var<private> a : array<f32, 4u>;
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4u)),
- ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // var<private> a : array<f32, 4u>;
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4u)), ast::StorageClass::kPrivate);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Pass) {
- // var<private> a : array<f32, 4>;
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4)),
- ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // var<private> a : array<f32, 4>;
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 4)), ast::StorageClass::kPrivate);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedConstant_Pass) {
- // let size = 4u;
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Expr(4u));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // let size = 4u;
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Expr(4u));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, ArraySize_SignedConstant_Pass) {
- // let size = 4;
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Expr(4));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // let size = 4;
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Expr(4));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedLiteral_Zero) {
- // var<private> a : array<f32, 0u>;
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0u)),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+ // var<private> a : array<f32, 0u>;
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0u)), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
}
TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Zero) {
- // var<private> a : array<f32, 0>;
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0)),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+ // var<private> a : array<f32, 0>;
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 0)), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
}
TEST_F(ResolverTypeValidationTest, ArraySize_SignedLiteral_Negative) {
- // var<private> a : array<f32, -10>;
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, -10)),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+ // var<private> a : array<f32, -10>;
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, -10)), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
}
TEST_F(ResolverTypeValidationTest, ArraySize_UnsignedConstant_Zero) {
- // let size = 0u;
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Expr(0u));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+ // let size = 0u;
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Expr(0u));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
}
TEST_F(ResolverTypeValidationTest, ArraySize_SignedConstant_Zero) {
- // let size = 0;
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Expr(0));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+ // let size = 0;
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Expr(0));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
}
TEST_F(ResolverTypeValidationTest, ArraySize_SignedConstant_Negative) {
- // let size = -10;
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Expr(-10));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
+ // let size = -10;
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Expr(-10));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be at least 1");
}
TEST_F(ResolverTypeValidationTest, ArraySize_FloatLiteral) {
- // var<private> a : array<f32, 10.0>;
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 10.f)),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+ // var<private> a : array<f32, 10.0>;
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, 10.f)), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
}
TEST_F(ResolverTypeValidationTest, ArraySize_IVecLiteral) {
- // var<private> a : array<f32, vec2<i32>(10, 10)>;
- Global(
- "a",
- ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.vec2<i32>(), 10, 10)),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+ // var<private> a : array<f32, vec2<i32>(10, 10)>;
+ Global("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.vec2<i32>(), 10, 10)),
+ ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
}
TEST_F(ResolverTypeValidationTest, ArraySize_FloatConstant) {
- // let size = 10.0;
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Expr(10.f));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+ // let size = 10.0;
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Expr(10.f));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
}
TEST_F(ResolverTypeValidationTest, ArraySize_IVecConstant) {
- // let size = vec2<i32>(100, 100);
- // var<private> a : array<f32, size>;
- GlobalConst("size", nullptr, Construct(ty.vec2<i32>(), 100, 100));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
+ // let size = vec2<i32>(100, 100);
+ // var<private> a : array<f32, size>;
+ GlobalConst("size", nullptr, Construct(ty.vec2<i32>(), 100, 100));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size must be integer scalar");
}
TEST_F(ResolverTypeValidationTest, ArraySize_TooBig_ImplicitStride) {
- // var<private> a : array<f32, 0x40000000>;
- Global("a", ty.array(Source{{12, 34}}, ty.f32(), 0x40000000),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: array size in bytes must not exceed 0xffffffff, but "
- "is 0x100000000");
+ // var<private> a : array<f32, 0x40000000>;
+ Global("a", ty.array(Source{{12, 34}}, ty.f32(), 0x40000000), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: array size in bytes must not exceed 0xffffffff, but "
+ "is 0x100000000");
}
TEST_F(ResolverTypeValidationTest, ArraySize_TooBig_ExplicitStride) {
- // var<private> a : @stride(8) array<f32, 0x20000000>;
- Global("a", ty.array(Source{{12, 34}}, ty.f32(), 0x20000000, 8),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: array size in bytes must not exceed 0xffffffff, but "
- "is 0x100000000");
+ // var<private> a : @stride(8) array<f32, 0x20000000>;
+ Global("a", ty.array(Source{{12, 34}}, ty.f32(), 0x20000000, 8), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: array size in bytes must not exceed 0xffffffff, but "
+ "is 0x100000000");
}
TEST_F(ResolverTypeValidationTest, ArraySize_OverridableConstant) {
- // override size = 10;
- // var<private> a : array<f32, size>;
- Override("size", nullptr, Expr(10));
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: array size expression must not be pipeline-overridable");
+ // override size = 10;
+ // var<private> a : array<f32, size>;
+ Override("size", nullptr, Expr(10));
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size expression must not be pipeline-overridable");
}
TEST_F(ResolverTypeValidationTest, ArraySize_ModuleVar) {
- // var<private> size : i32 = 10;
- // var<private> a : array<f32, size>;
- Global("size", ty.i32(), Expr(10), ast::StorageClass::kPrivate);
- Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: array size identifier must be a module-scope constant");
+ // var<private> size : i32 = 10;
+ // var<private> a : array<f32, size>;
+ Global("size", ty.i32(), Expr(10), ast::StorageClass::kPrivate);
+ Global("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")), ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size identifier must be a module-scope constant");
}
TEST_F(ResolverTypeValidationTest, ArraySize_FunctionConstant) {
- // {
- // let size = 10;
- // var a : array<f32, size>;
- // }
- auto* size = Let("size", nullptr, Expr(10));
- auto* a = Var("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")));
- WrapInFunction(Block(Decl(size), Decl(a)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: array size identifier must be a module-scope constant");
+ // {
+ // let size = 10;
+ // var a : array<f32, size>;
+ // }
+ auto* size = Let("size", nullptr, Expr(10));
+ auto* a = Var("a", ty.array(ty.f32(), Expr(Source{{12, 34}}, "size")));
+ WrapInFunction(Block(Decl(size), Decl(a)));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: array size identifier must be a module-scope constant");
}
TEST_F(ResolverTypeValidationTest, ArraySize_InvalidExpr) {
- // var a : array<f32, i32(4)>;
- auto* size = Let("size", nullptr, Expr(10));
- auto* a =
- Var("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.i32(), 4)));
- WrapInFunction(Block(Decl(size), Decl(a)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: array size expression must be either a literal or a "
- "module-scope constant");
+ // var a : array<f32, i32(4)>;
+ auto* size = Let("size", nullptr, Expr(10));
+ auto* a = Var("a", ty.array(ty.f32(), Construct(Source{{12, 34}}, ty.i32(), 4)));
+ WrapInFunction(Block(Decl(size), Decl(a)));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: array size expression must be either a literal or a "
+ "module-scope constant");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayInFunction_Fail) {
- /// @stage(vertex)
- // fn func() { var a : array<i32>; }
+ /// @stage(vertex)
+ // fn func() { var a : array<i32>; }
- auto* var =
- Var(Source{{12, 34}}, "a", ty.array<i32>(), ast::StorageClass::kNone);
+ auto* var = Var(Source{{12, 34}}, "a", ty.array<i32>(), ast::StorageClass::kNone);
- Func("func", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Decl(var),
- },
- ast::AttributeList{
- Stage(ast::PipelineStage::kVertex),
- });
+ Func("func", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Decl(var),
+ },
+ ast::AttributeList{
+ Stage(ast::PipelineStage::kVertex),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
12:34 note: while instantiating variable a)");
}
TEST_F(ResolverTypeValidationTest, Struct_Member_VectorNoType) {
- // struct S {
- // a: vec3;
- // };
+ // struct S {
+ // a: vec3;
+ // };
- Structure("S",
- {Member("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3))});
+ Structure("S", {Member("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
}
TEST_F(ResolverTypeValidationTest, Struct_Member_MatrixNoType) {
- // struct S {
- // a: mat3x3;
- // };
- Structure(
- "S", {Member("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3))});
+ // struct S {
+ // a: mat3x3;
+ // };
+ Structure("S", {Member("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3))});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
}
TEST_F(ResolverTypeValidationTest, Struct_TooBig) {
- // struct Foo {
- // a: array<f32, 0x20000000>;
- // b: array<f32, 0x20000000>;
- // };
+ // struct Foo {
+ // a: array<f32, 0x20000000>;
+ // b: array<f32, 0x20000000>;
+ // };
- Structure(Source{{12, 34}}, "Foo",
- {
- Member("a", ty.array<f32, 0x20000000>()),
- Member("b", ty.array<f32, 0x20000000>()),
- });
+ Structure(Source{{12, 34}}, "Foo",
+ {
+ Member("a", ty.array<f32, 0x20000000>()),
+ Member("b", ty.array<f32, 0x20000000>()),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: struct size in bytes must not exceed 0xffffffff, but "
- "is 0x100000000");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: struct size in bytes must not exceed 0xffffffff, but "
+ "is 0x100000000");
}
TEST_F(ResolverTypeValidationTest, Struct_MemberOffset_TooBig) {
- // struct Foo {
- // a: array<f32, 0x3fffffff>;
- // b: f32;
- // c: f32;
- // };
+ // struct Foo {
+ // a: array<f32, 0x3fffffff>;
+ // b: f32;
+ // c: f32;
+ // };
- Structure("Foo", {
- Member("a", ty.array<f32, 0x3fffffff>()),
- Member("b", ty.f32()),
- Member(Source{{12, 34}}, "c", ty.f32()),
- });
+ Structure("Foo", {
+ Member("a", ty.array<f32, 0x3fffffff>()),
+ Member("b", ty.f32()),
+ Member(Source{{12, 34}}, "c", ty.f32()),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: struct member has byte offset 0x100000000, but must "
- "not exceed 0xffffffff");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: struct member has byte offset 0x100000000, but must "
+ "not exceed 0xffffffff");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayIsLast_Pass) {
- // struct Foo {
- // vf: f32;
- // rt: array<f32>;
- // };
+ // struct Foo {
+ // vf: f32;
+ // rt: array<f32>;
+ // };
- Structure("Foo", {
- Member("vf", ty.f32()),
- Member("rt", ty.array<f32>()),
- });
+ Structure("Foo", {
+ Member("vf", ty.f32()),
+ Member("rt", ty.array<f32>()),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayInArray) {
- // struct Foo {
- // rt : array<array<f32>, 4>;
- // };
+ // struct Foo {
+ // rt : array<array<f32>, 4>;
+ // };
- Structure("Foo",
- {Member("rt", ty.array(Source{{12, 34}}, ty.array<f32>(), 4))});
+ Structure("Foo", {Member("rt", ty.array(Source{{12, 34}}, ty.array<f32>(), 4))});
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: an array element type cannot contain a runtime-sized "
- "array");
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: an array element type cannot contain a runtime-sized "
+ "array");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayInStructInArray) {
- // struct Foo {
- // rt : array<f32>;
- // };
- // var<private> a : array<Foo, 4>;
+ // struct Foo {
+ // rt : array<f32>;
+ // };
+ // var<private> a : array<Foo, 4>;
- auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
- Global("v", ty.array(Source{{12, 34}}, ty.Of(foo), 4),
- ast::StorageClass::kPrivate);
+ auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
+ Global("v", ty.array(Source{{12, 34}}, ty.Of(foo), 4), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: an array element type cannot contain a runtime-sized "
- "array");
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: an array element type cannot contain a runtime-sized "
+ "array");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayInStructInStruct) {
- // struct Foo {
- // rt : array<f32>;
- // };
- // struct Outer {
- // inner : Foo;
- // };
+ // struct Foo {
+ // rt : array<f32>;
+ // };
+ // struct Outer {
+ // inner : Foo;
+ // };
- auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
- Structure("Outer", {Member(Source{{12, 34}}, "inner", ty.Of(foo))});
+ auto* foo = Structure("Foo", {Member("rt", ty.array<f32>())});
+ Structure("Outer", {Member(Source{{12, 34}}, "inner", ty.Of(foo))});
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: a struct that contains a runtime array cannot be "
- "nested inside another struct");
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: a struct that contains a runtime array cannot be "
+ "nested inside another struct");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayIsNotLast_Fail) {
- // struct Foo {
- // rt: array<f32>;
- // vf: f32;
- // };
+ // struct Foo {
+ // rt: array<f32>;
+ // vf: f32;
+ // };
- Structure("Foo", {
- Member(Source{{12, 34}}, "rt", ty.array<f32>()),
- Member("vf", ty.f32()),
- });
+ Structure("Foo", {
+ Member(Source{{12, 34}}, "rt", ty.array<f32>()),
+ Member("vf", ty.f32()),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime arrays may only appear as the last member of a struct)");
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime arrays may only appear as the last member of a struct)");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayAsGlobalVariable) {
- Global(Source{{56, 78}}, "g", ty.array<i32>(), ast::StorageClass::kPrivate);
+ Global(Source{{56, 78}}, "g", ty.array<i32>(), ast::StorageClass::kPrivate);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayAsLocalVariable) {
- auto* v = Var(Source{{56, 78}}, "g", ty.array<i32>());
- WrapInFunction(v);
+ auto* v = Var(Source{{56, 78}}, "g", ty.array<i32>());
+ WrapInFunction(v);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_EQ(r()->error(),
+ R"(56:78 error: runtime-sized arrays can only be used in the <storage> storage class
56:78 note: while instantiating variable g)");
}
TEST_F(ResolverTypeValidationTest, RuntimeArrayAsParameter_Fail) {
- // fn func(a : array<u32>) {}
- // @stage(vertex) fn main() {}
+ // fn func(a : array<u32>) {}
+ // @stage(vertex) fn main() {}
- auto* param = Param(Source{{12, 34}}, "a", ty.array<i32>());
+ auto* param = Param(Source{{12, 34}}, "a", ty.array<i32>());
- Func("func", ast::VariableList{param}, ty.void_(),
- ast::StatementList{
- Return(),
- },
- ast::AttributeList{});
+ Func("func", ast::VariableList{param}, ty.void_(),
+ ast::StatementList{
+ Return(),
+ },
+ ast::AttributeList{});
- Func("main", ast::VariableList{}, ty.void_(),
- ast::StatementList{
- Return(),
- },
- ast::AttributeList{
- Stage(ast::PipelineStage::kVertex),
- });
+ Func("main", ast::VariableList{}, ty.void_(),
+ ast::StatementList{
+ Return(),
+ },
+ ast::AttributeList{
+ Stage(ast::PipelineStage::kVertex),
+ });
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
12:34 note: while instantiating parameter a)");
}
TEST_F(ResolverTypeValidationTest, PtrToRuntimeArrayAsParameter_Fail) {
- // fn func(a : ptr<workgroup, array<u32>>) {}
+ // fn func(a : ptr<workgroup, array<u32>>) {}
- auto* param =
- Param(Source{{12, 34}}, "a",
- ty.pointer(ty.array<i32>(), ast::StorageClass::kWorkgroup));
+ auto* param =
+ Param(Source{{12, 34}}, "a", ty.pointer(ty.array<i32>(), ast::StorageClass::kWorkgroup));
- Func("func", ast::VariableList{param}, ty.void_(),
- ast::StatementList{
- Return(),
- },
- ast::AttributeList{});
+ Func("func", ast::VariableList{param}, ty.void_(),
+ ast::StatementList{
+ Return(),
+ },
+ ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
12:34 note: while instantiating parameter a)");
}
TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsNotLast_Fail) {
- // type RTArr = array<u32>;
- // struct s {
- // b: RTArr;
- // a: u32;
- //}
+ // type RTArr = array<u32>;
+ // struct s {
+ // b: RTArr;
+ // a: u32;
+ //}
- auto* alias = Alias("RTArr", ty.array<u32>());
- Structure("s", {
- Member(Source{{12, 34}}, "b", ty.Of(alias)),
- Member("a", ty.u32()),
- });
+ auto* alias = Alias("RTArr", ty.array<u32>());
+ Structure("s", {
+ Member(Source{{12, 34}}, "b", ty.Of(alias)),
+ Member("a", ty.u32()),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: runtime arrays may only appear as the last member of "
- "a struct");
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: runtime arrays may only appear as the last member of "
+ "a struct");
}
TEST_F(ResolverTypeValidationTest, AliasRuntimeArrayIsLast_Pass) {
- // type RTArr = array<u32>;
- // struct s {
- // a: u32;
- // b: RTArr;
- //}
+ // type RTArr = array<u32>;
+ // struct s {
+ // a: u32;
+ // b: RTArr;
+ //}
- auto* alias = Alias("RTArr", ty.array<u32>());
- Structure("s", {
- Member("a", ty.u32()),
- Member("b", ty.Of(alias)),
- });
+ auto* alias = Alias("RTArr", ty.array<u32>());
+ Structure("s", {
+ Member("a", ty.u32()),
+ Member("b", ty.Of(alias)),
+ });
- WrapInFunction();
+ WrapInFunction();
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTypeValidationTest, ArrayOfNonStorableType) {
- auto* tex_ty = ty.sampled_texture(ast::TextureDimension::k2d, ty.f32());
- Global("arr", ty.array(Source{{12, 34}}, tex_ty, 4),
- ast::StorageClass::kPrivate);
+ auto* tex_ty = ty.sampled_texture(ast::TextureDimension::k2d, ty.f32());
+ Global("arr", ty.array(Source{{12, 34}}, tex_ty, 4), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: texture_2d<f32> cannot be used as an element type of "
- "an array");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: texture_2d<f32> cannot be used as an element type of "
+ "an array");
}
TEST_F(ResolverTypeValidationTest, VariableAsType) {
- // var<private> a : i32;
- // var<private> b : a;
- Global("a", ty.i32(), ast::StorageClass::kPrivate);
- Global("b", ty.type_name("a"), ast::StorageClass::kPrivate);
+ // var<private> a : i32;
+ // var<private> b : a;
+ Global("a", ty.i32(), ast::StorageClass::kPrivate);
+ Global("b", ty.type_name("a"), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: cannot use variable 'a' as type
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(error: cannot use variable 'a' as type
note: 'a' declared here)");
}
TEST_F(ResolverTypeValidationTest, FunctionAsType) {
- // fn f() {}
- // var<private> v : f;
- Func("f", {}, ty.void_(), {});
- Global("v", ty.type_name("f"), ast::StorageClass::kPrivate);
+ // fn f() {}
+ // var<private> v : f;
+ Func("f", {}, ty.void_(), {});
+ Global("v", ty.type_name("f"), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(error: cannot use function 'f' as type
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(error: cannot use function 'f' as type
note: 'f' declared here)");
}
TEST_F(ResolverTypeValidationTest, BuiltinAsType) {
- // var<private> v : max;
- Global("v", ty.type_name("max"), ast::StorageClass::kPrivate);
+ // var<private> v : max;
+ Global("v", ty.type_name("max"), ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "error: cannot use builtin 'max' as type");
}
namespace GetCanonicalTests {
struct Params {
- builder::ast_type_func_ptr create_ast_type;
- builder::sem_type_func_ptr create_sem_type;
+ builder::ast_type_func_ptr create_ast_type;
+ builder::sem_type_func_ptr create_sem_type;
};
template <typename T>
constexpr Params ParamsFor() {
- return Params{DataType<T>::AST, DataType<T>::Sem};
+ return Params{DataType<T>::AST, DataType<T>::Sem};
}
static constexpr Params cases[] = {
@@ -747,32 +701,30 @@
using CanonicalTest = ResolverTestWithParam<Params>;
TEST_P(CanonicalTest, All) {
- auto& params = GetParam();
+ auto& params = GetParam();
- auto* type = params.create_ast_type(*this);
+ auto* type = params.create_ast_type(*this);
- auto* var = Var("v", type);
- auto* expr = Expr("v");
- WrapInFunction(var, expr);
+ auto* var = Var("v", type);
+ auto* expr = Expr("v");
+ WrapInFunction(var, expr);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
- auto* got = TypeOf(expr)->UnwrapRef();
- auto* expected = params.create_sem_type(*this);
+ auto* got = TypeOf(expr)->UnwrapRef();
+ auto* expected = params.create_sem_type(*this);
- EXPECT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
- << "expected: " << FriendlyName(expected) << "\n";
+ EXPECT_EQ(got, expected) << "got: " << FriendlyName(got) << "\n"
+ << "expected: " << FriendlyName(expected) << "\n";
}
-INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
- CanonicalTest,
- testing::ValuesIn(cases));
+INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest, CanonicalTest, testing::ValuesIn(cases));
} // namespace GetCanonicalTests
namespace MultisampledTextureTests {
struct DimensionParams {
- ast::TextureDimension dim;
- bool is_valid;
+ ast::TextureDimension dim;
+ bool is_valid;
};
static constexpr DimensionParams dimension_cases[] = {
@@ -785,31 +737,29 @@
using MultisampledTextureDimensionTest = ResolverTestWithParam<DimensionParams>;
TEST_P(MultisampledTextureDimensionTest, All) {
- auto& params = GetParam();
- Global(Source{{12, 34}}, "a", ty.multisampled_texture(params.dim, ty.i32()),
- ast::StorageClass::kNone, nullptr,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ auto& params = GetParam();
+ Global(Source{{12, 34}}, "a", ty.multisampled_texture(params.dim, ty.i32()),
+ ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: only 2d multisampled textures are supported");
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: only 2d multisampled textures are supported");
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
MultisampledTextureDimensionTest,
testing::ValuesIn(dimension_cases));
struct TypeParams {
- builder::ast_type_func_ptr type_func;
- bool is_valid;
+ builder::ast_type_func_ptr type_func;
+ bool is_valid;
};
template <typename T>
constexpr TypeParams TypeParamsFor(bool is_valid) {
- return TypeParams{DataType<T>::AST, is_valid};
+ return TypeParams{DataType<T>::AST, is_valid};
}
static constexpr TypeParams type_cases[] = {
@@ -832,21 +782,19 @@
using MultisampledTextureTypeTest = ResolverTestWithParam<TypeParams>;
TEST_P(MultisampledTextureTypeTest, All) {
- auto& params = GetParam();
- Global(Source{{12, 34}}, "a",
- ty.multisampled_texture(ast::TextureDimension::k2d,
- params.type_func(*this)),
- ast::StorageClass::kNone, nullptr,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ auto& params = GetParam();
+ Global(Source{{12, 34}}, "a",
+ ty.multisampled_texture(ast::TextureDimension::k2d, params.type_func(*this)),
+ ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: texture_multisampled_2d<type>: type must be f32, "
- "i32 or u32");
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: texture_multisampled_2d<type>: type must be f32, "
+ "i32 or u32");
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
MultisampledTextureTypeTest,
@@ -856,8 +804,8 @@
namespace StorageTextureTests {
struct DimensionParams {
- ast::TextureDimension dim;
- bool is_valid;
+ ast::TextureDimension dim;
+ bool is_valid;
};
static constexpr DimensionParams Dimension_cases[] = {
@@ -870,94 +818,85 @@
using StorageTextureDimensionTest = ResolverTestWithParam<DimensionParams>;
TEST_P(StorageTextureDimensionTest, All) {
- // @group(0) @binding(0)
- // var a : texture_storage_*<ru32int, write>;
- auto& params = GetParam();
+ // @group(0) @binding(0)
+ // var a : texture_storage_*<ru32int, write>;
+ auto& params = GetParam();
- auto* st =
- ty.storage_texture(Source{{12, 34}}, params.dim,
- ast::TexelFormat::kR32Uint, ast::Access::kWrite);
+ auto* st = ty.storage_texture(Source{{12, 34}}, params.dim, ast::TexelFormat::kR32Uint,
+ ast::Access::kWrite);
- Global("a", st, ast::StorageClass::kNone,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ Global("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cube dimensions for storage textures are not "
- "supported");
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cube dimensions for storage textures are not "
+ "supported");
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
StorageTextureDimensionTest,
testing::ValuesIn(Dimension_cases));
struct FormatParams {
- ast::TexelFormat format;
- bool is_valid;
+ ast::TexelFormat format;
+ bool is_valid;
};
-static constexpr FormatParams format_cases[] = {
- FormatParams{ast::TexelFormat::kR32Float, true},
- FormatParams{ast::TexelFormat::kR32Sint, true},
- FormatParams{ast::TexelFormat::kR32Uint, true},
- FormatParams{ast::TexelFormat::kRg32Float, true},
- FormatParams{ast::TexelFormat::kRg32Sint, true},
- FormatParams{ast::TexelFormat::kRg32Uint, true},
- FormatParams{ast::TexelFormat::kRgba16Float, true},
- FormatParams{ast::TexelFormat::kRgba16Sint, true},
- FormatParams{ast::TexelFormat::kRgba16Uint, true},
- FormatParams{ast::TexelFormat::kRgba32Float, true},
- FormatParams{ast::TexelFormat::kRgba32Sint, true},
- FormatParams{ast::TexelFormat::kRgba32Uint, true},
- FormatParams{ast::TexelFormat::kRgba8Sint, true},
- FormatParams{ast::TexelFormat::kRgba8Snorm, true},
- FormatParams{ast::TexelFormat::kRgba8Uint, true},
- FormatParams{ast::TexelFormat::kRgba8Unorm, true}};
+static constexpr FormatParams format_cases[] = {FormatParams{ast::TexelFormat::kR32Float, true},
+ FormatParams{ast::TexelFormat::kR32Sint, true},
+ FormatParams{ast::TexelFormat::kR32Uint, true},
+ FormatParams{ast::TexelFormat::kRg32Float, true},
+ FormatParams{ast::TexelFormat::kRg32Sint, true},
+ FormatParams{ast::TexelFormat::kRg32Uint, true},
+ FormatParams{ast::TexelFormat::kRgba16Float, true},
+ FormatParams{ast::TexelFormat::kRgba16Sint, true},
+ FormatParams{ast::TexelFormat::kRgba16Uint, true},
+ FormatParams{ast::TexelFormat::kRgba32Float, true},
+ FormatParams{ast::TexelFormat::kRgba32Sint, true},
+ FormatParams{ast::TexelFormat::kRgba32Uint, true},
+ FormatParams{ast::TexelFormat::kRgba8Sint, true},
+ FormatParams{ast::TexelFormat::kRgba8Snorm, true},
+ FormatParams{ast::TexelFormat::kRgba8Uint, true},
+ FormatParams{ast::TexelFormat::kRgba8Unorm, true}};
using StorageTextureFormatTest = ResolverTestWithParam<FormatParams>;
TEST_P(StorageTextureFormatTest, All) {
- auto& params = GetParam();
- // @group(0) @binding(0)
- // var a : texture_storage_1d<*, write>;
- // @group(0) @binding(1)
- // var b : texture_storage_2d<*, write>;
- // @group(0) @binding(2)
- // var c : texture_storage_2d_array<*, write>;
- // @group(0) @binding(3)
- // var d : texture_storage_3d<*, write>;
+ auto& params = GetParam();
+ // @group(0) @binding(0)
+ // var a : texture_storage_1d<*, write>;
+ // @group(0) @binding(1)
+ // var b : texture_storage_2d<*, write>;
+ // @group(0) @binding(2)
+ // var c : texture_storage_2d_array<*, write>;
+ // @group(0) @binding(3)
+ // var d : texture_storage_3d<*, write>;
- auto* st_a = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
- params.format, ast::Access::kWrite);
- Global("a", st_a, ast::StorageClass::kNone,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ auto* st_a = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d, params.format,
+ ast::Access::kWrite);
+ Global("a", st_a, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
- auto* st_b = ty.storage_texture(ast::TextureDimension::k2d, params.format,
- ast::Access::kWrite);
- Global("b", st_b, ast::StorageClass::kNone,
- ast::AttributeList{GroupAndBinding(0, 1)});
+ auto* st_b = ty.storage_texture(ast::TextureDimension::k2d, params.format, ast::Access::kWrite);
+ Global("b", st_b, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 1)});
- auto* st_c = ty.storage_texture(ast::TextureDimension::k2dArray,
- params.format, ast::Access::kWrite);
- Global("c", st_c, ast::StorageClass::kNone,
- ast::AttributeList{GroupAndBinding(0, 2)});
+ auto* st_c =
+ ty.storage_texture(ast::TextureDimension::k2dArray, params.format, ast::Access::kWrite);
+ Global("c", st_c, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 2)});
- auto* st_d = ty.storage_texture(ast::TextureDimension::k3d, params.format,
- ast::Access::kWrite);
- Global("d", st_d, ast::StorageClass::kNone,
- ast::AttributeList{GroupAndBinding(0, 3)});
+ auto* st_d = ty.storage_texture(ast::TextureDimension::k3d, params.format, ast::Access::kWrite);
+ Global("d", st_d, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 3)});
- if (params.is_valid) {
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- } else {
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: image format must be one of the texel formats "
- "specified for storage textues in "
- "https://gpuweb.github.io/gpuweb/wgsl/#texel-formats");
- }
+ if (params.is_valid) {
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ } else {
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: image format must be one of the texel formats "
+ "specified for storage textues in "
+ "https://gpuweb.github.io/gpuweb/wgsl/#texel-formats");
+ }
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
StorageTextureFormatTest,
@@ -966,89 +905,81 @@
using StorageTextureAccessTest = ResolverTest;
TEST_F(StorageTextureAccessTest, MissingAccess_Fail) {
- // @group(0) @binding(0)
- // var a : texture_storage_1d<ru32int>;
+ // @group(0) @binding(0)
+ // var a : texture_storage_1d<ru32int>;
- auto* st =
- ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
- ast::TexelFormat::kR32Uint, ast::Access::kUndefined);
+ auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
+ ast::TexelFormat::kR32Uint, ast::Access::kUndefined);
- Global("a", st, ast::StorageClass::kNone,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ Global("a", st, ast::StorageClass::kNone, ast::AttributeList{GroupAndBinding(0, 0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: storage texture missing access control");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: storage texture missing access control");
}
TEST_F(StorageTextureAccessTest, RWAccess_Fail) {
- // @group(0) @binding(0)
- // var a : texture_storage_1d<ru32int, read_write>;
+ // @group(0) @binding(0)
+ // var a : texture_storage_1d<ru32int, read_write>;
- auto* st =
- ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
- ast::TexelFormat::kR32Uint, ast::Access::kReadWrite);
+ auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
+ ast::TexelFormat::kR32Uint, ast::Access::kReadWrite);
- Global("a", st, ast::StorageClass::kNone, nullptr,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ Global("a", st, ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: storage textures currently only support 'write' "
- "access control");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: storage textures currently only support 'write' "
+ "access control");
}
TEST_F(StorageTextureAccessTest, ReadOnlyAccess_Fail) {
- // @group(0) @binding(0)
- // var a : texture_storage_1d<ru32int, read>;
+ // @group(0) @binding(0)
+ // var a : texture_storage_1d<ru32int, read>;
- auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
- ast::TexelFormat::kR32Uint, ast::Access::kRead);
+ auto* st = ty.storage_texture(Source{{12, 34}}, ast::TextureDimension::k1d,
+ ast::TexelFormat::kR32Uint, ast::Access::kRead);
- Global("a", st, ast::StorageClass::kNone, nullptr,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ Global("a", st, ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: storage textures currently only support 'write' "
- "access control");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: storage textures currently only support 'write' "
+ "access control");
}
TEST_F(StorageTextureAccessTest, WriteOnlyAccess_Pass) {
- // @group(0) @binding(0)
- // var a : texture_storage_1d<ru32int, write>;
+ // @group(0) @binding(0)
+ // var a : texture_storage_1d<ru32int, write>;
- auto* st =
- ty.storage_texture(ast::TextureDimension::k1d, ast::TexelFormat::kR32Uint,
- ast::Access::kWrite);
+ auto* st = ty.storage_texture(ast::TextureDimension::k1d, ast::TexelFormat::kR32Uint,
+ ast::Access::kWrite);
- Global("a", st, ast::StorageClass::kNone, nullptr,
- ast::AttributeList{GroupAndBinding(0, 0)});
+ Global("a", st, ast::StorageClass::kNone, nullptr, ast::AttributeList{GroupAndBinding(0, 0)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
} // namespace StorageTextureTests
namespace MatrixTests {
struct Params {
- uint32_t columns;
- uint32_t rows;
- builder::ast_type_func_ptr elem_ty;
+ uint32_t columns;
+ uint32_t rows;
+ builder::ast_type_func_ptr elem_ty;
};
template <typename T>
constexpr Params ParamsFor(uint32_t columns, uint32_t rows) {
- return Params{columns, rows, DataType<T>::AST};
+ return Params{columns, rows, DataType<T>::AST};
}
using ValidMatrixTypes = ResolverTestWithParam<Params>;
TEST_P(ValidMatrixTypes, Okay) {
- // var a : matNxM<EL_TY>;
- auto& params = GetParam();
- Global("a", ty.mat(params.elem_ty(*this), params.columns, params.rows),
- ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // var a : matNxM<EL_TY>;
+ auto& params = GetParam();
+ Global("a", ty.mat(params.elem_ty(*this), params.columns, params.rows),
+ ast::StorageClass::kPrivate);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
ValidMatrixTypes,
@@ -1067,14 +998,12 @@
using InvalidMatrixElementTypes = ResolverTestWithParam<Params>;
TEST_P(InvalidMatrixElementTypes, InvalidElementType) {
- // var a : matNxM<EL_TY>;
- auto& params = GetParam();
- Global("a",
- ty.mat(Source{{12, 34}}, params.elem_ty(*this), params.columns,
- params.rows),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: matrix element type must be 'f32'");
+ // var a : matNxM<EL_TY>;
+ auto& params = GetParam();
+ Global("a", ty.mat(Source{{12, 34}}, params.elem_ty(*this), params.columns, params.rows),
+ ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: matrix element type must be 'f32'");
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
InvalidMatrixElementTypes,
@@ -1092,22 +1021,21 @@
namespace VectorTests {
struct Params {
- uint32_t width;
- builder::ast_type_func_ptr elem_ty;
+ uint32_t width;
+ builder::ast_type_func_ptr elem_ty;
};
template <typename T>
constexpr Params ParamsFor(uint32_t width) {
- return Params{width, DataType<T>::AST};
+ return Params{width, DataType<T>::AST};
}
using ValidVectorTypes = ResolverTestWithParam<Params>;
TEST_P(ValidVectorTypes, Okay) {
- // var a : vecN<EL_TY>;
- auto& params = GetParam();
- Global("a", ty.vec(params.elem_ty(*this), params.width),
- ast::StorageClass::kPrivate);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // var a : vecN<EL_TY>;
+ auto& params = GetParam();
+ Global("a", ty.vec(params.elem_ty(*this), params.width), ast::StorageClass::kPrivate);
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
ValidVectorTypes,
@@ -1130,14 +1058,14 @@
using InvalidVectorElementTypes = ResolverTestWithParam<Params>;
TEST_P(InvalidVectorElementTypes, InvalidElementType) {
- // var a : vecN<EL_TY>;
- auto& params = GetParam();
- Global("a", ty.vec(Source{{12, 34}}, params.elem_ty(*this), params.width),
- ast::StorageClass::kPrivate);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: vector element type must be 'bool', 'f32', 'i32' "
- "or 'u32'");
+ // var a : vecN<EL_TY>;
+ auto& params = GetParam();
+ Global("a", ty.vec(Source{{12, 34}}, params.elem_ty(*this), params.width),
+ ast::StorageClass::kPrivate);
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: vector element type must be 'bool', 'f32', 'i32' "
+ "or 'u32'");
}
INSTANTIATE_TEST_SUITE_P(ResolverTypeValidationTest,
InvalidVectorElementTypes,
diff --git a/src/tint/resolver/validation_test.cc b/src/tint/resolver/validation_test.cc
index 045adf5..94349d8 100644
--- a/src/tint/resolver/validation_test.cc
+++ b/src/tint/resolver/validation_test.cc
@@ -47,56 +47,54 @@
using ResolverValidationTest = ResolverTest;
class FakeStmt final : public Castable<FakeStmt, ast::Statement> {
- public:
- FakeStmt(ProgramID pid, Source src) : Base(pid, src) {}
- FakeStmt* Clone(CloneContext*) const override { return nullptr; }
+ public:
+ FakeStmt(ProgramID pid, Source src) : Base(pid, src) {}
+ FakeStmt* Clone(CloneContext*) const override { return nullptr; }
};
class FakeExpr final : public Castable<FakeExpr, ast::Expression> {
- public:
- FakeExpr(ProgramID pid, Source src) : Base(pid, src) {}
- FakeExpr* Clone(CloneContext*) const override { return nullptr; }
+ public:
+ FakeExpr(ProgramID pid, Source src) : Base(pid, src) {}
+ FakeExpr* Clone(CloneContext*) const override { return nullptr; }
};
TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInVertexStage) {
- Global(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
- Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
+ Global(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+ Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
- Func(Source{{9, 10}}, "f0", ast::VariableList{}, ty.vec4<f32>(),
- {stmt, Return(Expr("dst"))},
- ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
- ast::AttributeList{Builtin(ast::Builtin::kPosition)});
+ Func(Source{{9, 10}}, "f0", ast::VariableList{}, ty.vec4<f32>(), {stmt, Return(Expr("dst"))},
+ ast::AttributeList{Stage(ast::PipelineStage::kVertex)},
+ ast::AttributeList{Builtin(ast::Builtin::kPosition)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "3:4 error: workgroup memory cannot be used by vertex pipeline "
- "stage\n1:2 note: variable is declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "3:4 error: workgroup memory cannot be used by vertex pipeline "
+ "stage\n1:2 note: variable is declared here");
}
TEST_F(ResolverValidationTest, WorkgroupMemoryUsedInFragmentStage) {
- // var<workgroup> wg : vec4<f32>;
- // var<workgroup> dst : vec4<f32>;
- // fn f2(){ dst = wg; }
- // fn f1() { f2(); }
- // @stage(fragment)
- // fn f0() {
- // f1();
- //}
+ // var<workgroup> wg : vec4<f32>;
+ // var<workgroup> dst : vec4<f32>;
+ // fn f2(){ dst = wg; }
+ // fn f1() { f2(); }
+ // @stage(fragment)
+ // fn f0() {
+ // f1();
+ //}
- Global(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
- Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
+ Global(Source{{1, 2}}, "wg", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+ Global("dst", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ auto* stmt = Assign(Expr("dst"), Expr(Source{{3, 4}}, "wg"));
- Func(Source{{5, 6}}, "f2", {}, ty.void_(), {stmt});
- Func(Source{{7, 8}}, "f1", {}, ty.void_(), {CallStmt(Call("f2"))});
- Func(Source{{9, 10}}, "f0", {}, ty.void_(), {CallStmt(Call("f1"))},
- ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
+ Func(Source{{5, 6}}, "f2", {}, ty.void_(), {stmt});
+ Func(Source{{7, 8}}, "f1", {}, ty.void_(), {CallStmt(Call("f2"))});
+ Func(Source{{9, 10}}, "f0", {}, ty.void_(), {CallStmt(Call("f1"))},
+ ast::AttributeList{Stage(ast::PipelineStage::kFragment)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:4 error: workgroup memory cannot be used by fragment pipeline stage
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(3:4 error: workgroup memory cannot be used by fragment pipeline stage
1:2 note: variable is declared here
5:6 note: called by function 'f2'
7:8 note: called by function 'f1'
@@ -104,1207 +102,1150 @@
}
TEST_F(ResolverValidationTest, UnhandledStmt) {
- EXPECT_FATAL_FAILURE(
- {
- ProgramBuilder b;
- b.WrapInFunction(b.create<FakeStmt>());
- Program(std::move(b));
- },
- "internal compiler error: unhandled node type: tint::resolver::FakeStmt");
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b;
+ b.WrapInFunction(b.create<FakeStmt>());
+ Program(std::move(b));
+ },
+ "internal compiler error: unhandled node type: tint::resolver::FakeStmt");
}
TEST_F(ResolverValidationTest, Stmt_If_NonBool) {
- // if (1.23f) {}
+ // if (1.23f) {}
- WrapInFunction(If(Expr(Source{{12, 34}}, 1.23f), Block()));
+ WrapInFunction(If(Expr(Source{{12, 34}}, 1.23f), Block()));
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: if statement condition must be bool, got f32");
+ EXPECT_EQ(r()->error(), "12:34 error: if statement condition must be bool, got f32");
}
TEST_F(ResolverValidationTest, Stmt_ElseIf_NonBool) {
- // else if (1.23f) {}
+ // else if (1.23f) {}
- WrapInFunction(
- If(Expr(true), Block(), If(Expr(Source{{12, 34}}, 1.23f), Block())));
+ WrapInFunction(If(Expr(true), Block(), If(Expr(Source{{12, 34}}, 1.23f), Block())));
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: if statement condition must be bool, got f32");
+ EXPECT_EQ(r()->error(), "12:34 error: if statement condition must be bool, got f32");
}
TEST_F(ResolverValidationTest, Expr_ErrUnknownExprType) {
- EXPECT_FATAL_FAILURE(
- {
- ProgramBuilder b;
- b.WrapInFunction(b.create<FakeExpr>());
- Resolver(&b).Resolve();
- },
- "internal compiler error: unhandled expression type: "
- "tint::resolver::FakeExpr");
+ EXPECT_FATAL_FAILURE(
+ {
+ ProgramBuilder b;
+ b.WrapInFunction(b.create<FakeExpr>());
+ Resolver(&b).Resolve();
+ },
+ "internal compiler error: unhandled expression type: "
+ "tint::resolver::FakeExpr");
}
TEST_F(ResolverValidationTest, Expr_DontCall_Function) {
- Func("func", {}, ty.void_(), {}, {});
- WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "func"));
+ Func("func", {}, ty.void_(), {}, {});
+ WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "func"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:8 error: missing '(' for function call");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:8 error: missing '(' for function call");
}
TEST_F(ResolverValidationTest, Expr_DontCall_Builtin) {
- WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "round"));
+ WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "round"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:8 error: missing '(' for builtin call");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:8 error: missing '(' for builtin call");
}
TEST_F(ResolverValidationTest, Expr_DontCall_Type) {
- Alias("T", ty.u32());
- WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "T"));
+ Alias("T", ty.u32());
+ WrapInFunction(Expr(Source{{{3, 3}, {3, 8}}}, "T"));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "3:8 error: missing '(' for type constructor or cast");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:8 error: missing '(' for type constructor or cast");
}
TEST_F(ResolverValidationTest, AssignmentStmt_InvalidLHS_BuiltinFunctionName) {
- // normalize = 2;
+ // normalize = 2;
- auto* lhs = Expr(Source{{12, 34}}, "normalize");
- auto* rhs = Expr(2);
- auto* assign = Assign(lhs, rhs);
- WrapInFunction(assign);
+ auto* lhs = Expr(Source{{12, 34}}, "normalize");
+ auto* rhs = Expr(2);
+ auto* assign = Assign(lhs, rhs);
+ WrapInFunction(assign);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing '(' for builtin call");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing '(' for builtin call");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariable_Fail) {
- // b = 2;
+ // b = 2;
- auto* lhs = Expr(Source{{12, 34}}, "b");
- auto* rhs = Expr(2);
- auto* assign = Assign(lhs, rhs);
- WrapInFunction(assign);
+ auto* lhs = Expr(Source{{12, 34}}, "b");
+ auto* rhs = Expr(2);
+ auto* assign = Assign(lhs, rhs);
+ WrapInFunction(assign);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'b'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'b'");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableInBlockStatement_Fail) {
- // {
- // b = 2;
- // }
+ // {
+ // b = 2;
+ // }
- auto* lhs = Expr(Source{{12, 34}}, "b");
- auto* rhs = Expr(2);
+ auto* lhs = Expr(Source{{12, 34}}, "b");
+ auto* rhs = Expr(2);
- auto* body = Block(Assign(lhs, rhs));
- WrapInFunction(body);
+ auto* body = Block(Assign(lhs, rhs));
+ WrapInFunction(body);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'b'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'b'");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableGlobalVariable_Pass) {
- // var global_var: f32 = 2.1;
- // fn my_func() {
- // global_var = 3.14;
- // return;
- // }
+ // var global_var: f32 = 2.1;
+ // fn my_func() {
+ // global_var = 3.14;
+ // return;
+ // }
- Global("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
+ Global("global_var", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
- Func("my_func", ast::VariableList{}, ty.void_(),
- {
- Assign(Expr(Source{{12, 34}}, "global_var"), 3.14f),
- Return(),
- });
+ Func("my_func", ast::VariableList{}, ty.void_(),
+ {
+ Assign(Expr(Source{{12, 34}}, "global_var"), 3.14f),
+ Return(),
+ });
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableInnerScope_Fail) {
- // {
- // if (true) { var a : f32 = 2.0; }
- // a = 3.14;
- // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+ // {
+ // if (true) { var a : f32 = 2.0; }
+ // a = 3.14;
+ // }
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- auto* cond = Expr(true);
- auto* body = Block(Decl(var));
+ auto* cond = Expr(true);
+ auto* body = Block(Decl(var));
- SetSource(Source{{12, 34}});
- auto* lhs = Expr(Source{{12, 34}}, "a");
- auto* rhs = Expr(3.14f);
+ SetSource(Source{{12, 34}});
+ auto* lhs = Expr(Source{{12, 34}}, "a");
+ auto* rhs = Expr(3.14f);
- auto* outer_body = Block(If(cond, body), Assign(lhs, rhs));
+ auto* outer_body = Block(If(cond, body), Assign(lhs, rhs));
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableOuterScope_Pass) {
- // {
- // var a : f32 = 2.0;
- // if (true) { a = 3.14; }
- // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+ // {
+ // var a : f32 = 2.0;
+ // if (true) { a = 3.14; }
+ // }
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- auto* lhs = Expr(Source{{12, 34}}, "a");
- auto* rhs = Expr(3.14f);
+ auto* lhs = Expr(Source{{12, 34}}, "a");
+ auto* rhs = Expr(3.14f);
- auto* cond = Expr(true);
- auto* body = Block(Assign(lhs, rhs));
+ auto* cond = Expr(true);
+ auto* body = Block(Assign(lhs, rhs));
- auto* outer_body = Block(Decl(var), If(cond, body));
+ auto* outer_body = Block(Decl(var), If(cond, body));
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest, UsingUndefinedVariableDifferentScope_Fail) {
- // {
- // { var a : f32 = 2.0; }
- // { a = 3.14; }
- // }
- auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- auto* first_body = Block(Decl(var));
+ // {
+ // { var a : f32 = 2.0; }
+ // { a = 3.14; }
+ // }
+ auto* var = Var("a", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
+ auto* first_body = Block(Decl(var));
- auto* lhs = Expr(Source{{12, 34}}, "a");
- auto* rhs = Expr(3.14f);
- auto* second_body = Block(Assign(lhs, rhs));
+ auto* lhs = Expr(Source{{12, 34}}, "a");
+ auto* rhs = Expr(3.14f);
+ auto* second_body = Block(Assign(lhs, rhs));
- auto* outer_body = Block(first_body, second_body);
+ auto* outer_body = Block(first_body, second_body);
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: unknown identifier: 'a'");
}
TEST_F(ResolverValidationTest, StorageClass_FunctionVariableWorkgroupClass) {
- auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* stmt = Decl(var);
- Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+ auto* stmt = Decl(var);
+ Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: function variable has a non-function storage class");
+ EXPECT_EQ(r()->error(), "error: function variable has a non-function storage class");
}
TEST_F(ResolverValidationTest, StorageClass_FunctionVariableI32) {
- auto* var = Var("s", ty.i32(), ast::StorageClass::kPrivate);
+ auto* var = Var("s", ty.i32(), ast::StorageClass::kPrivate);
- auto* stmt = Decl(var);
- Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
+ auto* stmt = Decl(var);
+ Func("func", ast::VariableList{}, ty.void_(), {stmt}, ast::AttributeList{});
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: function variable has a non-function storage class");
+ EXPECT_EQ(r()->error(), "error: function variable has a non-function storage class");
}
TEST_F(ResolverValidationTest, StorageClass_SamplerExplicitStorageClass) {
- auto* t = ty.sampler(ast::SamplerKind::kSampler);
- Global(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* t = ty.sampler(ast::SamplerKind::kSampler);
+ Global(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- EXPECT_FALSE(r()->Resolve());
+ EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: variables of type 'sampler' must not have a storage class)");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: variables of type 'sampler' must not have a storage class)");
}
TEST_F(ResolverValidationTest, StorageClass_TextureExplicitStorageClass) {
- auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
- Global(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ auto* t = ty.sampled_texture(ast::TextureDimension::k1d, ty.f32());
+ Global(Source{{12, 34}}, "var", t, ast::StorageClass::kHandle,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: variables of type 'texture_1d<f32>' must not have a storage class)");
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: variables of type 'texture_1d<f32>' must not have a storage class)");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
- Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
- auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "xyqz");
+ auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "xyqz");
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:5 error: invalid vector swizzle character");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:5 error: invalid vector swizzle character");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
- Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
+ Global("my_vec", ty.vec4<f32>(), ast::StorageClass::kPrivate);
- auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "rgyw");
+ auto* ident = Expr(Source{{{3, 3}, {3, 7}}}, "rgyw");
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "3:3 error: invalid mixing of vector swizzle characters rgba with xyzw");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "3:3 error: invalid mixing of vector swizzle characters rgba with xyzw");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
- Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
+ Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kPrivate);
- auto* ident = Expr(Source{{{3, 3}, {3, 8}}}, "zzzzz");
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
+ auto* ident = Expr(Source{{{3, 3}, {3, 8}}}, "zzzzz");
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle size");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle size");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadIndex) {
- Global("my_vec", ty.vec2<f32>(), ast::StorageClass::kPrivate);
+ Global("my_vec", ty.vec2<f32>(), ast::StorageClass::kPrivate);
- auto* ident = Expr(Source{{3, 3}}, "z");
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
+ auto* ident = Expr(Source{{3, 3}}, "z");
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle member");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle member");
}
TEST_F(ResolverValidationTest, Expr_MemberAccessor_BadParent) {
- // var param: vec4<f32>
- // let ret: f32 = *(¶m).x;
- auto* param = Var("param", ty.vec4<f32>());
- auto* x = Expr(Source{{{3, 3}, {3, 8}}}, "x");
+ // var param: vec4<f32>
+ // let ret: f32 = *(¶m).x;
+ auto* param = Var("param", ty.vec4<f32>());
+ auto* x = Expr(Source{{{3, 3}, {3, 8}}}, "x");
- auto* addressOf_expr = AddressOf(Source{{12, 34}}, param);
- auto* accessor_expr = MemberAccessor(addressOf_expr, x);
- auto* star_p = Deref(accessor_expr);
- auto* ret = Var("r", ty.f32(), star_p);
- WrapInFunction(Decl(param), Decl(ret));
+ auto* addressOf_expr = AddressOf(Source{{12, 34}}, param);
+ auto* accessor_expr = MemberAccessor(addressOf_expr, x);
+ auto* star_p = Deref(accessor_expr);
+ auto* ret = Var("r", ty.f32(), star_p);
+ WrapInFunction(Decl(param), Decl(ret));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: invalid member accessor expression. Expected vector "
- "or struct, got 'ptr<function, vec4<f32>, read_write>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: invalid member accessor expression. Expected vector "
+ "or struct, got 'ptr<function, vec4<f32>, read_write>'");
}
TEST_F(ResolverValidationTest, EXpr_MemberAccessor_FuncGoodParent) {
- // fn func(p: ptr<function, vec4<f32>>) -> f32 {
- // let x: f32 = (*p).z;
- // return x;
- // }
- auto* p =
- Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
- auto* star_p = Deref(p);
- auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
- auto* accessor_expr = MemberAccessor(star_p, z);
- auto* x = Var("x", ty.f32(), accessor_expr);
- Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ // fn func(p: ptr<function, vec4<f32>>) -> f32 {
+ // let x: f32 = (*p).z;
+ // return x;
+ // }
+ auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
+ auto* star_p = Deref(p);
+ auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
+ auto* accessor_expr = MemberAccessor(star_p, z);
+ auto* x = Var("x", ty.f32(), accessor_expr);
+ Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest, EXpr_MemberAccessor_FuncBadParent) {
- // fn func(p: ptr<function, vec4<f32>>) -> f32 {
- // let x: f32 = *p.z;
- // return x;
- // }
- auto* p =
- Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
- auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
- auto* accessor_expr = MemberAccessor(p, z);
- auto* star_p = Deref(accessor_expr);
- auto* x = Var("x", ty.f32(), star_p);
- Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
+ // fn func(p: ptr<function, vec4<f32>>) -> f32 {
+ // let x: f32 = *p.z;
+ // return x;
+ // }
+ auto* p = Param("p", ty.pointer(ty.vec4<f32>(), ast::StorageClass::kFunction));
+ auto* z = Expr(Source{{{3, 3}, {3, 8}}}, "z");
+ auto* accessor_expr = MemberAccessor(p, z);
+ auto* star_p = Deref(accessor_expr);
+ auto* x = Var("x", ty.f32(), star_p);
+ Func("func", {p}, ty.f32(), {Decl(x), Return(x)});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "error: invalid member accessor expression. "
- "Expected vector or struct, got 'ptr<function, vec4<f32>, read_write>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "error: invalid member accessor expression. "
+ "Expected vector or struct, got 'ptr<function, vec4<f32>, read_write>'");
}
TEST_F(ResolverValidationTest,
Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing) {
- // loop {
- // continue; // Bypasses z decl
- // var z : i32; // unreachable
- //
- // continuing {
- // z = 2;
- // }
- // }
+ // loop {
+ // continue; // Bypasses z decl
+ // var z : i32; // unreachable
+ //
+ // continuing {
+ // z = 2;
+ // }
+ // }
- auto error_loc = Source{{12, 34}};
- auto* body =
- Block(Continue(),
- Decl(error_loc, Var("z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Assign(Expr("z"), 2));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto error_loc = Source{{12, 34}};
+ auto* body = Block(Continue(), Decl(error_loc, Var("z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(Assign(Expr("z"), 2));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- R"(12:34 warning: code is unreachable
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 warning: code is unreachable
error: continue statement bypasses declaration of 'z'
note: identifier 'z' declared here
note: identifier 'z' referenced in continuing block here)");
}
-TEST_F(ResolverValidationTest,
- Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing_InBlocks) {
- // loop {
- // if (false) { break; }
- // var z : i32;
- // {{{continue;}}}
- // continue; // Ok
- //
- // continuing {
- // z = 2;
- // }
- // }
+TEST_F(ResolverValidationTest, Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing_InBlocks) {
+ // loop {
+ // if (false) { break; }
+ // var z : i32;
+ // {{{continue;}}}
+ // continue; // Ok
+ //
+ // continuing {
+ // z = 2;
+ // }
+ // }
- auto* body = Block(If(false, Block(Break())), //
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
- Block(Block(Block(Continue()))));
- auto* continuing = Block(Assign(Expr("z"), 2));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto* body =
+ Block(If(false, Block(Break())), //
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), Block(Block(Block(Continue()))));
+ auto* continuing = Block(Assign(Expr("z"), 2));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverValidationTest,
- Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuing) {
- // loop {
- // if (true) {
- // continue; // Still bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // z = 2;
- // }
- // }
+TEST_F(ResolverValidationTest, Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuing) {
+ // loop {
+ // if (true) {
+ // continue; // Still bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // z = 2;
+ // }
+ // }
- auto cont_loc = Source{{12, 34}};
- auto decl_loc = Source{{56, 78}};
- auto ref_loc = Source{{90, 12}};
- auto* body =
- Block(If(Expr(true), Block(Continue(cont_loc))),
- Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Assign(Expr(ref_loc, "z"), 2));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto cont_loc = Source{{12, 34}};
+ auto decl_loc = Source{{56, 78}};
+ auto ref_loc = Source{{90, 12}};
+ auto* body = Block(If(Expr(true), Block(Continue(cont_loc))),
+ Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(Assign(Expr(ref_loc, "z"), 2));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- R"(12:34 error: continue statement bypasses declaration of 'z'
-56:78 note: identifier 'z' declared here
-90:12 note: identifier 'z' referenced in continuing block here)");
-}
-
-TEST_F(
- ResolverValidationTest,
- Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingSubscope) {
- // loop {
- // if (true) {
- // continue; // Still bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // if (true) {
- // z = 2; // Must fail even if z is in a sub-scope
- // }
- // }
- // }
-
- auto cont_loc = Source{{12, 34}};
- auto decl_loc = Source{{56, 78}};
- auto ref_loc = Source{{90, 12}};
- auto* body =
- Block(If(Expr(true), Block(Continue(cont_loc))),
- Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
-
- auto* continuing =
- Block(If(Expr(true), Block(Assign(Expr(ref_loc, "z"), 2))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- R"(12:34 error: continue statement bypasses declaration of 'z'
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continue statement bypasses declaration of 'z'
56:78 note: identifier 'z' declared here
90:12 note: identifier 'z' referenced in continuing block here)");
}
TEST_F(ResolverValidationTest,
- Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageOutsideBlock) {
- // loop {
- // if (true) {
- // continue; // bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // // Must fail even if z is used in an expression that isn't
- // // directly contained inside a block.
- // if (z < 2) {
- // }
- // }
- // }
+ Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingSubscope) {
+ // loop {
+ // if (true) {
+ // continue; // Still bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // if (true) {
+ // z = 2; // Must fail even if z is in a sub-scope
+ // }
+ // }
+ // }
- auto cont_loc = Source{{12, 34}};
- auto decl_loc = Source{{56, 78}};
- auto ref_loc = Source{{90, 12}};
- auto* body =
- Block(If(Expr(true), Block(Continue(cont_loc))),
- Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
- auto* compare = create<ast::BinaryExpression>(ast::BinaryOp::kLessThan,
- Expr(ref_loc, "z"), Expr(2));
- auto* continuing = Block(If(compare, Block()));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto cont_loc = Source{{12, 34}};
+ auto decl_loc = Source{{56, 78}};
+ auto ref_loc = Source{{90, 12}};
+ auto* body = Block(If(Expr(true), Block(Continue(cont_loc))),
+ Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- R"(12:34 error: continue statement bypasses declaration of 'z'
+ auto* continuing = Block(If(Expr(true), Block(Assign(Expr(ref_loc, "z"), 2))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continue statement bypasses declaration of 'z'
+56:78 note: identifier 'z' declared here
+90:12 note: identifier 'z' referenced in continuing block here)");
+}
+
+TEST_F(ResolverValidationTest, Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageOutsideBlock) {
+ // loop {
+ // if (true) {
+ // continue; // bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // // Must fail even if z is used in an expression that isn't
+ // // directly contained inside a block.
+ // if (z < 2) {
+ // }
+ // }
+ // }
+
+ auto cont_loc = Source{{12, 34}};
+ auto decl_loc = Source{{56, 78}};
+ auto ref_loc = Source{{90, 12}};
+ auto* body = Block(If(Expr(true), Block(Continue(cont_loc))),
+ Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
+ auto* compare =
+ create<ast::BinaryExpression>(ast::BinaryOp::kLessThan, Expr(ref_loc, "z"), Expr(2));
+ auto* continuing = Block(If(compare, Block()));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continue statement bypasses declaration of 'z'
56:78 note: identifier 'z' declared here
90:12 note: identifier 'z' referenced in continuing block here)");
}
TEST_F(ResolverValidationTest,
Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingLoop) {
- // loop {
- // if (true) {
- // continue; // Still bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // loop {
- // z = 2; // Must fail even if z is in a sub-scope
- // }
- // }
- // }
+ // loop {
+ // if (true) {
+ // continue; // Still bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // loop {
+ // z = 2; // Must fail even if z is in a sub-scope
+ // }
+ // }
+ // }
- auto cont_loc = Source{{12, 34}};
- auto decl_loc = Source{{56, 78}};
- auto ref_loc = Source{{90, 12}};
- auto* body =
- Block(If(Expr(true), Block(Continue(cont_loc))),
- Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
+ auto cont_loc = Source{{12, 34}};
+ auto decl_loc = Source{{56, 78}};
+ auto ref_loc = Source{{90, 12}};
+ auto* body = Block(If(Expr(true), Block(Continue(cont_loc))),
+ Decl(Var(decl_loc, "z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Loop(Block(Assign(Expr(ref_loc, "z"), 2))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto* continuing = Block(Loop(Block(Assign(Expr(ref_loc, "z"), 2))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- R"(12:34 error: continue statement bypasses declaration of 'z'
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continue statement bypasses declaration of 'z'
56:78 note: identifier 'z' declared here
90:12 note: identifier 'z' referenced in continuing block here)");
}
-TEST_F(ResolverValidationTest,
- Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
- // loop {
- // loop {
- // if (true) { continue; } // OK: not part of the outer loop
- // break;
- // }
- // var z : i32;
- // break;
- // continuing {
- // z = 2;
- // }
- // }
+TEST_F(ResolverValidationTest, Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
+ // loop {
+ // loop {
+ // if (true) { continue; } // OK: not part of the outer loop
+ // break;
+ // }
+ // var z : i32;
+ // break;
+ // continuing {
+ // z = 2;
+ // }
+ // }
- auto* inner_loop = Loop(Block( //
- If(true, Block(Continue())), //
- Break()));
- auto* body = Block(inner_loop, //
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), //
- Break());
- auto* continuing = Block(Assign("z", 2));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto* inner_loop = Loop(Block( //
+ If(true, Block(Continue())), //
+ Break()));
+ auto* body = Block(inner_loop, //
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), //
+ Break());
+ auto* continuing = Block(Assign("z", 2));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest,
Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingSubscope) {
- // loop {
- // loop {
- // if (true) { continue; } // OK: not part of the outer loop
- // break;
- // }
- // var z : i32;
- // break;
- // continuing {
- // if (true) {
- // z = 2;
- // }
- // }
- // }
+ // loop {
+ // loop {
+ // if (true) { continue; } // OK: not part of the outer loop
+ // break;
+ // }
+ // var z : i32;
+ // break;
+ // continuing {
+ // if (true) {
+ // z = 2;
+ // }
+ // }
+ // }
- auto* inner_loop = Loop(Block(If(true, Block(Continue())), //
- Break()));
- auto* body = Block(inner_loop, //
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), //
- Break());
- auto* continuing = Block(If(Expr(true), Block(Assign("z", 2))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto* inner_loop = Loop(Block(If(true, Block(Continue())), //
+ Break()));
+ auto* body = Block(inner_loop, //
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), //
+ Break());
+ auto* continuing = Block(If(Expr(true), Block(Assign("z", 2))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverValidationTest,
- Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
- // loop {
- // loop {
- // if (true) { continue; } // OK: not part of the outer loop
- // break;
- // }
- // var z : i32;
- // break;
- // continuing {
- // loop {
- // z = 2;
- // break;
- // }
- // }
- // }
+TEST_F(ResolverValidationTest, Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
+ // loop {
+ // loop {
+ // if (true) { continue; } // OK: not part of the outer loop
+ // break;
+ // }
+ // var z : i32;
+ // break;
+ // continuing {
+ // loop {
+ // z = 2;
+ // break;
+ // }
+ // }
+ // }
- auto* inner_loop = Loop(Block(If(true, Block(Continue())), //
- Break()));
- auto* body = Block(inner_loop, //
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), //
- Break());
- auto* continuing = Block(Loop(Block(Assign("z", 2), //
- Break())));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto* inner_loop = Loop(Block(If(true, Block(Continue())), //
+ Break()));
+ auto* body = Block(inner_loop, //
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), //
+ Break());
+ auto* continuing = Block(Loop(Block(Assign("z", 2), //
+ Break())));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyAfterDecl_UsageInContinuing) {
- // loop {
- // var z : i32;
- // if (true) { continue; }
- // break;
- // continuing {
- // z = 2;
- // }
- // }
+ // loop {
+ // var z : i32;
+ // if (true) { continue; }
+ // break;
+ // continuing {
+ // z = 2;
+ // }
+ // }
- auto error_loc = Source{{12, 34}};
- auto* body = Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
- If(true, Block(Continue())), //
- Break());
- auto* continuing = Block(Assign(Expr(error_loc, "z"), 2));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
+ auto error_loc = Source{{12, 34}};
+ auto* body =
+ Block(Decl(Var("z", ty.i32(), ast::StorageClass::kNone)), If(true, Block(Continue())), //
+ Break());
+ auto* continuing = Block(Assign(Expr(error_loc, "z"), 2));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
- EXPECT_TRUE(r()->Resolve());
+ EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverTest, Stmt_Loop_ReturnInContinuing_Direct) {
- // loop {
- // continuing {
- // return;
- // }
- // }
+ // loop {
+ // continuing {
+ // return;
+ // }
+ // }
- WrapInFunction(Loop( // loop
- Block(), // loop block
- Block( // loop continuing block
- Return(Source{{12, 34}}))));
+ WrapInFunction(Loop( // loop
+ Block(), // loop block
+ Block( // loop continuing block
+ Return(Source{{12, 34}}))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a return statement)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a return statement)");
}
TEST_F(ResolverTest, Stmt_Loop_ReturnInContinuing_Indirect) {
- // loop {
- // if (false) { break; }
- // continuing {
- // loop {
- // return;
- // }
- // }
- // }
+ // loop {
+ // if (false) { break; }
+ // continuing {
+ // loop {
+ // return;
+ // }
+ // }
+ // }
- WrapInFunction(Loop( // outer loop
- Block(If(false, Block(Break()))), // outer loop block
- Block(Source{{56, 78}}, // outer loop continuing block
- Loop( // inner loop
- Block( // inner loop block
- Return(Source{{12, 34}}))))));
+ WrapInFunction(Loop( // outer loop
+ Block(If(false, Block(Break()))), // outer loop block
+ Block(Source{{56, 78}}, // outer loop continuing block
+ Loop( // inner loop
+ Block( // inner loop block
+ Return(Source{{12, 34}}))))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a return statement
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a return statement
56:78 note: see continuing block here)");
}
TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Direct) {
- // loop {
- // continuing {
- // discard;
- // }
- // }
+ // loop {
+ // continuing {
+ // discard;
+ // }
+ // }
- WrapInFunction(Loop( // loop
- Block(), // loop block
- Block( // loop continuing block
- Discard(Source{{12, 34}}))));
+ WrapInFunction(Loop( // loop
+ Block(), // loop block
+ Block( // loop continuing block
+ Discard(Source{{12, 34}}))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a discard statement)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a discard statement)");
}
TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect) {
- // loop {
- // if (false) { break; }
- // continuing {
- // loop { discard; }
- // }
- // }
+ // loop {
+ // if (false) { break; }
+ // continuing {
+ // loop { discard; }
+ // }
+ // }
- WrapInFunction(Loop( // outer loop
- Block(If(false, Block(Break()))), // outer loop block
- Block(Source{{56, 78}}, // outer loop continuing block
- Loop( // inner loop
- Block( // inner loop block
- Discard(Source{{12, 34}}))))));
+ WrapInFunction(Loop( // outer loop
+ Block(If(false, Block(Break()))), // outer loop block
+ Block(Source{{56, 78}}, // outer loop continuing block
+ Loop( // inner loop
+ Block( // inner loop block
+ Discard(Source{{12, 34}}))))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a discard statement
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a discard statement
56:78 note: see continuing block here)");
}
TEST_F(ResolverTest, Stmt_Loop_DiscardInContinuing_Indirect_ViaCall) {
- // fn MayDiscard() { if (true) { discard; } }
- // fn F() { MayDiscard(); }
- // loop {
- // continuing {
- // loop { F(); }
- // }
- // }
+ // fn MayDiscard() { if (true) { discard; } }
+ // fn F() { MayDiscard(); }
+ // loop {
+ // continuing {
+ // loop { F(); }
+ // }
+ // }
- Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
- Func("SomeFunc", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
+ Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
+ Func("SomeFunc", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
- WrapInFunction(Loop( // outer loop
- Block(), // outer loop block
- Block(Source{{56, 78}}, // outer loop continuing block
- Loop( // inner loop
- Block( // inner loop block
- CallStmt(Call(Source{{12, 34}}, "SomeFunc")))))));
+ WrapInFunction(Loop( // outer loop
+ Block(), // outer loop block
+ Block(Source{{56, 78}}, // outer loop continuing block
+ Loop( // inner loop
+ Block( // inner loop block
+ CallStmt(Call(Source{{12, 34}}, "SomeFunc")))))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: cannot call a function that may discard inside a continuing block
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: cannot call a function that may discard inside a continuing block
56:78 note: see continuing block here)");
}
TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Direct) {
- // loop {
- // continuing {
- // continue;
- // }
- // }
+ // loop {
+ // continuing {
+ // continue;
+ // }
+ // }
- WrapInFunction(Loop( // loop
- Block(), // loop block
- Block(Source{{56, 78}}, // loop continuing block
- Continue(Source{{12, 34}}))));
+ WrapInFunction(Loop( // loop
+ Block(), // loop block
+ Block(Source{{56, 78}}, // loop continuing block
+ Continue(Source{{12, 34}}))));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: continuing blocks must not contain a continue statement");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: continuing blocks must not contain a continue statement");
}
TEST_F(ResolverTest, Stmt_Loop_ContinueInContinuing_Indirect) {
- // loop {
- // if (false) { break; }
- // continuing {
- // loop {
- // if (false) { break; }
- // continue;
- // }
- // }
- // }
+ // loop {
+ // if (false) { break; }
+ // continuing {
+ // loop {
+ // if (false) { break; }
+ // continue;
+ // }
+ // }
+ // }
- WrapInFunction(Loop( // outer loop
- Block( // outer loop block
- If(false, Block(Break()))), // if (false) { break; }
- Block( // outer loop continuing block
- Loop( // inner loop
- Block( // inner loop block
- If(false, Block(Break())), // if (false) { break; }
- Continue(Source{{12, 34}})))))); // continue
+ WrapInFunction(Loop( // outer loop
+ Block( // outer loop block
+ If(false, Block(Break()))), // if (false) { break; }
+ Block( // outer loop continuing block
+ Loop( // inner loop
+ Block( // inner loop block
+ If(false, Block(Break())), // if (false) { break; }
+ Continue(Source{{12, 34}})))))); // continue
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, Stmt_ForLoop_ReturnInContinuing_Direct) {
- // for(;; return) {
- // break;
- // }
+ // for(;; return) {
+ // break;
+ // }
- WrapInFunction(For(nullptr, nullptr, Return(Source{{12, 34}}), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr, Return(Source{{12, 34}}), //
+ Block(Break())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a return statement)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a return statement)");
}
TEST_F(ResolverTest, Stmt_ForLoop_ReturnInContinuing_Indirect) {
- // for(;; loop { return }) {
- // break;
- // }
+ // for(;; loop { return }) {
+ // break;
+ // }
- WrapInFunction(For(nullptr, nullptr,
- Loop(Source{{56, 78}}, //
- Block(Return(Source{{12, 34}}))), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr,
+ Loop(Source{{56, 78}}, //
+ Block(Return(Source{{12, 34}}))), //
+ Block(Break())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a return statement
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a return statement
56:78 note: see continuing block here)");
}
TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Direct) {
- // for(;; discard) {
- // break;
- // }
+ // for(;; discard) {
+ // break;
+ // }
- WrapInFunction(For(nullptr, nullptr, Discard(Source{{12, 34}}), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr, Discard(Source{{12, 34}}), //
+ Block(Break())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a discard statement)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a discard statement)");
}
TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Indirect) {
- // for(;; loop { discard }) {
- // break;
- // }
+ // for(;; loop { discard }) {
+ // break;
+ // }
- WrapInFunction(For(nullptr, nullptr,
- Loop(Source{{56, 78}}, //
- Block(Discard(Source{{12, 34}}))), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr,
+ Loop(Source{{56, 78}}, //
+ Block(Discard(Source{{12, 34}}))), //
+ Block(Break())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: continuing blocks must not contain a discard statement
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: continuing blocks must not contain a discard statement
56:78 note: see continuing block here)");
}
TEST_F(ResolverTest, Stmt_ForLoop_DiscardInContinuing_Indirect_ViaCall) {
- // fn MayDiscard() { if (true) { discard; } }
- // fn F() { MayDiscard(); }
- // for(;; loop { F() }) {
- // break;
- // }
+ // fn MayDiscard() { if (true) { discard; } }
+ // fn F() { MayDiscard(); }
+ // for(;; loop { F() }) {
+ // break;
+ // }
- Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
- Func("F", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
+ Func("MayDiscard", {}, ty.void_(), {If(true, Block(Discard()))});
+ Func("F", {}, ty.void_(), {CallStmt(Call("MayDiscard"))});
- WrapInFunction(For(nullptr, nullptr,
- Loop(Source{{56, 78}}, //
- Block(CallStmt(Call(Source{{12, 34}}, "F")))), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr,
+ Loop(Source{{56, 78}}, //
+ Block(CallStmt(Call(Source{{12, 34}}, "F")))), //
+ Block(Break())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: cannot call a function that may discard inside a continuing block
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: cannot call a function that may discard inside a continuing block
56:78 note: see continuing block here)");
}
TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Direct) {
- // for(;; continue) {
- // break;
- // }
+ // for(;; continue) {
+ // break;
+ // }
- WrapInFunction(For(nullptr, nullptr, Continue(Source{{12, 34}}), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr, Continue(Source{{12, 34}}), //
+ Block(Break())));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: continuing blocks must not contain a continue statement");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: continuing blocks must not contain a continue statement");
}
TEST_F(ResolverTest, Stmt_ForLoop_ContinueInContinuing_Indirect) {
- // for(;; loop { if (false) { break; } continue }) {
- // break;
- // }
+ // for(;; loop { if (false) { break; } continue }) {
+ // break;
+ // }
- WrapInFunction(For(nullptr, nullptr,
- Loop( //
- Block(If(false, Block(Break())), //
- Continue(Source{{12, 34}}))), //
- Block(Break())));
+ WrapInFunction(For(nullptr, nullptr,
+ Loop( //
+ Block(If(false, Block(Break())), //
+ Continue(Source{{12, 34}}))), //
+ Block(Break())));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, Stmt_ForLoop_CondIsBoolRef) {
- // var cond : bool = true;
- // for (; cond; ) {
- // }
+ // var cond : bool = true;
+ // for (; cond; ) {
+ // }
- auto* cond = Var("cond", ty.bool_(), Expr(true));
- WrapInFunction(Decl(cond), For(nullptr, "cond", nullptr, Block()));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ auto* cond = Var("cond", ty.bool_(), Expr(true));
+ WrapInFunction(Decl(cond), For(nullptr, "cond", nullptr, Block()));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverTest, Stmt_ForLoop_CondIsNotBool) {
- // for (; 1.0f; ) {
- // }
+ // for (; 1.0f; ) {
+ // }
- WrapInFunction(For(nullptr, Expr(Source{{12, 34}}, 1.0f), nullptr, Block()));
+ WrapInFunction(For(nullptr, Expr(Source{{12, 34}}, 1.0f), nullptr, Block()));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: for-loop condition must be bool, got f32");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: for-loop condition must be bool, got f32");
}
TEST_F(ResolverValidationTest, Stmt_ContinueInLoop) {
- WrapInFunction(Loop(Block(If(false, Block(Break())), //
- Continue(Source{{12, 34}}))));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ WrapInFunction(Loop(Block(If(false, Block(Break())), //
+ Continue(Source{{12, 34}}))));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest, Stmt_ContinueNotInLoop) {
- WrapInFunction(Continue(Source{{12, 34}}));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
+ WrapInFunction(Continue(Source{{12, 34}}));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
}
TEST_F(ResolverValidationTest, Stmt_BreakInLoop) {
- WrapInFunction(Loop(Block(Break(Source{{12, 34}}))));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ WrapInFunction(Loop(Block(Break(Source{{12, 34}}))));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest, Stmt_BreakInSwitch) {
- WrapInFunction(Loop(Block(Switch(Expr(1), //
- Case(Expr(1), //
- Block(Break())), //
- DefaultCase()), //
- Break()))); //
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ WrapInFunction(Loop(Block(Switch(Expr(1), //
+ Case(Expr(1), //
+ Block(Break())), //
+ DefaultCase()), //
+ Break()))); //
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueInContinuing) {
- auto* cont = Block( // continuing {
- If(true, Block( // if(true) {
- Break(Source{{12, 34}})))); // break;
- // }
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
- auto* cont = Block( // continuing {
- If(true, Block(), // if(true) {
- Block( // } else {
- Break(Source{{12, 34}})))); // break;
- // }
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
- auto* cont = Block( // continuing {
- Block(Break(Source{{12, 34}}))); // break;
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "12:34 note: break statement is not directly in if statement block");
-}
-
-TEST_F(ResolverValidationTest, Stmt_BreakInIfInIfInContinuing) {
- auto* cont = Block( // continuing {
- If(true, Block( // if(true) {
- If(Source{{56, 78}}, true, // if(true) {
- Block(Break(Source{{12, 34}})))))); // break;
- // }
- // }
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: if statement containing break statement is not directly in "
- "continuing block");
-}
-
-TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueMultipleStmtsInContinuing) {
- auto* cont = Block( // continuing {
- If(true, Block(Source{{56, 78}}, // if(true) {
- Assign(Phony(), 1), // _ = 1;
+ auto* cont = Block( // continuing {
+ If(true, Block( // if(true) {
Break(Source{{12, 34}})))); // break;
// }
// }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: if statement block contains multiple statements");
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
-TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
- auto* cont = Block( // continuing {
- If(true, Block(), // if(true) {
- Block(Source{{56, 78}}, // } else {
- Assign(Phony(), 1), // _ = 1;
+TEST_F(ResolverValidationTest, Stmt_BreakInIfElseInContinuing) {
+ auto* cont = Block( // continuing {
+ If(true, Block(), // if(true) {
+ Block( // } else {
Break(Source{{12, 34}})))); // break;
// }
// }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: if statement block contains multiple statements");
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakInContinuing) {
+ auto* cont = Block( // continuing {
+ Block(Break(Source{{12, 34}}))); // break;
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "12:34 note: break statement is not directly in if statement block");
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakInIfInIfInContinuing) {
+ auto* cont = Block( // continuing {
+ If(true, Block( // if(true) {
+ If(Source{{56, 78}}, true, // if(true) {
+ Block(Break(Source{{12, 34}})))))); // break;
+ // }
+ // }
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: if statement containing break statement is not directly in "
+ "continuing block");
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakInIfTrueMultipleStmtsInContinuing) {
+ auto* cont = Block( // continuing {
+ If(true, Block(Source{{56, 78}}, // if(true) {
+ Assign(Phony(), 1), // _ = 1;
+ Break(Source{{12, 34}})))); // break;
+ // }
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: if statement block contains multiple statements");
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakInIfElseMultipleStmtsInContinuing) {
+ auto* cont = Block( // continuing {
+ If(true, Block(), // if(true) {
+ Block(Source{{56, 78}}, // } else {
+ Assign(Phony(), 1), // _ = 1;
+ Break(Source{{12, 34}})))); // break;
+ // }
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: if statement block contains multiple statements");
}
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseIfInContinuing) {
- auto* cont = Block( // continuing {
- If(true, Block(), // if(true) {
- If(Source{{56, 78}}, Expr(true), // } else if (true) {
- Block(Break(Source{{12, 34}}))))); // break;
- // }
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: else has condition");
+ auto* cont = Block( // continuing {
+ If(true, Block(), // if(true) {
+ If(Source{{56, 78}}, Expr(true), // } else if (true) {
+ Block(Break(Source{{12, 34}}))))); // break;
+ // }
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: else has condition");
}
TEST_F(ResolverValidationTest, Stmt_BreakInIfNonEmptyElseInContinuing) {
- auto* cont = Block( // continuing {
- If(true, // if(true) {
- Block(Break(Source{{12, 34}})), // break;
- Block(Source{{56, 78}}, // } else {
- Assign(Phony(), 1)))); // _ = 1;
- // }
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: non-empty false block");
+ auto* cont = Block( // continuing {
+ If(true, // if(true) {
+ Block(Break(Source{{12, 34}})), // break;
+ Block(Source{{56, 78}}, // } else {
+ Assign(Phony(), 1)))); // _ = 1;
+ // }
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: non-empty false block");
}
TEST_F(ResolverValidationTest, Stmt_BreakInIfElseNonEmptyTrueInContinuing) {
- auto* cont = Block( // continuing {
- If(true, // if(true) {
- Block(Source{{56, 78}}, Assign(Phony(), 1)), // _ = 1;
- Block( // } else {
- Break(Source{{12, 34}})))); // break;
- // }
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: non-empty true block");
+ auto* cont = Block( // continuing {
+ If(true, // if(true) {
+ Block(Source{{56, 78}}, Assign(Phony(), 1)), // _ = 1;
+ Block( // } else {
+ Break(Source{{12, 34}})))); // break;
+ // }
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: non-empty true block");
}
TEST_F(ResolverValidationTest, Stmt_BreakInIfInContinuingNotLast) {
- auto* cont = Block( // continuing {
- If(Source{{56, 78}}, true, // if(true) {
- Block(Break(Source{{12, 34}}))), // break;
- // }
- Assign(Phony(), 1)); // _ = 1;
- // }
- WrapInFunction(Loop(Block(), cont));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: break statement in a continuing block must be the single "
- "statement of an if statement's true or false block, and that if "
- "statement must be the last statement of the continuing block\n"
- "56:78 note: if statement containing break statement is not the last "
- "statement of the continuing block");
+ auto* cont = Block( // continuing {
+ If(Source{{56, 78}}, true, // if(true) {
+ Block(Break(Source{{12, 34}}))), // break;
+ // }
+ Assign(Phony(), 1)); // _ = 1;
+ // }
+ WrapInFunction(Loop(Block(), cont));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement in a continuing block must be the single "
+ "statement of an if statement's true or false block, and that if "
+ "statement must be the last statement of the continuing block\n"
+ "56:78 note: if statement containing break statement is not the last "
+ "statement of the continuing block");
}
TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) {
- WrapInFunction(Break(Source{{12, 34}}));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: break statement must be in a loop or switch case");
+ WrapInFunction(Break(Source{{12, 34}}));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: break statement must be in a loop or switch case");
}
TEST_F(ResolverValidationTest, StructMemberDuplicateName) {
- Structure("S", {Member(Source{{12, 34}}, "a", ty.i32()),
- Member(Source{{56, 78}}, "a", ty.i32())});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: redefinition of 'a'\n12:34 note: previous definition "
- "is here");
+ Structure("S",
+ {Member(Source{{12, 34}}, "a", ty.i32()), Member(Source{{56, 78}}, "a", ty.i32())});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: redefinition of 'a'\n12:34 note: previous definition "
+ "is here");
}
TEST_F(ResolverValidationTest, StructMemberDuplicateNameDifferentTypes) {
- Structure("S", {Member(Source{{12, 34}}, "a", ty.bool_()),
- Member(Source{{12, 34}}, "a", ty.vec3<f32>())});
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: redefinition of 'a'\n12:34 note: previous definition "
- "is here");
+ Structure("S", {Member(Source{{12, 34}}, "a", ty.bool_()),
+ Member(Source{{12, 34}}, "a", ty.vec3<f32>())});
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: redefinition of 'a'\n12:34 note: previous definition "
+ "is here");
}
TEST_F(ResolverValidationTest, StructMemberDuplicateNamePass) {
- Structure("S", {Member("a", ty.i32()), Member("b", ty.f32())});
- Structure("S1", {Member("a", ty.i32()), Member("b", ty.f32())});
- EXPECT_TRUE(r()->Resolve());
+ Structure("S", {Member("a", ty.i32()), Member("b", ty.f32())});
+ Structure("S1", {Member("a", ty.i32()), Member("b", ty.f32())});
+ EXPECT_TRUE(r()->Resolve());
}
TEST_F(ResolverValidationTest, NonPOTStructMemberAlignAttribute) {
- Structure("S", {
- Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 3)}),
- });
+ Structure("S", {
+ Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 3)}),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: align value must be a positive, power-of-two integer");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: align value must be a positive, power-of-two integer");
}
TEST_F(ResolverValidationTest, ZeroStructMemberAlignAttribute) {
- Structure("S", {
- Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 0)}),
- });
+ Structure("S", {
+ Member("a", ty.f32(), {MemberAlign(Source{{12, 34}}, 0)}),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: align value must be a positive, power-of-two integer");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: align value must be a positive, power-of-two integer");
}
TEST_F(ResolverValidationTest, ZeroStructMemberSizeAttribute) {
- Structure("S", {
- Member("a", ty.f32(), {MemberSize(Source{{12, 34}}, 0)}),
- });
+ Structure("S", {
+ Member("a", ty.f32(), {MemberSize(Source{{12, 34}}, 0)}),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: size must be at least as big as the type's size (4)");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: size must be at least as big as the type's size (4)");
}
TEST_F(ResolverValidationTest, OffsetAndSizeAttribute) {
- Structure("S", {
- Member(Source{{12, 34}}, "a", ty.f32(),
- {MemberOffset(0), MemberSize(4)}),
- });
+ Structure("S", {
+ Member(Source{{12, 34}}, "a", ty.f32(), {MemberOffset(0), MemberSize(4)}),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: offset attributes cannot be used with align or size "
- "attributes");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: offset attributes cannot be used with align or size "
+ "attributes");
}
TEST_F(ResolverValidationTest, OffsetAndAlignAttribute) {
- Structure("S", {
- Member(Source{{12, 34}}, "a", ty.f32(),
- {MemberOffset(0), MemberAlign(4)}),
- });
+ Structure("S", {
+ Member(Source{{12, 34}}, "a", ty.f32(), {MemberOffset(0), MemberAlign(4)}),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: offset attributes cannot be used with align or size "
- "attributes");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: offset attributes cannot be used with align or size "
+ "attributes");
}
TEST_F(ResolverValidationTest, OffsetAndAlignAndSizeAttribute) {
- Structure("S", {
- Member(Source{{12, 34}}, "a", ty.f32(),
- {MemberOffset(0), MemberAlign(4), MemberSize(4)}),
- });
+ Structure("S", {
+ Member(Source{{12, 34}}, "a", ty.f32(),
+ {MemberOffset(0), MemberAlign(4), MemberSize(4)}),
+ });
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: offset attributes cannot be used with align or size "
- "attributes");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: offset attributes cannot be used with align or size "
+ "attributes");
}
TEST_F(ResolverTest, Expr_Constructor_Cast_Pointer) {
- auto* vf = Var("vf", ty.f32());
- auto* c =
- Construct(Source{{12, 34}}, ty.pointer<i32>(ast::StorageClass::kFunction),
- ExprList(vf));
- auto* ip = Let("ip", ty.pointer<i32>(ast::StorageClass::kFunction), c);
- WrapInFunction(Decl(vf), Decl(ip));
+ auto* vf = Var("vf", ty.f32());
+ auto* c =
+ Construct(Source{{12, 34}}, ty.pointer<i32>(ast::StorageClass::kFunction), ExprList(vf));
+ auto* ip = Let("ip", ty.pointer<i32>(ast::StorageClass::kFunction), c);
+ WrapInFunction(Decl(vf), Decl(ip));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: type is not constructible");
}
} // namespace
diff --git a/src/tint/resolver/validator.cc b/src/tint/resolver/validator.cc
index 87a374f..033c785 100644
--- a/src/tint/resolver/validator.cc
+++ b/src/tint/resolver/validator.cc
@@ -81,50 +81,50 @@
namespace {
bool IsValidStorageTextureDimension(ast::TextureDimension dim) {
- switch (dim) {
- case ast::TextureDimension::k1d:
- case ast::TextureDimension::k2d:
- case ast::TextureDimension::k2dArray:
- case ast::TextureDimension::k3d:
- return true;
- default:
- return false;
- }
+ switch (dim) {
+ case ast::TextureDimension::k1d:
+ case ast::TextureDimension::k2d:
+ case ast::TextureDimension::k2dArray:
+ case ast::TextureDimension::k3d:
+ return true;
+ default:
+ return false;
+ }
}
bool IsValidStorageTextureTexelFormat(ast::TexelFormat format) {
- switch (format) {
- case ast::TexelFormat::kR32Uint:
- case ast::TexelFormat::kR32Sint:
- case ast::TexelFormat::kR32Float:
- case ast::TexelFormat::kRg32Uint:
- case ast::TexelFormat::kRg32Sint:
- case ast::TexelFormat::kRg32Float:
- case ast::TexelFormat::kRgba8Unorm:
- case ast::TexelFormat::kRgba8Snorm:
- case ast::TexelFormat::kRgba8Uint:
- case ast::TexelFormat::kRgba8Sint:
- case ast::TexelFormat::kRgba16Uint:
- case ast::TexelFormat::kRgba16Sint:
- case ast::TexelFormat::kRgba16Float:
- case ast::TexelFormat::kRgba32Uint:
- case ast::TexelFormat::kRgba32Sint:
- case ast::TexelFormat::kRgba32Float:
- return true;
- default:
- return false;
- }
+ switch (format) {
+ case ast::TexelFormat::kR32Uint:
+ case ast::TexelFormat::kR32Sint:
+ case ast::TexelFormat::kR32Float:
+ case ast::TexelFormat::kRg32Uint:
+ case ast::TexelFormat::kRg32Sint:
+ case ast::TexelFormat::kRg32Float:
+ case ast::TexelFormat::kRgba8Unorm:
+ case ast::TexelFormat::kRgba8Snorm:
+ case ast::TexelFormat::kRgba8Uint:
+ case ast::TexelFormat::kRgba8Sint:
+ case ast::TexelFormat::kRgba16Uint:
+ case ast::TexelFormat::kRgba16Sint:
+ case ast::TexelFormat::kRgba16Float:
+ case ast::TexelFormat::kRgba32Uint:
+ case ast::TexelFormat::kRgba32Sint:
+ case ast::TexelFormat::kRgba32Float:
+ return true;
+ default:
+ return false;
+ }
}
// Helper to stringify a pipeline IO attribute.
std::string attr_to_str(const ast::Attribute* attr) {
- std::stringstream str;
- if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
- str << "builtin(" << builtin->builtin << ")";
- } else if (auto* location = attr->As<ast::LocationAttribute>()) {
- str << "location(" << location->value << ")";
- }
- return str.str();
+ std::stringstream str;
+ if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
+ str << "builtin(" << builtin->builtin << ")";
+ } else if (auto* location = attr->As<ast::LocationAttribute>()) {
+ str << "location(" << location->value << ")";
+ }
+ return str.str();
}
template <typename CALLBACK>
@@ -132,479 +132,455 @@
const sem::Function* from,
const sem::Function* to,
CALLBACK&& callback) {
- for (auto* f : from->TransitivelyCalledFunctions()) {
- if (f == to) {
- callback(f);
- return;
+ for (auto* f : from->TransitivelyCalledFunctions()) {
+ if (f == to) {
+ callback(f);
+ return;
+ }
+ if (f->TransitivelyCalledFunctions().contains(to)) {
+ TraverseCallChain(diagnostics, f, to, callback);
+ callback(f);
+ return;
+ }
}
- if (f->TransitivelyCalledFunctions().contains(to)) {
- TraverseCallChain(diagnostics, f, to, callback);
- callback(f);
- return;
- }
- }
- TINT_ICE(Resolver, diagnostics)
- << "TraverseCallChain() 'from' does not transitively call 'to'";
+ TINT_ICE(Resolver, diagnostics) << "TraverseCallChain() 'from' does not transitively call 'to'";
}
} // namespace
Validator::Validator(ProgramBuilder* builder, SemHelper& sem)
- : symbols_(builder->Symbols()),
- diagnostics_(builder->Diagnostics()),
- sem_(sem) {}
+ : symbols_(builder->Symbols()), diagnostics_(builder->Diagnostics()), sem_(sem) {}
Validator::~Validator() = default;
void Validator::AddError(const std::string& msg, const Source& source) const {
- diagnostics_.add_error(diag::System::Resolver, msg, source);
+ diagnostics_.add_error(diag::System::Resolver, msg, source);
}
void Validator::AddWarning(const std::string& msg, const Source& source) const {
- diagnostics_.add_warning(diag::System::Resolver, msg, source);
+ diagnostics_.add_warning(diag::System::Resolver, msg, source);
}
void Validator::AddNote(const std::string& msg, const Source& source) const {
- diagnostics_.add_note(diag::System::Resolver, msg, source);
+ diagnostics_.add_note(diag::System::Resolver, msg, source);
}
// https://gpuweb.github.io/gpuweb/wgsl/#plain-types-section
bool Validator::IsPlain(const sem::Type* type) const {
- return type->is_scalar() ||
- type->IsAnyOf<sem::Atomic, sem::Vector, sem::Matrix, sem::Array,
- sem::Struct>();
+ return type->is_scalar() ||
+ type->IsAnyOf<sem::Atomic, sem::Vector, sem::Matrix, sem::Array, sem::Struct>();
}
// https://gpuweb.github.io/gpuweb/wgsl/#fixed-footprint-types
bool Validator::IsFixedFootprint(const sem::Type* type) const {
- return Switch(
- type, //
- [&](const sem::Vector*) { return true; }, //
- [&](const sem::Matrix*) { return true; }, //
- [&](const sem::Atomic*) { return true; },
- [&](const sem::Array* arr) {
- return !arr->IsRuntimeSized() && IsFixedFootprint(arr->ElemType());
- },
- [&](const sem::Struct* str) {
- for (auto* member : str->Members()) {
- if (!IsFixedFootprint(member->Type())) {
- return false;
- }
- }
- return true;
- },
- [&](Default) { return type->is_scalar(); });
+ return Switch(
+ type, //
+ [&](const sem::Vector*) { return true; }, //
+ [&](const sem::Matrix*) { return true; }, //
+ [&](const sem::Atomic*) { return true; },
+ [&](const sem::Array* arr) {
+ return !arr->IsRuntimeSized() && IsFixedFootprint(arr->ElemType());
+ },
+ [&](const sem::Struct* str) {
+ for (auto* member : str->Members()) {
+ if (!IsFixedFootprint(member->Type())) {
+ return false;
+ }
+ }
+ return true;
+ },
+ [&](Default) { return type->is_scalar(); });
}
// https://gpuweb.github.io/gpuweb/wgsl.html#host-shareable-types
bool Validator::IsHostShareable(const sem::Type* type) const {
- if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
- return true;
- }
- return Switch(
- type, //
- [&](const sem::Vector* vec) { return IsHostShareable(vec->type()); },
- [&](const sem::Matrix* mat) { return IsHostShareable(mat->type()); },
- [&](const sem::Array* arr) { return IsHostShareable(arr->ElemType()); },
- [&](const sem::Struct* str) {
- for (auto* member : str->Members()) {
- if (!IsHostShareable(member->Type())) {
- return false;
- }
- }
+ if (type->IsAnyOf<sem::I32, sem::U32, sem::F32>()) {
return true;
- },
- [&](const sem::Atomic* atomic) {
- return IsHostShareable(atomic->Type());
- });
+ }
+ return Switch(
+ type, //
+ [&](const sem::Vector* vec) { return IsHostShareable(vec->type()); },
+ [&](const sem::Matrix* mat) { return IsHostShareable(mat->type()); },
+ [&](const sem::Array* arr) { return IsHostShareable(arr->ElemType()); },
+ [&](const sem::Struct* str) {
+ for (auto* member : str->Members()) {
+ if (!IsHostShareable(member->Type())) {
+ return false;
+ }
+ }
+ return true;
+ },
+ [&](const sem::Atomic* atomic) { return IsHostShareable(atomic->Type()); });
}
// https://gpuweb.github.io/gpuweb/wgsl.html#storable-types
bool Validator::IsStorable(const sem::Type* type) const {
- return IsPlain(type) || type->IsAnyOf<sem::Texture, sem::Sampler>();
+ return IsPlain(type) || type->IsAnyOf<sem::Texture, sem::Sampler>();
}
-const ast::Statement* Validator::ClosestContinuing(
- bool stop_at_loop,
- sem::Statement* current_statement) const {
- for (const auto* s = current_statement; s != nullptr; s = s->Parent()) {
- if (stop_at_loop && s->Is<sem::LoopStatement>()) {
- break;
+const ast::Statement* Validator::ClosestContinuing(bool stop_at_loop,
+ sem::Statement* current_statement) const {
+ for (const auto* s = current_statement; s != nullptr; s = s->Parent()) {
+ if (stop_at_loop && s->Is<sem::LoopStatement>()) {
+ break;
+ }
+ if (s->Is<sem::LoopContinuingBlockStatement>()) {
+ return s->Declaration();
+ }
+ if (auto* f = As<sem::ForLoopStatement>(s->Parent())) {
+ if (f->Declaration()->continuing == s->Declaration()) {
+ return s->Declaration();
+ }
+ if (stop_at_loop) {
+ break;
+ }
+ }
}
- if (s->Is<sem::LoopContinuingBlockStatement>()) {
- return s->Declaration();
- }
- if (auto* f = As<sem::ForLoopStatement>(s->Parent())) {
- if (f->Declaration()->continuing == s->Declaration()) {
- return s->Declaration();
- }
- if (stop_at_loop) {
- break;
- }
- }
- }
- return nullptr;
+ return nullptr;
}
bool Validator::Atomic(const ast::Atomic* a, const sem::Atomic* s) const {
- // https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
- // T must be either u32 or i32.
- if (!s->Type()->IsAnyOf<sem::U32, sem::I32>()) {
- AddError("atomic only supports i32 or u32 types",
- a->type ? a->type->source : a->source);
- return false;
- }
- return true;
+ // https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
+ // T must be either u32 or i32.
+ if (!s->Type()->IsAnyOf<sem::U32, sem::I32>()) {
+ AddError("atomic only supports i32 or u32 types", a->type ? a->type->source : a->source);
+ return false;
+ }
+ return true;
}
bool Validator::StorageTexture(const ast::StorageTexture* t) const {
- switch (t->access) {
- case ast::Access::kWrite:
- break;
- case ast::Access::kUndefined:
- AddError("storage texture missing access control", t->source);
- return false;
- default:
- AddError("storage textures currently only support 'write' access control",
- t->source);
- return false;
- }
+ switch (t->access) {
+ case ast::Access::kWrite:
+ break;
+ case ast::Access::kUndefined:
+ AddError("storage texture missing access control", t->source);
+ return false;
+ default:
+ AddError("storage textures currently only support 'write' access control", t->source);
+ return false;
+ }
- if (!IsValidStorageTextureDimension(t->dim)) {
- AddError("cube dimensions for storage textures are not supported",
- t->source);
- return false;
- }
+ if (!IsValidStorageTextureDimension(t->dim)) {
+ AddError("cube dimensions for storage textures are not supported", t->source);
+ return false;
+ }
- if (!IsValidStorageTextureTexelFormat(t->format)) {
- AddError(
- "image format must be one of the texel formats specified for storage "
- "textues in https://gpuweb.github.io/gpuweb/wgsl/#texel-formats",
- t->source);
- return false;
- }
- return true;
+ if (!IsValidStorageTextureTexelFormat(t->format)) {
+ AddError(
+ "image format must be one of the texel formats specified for storage "
+ "textues in https://gpuweb.github.io/gpuweb/wgsl/#texel-formats",
+ t->source);
+ return false;
+ }
+ return true;
}
bool Validator::VariableConstructorOrCast(const ast::Variable* var,
ast::StorageClass storage_class,
const sem::Type* storage_ty,
const sem::Type* rhs_ty) const {
- auto* value_type = rhs_ty->UnwrapRef(); // Implicit load of RHS
+ auto* value_type = rhs_ty->UnwrapRef(); // Implicit load of RHS
- // Value type has to match storage type
- if (storage_ty != value_type) {
- std::string decl = var->is_const ? "let" : "var";
- AddError("cannot initialize " + decl + " of type '" +
- sem_.TypeNameOf(storage_ty) + "' with value of type '" +
- sem_.TypeNameOf(rhs_ty) + "'",
- var->source);
- return false;
- }
-
- if (!var->is_const) {
- switch (storage_class) {
- case ast::StorageClass::kPrivate:
- case ast::StorageClass::kFunction:
- break; // Allowed an initializer
- default:
- // https://gpuweb.github.io/gpuweb/wgsl/#var-and-let
- // Optionally has an initializer expression, if the variable is in the
- // private or function storage classes.
- AddError("var of storage class '" +
- std::string(ast::ToString(storage_class)) +
- "' cannot have an initializer. var initializers are only "
- "supported for the storage classes "
- "'private' and 'function'",
+ // Value type has to match storage type
+ if (storage_ty != value_type) {
+ std::string decl = var->is_const ? "let" : "var";
+ AddError("cannot initialize " + decl + " of type '" + sem_.TypeNameOf(storage_ty) +
+ "' with value of type '" + sem_.TypeNameOf(rhs_ty) + "'",
var->source);
return false;
}
- }
- return true;
+ if (!var->is_const) {
+ switch (storage_class) {
+ case ast::StorageClass::kPrivate:
+ case ast::StorageClass::kFunction:
+ break; // Allowed an initializer
+ default:
+ // https://gpuweb.github.io/gpuweb/wgsl/#var-and-let
+ // Optionally has an initializer expression, if the variable is in the
+ // private or function storage classes.
+ AddError("var of storage class '" + std::string(ast::ToString(storage_class)) +
+ "' cannot have an initializer. var initializers are only "
+ "supported for the storage classes "
+ "'private' and 'function'",
+ var->source);
+ return false;
+ }
+ }
+
+ return true;
}
bool Validator::StorageClassLayout(const sem::Type* store_ty,
ast::StorageClass sc,
Source source,
ValidTypeStorageLayouts& layouts) const {
- // https://gpuweb.github.io/gpuweb/wgsl/#storage-class-layout-constraints
+ // https://gpuweb.github.io/gpuweb/wgsl/#storage-class-layout-constraints
- auto is_uniform_struct_or_array = [sc](const sem::Type* ty) {
- return sc == ast::StorageClass::kUniform &&
- ty->IsAnyOf<sem::Array, sem::Struct>();
- };
+ auto is_uniform_struct_or_array = [sc](const sem::Type* ty) {
+ return sc == ast::StorageClass::kUniform && ty->IsAnyOf<sem::Array, sem::Struct>();
+ };
- auto is_uniform_struct = [sc](const sem::Type* ty) {
- return sc == ast::StorageClass::kUniform && ty->Is<sem::Struct>();
- };
+ auto is_uniform_struct = [sc](const sem::Type* ty) {
+ return sc == ast::StorageClass::kUniform && ty->Is<sem::Struct>();
+ };
- auto required_alignment_of = [&](const sem::Type* ty) {
- uint32_t actual_align = ty->Align();
- uint32_t required_align = actual_align;
- if (is_uniform_struct_or_array(ty)) {
- required_align = utils::RoundUp(16u, actual_align);
+ auto required_alignment_of = [&](const sem::Type* ty) {
+ uint32_t actual_align = ty->Align();
+ uint32_t required_align = actual_align;
+ if (is_uniform_struct_or_array(ty)) {
+ required_align = utils::RoundUp(16u, actual_align);
+ }
+ return required_align;
+ };
+
+ auto member_name_of = [this](const sem::StructMember* sm) {
+ return symbols_.NameFor(sm->Declaration()->symbol);
+ };
+
+ // Cache result of type + storage class pair.
+ if (!layouts.emplace(store_ty, sc).second) {
+ return true;
}
- return required_align;
- };
- auto member_name_of = [this](const sem::StructMember* sm) {
- return symbols_.NameFor(sm->Declaration()->symbol);
- };
+ if (!ast::IsHostShareable(sc)) {
+ return true;
+ }
- // Cache result of type + storage class pair.
- if (!layouts.emplace(store_ty, sc).second) {
+ if (auto* str = store_ty->As<sem::Struct>()) {
+ for (size_t i = 0; i < str->Members().size(); ++i) {
+ auto* const m = str->Members()[i];
+ uint32_t required_align = required_alignment_of(m->Type());
+
+ // Recurse into the member type.
+ if (!StorageClassLayout(m->Type(), sc, m->Declaration()->type->source, layouts)) {
+ AddNote("see layout of struct:\n" + str->Layout(symbols_),
+ str->Declaration()->source);
+ return false;
+ }
+
+ // Validate that member is at a valid byte offset
+ if (m->Offset() % required_align != 0) {
+ AddError("the offset of a struct member of type '" +
+ m->Type()->UnwrapRef()->FriendlyName(symbols_) +
+ "' in storage class '" + ast::ToString(sc) +
+ "' must be a multiple of " + std::to_string(required_align) +
+ " bytes, but '" + member_name_of(m) + "' is currently at offset " +
+ std::to_string(m->Offset()) + ". Consider setting @align(" +
+ std::to_string(required_align) + ") on this member",
+ m->Declaration()->source);
+
+ AddNote("see layout of struct:\n" + str->Layout(symbols_),
+ str->Declaration()->source);
+
+ if (auto* member_str = m->Type()->As<sem::Struct>()) {
+ AddNote("and layout of struct member:\n" + member_str->Layout(symbols_),
+ member_str->Declaration()->source);
+ }
+
+ return false;
+ }
+
+ // For uniform buffers, validate that the number of bytes between the
+ // previous member of type struct and the current is a multiple of 16
+ // bytes.
+ auto* const prev_member = (i == 0) ? nullptr : str->Members()[i - 1];
+ if (prev_member && is_uniform_struct(prev_member->Type())) {
+ const uint32_t prev_to_curr_offset = m->Offset() - prev_member->Offset();
+ if (prev_to_curr_offset % 16 != 0) {
+ AddError(
+ "uniform storage requires that the number of bytes between the "
+ "start of the previous member of type struct and the current "
+ "member be a multiple of 16 bytes, but there are currently " +
+ std::to_string(prev_to_curr_offset) + " bytes between '" +
+ member_name_of(prev_member) + "' and '" + member_name_of(m) +
+ "'. Consider setting @align(16) on this member",
+ m->Declaration()->source);
+
+ AddNote("see layout of struct:\n" + str->Layout(symbols_),
+ str->Declaration()->source);
+
+ auto* prev_member_str = prev_member->Type()->As<sem::Struct>();
+ AddNote("and layout of previous member struct:\n" +
+ prev_member_str->Layout(symbols_),
+ prev_member_str->Declaration()->source);
+ return false;
+ }
+ }
+ }
+ }
+
+ // For uniform buffer array members, validate that array elements are
+ // aligned to 16 bytes
+ if (auto* arr = store_ty->As<sem::Array>()) {
+ // Recurse into the element type.
+ // TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested
+ // element type here, but we can't easily get that from the semantic node.
+ // We should consider recursing through the AST type nodes instead.
+ if (!StorageClassLayout(arr->ElemType(), sc, source, layouts)) {
+ return false;
+ }
+
+ if (sc == ast::StorageClass::kUniform) {
+ // We already validated that this array member is itself aligned to 16
+ // bytes above, so we only need to validate that stride is a multiple
+ // of 16 bytes.
+ if (arr->Stride() % 16 != 0) {
+ // Since WGSL has no stride attribute, try to provide a useful hint
+ // for how the shader author can resolve the issue.
+ std::string hint;
+ if (arr->ElemType()->is_scalar()) {
+ hint =
+ "Consider using a vector or struct as the element type "
+ "instead.";
+ } else if (auto* vec = arr->ElemType()->As<sem::Vector>();
+ vec && vec->type()->Size() == 4) {
+ hint = "Consider using a vec4 instead.";
+ } else if (arr->ElemType()->Is<sem::Struct>()) {
+ hint =
+ "Consider using the @size attribute on the last struct "
+ "member.";
+ } else {
+ hint =
+ "Consider wrapping the element type in a struct and using "
+ "the "
+ "@size attribute.";
+ }
+ AddError(
+ "uniform storage requires that array elements be aligned to 16 "
+ "bytes, but array element alignment is currently " +
+ std::to_string(arr->Stride()) + ". " + hint,
+ source);
+ return false;
+ }
+ }
+ }
+
return true;
- }
-
- if (!ast::IsHostShareable(sc)) {
- return true;
- }
-
- if (auto* str = store_ty->As<sem::Struct>()) {
- for (size_t i = 0; i < str->Members().size(); ++i) {
- auto* const m = str->Members()[i];
- uint32_t required_align = required_alignment_of(m->Type());
-
- // Recurse into the member type.
- if (!StorageClassLayout(m->Type(), sc, m->Declaration()->type->source,
- layouts)) {
- AddNote("see layout of struct:\n" + str->Layout(symbols_),
- str->Declaration()->source);
- return false;
- }
-
- // Validate that member is at a valid byte offset
- if (m->Offset() % required_align != 0) {
- AddError("the offset of a struct member of type '" +
- m->Type()->UnwrapRef()->FriendlyName(symbols_) +
- "' in storage class '" + ast::ToString(sc) +
- "' must be a multiple of " +
- std::to_string(required_align) + " bytes, but '" +
- member_name_of(m) + "' is currently at offset " +
- std::to_string(m->Offset()) +
- ". Consider setting @align(" +
- std::to_string(required_align) + ") on this member",
- m->Declaration()->source);
-
- AddNote("see layout of struct:\n" + str->Layout(symbols_),
- str->Declaration()->source);
-
- if (auto* member_str = m->Type()->As<sem::Struct>()) {
- AddNote(
- "and layout of struct member:\n" + member_str->Layout(symbols_),
- member_str->Declaration()->source);
- }
-
- return false;
- }
-
- // For uniform buffers, validate that the number of bytes between the
- // previous member of type struct and the current is a multiple of 16
- // bytes.
- auto* const prev_member = (i == 0) ? nullptr : str->Members()[i - 1];
- if (prev_member && is_uniform_struct(prev_member->Type())) {
- const uint32_t prev_to_curr_offset =
- m->Offset() - prev_member->Offset();
- if (prev_to_curr_offset % 16 != 0) {
- AddError(
- "uniform storage requires that the number of bytes between the "
- "start of the previous member of type struct and the current "
- "member be a multiple of 16 bytes, but there are currently " +
- std::to_string(prev_to_curr_offset) + " bytes between '" +
- member_name_of(prev_member) + "' and '" + member_name_of(m) +
- "'. Consider setting @align(16) on this member",
- m->Declaration()->source);
-
- AddNote("see layout of struct:\n" + str->Layout(symbols_),
- str->Declaration()->source);
-
- auto* prev_member_str = prev_member->Type()->As<sem::Struct>();
- AddNote("and layout of previous member struct:\n" +
- prev_member_str->Layout(symbols_),
- prev_member_str->Declaration()->source);
- return false;
- }
- }
- }
- }
-
- // For uniform buffer array members, validate that array elements are
- // aligned to 16 bytes
- if (auto* arr = store_ty->As<sem::Array>()) {
- // Recurse into the element type.
- // TODO(crbug.com/tint/1388): Ideally we'd pass the source for nested
- // element type here, but we can't easily get that from the semantic node.
- // We should consider recursing through the AST type nodes instead.
- if (!StorageClassLayout(arr->ElemType(), sc, source, layouts)) {
- return false;
- }
-
- if (sc == ast::StorageClass::kUniform) {
- // We already validated that this array member is itself aligned to 16
- // bytes above, so we only need to validate that stride is a multiple
- // of 16 bytes.
- if (arr->Stride() % 16 != 0) {
- // Since WGSL has no stride attribute, try to provide a useful hint
- // for how the shader author can resolve the issue.
- std::string hint;
- if (arr->ElemType()->is_scalar()) {
- hint =
- "Consider using a vector or struct as the element type "
- "instead.";
- } else if (auto* vec = arr->ElemType()->As<sem::Vector>();
- vec && vec->type()->Size() == 4) {
- hint = "Consider using a vec4 instead.";
- } else if (arr->ElemType()->Is<sem::Struct>()) {
- hint =
- "Consider using the @size attribute on the last struct "
- "member.";
- } else {
- hint =
- "Consider wrapping the element type in a struct and using "
- "the "
- "@size attribute.";
- }
- AddError(
- "uniform storage requires that array elements be aligned to 16 "
- "bytes, but array element alignment is currently " +
- std::to_string(arr->Stride()) + ". " + hint,
- source);
- return false;
- }
- }
- }
-
- return true;
}
bool Validator::StorageClassLayout(const sem::Variable* var,
ValidTypeStorageLayouts& layouts) const {
- if (auto* str = var->Type()->UnwrapRef()->As<sem::Struct>()) {
- if (!StorageClassLayout(str, var->StorageClass(),
- str->Declaration()->source, layouts)) {
- AddNote("see declaration of variable", var->Declaration()->source);
- return false;
+ if (auto* str = var->Type()->UnwrapRef()->As<sem::Struct>()) {
+ if (!StorageClassLayout(str, var->StorageClass(), str->Declaration()->source, layouts)) {
+ AddNote("see declaration of variable", var->Declaration()->source);
+ return false;
+ }
+ } else {
+ Source source = var->Declaration()->source;
+ if (var->Declaration()->type) {
+ source = var->Declaration()->type->source;
+ }
+ if (!StorageClassLayout(var->Type()->UnwrapRef(), var->StorageClass(), source, layouts)) {
+ return false;
+ }
}
- } else {
- Source source = var->Declaration()->source;
- if (var->Declaration()->type) {
- source = var->Declaration()->type->source;
- }
- if (!StorageClassLayout(var->Type()->UnwrapRef(), var->StorageClass(),
- source, layouts)) {
- return false;
- }
- }
- return true;
+ return true;
}
bool Validator::GlobalVariable(
const sem::Variable* var,
std::unordered_map<uint32_t, const sem::Variable*> constant_ids,
- std::unordered_map<const sem::Type*, const Source&> atomic_composite_info)
- const {
- auto* decl = var->Declaration();
- if (!NoDuplicateAttributes(decl->attributes)) {
- return false;
- }
-
- for (auto* attr : decl->attributes) {
- if (decl->is_const) {
- if (auto* id_attr = attr->As<ast::IdAttribute>()) {
- uint32_t id = id_attr->value;
- auto it = constant_ids.find(id);
- if (it != constant_ids.end() && it->second != var) {
- AddError("pipeline constant IDs must be unique", attr->source);
- AddNote("a pipeline constant with an ID of " + std::to_string(id) +
- " was previously declared "
- "here:",
- ast::GetAttribute<ast::IdAttribute>(
- it->second->Declaration()->attributes)
- ->source);
- return false;
- }
- if (id > 65535) {
- AddError("pipeline constant IDs must be between 0 and 65535",
- attr->source);
- return false;
- }
- } else {
- AddError("attribute is not valid for constants", attr->source);
+ std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const {
+ auto* decl = var->Declaration();
+ if (!NoDuplicateAttributes(decl->attributes)) {
return false;
- }
- } else {
- bool is_shader_io_attribute =
- attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
- ast::InvariantAttribute, ast::LocationAttribute>();
- bool has_io_storage_class =
- var->StorageClass() == ast::StorageClass::kInput ||
- var->StorageClass() == ast::StorageClass::kOutput;
- if (!(attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
- ast::InternalAttribute>()) &&
- (!is_shader_io_attribute || !has_io_storage_class)) {
- AddError("attribute is not valid for variables", attr->source);
- return false;
- }
}
- }
- if (var->StorageClass() == ast::StorageClass::kFunction) {
- AddError(
- "variables declared at module scope must not be in the function "
- "storage class",
- decl->source);
- return false;
- }
+ for (auto* attr : decl->attributes) {
+ if (decl->is_const) {
+ if (auto* id_attr = attr->As<ast::IdAttribute>()) {
+ uint32_t id = id_attr->value;
+ auto it = constant_ids.find(id);
+ if (it != constant_ids.end() && it->second != var) {
+ AddError("pipeline constant IDs must be unique", attr->source);
+ AddNote(
+ "a pipeline constant with an ID of " + std::to_string(id) +
+ " was previously declared "
+ "here:",
+ ast::GetAttribute<ast::IdAttribute>(it->second->Declaration()->attributes)
+ ->source);
+ return false;
+ }
+ if (id > 65535) {
+ AddError("pipeline constant IDs must be between 0 and 65535", attr->source);
+ return false;
+ }
+ } else {
+ AddError("attribute is not valid for constants", attr->source);
+ return false;
+ }
+ } else {
+ bool is_shader_io_attribute =
+ attr->IsAnyOf<ast::BuiltinAttribute, ast::InterpolateAttribute,
+ ast::InvariantAttribute, ast::LocationAttribute>();
+ bool has_io_storage_class = var->StorageClass() == ast::StorageClass::kInput ||
+ var->StorageClass() == ast::StorageClass::kOutput;
+ if (!(attr->IsAnyOf<ast::BindingAttribute, ast::GroupAttribute,
+ ast::InternalAttribute>()) &&
+ (!is_shader_io_attribute || !has_io_storage_class)) {
+ AddError("attribute is not valid for variables", attr->source);
+ return false;
+ }
+ }
+ }
- auto binding_point = decl->BindingPoint();
- switch (var->StorageClass()) {
- case ast::StorageClass::kUniform:
- case ast::StorageClass::kStorage:
- case ast::StorageClass::kHandle: {
- // https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
- // Each resource variable must be declared with both group and binding
- // attributes.
- if (!binding_point) {
+ if (var->StorageClass() == ast::StorageClass::kFunction) {
AddError(
- "resource variables require @group and @binding "
- "attributes",
+ "variables declared at module scope must not be in the function "
+ "storage class",
decl->source);
return false;
- }
- break;
}
- default:
- if (binding_point.binding || binding_point.group) {
- // https://gpuweb.github.io/gpuweb/wgsl/#attribute-binding
- // Must only be applied to a resource variable
- AddError(
- "non-resource variables must not have @group or @binding "
- "attributes",
- decl->source);
+
+ auto binding_point = decl->BindingPoint();
+ switch (var->StorageClass()) {
+ case ast::StorageClass::kUniform:
+ case ast::StorageClass::kStorage:
+ case ast::StorageClass::kHandle: {
+ // https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
+ // Each resource variable must be declared with both group and binding
+ // attributes.
+ if (!binding_point) {
+ AddError(
+ "resource variables require @group and @binding "
+ "attributes",
+ decl->source);
+ return false;
+ }
+ break;
+ }
+ default:
+ if (binding_point.binding || binding_point.group) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#attribute-binding
+ // Must only be applied to a resource variable
+ AddError(
+ "non-resource variables must not have @group or @binding "
+ "attributes",
+ decl->source);
+ return false;
+ }
+ }
+
+ // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
+ // The access mode always has a default, and except for variables in the
+ // storage storage class, must not be written.
+ if (var->StorageClass() != ast::StorageClass::kStorage &&
+ decl->declared_access != ast::Access::kUndefined) {
+ AddError("only variables in <storage> storage class may declare an access mode",
+ decl->source);
return false;
- }
- }
-
- // https://gpuweb.github.io/gpuweb/wgsl/#variable-declaration
- // The access mode always has a default, and except for variables in the
- // storage storage class, must not be written.
- if (var->StorageClass() != ast::StorageClass::kStorage &&
- decl->declared_access != ast::Access::kUndefined) {
- AddError(
- "only variables in <storage> storage class may declare an access mode",
- decl->source);
- return false;
- }
-
- if (!decl->is_const) {
- if (!AtomicVariable(var, atomic_composite_info)) {
- return false;
}
- }
- return Variable(var);
+ if (!decl->is_const) {
+ if (!AtomicVariable(var, atomic_composite_info)) {
+ return false;
+ }
+ }
+
+ return Variable(var);
}
// https://gpuweb.github.io/gpuweb/wgsl/#atomic-types
@@ -612,1635 +588,1526 @@
// class or by storage buffer variables with a read_write access mode.
bool Validator::AtomicVariable(
const sem::Variable* var,
- std::unordered_map<const sem::Type*, const Source&> atomic_composite_info)
- const {
- auto sc = var->StorageClass();
- auto* decl = var->Declaration();
- auto access = var->Access();
- auto* type = var->Type()->UnwrapRef();
- auto source = decl->type ? decl->type->source : decl->source;
+ std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const {
+ auto sc = var->StorageClass();
+ auto* decl = var->Declaration();
+ auto access = var->Access();
+ auto* type = var->Type()->UnwrapRef();
+ auto source = decl->type ? decl->type->source : decl->source;
- if (type->Is<sem::Atomic>()) {
- if (sc != ast::StorageClass::kWorkgroup &&
- sc != ast::StorageClass::kStorage) {
- AddError(
- "atomic variables must have <storage> or <workgroup> storage class",
- source);
- return false;
+ if (type->Is<sem::Atomic>()) {
+ if (sc != ast::StorageClass::kWorkgroup && sc != ast::StorageClass::kStorage) {
+ AddError("atomic variables must have <storage> or <workgroup> storage class", source);
+ return false;
+ }
+ } else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
+ auto found = atomic_composite_info.find(type);
+ if (found != atomic_composite_info.end()) {
+ if (sc != ast::StorageClass::kStorage && sc != ast::StorageClass::kWorkgroup) {
+ AddError("atomic variables must have <storage> or <workgroup> storage class",
+ source);
+ AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
+ found->second);
+ return false;
+ } else if (sc == ast::StorageClass::kStorage && access != ast::Access::kReadWrite) {
+ AddError(
+ "atomic variables in <storage> storage class must have read_write "
+ "access mode",
+ source);
+ AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) + "' is declared here",
+ found->second);
+ return false;
+ }
+ }
}
- } else if (type->IsAnyOf<sem::Struct, sem::Array>()) {
- auto found = atomic_composite_info.find(type);
- if (found != atomic_composite_info.end()) {
- if (sc != ast::StorageClass::kStorage &&
- sc != ast::StorageClass::kWorkgroup) {
- AddError(
- "atomic variables must have <storage> or <workgroup> storage class",
- source);
- AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) +
- "' is declared here",
- found->second);
- return false;
- } else if (sc == ast::StorageClass::kStorage &&
- access != ast::Access::kReadWrite) {
- AddError(
- "atomic variables in <storage> storage class must have read_write "
- "access mode",
- source);
- AddNote("atomic sub-type of '" + sem_.TypeNameOf(type) +
- "' is declared here",
- found->second);
- return false;
- }
- }
- }
- return true;
+ return true;
}
bool Validator::Variable(const sem::Variable* var) const {
- auto* decl = var->Declaration();
- auto* storage_ty = var->Type()->UnwrapRef();
+ auto* decl = var->Declaration();
+ auto* storage_ty = var->Type()->UnwrapRef();
- if (var->Is<sem::GlobalVariable>()) {
- auto name = symbols_.NameFor(decl->symbol);
- if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
- auto* kind = var->Declaration()->is_const ? "let" : "var";
- AddError(
- "'" + name +
- "' is a builtin and cannot be redeclared as a module-scope " +
- kind,
- decl->source);
- return false;
- }
- }
-
- if (!decl->is_const && !IsStorable(storage_ty)) {
- AddError(
- sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a var",
- decl->source);
- return false;
- }
-
- if (decl->is_const && !var->Is<sem::Parameter>() &&
- !(storage_ty->IsConstructible() || storage_ty->Is<sem::Pointer>())) {
- AddError(
- sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a let",
- decl->source);
- return false;
- }
-
- if (auto* r = storage_ty->As<sem::MultisampledTexture>()) {
- if (r->dim() != ast::TextureDimension::k2d) {
- AddError("only 2d multisampled textures are supported", decl->source);
- return false;
+ if (var->Is<sem::GlobalVariable>()) {
+ auto name = symbols_.NameFor(decl->symbol);
+ if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+ auto* kind = var->Declaration()->is_const ? "let" : "var";
+ AddError(
+ "'" + name + "' is a builtin and cannot be redeclared as a module-scope " + kind,
+ decl->source);
+ return false;
+ }
}
- if (!r->type()->UnwrapRef()->is_numeric_scalar()) {
- AddError("texture_multisampled_2d<type>: type must be f32, i32 or u32",
- decl->source);
- return false;
+ if (!decl->is_const && !IsStorable(storage_ty)) {
+ AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a var",
+ decl->source);
+ return false;
}
- }
- if (var->Is<sem::LocalVariable>() && !decl->is_const &&
- IsValidationEnabled(decl->attributes,
- ast::DisabledValidation::kIgnoreStorageClass)) {
- if (!var->Type()->UnwrapRef()->IsConstructible()) {
- AddError("function variable must have a constructible type",
- decl->type ? decl->type->source : decl->source);
- return false;
+ if (decl->is_const && !var->Is<sem::Parameter>() &&
+ !(storage_ty->IsConstructible() || storage_ty->Is<sem::Pointer>())) {
+ AddError(sem_.TypeNameOf(storage_ty) + " cannot be used as the type of a let",
+ decl->source);
+ return false;
}
- }
- if (storage_ty->is_handle() &&
- decl->declared_storage_class != ast::StorageClass::kNone) {
- // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
- // If the store type is a texture type or a sampler type, then the
- // variable declaration must not have a storage class attribute. The
- // storage class will always be handle.
- AddError("variables of type '" + sem_.TypeNameOf(storage_ty) +
- "' must not have a storage class",
- decl->source);
- return false;
- }
+ if (auto* r = storage_ty->As<sem::MultisampledTexture>()) {
+ if (r->dim() != ast::TextureDimension::k2d) {
+ AddError("only 2d multisampled textures are supported", decl->source);
+ return false;
+ }
- if (IsValidationEnabled(decl->attributes,
- ast::DisabledValidation::kIgnoreStorageClass) &&
- (decl->declared_storage_class == ast::StorageClass::kInput ||
- decl->declared_storage_class == ast::StorageClass::kOutput)) {
- AddError("invalid use of input/output storage class", decl->source);
- return false;
- }
- return true;
+ if (!r->type()->UnwrapRef()->is_numeric_scalar()) {
+ AddError("texture_multisampled_2d<type>: type must be f32, i32 or u32", decl->source);
+ return false;
+ }
+ }
+
+ if (var->Is<sem::LocalVariable>() && !decl->is_const &&
+ IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreStorageClass)) {
+ if (!var->Type()->UnwrapRef()->IsConstructible()) {
+ AddError("function variable must have a constructible type",
+ decl->type ? decl->type->source : decl->source);
+ return false;
+ }
+ }
+
+ if (storage_ty->is_handle() && decl->declared_storage_class != ast::StorageClass::kNone) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#module-scope-variables
+ // If the store type is a texture type or a sampler type, then the
+ // variable declaration must not have a storage class attribute. The
+ // storage class will always be handle.
+ AddError(
+ "variables of type '" + sem_.TypeNameOf(storage_ty) + "' must not have a storage class",
+ decl->source);
+ return false;
+ }
+
+ if (IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreStorageClass) &&
+ (decl->declared_storage_class == ast::StorageClass::kInput ||
+ decl->declared_storage_class == ast::StorageClass::kOutput)) {
+ AddError("invalid use of input/output storage class", decl->source);
+ return false;
+ }
+ return true;
}
-bool Validator::FunctionParameter(const ast::Function* func,
- const sem::Variable* var) const {
- if (!Variable(var)) {
- return false;
- }
-
- auto* decl = var->Declaration();
-
- for (auto* attr : decl->attributes) {
- if (!func->IsEntryPoint() && !attr->Is<ast::InternalAttribute>()) {
- AddError("attribute is not valid for non-entry point function parameters",
- attr->source);
- return false;
- } else if (!attr->IsAnyOf<ast::BuiltinAttribute, ast::InvariantAttribute,
- ast::LocationAttribute, ast::InterpolateAttribute,
- ast::InternalAttribute>() &&
- (IsValidationEnabled(
- decl->attributes,
- ast::DisabledValidation::kEntryPointParameter) &&
- IsValidationEnabled(
- decl->attributes,
- ast::DisabledValidation::
- kIgnoreConstructibleFunctionParameter))) {
- AddError("attribute is not valid for function parameters", attr->source);
- return false;
+bool Validator::FunctionParameter(const ast::Function* func, const sem::Variable* var) const {
+ if (!Variable(var)) {
+ return false;
}
- }
- if (auto* ref = var->Type()->As<sem::Pointer>()) {
- auto sc = ref->StorageClass();
- if (!(sc == ast::StorageClass::kFunction ||
- sc == ast::StorageClass::kPrivate ||
- sc == ast::StorageClass::kWorkgroup) &&
- IsValidationEnabled(decl->attributes,
- ast::DisabledValidation::kIgnoreStorageClass)) {
- std::stringstream ss;
- ss << "function parameter of pointer type cannot be in '" << sc
- << "' storage class";
- AddError(ss.str(), decl->source);
- return false;
+ auto* decl = var->Declaration();
+
+ for (auto* attr : decl->attributes) {
+ if (!func->IsEntryPoint() && !attr->Is<ast::InternalAttribute>()) {
+ AddError("attribute is not valid for non-entry point function parameters",
+ attr->source);
+ return false;
+ } else if (!attr->IsAnyOf<ast::BuiltinAttribute, ast::InvariantAttribute,
+ ast::LocationAttribute, ast::InterpolateAttribute,
+ ast::InternalAttribute>() &&
+ (IsValidationEnabled(decl->attributes,
+ ast::DisabledValidation::kEntryPointParameter) &&
+ IsValidationEnabled(
+ decl->attributes,
+ ast::DisabledValidation::kIgnoreConstructibleFunctionParameter))) {
+ AddError("attribute is not valid for function parameters", attr->source);
+ return false;
+ }
}
- }
- if (IsPlain(var->Type())) {
- if (!var->Type()->IsConstructible() &&
- IsValidationEnabled(
- decl->attributes,
- ast::DisabledValidation::kIgnoreConstructibleFunctionParameter)) {
- AddError("store type of function parameter must be a constructible type",
- decl->source);
- return false;
+ if (auto* ref = var->Type()->As<sem::Pointer>()) {
+ auto sc = ref->StorageClass();
+ if (!(sc == ast::StorageClass::kFunction || sc == ast::StorageClass::kPrivate ||
+ sc == ast::StorageClass::kWorkgroup) &&
+ IsValidationEnabled(decl->attributes, ast::DisabledValidation::kIgnoreStorageClass)) {
+ std::stringstream ss;
+ ss << "function parameter of pointer type cannot be in '" << sc << "' storage class";
+ AddError(ss.str(), decl->source);
+ return false;
+ }
}
- } else if (!var->Type()
- ->IsAnyOf<sem::Texture, sem::Sampler, sem::Pointer>()) {
- AddError("store type of function parameter cannot be " +
- sem_.TypeNameOf(var->Type()),
- decl->source);
- return false;
- }
- return true;
+ if (IsPlain(var->Type())) {
+ if (!var->Type()->IsConstructible() &&
+ IsValidationEnabled(decl->attributes,
+ ast::DisabledValidation::kIgnoreConstructibleFunctionParameter)) {
+ AddError("store type of function parameter must be a constructible type", decl->source);
+ return false;
+ }
+ } else if (!var->Type()->IsAnyOf<sem::Texture, sem::Sampler, sem::Pointer>()) {
+ AddError("store type of function parameter cannot be " + sem_.TypeNameOf(var->Type()),
+ decl->source);
+ return false;
+ }
+
+ return true;
}
bool Validator::BuiltinAttribute(const ast::BuiltinAttribute* attr,
const sem::Type* storage_ty,
ast::PipelineStage stage,
const bool is_input) const {
- auto* type = storage_ty->UnwrapRef();
- std::stringstream stage_name;
- stage_name << stage;
- bool is_stage_mismatch = false;
- bool is_output = !is_input;
- switch (attr->builtin) {
- case ast::Builtin::kPosition:
- if (stage != ast::PipelineStage::kNone &&
- !((is_input && stage == ast::PipelineStage::kFragment) ||
- (is_output && stage == ast::PipelineStage::kVertex))) {
- is_stage_mismatch = true;
- }
- if (!(type->is_float_vector() && type->As<sem::Vector>()->Width() == 4)) {
- AddError("store type of " + attr_to_str(attr) + " must be 'vec4<f32>'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kGlobalInvocationId:
- case ast::Builtin::kLocalInvocationId:
- case ast::Builtin::kNumWorkgroups:
- case ast::Builtin::kWorkgroupId:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kCompute && is_input)) {
- is_stage_mismatch = true;
- }
- if (!(type->is_unsigned_integer_vector() &&
- type->As<sem::Vector>()->Width() == 3)) {
- AddError("store type of " + attr_to_str(attr) + " must be 'vec3<u32>'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kFragDepth:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kFragment && !is_input)) {
- is_stage_mismatch = true;
- }
- if (!type->Is<sem::F32>()) {
- AddError("store type of " + attr_to_str(attr) + " must be 'f32'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kFrontFacing:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kFragment && is_input)) {
- is_stage_mismatch = true;
- }
- if (!type->Is<sem::Bool>()) {
- AddError("store type of " + attr_to_str(attr) + " must be 'bool'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kLocalInvocationIndex:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kCompute && is_input)) {
- is_stage_mismatch = true;
- }
- if (!type->Is<sem::U32>()) {
- AddError("store type of " + attr_to_str(attr) + " must be 'u32'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kVertexIndex:
- case ast::Builtin::kInstanceIndex:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kVertex && is_input)) {
- is_stage_mismatch = true;
- }
- if (!type->Is<sem::U32>()) {
- AddError("store type of " + attr_to_str(attr) + " must be 'u32'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kSampleMask:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kFragment)) {
- is_stage_mismatch = true;
- }
- if (!type->Is<sem::U32>()) {
- AddError("store type of " + attr_to_str(attr) + " must be 'u32'",
- attr->source);
- return false;
- }
- break;
- case ast::Builtin::kSampleIndex:
- if (stage != ast::PipelineStage::kNone &&
- !(stage == ast::PipelineStage::kFragment && is_input)) {
- is_stage_mismatch = true;
- }
- if (!type->Is<sem::U32>()) {
- AddError("store type of " + attr_to_str(attr) + " must be 'u32'",
- attr->source);
- return false;
- }
- break;
- default:
- break;
- }
+ auto* type = storage_ty->UnwrapRef();
+ std::stringstream stage_name;
+ stage_name << stage;
+ bool is_stage_mismatch = false;
+ bool is_output = !is_input;
+ switch (attr->builtin) {
+ case ast::Builtin::kPosition:
+ if (stage != ast::PipelineStage::kNone &&
+ !((is_input && stage == ast::PipelineStage::kFragment) ||
+ (is_output && stage == ast::PipelineStage::kVertex))) {
+ is_stage_mismatch = true;
+ }
+ if (!(type->is_float_vector() && type->As<sem::Vector>()->Width() == 4)) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'vec4<f32>'",
+ attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kGlobalInvocationId:
+ case ast::Builtin::kLocalInvocationId:
+ case ast::Builtin::kNumWorkgroups:
+ case ast::Builtin::kWorkgroupId:
+ if (stage != ast::PipelineStage::kNone &&
+ !(stage == ast::PipelineStage::kCompute && is_input)) {
+ is_stage_mismatch = true;
+ }
+ if (!(type->is_unsigned_integer_vector() && type->As<sem::Vector>()->Width() == 3)) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'vec3<u32>'",
+ attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kFragDepth:
+ if (stage != ast::PipelineStage::kNone &&
+ !(stage == ast::PipelineStage::kFragment && !is_input)) {
+ is_stage_mismatch = true;
+ }
+ if (!type->Is<sem::F32>()) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'f32'", attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kFrontFacing:
+ if (stage != ast::PipelineStage::kNone &&
+ !(stage == ast::PipelineStage::kFragment && is_input)) {
+ is_stage_mismatch = true;
+ }
+ if (!type->Is<sem::Bool>()) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'bool'", attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kLocalInvocationIndex:
+ if (stage != ast::PipelineStage::kNone &&
+ !(stage == ast::PipelineStage::kCompute && is_input)) {
+ is_stage_mismatch = true;
+ }
+ if (!type->Is<sem::U32>()) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kVertexIndex:
+ case ast::Builtin::kInstanceIndex:
+ if (stage != ast::PipelineStage::kNone &&
+ !(stage == ast::PipelineStage::kVertex && is_input)) {
+ is_stage_mismatch = true;
+ }
+ if (!type->Is<sem::U32>()) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kSampleMask:
+ if (stage != ast::PipelineStage::kNone && !(stage == ast::PipelineStage::kFragment)) {
+ is_stage_mismatch = true;
+ }
+ if (!type->Is<sem::U32>()) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
+ return false;
+ }
+ break;
+ case ast::Builtin::kSampleIndex:
+ if (stage != ast::PipelineStage::kNone &&
+ !(stage == ast::PipelineStage::kFragment && is_input)) {
+ is_stage_mismatch = true;
+ }
+ if (!type->Is<sem::U32>()) {
+ AddError("store type of " + attr_to_str(attr) + " must be 'u32'", attr->source);
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
- if (is_stage_mismatch) {
- AddError(attr_to_str(attr) + " cannot be used in " +
- (is_input ? "input of " : "output of ") + stage_name.str() +
- " pipeline stage",
- attr->source);
- return false;
- }
+ if (is_stage_mismatch) {
+ AddError(attr_to_str(attr) + " cannot be used in " +
+ (is_input ? "input of " : "output of ") + stage_name.str() + " pipeline stage",
+ attr->source);
+ return false;
+ }
- return true;
+ return true;
}
bool Validator::InterpolateAttribute(const ast::InterpolateAttribute* attr,
const sem::Type* storage_ty) const {
- auto* type = storage_ty->UnwrapRef();
+ auto* type = storage_ty->UnwrapRef();
- if (type->is_integer_scalar_or_vector() &&
- attr->type != ast::InterpolationType::kFlat) {
- AddError(
- "interpolation type must be 'flat' for integral user-defined IO types",
- attr->source);
- return false;
- }
-
- if (attr->type == ast::InterpolationType::kFlat &&
- attr->sampling != ast::InterpolationSampling::kNone) {
- AddError("flat interpolation attribute must not have a sampling parameter",
- attr->source);
- return false;
- }
-
- return true;
-}
-
-bool Validator::Function(const sem::Function* func,
- ast::PipelineStage stage) const {
- auto* decl = func->Declaration();
-
- auto name = symbols_.NameFor(decl->symbol);
- if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
- AddError(
- "'" + name + "' is a builtin and cannot be redeclared as a function",
- decl->source);
- return false;
- }
-
- for (auto* attr : decl->attributes) {
- if (attr->Is<ast::WorkgroupAttribute>()) {
- if (decl->PipelineStage() != ast::PipelineStage::kCompute) {
- AddError(
- "the workgroup_size attribute is only valid for compute stages",
- attr->source);
- return false;
- }
- } else if (!attr->IsAnyOf<ast::StageAttribute, ast::InternalAttribute>()) {
- AddError("attribute is not valid for functions", attr->source);
- return false;
- }
- }
-
- if (decl->params.size() > 255) {
- AddError("functions may declare at most 255 parameters", decl->source);
- return false;
- }
-
- for (size_t i = 0; i < decl->params.size(); i++) {
- if (!FunctionParameter(decl, func->Parameters()[i])) {
- return false;
- }
- }
-
- if (!func->ReturnType()->Is<sem::Void>()) {
- if (!func->ReturnType()->IsConstructible()) {
- AddError("function return type must be a constructible type",
- decl->return_type->source);
- return false;
- }
-
- if (decl->body) {
- sem::Behaviors behaviors{sem::Behavior::kNext};
- if (auto* last = decl->body->Last()) {
- behaviors = sem_.Get(last)->Behaviors();
- }
- if (behaviors.Contains(sem::Behavior::kNext)) {
- AddError("missing return at end of function", decl->source);
- return false;
- }
- } else if (IsValidationEnabled(
- decl->attributes,
- ast::DisabledValidation::kFunctionHasNoBody)) {
- TINT_ICE(Resolver, diagnostics_)
- << "Function " << symbols_.NameFor(decl->symbol) << " has no body";
- }
-
- for (auto* attr : decl->return_type_attributes) {
- if (!decl->IsEntryPoint()) {
- AddError(
- "attribute is not valid for non-entry point function return types",
- attr->source);
- return false;
- }
- if (!attr->IsAnyOf<ast::BuiltinAttribute, ast::InternalAttribute,
- ast::LocationAttribute, ast::InterpolateAttribute,
- ast::InvariantAttribute>() &&
- (IsValidationEnabled(decl->attributes,
- ast::DisabledValidation::kEntryPointParameter) &&
- IsValidationEnabled(decl->attributes,
- ast::DisabledValidation::
- kIgnoreConstructibleFunctionParameter))) {
- AddError("attribute is not valid for entry point return types",
+ if (type->is_integer_scalar_or_vector() && attr->type != ast::InterpolationType::kFlat) {
+ AddError("interpolation type must be 'flat' for integral user-defined IO types",
attr->source);
return false;
- }
}
- }
- if (decl->IsEntryPoint()) {
- if (!EntryPoint(func, stage)) {
- return false;
- }
- }
-
- // https://www.w3.org/TR/WGSL/#behaviors-rules
- // a function behavior is always one of {}, {Next}, {Discard}, or
- // {Next, Discard}.
- if (func->Behaviors() != sem::Behaviors{} && // NOLINT: bad warning
- func->Behaviors() != sem::Behavior::kNext &&
- func->Behaviors() != sem::Behavior::kDiscard &&
- func->Behaviors() != sem::Behaviors{sem::Behavior::kNext, //
- sem::Behavior::kDiscard}) {
- TINT_ICE(Resolver, diagnostics_)
- << "function '" << name << "' behaviors are: " << func->Behaviors();
- }
-
- return true;
-}
-
-bool Validator::EntryPoint(const sem::Function* func,
- ast::PipelineStage stage) const {
- auto* decl = func->Declaration();
-
- // Use a lambda to validate the entry point attributes for a type.
- // Persistent state is used to track which builtins and locations have
- // already been seen, in order to catch conflicts.
- // TODO(jrprice): This state could be stored in sem::Function instead, and
- // then passed to sem::Function since it would be useful there too.
- std::unordered_set<ast::Builtin> builtins;
- std::unordered_set<uint32_t> locations;
- enum class ParamOrRetType {
- kParameter,
- kReturnType,
- };
-
- // Inner lambda that is applied to a type and all of its members.
- auto validate_entry_point_attributes_inner = [&](const ast::AttributeList&
- attrs,
- const sem::Type* ty,
- Source source,
- ParamOrRetType param_or_ret,
- bool is_struct_member) {
- // Scan attributes for pipeline IO attributes.
- // Check for overlap with attributes that have been seen previously.
- const ast::Attribute* pipeline_io_attribute = nullptr;
- const ast::InterpolateAttribute* interpolate_attribute = nullptr;
- const ast::InvariantAttribute* invariant_attribute = nullptr;
- for (auto* attr : attrs) {
- auto is_invalid_compute_shader_attribute = false;
-
- if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
- if (pipeline_io_attribute) {
- AddError("multiple entry point IO attributes", attr->source);
- AddNote("previously consumed " + attr_to_str(pipeline_io_attribute),
- pipeline_io_attribute->source);
- return false;
- }
- pipeline_io_attribute = attr;
-
- if (builtins.count(builtin->builtin)) {
- AddError(attr_to_str(builtin) +
- " attribute appears multiple times as pipeline " +
- (param_or_ret == ParamOrRetType::kParameter ? "input"
- : "output"),
- decl->source);
- return false;
- }
-
- if (!BuiltinAttribute(
- builtin, ty, stage,
- /* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
- return false;
- }
- builtins.emplace(builtin->builtin);
- } else if (auto* location = attr->As<ast::LocationAttribute>()) {
- if (pipeline_io_attribute) {
- AddError("multiple entry point IO attributes", attr->source);
- AddNote("previously consumed " + attr_to_str(pipeline_io_attribute),
- pipeline_io_attribute->source);
- return false;
- }
- pipeline_io_attribute = attr;
-
- bool is_input = param_or_ret == ParamOrRetType::kParameter;
-
- if (!LocationAttribute(location, ty, locations, stage, source,
- is_input)) {
- return false;
- }
- } else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
- if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
- is_invalid_compute_shader_attribute = true;
- } else if (!InterpolateAttribute(interpolate, ty)) {
- return false;
- }
- interpolate_attribute = interpolate;
- } else if (auto* invariant = attr->As<ast::InvariantAttribute>()) {
- if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
- is_invalid_compute_shader_attribute = true;
- }
- invariant_attribute = invariant;
- }
- if (is_invalid_compute_shader_attribute) {
- std::string input_or_output =
- param_or_ret == ParamOrRetType::kParameter ? "inputs" : "output";
- AddError("attribute is not valid for compute shader " + input_or_output,
- attr->source);
+ if (attr->type == ast::InterpolationType::kFlat &&
+ attr->sampling != ast::InterpolationSampling::kNone) {
+ AddError("flat interpolation attribute must not have a sampling parameter", attr->source);
return false;
- }
- }
-
- if (IsValidationEnabled(attrs,
- ast::DisabledValidation::kEntryPointParameter)) {
- if (is_struct_member && ty->Is<sem::Struct>()) {
- AddError("nested structures cannot be used for entry point IO", source);
- return false;
- }
-
- if (!ty->Is<sem::Struct>() && !pipeline_io_attribute) {
- std::string err = "missing entry point IO attribute";
- if (!is_struct_member) {
- err +=
- (param_or_ret == ParamOrRetType::kParameter ? " on parameter"
- : " on return type");
- }
- AddError(err, source);
- return false;
- }
-
- if (pipeline_io_attribute &&
- pipeline_io_attribute->Is<ast::LocationAttribute>()) {
- if (ty->is_integer_scalar_or_vector() && !interpolate_attribute) {
- if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
- param_or_ret == ParamOrRetType::kReturnType) {
- AddError(
- "integral user-defined vertex outputs must have a flat "
- "interpolation attribute",
- source);
- return false;
- }
- if (decl->PipelineStage() == ast::PipelineStage::kFragment &&
- param_or_ret == ParamOrRetType::kParameter) {
- AddError(
- "integral user-defined fragment inputs must have a flat "
- "interpolation attribute",
- source);
- return false;
- }
- }
- }
-
- if (interpolate_attribute) {
- if (!pipeline_io_attribute ||
- !pipeline_io_attribute->Is<ast::LocationAttribute>()) {
- AddError("interpolate attribute must only be used with @location",
- interpolate_attribute->source);
- return false;
- }
- }
-
- if (invariant_attribute) {
- bool has_position = false;
- if (pipeline_io_attribute) {
- if (auto* builtin =
- pipeline_io_attribute->As<ast::BuiltinAttribute>()) {
- has_position = (builtin->builtin == ast::Builtin::kPosition);
- }
- }
- if (!has_position) {
- AddError(
- "invariant attribute must only be applied to a position "
- "builtin",
- invariant_attribute->source);
- return false;
- }
- }
- }
- return true;
- };
-
- // Outer lambda for validating the entry point attributes for a type.
- auto validate_entry_point_attributes = [&](const ast::AttributeList& attrs,
- const sem::Type* ty, Source source,
- ParamOrRetType param_or_ret) {
- if (!validate_entry_point_attributes_inner(attrs, ty, source, param_or_ret,
- /*is_struct_member*/ false)) {
- return false;
- }
-
- if (auto* str = ty->As<sem::Struct>()) {
- for (auto* member : str->Members()) {
- if (!validate_entry_point_attributes_inner(
- member->Declaration()->attributes, member->Type(),
- member->Declaration()->source, param_or_ret,
- /*is_struct_member*/ true)) {
- AddNote("while analysing entry point '" +
- symbols_.NameFor(decl->symbol) + "'",
- decl->source);
- return false;
- }
- }
}
return true;
- };
+}
- for (auto* param : func->Parameters()) {
- auto* param_decl = param->Declaration();
- if (!validate_entry_point_attributes(param_decl->attributes, param->Type(),
- param_decl->source,
- ParamOrRetType::kParameter)) {
- return false;
+bool Validator::Function(const sem::Function* func, ast::PipelineStage stage) const {
+ auto* decl = func->Declaration();
+
+ auto name = symbols_.NameFor(decl->symbol);
+ if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+ AddError("'" + name + "' is a builtin and cannot be redeclared as a function",
+ decl->source);
+ return false;
}
- }
- // Clear IO sets after parameter validation. Builtin and location attributes
- // in return types should be validated independently from those used in
- // parameters.
- builtins.clear();
- locations.clear();
-
- if (!func->ReturnType()->Is<sem::Void>()) {
- if (!validate_entry_point_attributes(decl->return_type_attributes,
- func->ReturnType(), decl->source,
- ParamOrRetType::kReturnType)) {
- return false;
- }
- }
-
- if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
- builtins.count(ast::Builtin::kPosition) == 0) {
- // Check module-scope variables, as the SPIR-V sanitizer generates these.
- bool found = false;
- for (auto* global : func->TransitivelyReferencedGlobals()) {
- if (auto* builtin = ast::GetAttribute<ast::BuiltinAttribute>(
- global->Declaration()->attributes)) {
- if (builtin->builtin == ast::Builtin::kPosition) {
- found = true;
- break;
+ for (auto* attr : decl->attributes) {
+ if (attr->Is<ast::WorkgroupAttribute>()) {
+ if (decl->PipelineStage() != ast::PipelineStage::kCompute) {
+ AddError("the workgroup_size attribute is only valid for compute stages",
+ attr->source);
+ return false;
+ }
+ } else if (!attr->IsAnyOf<ast::StageAttribute, ast::InternalAttribute>()) {
+ AddError("attribute is not valid for functions", attr->source);
+ return false;
}
- }
}
- if (!found) {
- AddError(
- "a vertex shader must include the 'position' builtin in its return "
- "type",
- decl->source);
- return false;
- }
- }
- if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
- if (!ast::HasAttribute<ast::WorkgroupAttribute>(decl->attributes)) {
- AddError(
- "a compute shader must include 'workgroup_size' in its "
- "attributes",
- decl->source);
- return false;
+ if (decl->params.size() > 255) {
+ AddError("functions may declare at most 255 parameters", decl->source);
+ return false;
}
- }
- // Validate there are no resource variable binding collisions
- std::unordered_map<sem::BindingPoint, const ast::Variable*> binding_points;
- for (auto* var : func->TransitivelyReferencedGlobals()) {
- auto* var_decl = var->Declaration();
- if (!var_decl->BindingPoint()) {
- continue;
+ for (size_t i = 0; i < decl->params.size(); i++) {
+ if (!FunctionParameter(decl, func->Parameters()[i])) {
+ return false;
+ }
}
- auto bp = var->BindingPoint();
- auto res = binding_points.emplace(bp, var_decl);
- if (!res.second &&
- IsValidationEnabled(decl->attributes,
- ast::DisabledValidation::kBindingPointCollision) &&
- IsValidationEnabled(res.first->second->attributes,
- ast::DisabledValidation::kBindingPointCollision)) {
- // https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
- // Bindings must not alias within a shader stage: two different
- // variables in the resource interface of a given shader must not have
- // the same group and binding values, when considered as a pair of
- // values.
- auto func_name = symbols_.NameFor(decl->symbol);
- AddError("entry point '" + func_name +
- "' references multiple variables that use the "
- "same resource binding @group(" +
- std::to_string(bp.group) + "), @binding(" +
- std::to_string(bp.binding) + ")",
- var_decl->source);
- AddNote("first resource binding usage declared here",
- res.first->second->source);
- return false;
- }
- }
- return true;
+ if (!func->ReturnType()->Is<sem::Void>()) {
+ if (!func->ReturnType()->IsConstructible()) {
+ AddError("function return type must be a constructible type",
+ decl->return_type->source);
+ return false;
+ }
+
+ if (decl->body) {
+ sem::Behaviors behaviors{sem::Behavior::kNext};
+ if (auto* last = decl->body->Last()) {
+ behaviors = sem_.Get(last)->Behaviors();
+ }
+ if (behaviors.Contains(sem::Behavior::kNext)) {
+ AddError("missing return at end of function", decl->source);
+ return false;
+ }
+ } else if (IsValidationEnabled(decl->attributes,
+ ast::DisabledValidation::kFunctionHasNoBody)) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "Function " << symbols_.NameFor(decl->symbol) << " has no body";
+ }
+
+ for (auto* attr : decl->return_type_attributes) {
+ if (!decl->IsEntryPoint()) {
+ AddError("attribute is not valid for non-entry point function return types",
+ attr->source);
+ return false;
+ }
+ if (!attr->IsAnyOf<ast::BuiltinAttribute, ast::InternalAttribute,
+ ast::LocationAttribute, ast::InterpolateAttribute,
+ ast::InvariantAttribute>() &&
+ (IsValidationEnabled(decl->attributes,
+ ast::DisabledValidation::kEntryPointParameter) &&
+ IsValidationEnabled(
+ decl->attributes,
+ ast::DisabledValidation::kIgnoreConstructibleFunctionParameter))) {
+ AddError("attribute is not valid for entry point return types", attr->source);
+ return false;
+ }
+ }
+ }
+
+ if (decl->IsEntryPoint()) {
+ if (!EntryPoint(func, stage)) {
+ return false;
+ }
+ }
+
+ // https://www.w3.org/TR/WGSL/#behaviors-rules
+ // a function behavior is always one of {}, {Next}, {Discard}, or
+ // {Next, Discard}.
+ if (func->Behaviors() != sem::Behaviors{} && // NOLINT: bad warning
+ func->Behaviors() != sem::Behavior::kNext && func->Behaviors() != sem::Behavior::kDiscard &&
+ func->Behaviors() != sem::Behaviors{sem::Behavior::kNext, //
+ sem::Behavior::kDiscard}) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "function '" << name << "' behaviors are: " << func->Behaviors();
+ }
+
+ return true;
+}
+
+bool Validator::EntryPoint(const sem::Function* func, ast::PipelineStage stage) const {
+ auto* decl = func->Declaration();
+
+ // Use a lambda to validate the entry point attributes for a type.
+ // Persistent state is used to track which builtins and locations have
+ // already been seen, in order to catch conflicts.
+ // TODO(jrprice): This state could be stored in sem::Function instead, and
+ // then passed to sem::Function since it would be useful there too.
+ std::unordered_set<ast::Builtin> builtins;
+ std::unordered_set<uint32_t> locations;
+ enum class ParamOrRetType {
+ kParameter,
+ kReturnType,
+ };
+
+ // Inner lambda that is applied to a type and all of its members.
+ auto validate_entry_point_attributes_inner = [&](const ast::AttributeList& attrs,
+ const sem::Type* ty, Source source,
+ ParamOrRetType param_or_ret,
+ bool is_struct_member) {
+ // Scan attributes for pipeline IO attributes.
+ // Check for overlap with attributes that have been seen previously.
+ const ast::Attribute* pipeline_io_attribute = nullptr;
+ const ast::InterpolateAttribute* interpolate_attribute = nullptr;
+ const ast::InvariantAttribute* invariant_attribute = nullptr;
+ for (auto* attr : attrs) {
+ auto is_invalid_compute_shader_attribute = false;
+
+ if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
+ if (pipeline_io_attribute) {
+ AddError("multiple entry point IO attributes", attr->source);
+ AddNote("previously consumed " + attr_to_str(pipeline_io_attribute),
+ pipeline_io_attribute->source);
+ return false;
+ }
+ pipeline_io_attribute = attr;
+
+ if (builtins.count(builtin->builtin)) {
+ AddError(attr_to_str(builtin) +
+ " attribute appears multiple times as pipeline " +
+ (param_or_ret == ParamOrRetType::kParameter ? "input" : "output"),
+ decl->source);
+ return false;
+ }
+
+ if (!BuiltinAttribute(builtin, ty, stage,
+ /* is_input */ param_or_ret == ParamOrRetType::kParameter)) {
+ return false;
+ }
+ builtins.emplace(builtin->builtin);
+ } else if (auto* location = attr->As<ast::LocationAttribute>()) {
+ if (pipeline_io_attribute) {
+ AddError("multiple entry point IO attributes", attr->source);
+ AddNote("previously consumed " + attr_to_str(pipeline_io_attribute),
+ pipeline_io_attribute->source);
+ return false;
+ }
+ pipeline_io_attribute = attr;
+
+ bool is_input = param_or_ret == ParamOrRetType::kParameter;
+
+ if (!LocationAttribute(location, ty, locations, stage, source, is_input)) {
+ return false;
+ }
+ } else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
+ if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
+ is_invalid_compute_shader_attribute = true;
+ } else if (!InterpolateAttribute(interpolate, ty)) {
+ return false;
+ }
+ interpolate_attribute = interpolate;
+ } else if (auto* invariant = attr->As<ast::InvariantAttribute>()) {
+ if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
+ is_invalid_compute_shader_attribute = true;
+ }
+ invariant_attribute = invariant;
+ }
+ if (is_invalid_compute_shader_attribute) {
+ std::string input_or_output =
+ param_or_ret == ParamOrRetType::kParameter ? "inputs" : "output";
+ AddError("attribute is not valid for compute shader " + input_or_output,
+ attr->source);
+ return false;
+ }
+ }
+
+ if (IsValidationEnabled(attrs, ast::DisabledValidation::kEntryPointParameter)) {
+ if (is_struct_member && ty->Is<sem::Struct>()) {
+ AddError("nested structures cannot be used for entry point IO", source);
+ return false;
+ }
+
+ if (!ty->Is<sem::Struct>() && !pipeline_io_attribute) {
+ std::string err = "missing entry point IO attribute";
+ if (!is_struct_member) {
+ err += (param_or_ret == ParamOrRetType::kParameter ? " on parameter"
+ : " on return type");
+ }
+ AddError(err, source);
+ return false;
+ }
+
+ if (pipeline_io_attribute && pipeline_io_attribute->Is<ast::LocationAttribute>()) {
+ if (ty->is_integer_scalar_or_vector() && !interpolate_attribute) {
+ if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
+ param_or_ret == ParamOrRetType::kReturnType) {
+ AddError(
+ "integral user-defined vertex outputs must have a flat "
+ "interpolation attribute",
+ source);
+ return false;
+ }
+ if (decl->PipelineStage() == ast::PipelineStage::kFragment &&
+ param_or_ret == ParamOrRetType::kParameter) {
+ AddError(
+ "integral user-defined fragment inputs must have a flat "
+ "interpolation attribute",
+ source);
+ return false;
+ }
+ }
+ }
+
+ if (interpolate_attribute) {
+ if (!pipeline_io_attribute ||
+ !pipeline_io_attribute->Is<ast::LocationAttribute>()) {
+ AddError("interpolate attribute must only be used with @location",
+ interpolate_attribute->source);
+ return false;
+ }
+ }
+
+ if (invariant_attribute) {
+ bool has_position = false;
+ if (pipeline_io_attribute) {
+ if (auto* builtin = pipeline_io_attribute->As<ast::BuiltinAttribute>()) {
+ has_position = (builtin->builtin == ast::Builtin::kPosition);
+ }
+ }
+ if (!has_position) {
+ AddError(
+ "invariant attribute must only be applied to a position "
+ "builtin",
+ invariant_attribute->source);
+ return false;
+ }
+ }
+ }
+ return true;
+ };
+
+ // Outer lambda for validating the entry point attributes for a type.
+ auto validate_entry_point_attributes = [&](const ast::AttributeList& attrs, const sem::Type* ty,
+ Source source, ParamOrRetType param_or_ret) {
+ if (!validate_entry_point_attributes_inner(attrs, ty, source, param_or_ret,
+ /*is_struct_member*/ false)) {
+ return false;
+ }
+
+ if (auto* str = ty->As<sem::Struct>()) {
+ for (auto* member : str->Members()) {
+ if (!validate_entry_point_attributes_inner(
+ member->Declaration()->attributes, member->Type(),
+ member->Declaration()->source, param_or_ret,
+ /*is_struct_member*/ true)) {
+ AddNote("while analysing entry point '" + symbols_.NameFor(decl->symbol) + "'",
+ decl->source);
+ return false;
+ }
+ }
+ }
+
+ return true;
+ };
+
+ for (auto* param : func->Parameters()) {
+ auto* param_decl = param->Declaration();
+ if (!validate_entry_point_attributes(param_decl->attributes, param->Type(),
+ param_decl->source, ParamOrRetType::kParameter)) {
+ return false;
+ }
+ }
+
+ // Clear IO sets after parameter validation. Builtin and location attributes
+ // in return types should be validated independently from those used in
+ // parameters.
+ builtins.clear();
+ locations.clear();
+
+ if (!func->ReturnType()->Is<sem::Void>()) {
+ if (!validate_entry_point_attributes(decl->return_type_attributes, func->ReturnType(),
+ decl->source, ParamOrRetType::kReturnType)) {
+ return false;
+ }
+ }
+
+ if (decl->PipelineStage() == ast::PipelineStage::kVertex &&
+ builtins.count(ast::Builtin::kPosition) == 0) {
+ // Check module-scope variables, as the SPIR-V sanitizer generates these.
+ bool found = false;
+ for (auto* global : func->TransitivelyReferencedGlobals()) {
+ if (auto* builtin =
+ ast::GetAttribute<ast::BuiltinAttribute>(global->Declaration()->attributes)) {
+ if (builtin->builtin == ast::Builtin::kPosition) {
+ found = true;
+ break;
+ }
+ }
+ }
+ if (!found) {
+ AddError(
+ "a vertex shader must include the 'position' builtin in its return "
+ "type",
+ decl->source);
+ return false;
+ }
+ }
+
+ if (decl->PipelineStage() == ast::PipelineStage::kCompute) {
+ if (!ast::HasAttribute<ast::WorkgroupAttribute>(decl->attributes)) {
+ AddError(
+ "a compute shader must include 'workgroup_size' in its "
+ "attributes",
+ decl->source);
+ return false;
+ }
+ }
+
+ // Validate there are no resource variable binding collisions
+ std::unordered_map<sem::BindingPoint, const ast::Variable*> binding_points;
+ for (auto* var : func->TransitivelyReferencedGlobals()) {
+ auto* var_decl = var->Declaration();
+ if (!var_decl->BindingPoint()) {
+ continue;
+ }
+ auto bp = var->BindingPoint();
+ auto res = binding_points.emplace(bp, var_decl);
+ if (!res.second &&
+ IsValidationEnabled(decl->attributes,
+ ast::DisabledValidation::kBindingPointCollision) &&
+ IsValidationEnabled(res.first->second->attributes,
+ ast::DisabledValidation::kBindingPointCollision)) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#resource-interface
+ // Bindings must not alias within a shader stage: two different
+ // variables in the resource interface of a given shader must not have
+ // the same group and binding values, when considered as a pair of
+ // values.
+ auto func_name = symbols_.NameFor(decl->symbol);
+ AddError("entry point '" + func_name +
+ "' references multiple variables that use the "
+ "same resource binding @group(" +
+ std::to_string(bp.group) + "), @binding(" + std::to_string(bp.binding) +
+ ")",
+ var_decl->source);
+ AddNote("first resource binding usage declared here", res.first->second->source);
+ return false;
+ }
+ }
+
+ return true;
}
bool Validator::Statements(const ast::StatementList& stmts) const {
- for (auto* stmt : stmts) {
- if (!sem_.Get(stmt)->IsReachable()) {
- /// TODO(https://github.com/gpuweb/gpuweb/issues/2378): This may need to
- /// become an error.
- AddWarning("code is unreachable", stmt->source);
- break;
+ for (auto* stmt : stmts) {
+ if (!sem_.Get(stmt)->IsReachable()) {
+ /// TODO(https://github.com/gpuweb/gpuweb/issues/2378): This may need to
+ /// become an error.
+ AddWarning("code is unreachable", stmt->source);
+ break;
+ }
}
- }
- return true;
+ return true;
}
-bool Validator::Bitcast(const ast::BitcastExpression* cast,
- const sem::Type* to) const {
- auto* from = sem_.TypeOf(cast->expr)->UnwrapRef();
- if (!from->is_numeric_scalar_or_vector()) {
- AddError("'" + sem_.TypeNameOf(from) + "' cannot be bitcast",
- cast->expr->source);
- return false;
- }
- if (!to->is_numeric_scalar_or_vector()) {
- AddError("cannot bitcast to '" + sem_.TypeNameOf(to) + "'",
- cast->type->source);
- return false;
- }
-
- auto width = [&](const sem::Type* ty) {
- if (auto* vec = ty->As<sem::Vector>()) {
- return vec->Width();
+bool Validator::Bitcast(const ast::BitcastExpression* cast, const sem::Type* to) const {
+ auto* from = sem_.TypeOf(cast->expr)->UnwrapRef();
+ if (!from->is_numeric_scalar_or_vector()) {
+ AddError("'" + sem_.TypeNameOf(from) + "' cannot be bitcast", cast->expr->source);
+ return false;
}
- return 1u;
- };
+ if (!to->is_numeric_scalar_or_vector()) {
+ AddError("cannot bitcast to '" + sem_.TypeNameOf(to) + "'", cast->type->source);
+ return false;
+ }
- if (width(from) != width(to)) {
- AddError("cannot bitcast from '" + sem_.TypeNameOf(from) + "' to '" +
- sem_.TypeNameOf(to) + "'",
- cast->source);
- return false;
- }
+ auto width = [&](const sem::Type* ty) {
+ if (auto* vec = ty->As<sem::Vector>()) {
+ return vec->Width();
+ }
+ return 1u;
+ };
- return true;
+ if (width(from) != width(to)) {
+ AddError(
+ "cannot bitcast from '" + sem_.TypeNameOf(from) + "' to '" + sem_.TypeNameOf(to) + "'",
+ cast->source);
+ return false;
+ }
+
+ return true;
}
bool Validator::BreakStatement(const sem::Statement* stmt,
sem::Statement* current_statement) const {
- if (!stmt->FindFirstParent<sem::LoopBlockStatement, sem::CaseStatement>()) {
- AddError("break statement must be in a loop or switch case",
- stmt->Declaration()->source);
- return false;
- }
- if (auto* continuing =
- ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
- auto fail = [&](const char* note_msg, const Source& note_src) {
- constexpr const char* kErrorMsg =
- "break statement in a continuing block must be the single statement "
- "of an if statement's true or false block, and that if statement "
- "must be the last statement of the continuing block";
- AddError(kErrorMsg, stmt->Declaration()->source);
- AddNote(note_msg, note_src);
- return false;
- };
-
- if (auto* block = stmt->Parent()->As<sem::BlockStatement>()) {
- auto* block_parent = block->Parent();
- auto* if_stmt = block_parent->As<sem::IfStatement>();
- if (!if_stmt) {
- return fail("break statement is not directly in if statement block",
- stmt->Declaration()->source);
- }
- if (block->Declaration()->statements.size() != 1) {
- return fail("if statement block contains multiple statements",
- block->Declaration()->source);
- }
-
- if (if_stmt->Parent()->Is<sem::IfStatement>()) {
- return fail("else has condition", if_stmt->Declaration()->source);
- }
-
- bool el_contains_break =
- block->Declaration() == if_stmt->Declaration()->else_statement;
- if (el_contains_break) {
- if (auto* true_block = if_stmt->Declaration()->body;
- !true_block->Empty()) {
- return fail("non-empty true block", true_block->source);
- }
- } else {
- auto* else_stmt = if_stmt->Declaration()->else_statement;
- if (else_stmt) {
- return fail("non-empty false block", else_stmt->source);
- }
- }
-
- if (if_stmt->Parent()->Declaration() != continuing) {
- return fail(
- "if statement containing break statement is not directly in "
- "continuing block",
- if_stmt->Declaration()->source);
- }
- if (auto* cont_block = continuing->As<ast::BlockStatement>()) {
- if (if_stmt->Declaration() != cont_block->Last()) {
- return fail(
- "if statement containing break statement is not the last "
- "statement of the continuing block",
- if_stmt->Declaration()->source);
- }
- }
+ if (!stmt->FindFirstParent<sem::LoopBlockStatement, sem::CaseStatement>()) {
+ AddError("break statement must be in a loop or switch case", stmt->Declaration()->source);
+ return false;
}
- }
- return true;
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
+ auto fail = [&](const char* note_msg, const Source& note_src) {
+ constexpr const char* kErrorMsg =
+ "break statement in a continuing block must be the single statement "
+ "of an if statement's true or false block, and that if statement "
+ "must be the last statement of the continuing block";
+ AddError(kErrorMsg, stmt->Declaration()->source);
+ AddNote(note_msg, note_src);
+ return false;
+ };
+
+ if (auto* block = stmt->Parent()->As<sem::BlockStatement>()) {
+ auto* block_parent = block->Parent();
+ auto* if_stmt = block_parent->As<sem::IfStatement>();
+ if (!if_stmt) {
+ return fail("break statement is not directly in if statement block",
+ stmt->Declaration()->source);
+ }
+ if (block->Declaration()->statements.size() != 1) {
+ return fail("if statement block contains multiple statements",
+ block->Declaration()->source);
+ }
+
+ if (if_stmt->Parent()->Is<sem::IfStatement>()) {
+ return fail("else has condition", if_stmt->Declaration()->source);
+ }
+
+ bool el_contains_break = block->Declaration() == if_stmt->Declaration()->else_statement;
+ if (el_contains_break) {
+ if (auto* true_block = if_stmt->Declaration()->body; !true_block->Empty()) {
+ return fail("non-empty true block", true_block->source);
+ }
+ } else {
+ auto* else_stmt = if_stmt->Declaration()->else_statement;
+ if (else_stmt) {
+ return fail("non-empty false block", else_stmt->source);
+ }
+ }
+
+ if (if_stmt->Parent()->Declaration() != continuing) {
+ return fail(
+ "if statement containing break statement is not directly in "
+ "continuing block",
+ if_stmt->Declaration()->source);
+ }
+ if (auto* cont_block = continuing->As<ast::BlockStatement>()) {
+ if (if_stmt->Declaration() != cont_block->Last()) {
+ return fail(
+ "if statement containing break statement is not the last "
+ "statement of the continuing block",
+ if_stmt->Declaration()->source);
+ }
+ }
+ }
+ }
+ return true;
}
bool Validator::ContinueStatement(const sem::Statement* stmt,
sem::Statement* current_statement) const {
- if (auto* continuing =
- ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
- AddError("continuing blocks must not contain a continue statement",
- stmt->Declaration()->source);
- if (continuing != stmt->Declaration() &&
- continuing != stmt->Parent()->Declaration()) {
- AddNote("see continuing block here", continuing->source);
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ true, current_statement)) {
+ AddError("continuing blocks must not contain a continue statement",
+ stmt->Declaration()->source);
+ if (continuing != stmt->Declaration() && continuing != stmt->Parent()->Declaration()) {
+ AddNote("see continuing block here", continuing->source);
+ }
+ return false;
}
- return false;
- }
- if (!stmt->FindFirstParent<sem::LoopBlockStatement>()) {
- AddError("continue statement must be in a loop",
- stmt->Declaration()->source);
- return false;
- }
+ if (!stmt->FindFirstParent<sem::LoopBlockStatement>()) {
+ AddError("continue statement must be in a loop", stmt->Declaration()->source);
+ return false;
+ }
- return true;
+ return true;
}
bool Validator::DiscardStatement(const sem::Statement* stmt,
sem::Statement* current_statement) const {
- if (auto* continuing =
- ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
- AddError("continuing blocks must not contain a discard statement",
- stmt->Declaration()->source);
- if (continuing != stmt->Declaration() &&
- continuing != stmt->Parent()->Declaration()) {
- AddNote("see continuing block here", continuing->source);
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
+ AddError("continuing blocks must not contain a discard statement",
+ stmt->Declaration()->source);
+ if (continuing != stmt->Declaration() && continuing != stmt->Parent()->Declaration()) {
+ AddNote("see continuing block here", continuing->source);
+ }
+ return false;
}
- return false;
- }
- return true;
+ return true;
}
bool Validator::FallthroughStatement(const sem::Statement* stmt) const {
- if (auto* block = As<sem::BlockStatement>(stmt->Parent())) {
- if (auto* c = As<sem::CaseStatement>(block->Parent())) {
- if (block->Declaration()->Last() == stmt->Declaration()) {
- if (auto* s = As<sem::SwitchStatement>(c->Parent())) {
- if (c->Declaration() != s->Declaration()->body.back()) {
- return true;
- }
- AddError(
- "a fallthrough statement must not be used in the last switch "
- "case",
- stmt->Declaration()->source);
- return false;
+ if (auto* block = As<sem::BlockStatement>(stmt->Parent())) {
+ if (auto* c = As<sem::CaseStatement>(block->Parent())) {
+ if (block->Declaration()->Last() == stmt->Declaration()) {
+ if (auto* s = As<sem::SwitchStatement>(c->Parent())) {
+ if (c->Declaration() != s->Declaration()->body.back()) {
+ return true;
+ }
+ AddError(
+ "a fallthrough statement must not be used in the last switch "
+ "case",
+ stmt->Declaration()->source);
+ return false;
+ }
+ }
}
- }
}
- }
- AddError(
- "fallthrough must only be used as the last statement of a case block",
- stmt->Declaration()->source);
- return false;
+ AddError("fallthrough must only be used as the last statement of a case block",
+ stmt->Declaration()->source);
+ return false;
}
bool Validator::LoopStatement(const sem::LoopStatement* stmt) const {
- if (stmt->Behaviors().Empty()) {
- AddError("loop does not exit", stmt->Declaration()->source.Begin());
- return false;
- }
- return true;
+ if (stmt->Behaviors().Empty()) {
+ AddError("loop does not exit", stmt->Declaration()->source.Begin());
+ return false;
+ }
+ return true;
}
bool Validator::ForLoopStatement(const sem::ForLoopStatement* stmt) const {
- if (stmt->Behaviors().Empty()) {
- AddError("for-loop does not exit", stmt->Declaration()->source.Begin());
- return false;
- }
- if (auto* cond = stmt->Condition()) {
- auto* cond_ty = cond->Type()->UnwrapRef();
- if (!cond_ty->Is<sem::Bool>()) {
- AddError(
- "for-loop condition must be bool, got " + sem_.TypeNameOf(cond_ty),
- stmt->Condition()->Declaration()->source);
- return false;
+ if (stmt->Behaviors().Empty()) {
+ AddError("for-loop does not exit", stmt->Declaration()->source.Begin());
+ return false;
}
- }
- return true;
+ if (auto* cond = stmt->Condition()) {
+ auto* cond_ty = cond->Type()->UnwrapRef();
+ if (!cond_ty->Is<sem::Bool>()) {
+ AddError("for-loop condition must be bool, got " + sem_.TypeNameOf(cond_ty),
+ stmt->Condition()->Declaration()->source);
+ return false;
+ }
+ }
+ return true;
}
bool Validator::IfStatement(const sem::IfStatement* stmt) const {
- auto* cond_ty = stmt->Condition()->Type()->UnwrapRef();
- if (!cond_ty->Is<sem::Bool>()) {
- AddError(
- "if statement condition must be bool, got " + sem_.TypeNameOf(cond_ty),
- stmt->Condition()->Declaration()->source);
- return false;
- }
- return true;
+ auto* cond_ty = stmt->Condition()->Type()->UnwrapRef();
+ if (!cond_ty->Is<sem::Bool>()) {
+ AddError("if statement condition must be bool, got " + sem_.TypeNameOf(cond_ty),
+ stmt->Condition()->Declaration()->source);
+ return false;
+ }
+ return true;
}
bool Validator::BuiltinCall(const sem::Call* call) const {
- if (call->Type()->Is<sem::Void>()) {
- bool is_call_statement = false;
- if (auto* call_stmt = As<ast::CallStatement>(call->Stmt()->Declaration())) {
- if (call_stmt->expr == call->Declaration()) {
- is_call_statement = true;
- }
+ if (call->Type()->Is<sem::Void>()) {
+ bool is_call_statement = false;
+ if (auto* call_stmt = As<ast::CallStatement>(call->Stmt()->Declaration())) {
+ if (call_stmt->expr == call->Declaration()) {
+ is_call_statement = true;
+ }
+ }
+ if (!is_call_statement) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#function-call-expr
+ // If the called function does not return a value, a function call
+ // statement should be used instead.
+ auto* ident = call->Declaration()->target.name;
+ auto name = symbols_.NameFor(ident->symbol);
+ AddError("builtin '" + name + "' does not return a value", call->Declaration()->source);
+ return false;
+ }
}
- if (!is_call_statement) {
- // https://gpuweb.github.io/gpuweb/wgsl/#function-call-expr
- // If the called function does not return a value, a function call
- // statement should be used instead.
- auto* ident = call->Declaration()->target.name;
- auto name = symbols_.NameFor(ident->symbol);
- AddError("builtin '" + name + "' does not return a value",
- call->Declaration()->source);
- return false;
- }
- }
- return true;
+ return true;
}
bool Validator::TextureBuiltinFunction(const sem::Call* call) const {
- auto* builtin = call->Target()->As<sem::Builtin>();
- if (!builtin) {
- return false;
- }
-
- std::string func_name = builtin->str();
- auto& signature = builtin->Signature();
-
- auto check_arg_is_constexpr = [&](sem::ParameterUsage usage, int min,
- int max) {
- auto index = signature.IndexOf(usage);
- if (index < 0) {
- return true;
- }
- std::string name = sem::str(usage);
- auto* arg = call->Arguments()[index];
- if (auto values = arg->ConstantValue()) {
- // Assert that the constant values are of the expected type.
- if (!values.Type()->IsAnyOf<sem::I32, sem::Vector>() ||
- !values.ElementType()->Is<sem::I32>()) {
- TINT_ICE(Resolver, diagnostics_)
- << "failed to resolve '" + func_name + "' " << name
- << " parameter type";
+ auto* builtin = call->Target()->As<sem::Builtin>();
+ if (!builtin) {
return false;
- }
+ }
- // Currently const_expr is restricted to literals and type constructors.
- // Check that that's all we have for the parameter.
- bool is_const_expr = true;
- ast::TraverseExpressions(
- arg->Declaration(), diagnostics_, [&](const ast::Expression* e) {
- if (e->IsAnyOf<ast::LiteralExpression, ast::CallExpression>()) {
- return ast::TraverseAction::Descend;
+ std::string func_name = builtin->str();
+ auto& signature = builtin->Signature();
+
+ auto check_arg_is_constexpr = [&](sem::ParameterUsage usage, int min, int max) {
+ auto index = signature.IndexOf(usage);
+ if (index < 0) {
+ return true;
+ }
+ std::string name = sem::str(usage);
+ auto* arg = call->Arguments()[index];
+ if (auto values = arg->ConstantValue()) {
+ // Assert that the constant values are of the expected type.
+ if (!values.Type()->IsAnyOf<sem::I32, sem::Vector>() ||
+ !values.ElementType()->Is<sem::I32>()) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "failed to resolve '" + func_name + "' " << name << " parameter type";
+ return false;
}
- is_const_expr = false;
- return ast::TraverseAction::Stop;
- });
- if (is_const_expr) {
- auto vector = builtin->Parameters()[index]->Type()->Is<sem::Vector>();
- for (size_t i = 0; i < values.Elements().size(); i++) {
- auto value = values.Elements()[i].i32;
- if (value < min || value > max) {
- if (vector) {
- AddError("each component of the " + name +
- " argument must be at least " + std::to_string(min) +
- " and at most " + std::to_string(max) + ". " + name +
- " component " + std::to_string(i) + " is " +
- std::to_string(value),
- arg->Declaration()->source);
- } else {
- AddError("the " + name + " argument must be at least " +
- std::to_string(min) + " and at most " +
- std::to_string(max) + ". " + name + " is " +
- std::to_string(value),
- arg->Declaration()->source);
+
+ // Currently const_expr is restricted to literals and type constructors.
+ // Check that that's all we have for the parameter.
+ bool is_const_expr = true;
+ ast::TraverseExpressions(
+ arg->Declaration(), diagnostics_, [&](const ast::Expression* e) {
+ if (e->IsAnyOf<ast::LiteralExpression, ast::CallExpression>()) {
+ return ast::TraverseAction::Descend;
+ }
+ is_const_expr = false;
+ return ast::TraverseAction::Stop;
+ });
+ if (is_const_expr) {
+ auto vector = builtin->Parameters()[index]->Type()->Is<sem::Vector>();
+ for (size_t i = 0; i < values.Elements().size(); i++) {
+ auto value = values.Elements()[i].i32;
+ if (value < min || value > max) {
+ if (vector) {
+ AddError("each component of the " + name +
+ " argument must be at least " + std::to_string(min) +
+ " and at most " + std::to_string(max) + ". " + name +
+ " component " + std::to_string(i) + " is " +
+ std::to_string(value),
+ arg->Declaration()->source);
+ } else {
+ AddError("the " + name + " argument must be at least " +
+ std::to_string(min) + " and at most " +
+ std::to_string(max) + ". " + name + " is " +
+ std::to_string(value),
+ arg->Declaration()->source);
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+ AddError("the " + name + " argument must be a const_expression",
+ arg->Declaration()->source);
+ return false;
+ };
+
+ return check_arg_is_constexpr(sem::ParameterUsage::kOffset, -8, 7) &&
+ check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
+}
+
+bool Validator::FunctionCall(const sem::Call* call, sem::Statement* current_statement) const {
+ auto* decl = call->Declaration();
+ auto* target = call->Target()->As<sem::Function>();
+ auto sym = decl->target.name->symbol;
+ auto name = symbols_.NameFor(sym);
+
+ if (target->Declaration()->IsEntryPoint()) {
+ // https://www.w3.org/TR/WGSL/#function-restriction
+ // An entry point must never be the target of a function call.
+ AddError("entry point functions cannot be the target of a function call", decl->source);
+ return false;
+ }
+
+ if (decl->args.size() != target->Parameters().size()) {
+ bool more = decl->args.size() > target->Parameters().size();
+ AddError("too " + (more ? std::string("many") : std::string("few")) +
+ " arguments in call to '" + name + "', expected " +
+ std::to_string(target->Parameters().size()) + ", got " +
+ std::to_string(call->Arguments().size()),
+ decl->source);
+ return false;
+ }
+
+ for (size_t i = 0; i < call->Arguments().size(); ++i) {
+ const sem::Variable* param = target->Parameters()[i];
+ const ast::Expression* arg_expr = decl->args[i];
+ auto* param_type = param->Type();
+ auto* arg_type = sem_.TypeOf(arg_expr)->UnwrapRef();
+
+ if (param_type != arg_type) {
+ AddError("type mismatch for argument " + std::to_string(i + 1) + " in call to '" +
+ name + "', expected '" + sem_.TypeNameOf(param_type) + "', got '" +
+ sem_.TypeNameOf(arg_type) + "'",
+ arg_expr->source);
+ return false;
+ }
+
+ if (param_type->Is<sem::Pointer>()) {
+ auto is_valid = false;
+ if (auto* ident_expr = arg_expr->As<ast::IdentifierExpression>()) {
+ auto* var = sem_.ResolvedSymbol<sem::Variable>(ident_expr);
+ if (!var) {
+ TINT_ICE(Resolver, diagnostics_) << "failed to resolve identifier";
+ return false;
+ }
+ if (var->Is<sem::Parameter>()) {
+ is_valid = true;
+ }
+ } else if (auto* unary = arg_expr->As<ast::UnaryOpExpression>()) {
+ if (unary->op == ast::UnaryOp::kAddressOf) {
+ if (auto* ident_unary = unary->expr->As<ast::IdentifierExpression>()) {
+ auto* var = sem_.ResolvedSymbol<sem::Variable>(ident_unary);
+ if (!var) {
+ TINT_ICE(Resolver, diagnostics_) << "failed to resolve identifier";
+ return false;
+ }
+ if (var->Declaration()->is_const) {
+ TINT_ICE(Resolver, diagnostics_)
+ << "Resolver::FunctionCall() encountered an address-of "
+ "expression of a constant identifier expression";
+ return false;
+ }
+ is_valid = true;
+ }
+ }
+ }
+
+ if (!is_valid &&
+ IsValidationEnabled(param->Declaration()->attributes,
+ ast::DisabledValidation::kIgnoreInvalidPointerArgument)) {
+ AddError(
+ "expected an address-of expression of a variable identifier "
+ "expression or a function parameter",
+ arg_expr->source);
+ return false;
+ }
+ }
+ }
+
+ if (call->Type()->Is<sem::Void>()) {
+ bool is_call_statement = false;
+ if (auto* call_stmt = As<ast::CallStatement>(call->Stmt()->Declaration())) {
+ if (call_stmt->expr == call->Declaration()) {
+ is_call_statement = true;
+ }
+ }
+ if (!is_call_statement) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#function-call-expr
+ // If the called function does not return a value, a function call
+ // statement should be used instead.
+ AddError("function '" + name + "' does not return a value", decl->source);
+ return false;
+ }
+ }
+
+ if (call->Behaviors().Contains(sem::Behavior::kDiscard)) {
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
+ AddError("cannot call a function that may discard inside a continuing block",
+ call->Declaration()->source);
+ if (continuing != call->Stmt()->Declaration() &&
+ continuing != call->Stmt()->Parent()->Declaration()) {
+ AddNote("see continuing block here", continuing->source);
}
return false;
- }
}
- return true;
- }
}
- AddError("the " + name + " argument must be a const_expression",
- arg->Declaration()->source);
- return false;
- };
- return check_arg_is_constexpr(sem::ParameterUsage::kOffset, -8, 7) &&
- check_arg_is_constexpr(sem::ParameterUsage::kComponent, 0, 3);
+ return true;
}
-bool Validator::FunctionCall(const sem::Call* call,
- sem::Statement* current_statement) const {
- auto* decl = call->Declaration();
- auto* target = call->Target()->As<sem::Function>();
- auto sym = decl->target.name->symbol;
- auto name = symbols_.NameFor(sym);
-
- if (target->Declaration()->IsEntryPoint()) {
- // https://www.w3.org/TR/WGSL/#function-restriction
- // An entry point must never be the target of a function call.
- AddError("entry point functions cannot be the target of a function call",
- decl->source);
- return false;
- }
-
- if (decl->args.size() != target->Parameters().size()) {
- bool more = decl->args.size() > target->Parameters().size();
- AddError("too " + (more ? std::string("many") : std::string("few")) +
- " arguments in call to '" + name + "', expected " +
- std::to_string(target->Parameters().size()) + ", got " +
- std::to_string(call->Arguments().size()),
- decl->source);
- return false;
- }
-
- for (size_t i = 0; i < call->Arguments().size(); ++i) {
- const sem::Variable* param = target->Parameters()[i];
- const ast::Expression* arg_expr = decl->args[i];
- auto* param_type = param->Type();
- auto* arg_type = sem_.TypeOf(arg_expr)->UnwrapRef();
-
- if (param_type != arg_type) {
- AddError("type mismatch for argument " + std::to_string(i + 1) +
- " in call to '" + name + "', expected '" +
- sem_.TypeNameOf(param_type) + "', got '" +
- sem_.TypeNameOf(arg_type) + "'",
- arg_expr->source);
- return false;
- }
-
- if (param_type->Is<sem::Pointer>()) {
- auto is_valid = false;
- if (auto* ident_expr = arg_expr->As<ast::IdentifierExpression>()) {
- auto* var = sem_.ResolvedSymbol<sem::Variable>(ident_expr);
- if (!var) {
- TINT_ICE(Resolver, diagnostics_) << "failed to resolve identifier";
- return false;
- }
- if (var->Is<sem::Parameter>()) {
- is_valid = true;
- }
- } else if (auto* unary = arg_expr->As<ast::UnaryOpExpression>()) {
- if (unary->op == ast::UnaryOp::kAddressOf) {
- if (auto* ident_unary =
- unary->expr->As<ast::IdentifierExpression>()) {
- auto* var = sem_.ResolvedSymbol<sem::Variable>(ident_unary);
- if (!var) {
- TINT_ICE(Resolver, diagnostics_)
- << "failed to resolve identifier";
- return false;
- }
- if (var->Declaration()->is_const) {
- TINT_ICE(Resolver, diagnostics_)
- << "Resolver::FunctionCall() encountered an address-of "
- "expression of a constant identifier expression";
- return false;
- }
- is_valid = true;
- }
- }
- }
-
- if (!is_valid &&
- IsValidationEnabled(
- param->Declaration()->attributes,
- ast::DisabledValidation::kIgnoreInvalidPointerArgument)) {
- AddError(
- "expected an address-of expression of a variable identifier "
- "expression or a function parameter",
- arg_expr->source);
+bool Validator::StructureConstructorOrCast(const ast::CallExpression* ctor,
+ const sem::Struct* struct_type) const {
+ if (!struct_type->IsConstructible()) {
+ AddError("struct constructor has non-constructible type", ctor->source);
return false;
- }
}
- }
- if (call->Type()->Is<sem::Void>()) {
- bool is_call_statement = false;
- if (auto* call_stmt = As<ast::CallStatement>(call->Stmt()->Declaration())) {
- if (call_stmt->expr == call->Declaration()) {
- is_call_statement = true;
- }
+ if (ctor->args.size() > 0) {
+ if (ctor->args.size() != struct_type->Members().size()) {
+ std::string fm = ctor->args.size() < struct_type->Members().size() ? "few" : "many";
+ AddError("struct constructor has too " + fm + " inputs: expected " +
+ std::to_string(struct_type->Members().size()) + ", found " +
+ std::to_string(ctor->args.size()),
+ ctor->source);
+ return false;
+ }
+ for (auto* member : struct_type->Members()) {
+ auto* value = ctor->args[member->Index()];
+ auto* value_ty = sem_.TypeOf(value);
+ if (member->Type() != value_ty->UnwrapRef()) {
+ AddError(
+ "type in struct constructor does not match struct member type: "
+ "expected '" +
+ sem_.TypeNameOf(member->Type()) + "', found '" + sem_.TypeNameOf(value_ty) +
+ "'",
+ value->source);
+ return false;
+ }
+ }
}
- if (!is_call_statement) {
- // https://gpuweb.github.io/gpuweb/wgsl/#function-call-expr
- // If the called function does not return a value, a function call
- // statement should be used instead.
- AddError("function '" + name + "' does not return a value", decl->source);
- return false;
- }
- }
-
- if (call->Behaviors().Contains(sem::Behavior::kDiscard)) {
- if (auto* continuing =
- ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
- AddError(
- "cannot call a function that may discard inside a continuing block",
- call->Declaration()->source);
- if (continuing != call->Stmt()->Declaration() &&
- continuing != call->Stmt()->Parent()->Declaration()) {
- AddNote("see continuing block here", continuing->source);
- }
- return false;
- }
- }
-
- return true;
-}
-
-bool Validator::StructureConstructorOrCast(
- const ast::CallExpression* ctor,
- const sem::Struct* struct_type) const {
- if (!struct_type->IsConstructible()) {
- AddError("struct constructor has non-constructible type", ctor->source);
- return false;
- }
-
- if (ctor->args.size() > 0) {
- if (ctor->args.size() != struct_type->Members().size()) {
- std::string fm =
- ctor->args.size() < struct_type->Members().size() ? "few" : "many";
- AddError("struct constructor has too " + fm + " inputs: expected " +
- std::to_string(struct_type->Members().size()) + ", found " +
- std::to_string(ctor->args.size()),
- ctor->source);
- return false;
- }
- for (auto* member : struct_type->Members()) {
- auto* value = ctor->args[member->Index()];
- auto* value_ty = sem_.TypeOf(value);
- if (member->Type() != value_ty->UnwrapRef()) {
- AddError(
- "type in struct constructor does not match struct member type: "
- "expected '" +
- sem_.TypeNameOf(member->Type()) + "', found '" +
- sem_.TypeNameOf(value_ty) + "'",
- value->source);
- return false;
- }
- }
- }
- return true;
+ return true;
}
bool Validator::ArrayConstructorOrCast(const ast::CallExpression* ctor,
const sem::Array* array_type) const {
- auto& values = ctor->args;
- auto* elem_ty = array_type->ElemType();
- for (auto* value : values) {
- auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
- if (value_ty != elem_ty) {
- AddError(
- "type in array constructor does not match array type: "
- "expected '" +
- sem_.TypeNameOf(elem_ty) + "', found '" +
- sem_.TypeNameOf(value_ty) + "'",
- value->source);
- return false;
+ auto& values = ctor->args;
+ auto* elem_ty = array_type->ElemType();
+ for (auto* value : values) {
+ auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
+ if (value_ty != elem_ty) {
+ AddError(
+ "type in array constructor does not match array type: "
+ "expected '" +
+ sem_.TypeNameOf(elem_ty) + "', found '" + sem_.TypeNameOf(value_ty) + "'",
+ value->source);
+ return false;
+ }
}
- }
- if (array_type->IsRuntimeSized()) {
- AddError("cannot init a runtime-sized array", ctor->source);
- return false;
- } else if (!elem_ty->IsConstructible()) {
- AddError("array constructor has non-constructible element type",
- ctor->source);
- return false;
- } else if (!values.empty() && (values.size() != array_type->Count())) {
- std::string fm = values.size() < array_type->Count() ? "few" : "many";
- AddError("array constructor has too " + fm + " elements: expected " +
- std::to_string(array_type->Count()) + ", found " +
- std::to_string(values.size()),
- ctor->source);
- return false;
- } else if (values.size() > array_type->Count()) {
- AddError("array constructor has too many elements: expected " +
- std::to_string(array_type->Count()) + ", found " +
- std::to_string(values.size()),
- ctor->source);
- return false;
- }
- return true;
+ if (array_type->IsRuntimeSized()) {
+ AddError("cannot init a runtime-sized array", ctor->source);
+ return false;
+ } else if (!elem_ty->IsConstructible()) {
+ AddError("array constructor has non-constructible element type", ctor->source);
+ return false;
+ } else if (!values.empty() && (values.size() != array_type->Count())) {
+ std::string fm = values.size() < array_type->Count() ? "few" : "many";
+ AddError("array constructor has too " + fm + " elements: expected " +
+ std::to_string(array_type->Count()) + ", found " +
+ std::to_string(values.size()),
+ ctor->source);
+ return false;
+ } else if (values.size() > array_type->Count()) {
+ AddError("array constructor has too many elements: expected " +
+ std::to_string(array_type->Count()) + ", found " +
+ std::to_string(values.size()),
+ ctor->source);
+ return false;
+ }
+ return true;
}
bool Validator::VectorConstructorOrCast(const ast::CallExpression* ctor,
const sem::Vector* vec_type) const {
- auto& values = ctor->args;
- auto* elem_ty = vec_type->type();
- size_t value_cardinality_sum = 0;
- for (auto* value : values) {
- auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
- if (value_ty->is_scalar()) {
- if (elem_ty != value_ty) {
- AddError(
- "type in vector constructor does not match vector type: "
- "expected '" +
- sem_.TypeNameOf(elem_ty) + "', found '" +
- sem_.TypeNameOf(value_ty) + "'",
- value->source);
- return false;
- }
+ auto& values = ctor->args;
+ auto* elem_ty = vec_type->type();
+ size_t value_cardinality_sum = 0;
+ for (auto* value : values) {
+ auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
+ if (value_ty->is_scalar()) {
+ if (elem_ty != value_ty) {
+ AddError(
+ "type in vector constructor does not match vector type: "
+ "expected '" +
+ sem_.TypeNameOf(elem_ty) + "', found '" + sem_.TypeNameOf(value_ty) + "'",
+ value->source);
+ return false;
+ }
- value_cardinality_sum++;
- } else if (auto* value_vec = value_ty->As<sem::Vector>()) {
- auto* value_elem_ty = value_vec->type();
- // A mismatch of vector type parameter T is only an error if multiple
- // arguments are present. A single argument constructor constitutes a
- // type conversion expression.
- if (elem_ty != value_elem_ty && values.size() > 1u) {
- AddError(
- "type in vector constructor does not match vector type: "
- "expected '" +
- sem_.TypeNameOf(elem_ty) + "', found '" +
- sem_.TypeNameOf(value_elem_ty) + "'",
- value->source);
- return false;
- }
+ value_cardinality_sum++;
+ } else if (auto* value_vec = value_ty->As<sem::Vector>()) {
+ auto* value_elem_ty = value_vec->type();
+ // A mismatch of vector type parameter T is only an error if multiple
+ // arguments are present. A single argument constructor constitutes a
+ // type conversion expression.
+ if (elem_ty != value_elem_ty && values.size() > 1u) {
+ AddError(
+ "type in vector constructor does not match vector type: "
+ "expected '" +
+ sem_.TypeNameOf(elem_ty) + "', found '" + sem_.TypeNameOf(value_elem_ty) +
+ "'",
+ value->source);
+ return false;
+ }
- value_cardinality_sum += value_vec->Width();
- } else {
- // A vector constructor can only accept vectors and scalars.
- AddError("expected vector or scalar type in vector constructor; found: " +
- sem_.TypeNameOf(value_ty),
- value->source);
- return false;
+ value_cardinality_sum += value_vec->Width();
+ } else {
+ // A vector constructor can only accept vectors and scalars.
+ AddError("expected vector or scalar type in vector constructor; found: " +
+ sem_.TypeNameOf(value_ty),
+ value->source);
+ return false;
+ }
}
- }
- // A correct vector constructor must either be a zero-value expression,
- // a single-value initializer (splat) expression, or the number of components
- // of all constructor arguments must add up to the vector cardinality.
- if (value_cardinality_sum > 1 && value_cardinality_sum != vec_type->Width()) {
- if (values.empty()) {
- TINT_ICE(Resolver, diagnostics_)
- << "constructor arguments expected to be non-empty!";
+ // A correct vector constructor must either be a zero-value expression,
+ // a single-value initializer (splat) expression, or the number of components
+ // of all constructor arguments must add up to the vector cardinality.
+ if (value_cardinality_sum > 1 && value_cardinality_sum != vec_type->Width()) {
+ if (values.empty()) {
+ TINT_ICE(Resolver, diagnostics_) << "constructor arguments expected to be non-empty!";
+ }
+ const Source& values_start = values[0]->source;
+ const Source& values_end = values[values.size() - 1]->source;
+ AddError("attempted to construct '" + sem_.TypeNameOf(vec_type) + "' with " +
+ std::to_string(value_cardinality_sum) + " component(s)",
+ Source::Combine(values_start, values_end));
+ return false;
}
- const Source& values_start = values[0]->source;
- const Source& values_end = values[values.size() - 1]->source;
- AddError("attempted to construct '" + sem_.TypeNameOf(vec_type) +
- "' with " + std::to_string(value_cardinality_sum) +
- " component(s)",
- Source::Combine(values_start, values_end));
- return false;
- }
- return true;
+ return true;
}
bool Validator::Vector(const sem::Vector* ty, const Source& source) const {
- if (!ty->type()->is_scalar()) {
- AddError("vector element type must be 'bool', 'f32', 'i32' or 'u32'",
- source);
- return false;
- }
- return true;
+ if (!ty->type()->is_scalar()) {
+ AddError("vector element type must be 'bool', 'f32', 'i32' or 'u32'", source);
+ return false;
+ }
+ return true;
}
bool Validator::Matrix(const sem::Matrix* ty, const Source& source) const {
- if (!ty->is_float_matrix()) {
- AddError("matrix element type must be 'f32'", source);
- return false;
- }
- return true;
+ if (!ty->is_float_matrix()) {
+ AddError("matrix element type must be 'f32'", source);
+ return false;
+ }
+ return true;
}
bool Validator::MatrixConstructorOrCast(const ast::CallExpression* ctor,
const sem::Matrix* matrix_ty) const {
- auto& values = ctor->args;
- // Zero Value expression
- if (values.empty()) {
+ auto& values = ctor->args;
+ // Zero Value expression
+ if (values.empty()) {
+ return true;
+ }
+
+ if (!Matrix(matrix_ty, ctor->source)) {
+ return false;
+ }
+
+ std::vector<const sem::Type*> arg_tys;
+ arg_tys.reserve(values.size());
+ for (auto* value : values) {
+ arg_tys.emplace_back(sem_.TypeOf(value)->UnwrapRef());
+ }
+
+ auto* elem_type = matrix_ty->type();
+ auto num_elements = matrix_ty->columns() * matrix_ty->rows();
+
+ // Print a generic error for an invalid matrix constructor, showing the
+ // available overloads.
+ auto print_error = [&]() {
+ const Source& values_start = values[0]->source;
+ const Source& values_end = values[values.size() - 1]->source;
+ auto type_name = sem_.TypeNameOf(matrix_ty);
+ auto elem_type_name = sem_.TypeNameOf(elem_type);
+ std::stringstream ss;
+ ss << "no matching constructor " + type_name << "(";
+ for (size_t i = 0; i < values.size(); i++) {
+ if (i > 0) {
+ ss << ", ";
+ }
+ ss << arg_tys[i]->FriendlyName(symbols_);
+ }
+ ss << ")" << std::endl << std::endl;
+ ss << "3 candidates available:" << std::endl;
+ ss << " " << type_name << "()" << std::endl;
+ ss << " " << type_name << "(" << elem_type_name << ",...," << elem_type_name << ")"
+ << " // " << std::to_string(num_elements) << " arguments" << std::endl;
+ ss << " " << type_name << "(";
+ for (uint32_t c = 0; c < matrix_ty->columns(); c++) {
+ if (c > 0) {
+ ss << ", ";
+ }
+ ss << VectorPretty(matrix_ty->rows(), elem_type);
+ }
+ ss << ")" << std::endl;
+ AddError(ss.str(), Source::Combine(values_start, values_end));
+ };
+
+ const sem::Type* expected_arg_type = nullptr;
+ if (num_elements == values.size()) {
+ // Column-major construction from scalar elements.
+ expected_arg_type = matrix_ty->type();
+ } else if (matrix_ty->columns() == values.size()) {
+ // Column-by-column construction from vectors.
+ expected_arg_type = matrix_ty->ColumnType();
+ } else {
+ print_error();
+ return false;
+ }
+
+ for (auto* arg_ty : arg_tys) {
+ if (arg_ty != expected_arg_type) {
+ print_error();
+ return false;
+ }
+ }
+
return true;
- }
-
- if (!Matrix(matrix_ty, ctor->source)) {
- return false;
- }
-
- std::vector<const sem::Type*> arg_tys;
- arg_tys.reserve(values.size());
- for (auto* value : values) {
- arg_tys.emplace_back(sem_.TypeOf(value)->UnwrapRef());
- }
-
- auto* elem_type = matrix_ty->type();
- auto num_elements = matrix_ty->columns() * matrix_ty->rows();
-
- // Print a generic error for an invalid matrix constructor, showing the
- // available overloads.
- auto print_error = [&]() {
- const Source& values_start = values[0]->source;
- const Source& values_end = values[values.size() - 1]->source;
- auto type_name = sem_.TypeNameOf(matrix_ty);
- auto elem_type_name = sem_.TypeNameOf(elem_type);
- std::stringstream ss;
- ss << "no matching constructor " + type_name << "(";
- for (size_t i = 0; i < values.size(); i++) {
- if (i > 0) {
- ss << ", ";
- }
- ss << arg_tys[i]->FriendlyName(symbols_);
- }
- ss << ")" << std::endl << std::endl;
- ss << "3 candidates available:" << std::endl;
- ss << " " << type_name << "()" << std::endl;
- ss << " " << type_name << "(" << elem_type_name << ",...,"
- << elem_type_name << ")"
- << " // " << std::to_string(num_elements) << " arguments" << std::endl;
- ss << " " << type_name << "(";
- for (uint32_t c = 0; c < matrix_ty->columns(); c++) {
- if (c > 0) {
- ss << ", ";
- }
- ss << VectorPretty(matrix_ty->rows(), elem_type);
- }
- ss << ")" << std::endl;
- AddError(ss.str(), Source::Combine(values_start, values_end));
- };
-
- const sem::Type* expected_arg_type = nullptr;
- if (num_elements == values.size()) {
- // Column-major construction from scalar elements.
- expected_arg_type = matrix_ty->type();
- } else if (matrix_ty->columns() == values.size()) {
- // Column-by-column construction from vectors.
- expected_arg_type = matrix_ty->ColumnType();
- } else {
- print_error();
- return false;
- }
-
- for (auto* arg_ty : arg_tys) {
- if (arg_ty != expected_arg_type) {
- print_error();
- return false;
- }
- }
-
- return true;
}
bool Validator::ScalarConstructorOrCast(const ast::CallExpression* ctor,
const sem::Type* ty) const {
- if (ctor->args.size() == 0) {
+ if (ctor->args.size() == 0) {
+ return true;
+ }
+ if (ctor->args.size() > 1) {
+ AddError(
+ "expected zero or one value in constructor, got " + std::to_string(ctor->args.size()),
+ ctor->source);
+ return false;
+ }
+
+ // Validate constructor
+ auto* value = ctor->args[0];
+ auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
+
+ using Bool = sem::Bool;
+ using I32 = sem::I32;
+ using U32 = sem::U32;
+ using F32 = sem::F32;
+
+ const bool is_valid =
+ (ty->Is<Bool>() && value_ty->is_scalar()) || (ty->Is<I32>() && value_ty->is_scalar()) ||
+ (ty->Is<U32>() && value_ty->is_scalar()) || (ty->Is<F32>() && value_ty->is_scalar());
+ if (!is_valid) {
+ AddError("cannot construct '" + sem_.TypeNameOf(ty) + "' with a value of type '" +
+ sem_.TypeNameOf(value_ty) + "'",
+ ctor->source);
+
+ return false;
+ }
+
return true;
- }
- if (ctor->args.size() > 1) {
- AddError("expected zero or one value in constructor, got " +
- std::to_string(ctor->args.size()),
- ctor->source);
- return false;
- }
-
- // Validate constructor
- auto* value = ctor->args[0];
- auto* value_ty = sem_.TypeOf(value)->UnwrapRef();
-
- using Bool = sem::Bool;
- using I32 = sem::I32;
- using U32 = sem::U32;
- using F32 = sem::F32;
-
- const bool is_valid = (ty->Is<Bool>() && value_ty->is_scalar()) ||
- (ty->Is<I32>() && value_ty->is_scalar()) ||
- (ty->Is<U32>() && value_ty->is_scalar()) ||
- (ty->Is<F32>() && value_ty->is_scalar());
- if (!is_valid) {
- AddError("cannot construct '" + sem_.TypeNameOf(ty) +
- "' with a value of type '" + sem_.TypeNameOf(value_ty) + "'",
- ctor->source);
-
- return false;
- }
-
- return true;
}
-bool Validator::PipelineStages(
- const std::vector<sem::Function*>& entry_points) const {
- auto check_workgroup_storage = [&](const sem::Function* func,
- const sem::Function* entry_point) {
- auto stage = entry_point->Declaration()->PipelineStage();
- if (stage != ast::PipelineStage::kCompute) {
- for (auto* var : func->DirectlyReferencedGlobals()) {
- if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
- std::stringstream stage_name;
- stage_name << stage;
- for (auto* user : var->Users()) {
- if (func == user->Stmt()->Function()) {
- AddError("workgroup memory cannot be used by " +
- stage_name.str() + " pipeline stage",
- user->Declaration()->source);
- break;
+bool Validator::PipelineStages(const std::vector<sem::Function*>& entry_points) const {
+ auto check_workgroup_storage = [&](const sem::Function* func,
+ const sem::Function* entry_point) {
+ auto stage = entry_point->Declaration()->PipelineStage();
+ if (stage != ast::PipelineStage::kCompute) {
+ for (auto* var : func->DirectlyReferencedGlobals()) {
+ if (var->StorageClass() == ast::StorageClass::kWorkgroup) {
+ std::stringstream stage_name;
+ stage_name << stage;
+ for (auto* user : var->Users()) {
+ if (func == user->Stmt()->Function()) {
+ AddError("workgroup memory cannot be used by " + stage_name.str() +
+ " pipeline stage",
+ user->Declaration()->source);
+ break;
+ }
+ }
+ AddNote("variable is declared here", var->Declaration()->source);
+ if (func != entry_point) {
+ TraverseCallChain(
+ diagnostics_, entry_point, func, [&](const sem::Function* f) {
+ AddNote("called by function '" +
+ symbols_.NameFor(f->Declaration()->symbol) + "'",
+ f->Declaration()->source);
+ });
+ AddNote("called by entry point '" +
+ symbols_.NameFor(entry_point->Declaration()->symbol) + "'",
+ entry_point->Declaration()->source);
+ }
+ return false;
+ }
}
- }
- AddNote("variable is declared here", var->Declaration()->source);
- if (func != entry_point) {
- TraverseCallChain(
- diagnostics_, entry_point, func, [&](const sem::Function* f) {
- AddNote("called by function '" +
- symbols_.NameFor(f->Declaration()->symbol) + "'",
- f->Declaration()->source);
- });
- AddNote("called by entry point '" +
- symbols_.NameFor(entry_point->Declaration()->symbol) +
- "'",
- entry_point->Declaration()->source);
- }
- return false;
}
- }
+ return true;
+ };
+
+ for (auto* entry_point : entry_points) {
+ if (!check_workgroup_storage(entry_point, entry_point)) {
+ return false;
+ }
+ for (auto* func : entry_point->TransitivelyCalledFunctions()) {
+ if (!check_workgroup_storage(func, entry_point)) {
+ return false;
+ }
+ }
+ }
+
+ auto check_builtin_calls = [&](const sem::Function* func, const sem::Function* entry_point) {
+ auto stage = entry_point->Declaration()->PipelineStage();
+ for (auto* builtin : func->DirectlyCalledBuiltins()) {
+ if (!builtin->SupportedStages().Contains(stage)) {
+ auto* call = func->FindDirectCallTo(builtin);
+ std::stringstream err;
+ err << "built-in cannot be used by " << stage << " pipeline stage";
+ AddError(err.str(),
+ call ? call->Declaration()->source : func->Declaration()->source);
+ if (func != entry_point) {
+ TraverseCallChain(diagnostics_, entry_point, func, [&](const sem::Function* f) {
+ AddNote("called by function '" +
+ symbols_.NameFor(f->Declaration()->symbol) + "'",
+ f->Declaration()->source);
+ });
+ AddNote("called by entry point '" +
+ symbols_.NameFor(entry_point->Declaration()->symbol) + "'",
+ entry_point->Declaration()->source);
+ }
+ return false;
+ }
+ }
+ return true;
+ };
+
+ for (auto* entry_point : entry_points) {
+ if (!check_builtin_calls(entry_point, entry_point)) {
+ return false;
+ }
+ for (auto* func : entry_point->TransitivelyCalledFunctions()) {
+ if (!check_builtin_calls(func, entry_point)) {
+ return false;
+ }
+ }
}
return true;
- };
-
- for (auto* entry_point : entry_points) {
- if (!check_workgroup_storage(entry_point, entry_point)) {
- return false;
- }
- for (auto* func : entry_point->TransitivelyCalledFunctions()) {
- if (!check_workgroup_storage(func, entry_point)) {
- return false;
- }
- }
- }
-
- auto check_builtin_calls = [&](const sem::Function* func,
- const sem::Function* entry_point) {
- auto stage = entry_point->Declaration()->PipelineStage();
- for (auto* builtin : func->DirectlyCalledBuiltins()) {
- if (!builtin->SupportedStages().Contains(stage)) {
- auto* call = func->FindDirectCallTo(builtin);
- std::stringstream err;
- err << "built-in cannot be used by " << stage << " pipeline stage";
- AddError(err.str(), call ? call->Declaration()->source
- : func->Declaration()->source);
- if (func != entry_point) {
- TraverseCallChain(
- diagnostics_, entry_point, func, [&](const sem::Function* f) {
- AddNote("called by function '" +
- symbols_.NameFor(f->Declaration()->symbol) + "'",
- f->Declaration()->source);
- });
- AddNote("called by entry point '" +
- symbols_.NameFor(entry_point->Declaration()->symbol) +
- "'",
- entry_point->Declaration()->source);
- }
- return false;
- }
- }
- return true;
- };
-
- for (auto* entry_point : entry_points) {
- if (!check_builtin_calls(entry_point, entry_point)) {
- return false;
- }
- for (auto* func : entry_point->TransitivelyCalledFunctions()) {
- if (!check_builtin_calls(func, entry_point)) {
- return false;
- }
- }
- }
- return true;
}
bool Validator::Array(const sem::Array* arr, const Source& source) const {
- auto* el_ty = arr->ElemType();
+ auto* el_ty = arr->ElemType();
- if (!IsFixedFootprint(el_ty)) {
- AddError("an array element type cannot contain a runtime-sized array",
- source);
- return false;
- }
- return true;
+ if (!IsFixedFootprint(el_ty)) {
+ AddError("an array element type cannot contain a runtime-sized array", source);
+ return false;
+ }
+ return true;
}
bool Validator::ArrayStrideAttribute(const ast::StrideAttribute* attr,
uint32_t el_size,
uint32_t el_align,
const Source& source) const {
- auto stride = attr->stride;
- bool is_valid_stride =
- (stride >= el_size) && (stride >= el_align) && (stride % el_align == 0);
- if (!is_valid_stride) {
- // https://gpuweb.github.io/gpuweb/wgsl/#array-layout-rules
- // Arrays decorated with the stride attribute must have a stride that is
- // at least the size of the element type, and be a multiple of the
- // element type's alignment value.
- AddError(
- "arrays decorated with the stride attribute must have a stride "
- "that is at least the size of the element type, and be a multiple "
- "of the element type's alignment value.",
- source);
- return false;
- }
- return true;
+ auto stride = attr->stride;
+ bool is_valid_stride = (stride >= el_size) && (stride >= el_align) && (stride % el_align == 0);
+ if (!is_valid_stride) {
+ // https://gpuweb.github.io/gpuweb/wgsl/#array-layout-rules
+ // Arrays decorated with the stride attribute must have a stride that is
+ // at least the size of the element type, and be a multiple of the
+ // element type's alignment value.
+ AddError(
+ "arrays decorated with the stride attribute must have a stride "
+ "that is at least the size of the element type, and be a multiple "
+ "of the element type's alignment value.",
+ source);
+ return false;
+ }
+ return true;
}
bool Validator::Alias(const ast::Alias* alias) const {
- auto name = symbols_.NameFor(alias->name);
- if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
- AddError("'" + name + "' is a builtin and cannot be redeclared as an alias",
- alias->source);
- return false;
- }
+ auto name = symbols_.NameFor(alias->name);
+ if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+ AddError("'" + name + "' is a builtin and cannot be redeclared as an alias", alias->source);
+ return false;
+ }
- return true;
+ return true;
}
-bool Validator::Structure(const sem::Struct* str,
- ast::PipelineStage stage) const {
- auto name = symbols_.NameFor(str->Declaration()->name);
- if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
- AddError("'" + name + "' is a builtin and cannot be redeclared as a struct",
- str->Declaration()->source);
- return false;
- }
-
- if (str->Members().empty()) {
- AddError("structures must have at least one member",
- str->Declaration()->source);
- return false;
- }
-
- std::unordered_set<uint32_t> locations;
- for (auto* member : str->Members()) {
- if (auto* r = member->Type()->As<sem::Array>()) {
- if (r->IsRuntimeSized()) {
- if (member != str->Members().back()) {
- AddError(
- "runtime arrays may only appear as the last member of a struct",
- member->Declaration()->source);
- return false;
- }
- }
- } else if (!IsFixedFootprint(member->Type())) {
- AddError(
- "a struct that contains a runtime array cannot be nested inside "
- "another struct",
- member->Declaration()->source);
- return false;
- }
-
- auto has_location = false;
- auto has_position = false;
- const ast::InvariantAttribute* invariant_attribute = nullptr;
- const ast::InterpolateAttribute* interpolate_attribute = nullptr;
- for (auto* attr : member->Declaration()->attributes) {
- if (!attr->IsAnyOf<ast::BuiltinAttribute, //
- ast::InternalAttribute, //
- ast::InterpolateAttribute, //
- ast::InvariantAttribute, //
- ast::LocationAttribute, //
- ast::StructMemberOffsetAttribute, //
- ast::StructMemberSizeAttribute, //
- ast::StructMemberAlignAttribute>()) {
- if (attr->Is<ast::StrideAttribute>() &&
- IsValidationDisabled(
- member->Declaration()->attributes,
- ast::DisabledValidation::kIgnoreStrideAttribute)) {
- continue;
- }
- AddError("attribute is not valid for structure members", attr->source);
+bool Validator::Structure(const sem::Struct* str, ast::PipelineStage stage) const {
+ auto name = symbols_.NameFor(str->Declaration()->name);
+ if (sem::ParseBuiltinType(name) != sem::BuiltinType::kNone) {
+ AddError("'" + name + "' is a builtin and cannot be redeclared as a struct",
+ str->Declaration()->source);
return false;
- }
-
- if (auto* invariant = attr->As<ast::InvariantAttribute>()) {
- invariant_attribute = invariant;
- } else if (auto* location = attr->As<ast::LocationAttribute>()) {
- has_location = true;
- if (!LocationAttribute(location, member->Type(), locations, stage,
- member->Declaration()->source)) {
- return false;
- }
- } else if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
- if (!BuiltinAttribute(builtin, member->Type(), stage,
- /* is_input */ false)) {
- return false;
- }
- if (builtin->builtin == ast::Builtin::kPosition) {
- has_position = true;
- }
- } else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
- interpolate_attribute = interpolate;
- if (!InterpolateAttribute(interpolate, member->Type())) {
- return false;
- }
- }
}
- if (invariant_attribute && !has_position) {
- AddError("invariant attribute must only be applied to a position builtin",
- invariant_attribute->source);
- return false;
+ if (str->Members().empty()) {
+ AddError("structures must have at least one member", str->Declaration()->source);
+ return false;
}
- if (interpolate_attribute && !has_location) {
- AddError("interpolate attribute must only be used with @location",
- interpolate_attribute->source);
- return false;
- }
- }
+ std::unordered_set<uint32_t> locations;
+ for (auto* member : str->Members()) {
+ if (auto* r = member->Type()->As<sem::Array>()) {
+ if (r->IsRuntimeSized()) {
+ if (member != str->Members().back()) {
+ AddError("runtime arrays may only appear as the last member of a struct",
+ member->Declaration()->source);
+ return false;
+ }
+ }
+ } else if (!IsFixedFootprint(member->Type())) {
+ AddError(
+ "a struct that contains a runtime array cannot be nested inside "
+ "another struct",
+ member->Declaration()->source);
+ return false;
+ }
- for (auto* attr : str->Declaration()->attributes) {
- if (!(attr->IsAnyOf<ast::InternalAttribute>())) {
- AddError("attribute is not valid for struct declarations", attr->source);
- return false;
- }
- }
+ auto has_location = false;
+ auto has_position = false;
+ const ast::InvariantAttribute* invariant_attribute = nullptr;
+ const ast::InterpolateAttribute* interpolate_attribute = nullptr;
+ for (auto* attr : member->Declaration()->attributes) {
+ if (!attr->IsAnyOf<ast::BuiltinAttribute, //
+ ast::InternalAttribute, //
+ ast::InterpolateAttribute, //
+ ast::InvariantAttribute, //
+ ast::LocationAttribute, //
+ ast::StructMemberOffsetAttribute, //
+ ast::StructMemberSizeAttribute, //
+ ast::StructMemberAlignAttribute>()) {
+ if (attr->Is<ast::StrideAttribute>() &&
+ IsValidationDisabled(member->Declaration()->attributes,
+ ast::DisabledValidation::kIgnoreStrideAttribute)) {
+ continue;
+ }
+ AddError("attribute is not valid for structure members", attr->source);
+ return false;
+ }
- return true;
+ if (auto* invariant = attr->As<ast::InvariantAttribute>()) {
+ invariant_attribute = invariant;
+ } else if (auto* location = attr->As<ast::LocationAttribute>()) {
+ has_location = true;
+ if (!LocationAttribute(location, member->Type(), locations, stage,
+ member->Declaration()->source)) {
+ return false;
+ }
+ } else if (auto* builtin = attr->As<ast::BuiltinAttribute>()) {
+ if (!BuiltinAttribute(builtin, member->Type(), stage,
+ /* is_input */ false)) {
+ return false;
+ }
+ if (builtin->builtin == ast::Builtin::kPosition) {
+ has_position = true;
+ }
+ } else if (auto* interpolate = attr->As<ast::InterpolateAttribute>()) {
+ interpolate_attribute = interpolate;
+ if (!InterpolateAttribute(interpolate, member->Type())) {
+ return false;
+ }
+ }
+ }
+
+ if (invariant_attribute && !has_position) {
+ AddError("invariant attribute must only be applied to a position builtin",
+ invariant_attribute->source);
+ return false;
+ }
+
+ if (interpolate_attribute && !has_location) {
+ AddError("interpolate attribute must only be used with @location",
+ interpolate_attribute->source);
+ return false;
+ }
+ }
+
+ for (auto* attr : str->Declaration()->attributes) {
+ if (!(attr->IsAnyOf<ast::InternalAttribute>())) {
+ AddError("attribute is not valid for struct declarations", attr->source);
+ return false;
+ }
+ }
+
+ return true;
}
bool Validator::LocationAttribute(const ast::LocationAttribute* location,
@@ -2249,282 +2116,259 @@
ast::PipelineStage stage,
const Source& source,
const bool is_input) const {
- std::string inputs_or_output = is_input ? "inputs" : "output";
- if (stage == ast::PipelineStage::kCompute) {
- AddError("attribute is not valid for compute shader " + inputs_or_output,
- location->source);
- return false;
- }
+ std::string inputs_or_output = is_input ? "inputs" : "output";
+ if (stage == ast::PipelineStage::kCompute) {
+ AddError("attribute is not valid for compute shader " + inputs_or_output, location->source);
+ return false;
+ }
- if (!type->is_numeric_scalar_or_vector()) {
- std::string invalid_type = sem_.TypeNameOf(type);
- AddError("cannot apply 'location' attribute to declaration of type '" +
- invalid_type + "'",
- source);
- AddNote(
- "'location' attribute must only be applied to declarations of "
- "numeric scalar or numeric vector type",
- location->source);
- return false;
- }
+ if (!type->is_numeric_scalar_or_vector()) {
+ std::string invalid_type = sem_.TypeNameOf(type);
+ AddError("cannot apply 'location' attribute to declaration of type '" + invalid_type + "'",
+ source);
+ AddNote(
+ "'location' attribute must only be applied to declarations of "
+ "numeric scalar or numeric vector type",
+ location->source);
+ return false;
+ }
- if (locations.count(location->value)) {
- AddError(attr_to_str(location) + " attribute appears multiple times",
- location->source);
- return false;
- }
- locations.emplace(location->value);
+ if (locations.count(location->value)) {
+ AddError(attr_to_str(location) + " attribute appears multiple times", location->source);
+ return false;
+ }
+ locations.emplace(location->value);
- return true;
+ return true;
}
bool Validator::Return(const ast::ReturnStatement* ret,
const sem::Type* func_type,
const sem::Type* ret_type,
sem::Statement* current_statement) const {
- if (func_type->UnwrapRef() != ret_type) {
- AddError(
- "return statement type must match its function "
- "return type, returned '" +
- sem_.TypeNameOf(ret_type) + "', expected '" +
- sem_.TypeNameOf(func_type) + "'",
- ret->source);
- return false;
- }
-
- auto* sem = sem_.Get(ret);
- if (auto* continuing =
- ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
- AddError("continuing blocks must not contain a return statement",
- ret->source);
- if (continuing != sem->Declaration() &&
- continuing != sem->Parent()->Declaration()) {
- AddNote("see continuing block here", continuing->source);
+ if (func_type->UnwrapRef() != ret_type) {
+ AddError(
+ "return statement type must match its function "
+ "return type, returned '" +
+ sem_.TypeNameOf(ret_type) + "', expected '" + sem_.TypeNameOf(func_type) + "'",
+ ret->source);
+ return false;
}
- return false;
- }
- return true;
+ auto* sem = sem_.Get(ret);
+ if (auto* continuing = ClosestContinuing(/*stop_at_loop*/ false, current_statement)) {
+ AddError("continuing blocks must not contain a return statement", ret->source);
+ if (continuing != sem->Declaration() && continuing != sem->Parent()->Declaration()) {
+ AddNote("see continuing block here", continuing->source);
+ }
+ return false;
+ }
+
+ return true;
}
bool Validator::SwitchStatement(const ast::SwitchStatement* s) {
- auto* cond_ty = sem_.TypeOf(s->condition)->UnwrapRef();
- if (!cond_ty->is_integer_scalar()) {
- AddError(
- "switch statement selector expression must be of a "
- "scalar integer type",
- s->condition->source);
- return false;
- }
-
- bool has_default = false;
- std::unordered_map<uint32_t, Source> selectors;
-
- for (auto* case_stmt : s->body) {
- if (case_stmt->IsDefault()) {
- if (has_default) {
- // More than one default clause
- AddError("switch statement must have exactly one default clause",
- case_stmt->source);
- return false;
- }
- has_default = true;
- }
-
- for (auto* selector : case_stmt->selectors) {
- if (cond_ty != sem_.TypeOf(selector)) {
+ auto* cond_ty = sem_.TypeOf(s->condition)->UnwrapRef();
+ if (!cond_ty->is_integer_scalar()) {
AddError(
- "the case selector values must have the same "
- "type as the selector expression.",
- case_stmt->source);
+ "switch statement selector expression must be of a "
+ "scalar integer type",
+ s->condition->source);
return false;
- }
+ }
- auto v = selector->ValueAsU32();
- auto it = selectors.find(v);
- if (it != selectors.end()) {
- auto val = selector->Is<ast::IntLiteralExpression>()
- ? std::to_string(selector->ValueAsI32())
- : std::to_string(selector->ValueAsU32());
- AddError("duplicate switch case '" + val + "'", selector->source);
- AddNote("previous case declared here", it->second);
+ bool has_default = false;
+ std::unordered_map<uint32_t, Source> selectors;
+
+ for (auto* case_stmt : s->body) {
+ if (case_stmt->IsDefault()) {
+ if (has_default) {
+ // More than one default clause
+ AddError("switch statement must have exactly one default clause",
+ case_stmt->source);
+ return false;
+ }
+ has_default = true;
+ }
+
+ for (auto* selector : case_stmt->selectors) {
+ if (cond_ty != sem_.TypeOf(selector)) {
+ AddError(
+ "the case selector values must have the same "
+ "type as the selector expression.",
+ case_stmt->source);
+ return false;
+ }
+
+ auto v = selector->ValueAsU32();
+ auto it = selectors.find(v);
+ if (it != selectors.end()) {
+ auto val = selector->Is<ast::IntLiteralExpression>()
+ ? std::to_string(selector->ValueAsI32())
+ : std::to_string(selector->ValueAsU32());
+ AddError("duplicate switch case '" + val + "'", selector->source);
+ AddNote("previous case declared here", it->second);
+ return false;
+ }
+ selectors.emplace(v, selector->source);
+ }
+ }
+
+ if (!has_default) {
+ // No default clause
+ AddError("switch statement must have a default clause", s->source);
return false;
- }
- selectors.emplace(v, selector->source);
}
- }
- if (!has_default) {
- // No default clause
- AddError("switch statement must have a default clause", s->source);
- return false;
- }
-
- return true;
+ return true;
}
-bool Validator::Assignment(const ast::Statement* a,
- const sem::Type* rhs_ty) const {
- const ast::Expression* lhs;
- const ast::Expression* rhs;
- if (auto* assign = a->As<ast::AssignmentStatement>()) {
- lhs = assign->lhs;
- rhs = assign->rhs;
- } else if (auto* compound = a->As<ast::CompoundAssignmentStatement>()) {
- lhs = compound->lhs;
- rhs = compound->rhs;
- } else {
- TINT_ICE(Resolver, diagnostics_) << "invalid assignment statement";
- return false;
- }
-
- if (lhs->Is<ast::PhonyExpression>()) {
- // https://www.w3.org/TR/WGSL/#phony-assignment-section
- auto* ty = rhs_ty->UnwrapRef();
- if (!ty->IsConstructible() &&
- !ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler>()) {
- AddError(
- "cannot assign '" + sem_.TypeNameOf(rhs_ty) +
- "' to '_'. '_' can only be assigned a constructible, pointer, "
- "texture or sampler type",
- rhs->source);
- return false;
+bool Validator::Assignment(const ast::Statement* a, const sem::Type* rhs_ty) const {
+ const ast::Expression* lhs;
+ const ast::Expression* rhs;
+ if (auto* assign = a->As<ast::AssignmentStatement>()) {
+ lhs = assign->lhs;
+ rhs = assign->rhs;
+ } else if (auto* compound = a->As<ast::CompoundAssignmentStatement>()) {
+ lhs = compound->lhs;
+ rhs = compound->rhs;
+ } else {
+ TINT_ICE(Resolver, diagnostics_) << "invalid assignment statement";
+ return false;
}
- return true; // RHS can be anything.
- }
- // https://gpuweb.github.io/gpuweb/wgsl/#assignment-statement
- auto const* lhs_ty = sem_.TypeOf(lhs);
-
- if (auto* var = sem_.ResolvedSymbol<sem::Variable>(lhs)) {
- auto* decl = var->Declaration();
- if (var->Is<sem::Parameter>()) {
- AddError("cannot assign to function parameter", lhs->source);
- AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:",
- decl->source);
- return false;
+ if (lhs->Is<ast::PhonyExpression>()) {
+ // https://www.w3.org/TR/WGSL/#phony-assignment-section
+ auto* ty = rhs_ty->UnwrapRef();
+ if (!ty->IsConstructible() && !ty->IsAnyOf<sem::Pointer, sem::Texture, sem::Sampler>()) {
+ AddError("cannot assign '" + sem_.TypeNameOf(rhs_ty) +
+ "' to '_'. '_' can only be assigned a constructible, pointer, "
+ "texture or sampler type",
+ rhs->source);
+ return false;
+ }
+ return true; // RHS can be anything.
}
- if (decl->is_const) {
- AddError("cannot assign to const", lhs->source);
- AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:",
- decl->source);
- return false;
+
+ // https://gpuweb.github.io/gpuweb/wgsl/#assignment-statement
+ auto const* lhs_ty = sem_.TypeOf(lhs);
+
+ if (auto* var = sem_.ResolvedSymbol<sem::Variable>(lhs)) {
+ auto* decl = var->Declaration();
+ if (var->Is<sem::Parameter>()) {
+ AddError("cannot assign to function parameter", lhs->source);
+ AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
+ return false;
+ }
+ if (decl->is_const) {
+ AddError("cannot assign to const", lhs->source);
+ AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
+ return false;
+ }
}
- }
- auto* lhs_ref = lhs_ty->As<sem::Reference>();
- if (!lhs_ref) {
- // LHS is not a reference, so it has no storage.
- AddError("cannot assign to value of type '" + sem_.TypeNameOf(lhs_ty) + "'",
- lhs->source);
- return false;
- }
+ auto* lhs_ref = lhs_ty->As<sem::Reference>();
+ if (!lhs_ref) {
+ // LHS is not a reference, so it has no storage.
+ AddError("cannot assign to value of type '" + sem_.TypeNameOf(lhs_ty) + "'", lhs->source);
+ return false;
+ }
- auto* storage_ty = lhs_ref->StoreType();
- auto* value_type = rhs_ty->UnwrapRef(); // Implicit load of RHS
+ auto* storage_ty = lhs_ref->StoreType();
+ auto* value_type = rhs_ty->UnwrapRef(); // Implicit load of RHS
- // Value type has to match storage type
- if (storage_ty != value_type) {
- AddError("cannot assign '" + sem_.TypeNameOf(rhs_ty) + "' to '" +
- sem_.TypeNameOf(lhs_ty) + "'",
- a->source);
- return false;
- }
- if (!storage_ty->IsConstructible()) {
- AddError("storage type of assignment must be constructible", a->source);
- return false;
- }
- if (lhs_ref->Access() == ast::Access::kRead) {
- AddError("cannot store into a read-only type '" +
- sem_.RawTypeNameOf(lhs_ty) + "'",
- a->source);
- return false;
- }
- return true;
+ // Value type has to match storage type
+ if (storage_ty != value_type) {
+ AddError(
+ "cannot assign '" + sem_.TypeNameOf(rhs_ty) + "' to '" + sem_.TypeNameOf(lhs_ty) + "'",
+ a->source);
+ return false;
+ }
+ if (!storage_ty->IsConstructible()) {
+ AddError("storage type of assignment must be constructible", a->source);
+ return false;
+ }
+ if (lhs_ref->Access() == ast::Access::kRead) {
+ AddError("cannot store into a read-only type '" + sem_.RawTypeNameOf(lhs_ty) + "'",
+ a->source);
+ return false;
+ }
+ return true;
}
-bool Validator::IncrementDecrementStatement(
- const ast::IncrementDecrementStatement* inc) const {
- const ast::Expression* lhs = inc->lhs;
+bool Validator::IncrementDecrementStatement(const ast::IncrementDecrementStatement* inc) const {
+ const ast::Expression* lhs = inc->lhs;
- // https://gpuweb.github.io/gpuweb/wgsl/#increment-decrement
+ // https://gpuweb.github.io/gpuweb/wgsl/#increment-decrement
- if (auto* var = sem_.ResolvedSymbol<sem::Variable>(lhs)) {
- auto* decl = var->Declaration();
- if (var->Is<sem::Parameter>()) {
- AddError("cannot modify function parameter", lhs->source);
- AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:",
- decl->source);
- return false;
+ if (auto* var = sem_.ResolvedSymbol<sem::Variable>(lhs)) {
+ auto* decl = var->Declaration();
+ if (var->Is<sem::Parameter>()) {
+ AddError("cannot modify function parameter", lhs->source);
+ AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
+ return false;
+ }
+ if (decl->is_const) {
+ AddError("cannot modify constant value", lhs->source);
+ AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:", decl->source);
+ return false;
+ }
}
- if (decl->is_const) {
- AddError("cannot modify constant value", lhs->source);
- AddNote("'" + symbols_.NameFor(decl->symbol) + "' is declared here:",
- decl->source);
- return false;
+
+ auto const* lhs_ty = sem_.TypeOf(lhs);
+ auto* lhs_ref = lhs_ty->As<sem::Reference>();
+ if (!lhs_ref) {
+ // LHS is not a reference, so it has no storage.
+ AddError("cannot modify value of type '" + sem_.TypeNameOf(lhs_ty) + "'", lhs->source);
+ return false;
}
- }
- auto const* lhs_ty = sem_.TypeOf(lhs);
- auto* lhs_ref = lhs_ty->As<sem::Reference>();
- if (!lhs_ref) {
- // LHS is not a reference, so it has no storage.
- AddError("cannot modify value of type '" + sem_.TypeNameOf(lhs_ty) + "'",
- lhs->source);
- return false;
- }
+ if (!lhs_ref->StoreType()->is_integer_scalar()) {
+ const std::string kind = inc->increment ? "increment" : "decrement";
+ AddError(kind + " statement can only be applied to an integer scalar", lhs->source);
+ return false;
+ }
- if (!lhs_ref->StoreType()->is_integer_scalar()) {
- const std::string kind = inc->increment ? "increment" : "decrement";
- AddError(kind + " statement can only be applied to an integer scalar",
- lhs->source);
- return false;
- }
-
- if (lhs_ref->Access() == ast::Access::kRead) {
- AddError(
- "cannot modify read-only type '" + sem_.RawTypeNameOf(lhs_ty) + "'",
- inc->source);
- return false;
- }
- return true;
+ if (lhs_ref->Access() == ast::Access::kRead) {
+ AddError("cannot modify read-only type '" + sem_.RawTypeNameOf(lhs_ty) + "'", inc->source);
+ return false;
+ }
+ return true;
}
-bool Validator::NoDuplicateAttributes(
- const ast::AttributeList& attributes) const {
- std::unordered_map<const TypeInfo*, Source> seen;
- for (auto* d : attributes) {
- auto res = seen.emplace(&d->TypeInfo(), d->source);
- if (!res.second && !d->Is<ast::InternalAttribute>()) {
- AddError("duplicate " + d->Name() + " attribute", d->source);
- AddNote("first attribute declared here", res.first->second);
- return false;
+bool Validator::NoDuplicateAttributes(const ast::AttributeList& attributes) const {
+ std::unordered_map<const TypeInfo*, Source> seen;
+ for (auto* d : attributes) {
+ auto res = seen.emplace(&d->TypeInfo(), d->source);
+ if (!res.second && !d->Is<ast::InternalAttribute>()) {
+ AddError("duplicate " + d->Name() + " attribute", d->source);
+ AddNote("first attribute declared here", res.first->second);
+ return false;
+ }
}
- }
- return true;
+ return true;
}
bool Validator::IsValidationDisabled(const ast::AttributeList& attributes,
ast::DisabledValidation validation) const {
- for (auto* attribute : attributes) {
- if (auto* dv = attribute->As<ast::DisableValidationAttribute>()) {
- if (dv->validation == validation) {
- return true;
- }
+ for (auto* attribute : attributes) {
+ if (auto* dv = attribute->As<ast::DisableValidationAttribute>()) {
+ if (dv->validation == validation) {
+ return true;
+ }
+ }
}
- }
- return false;
+ return false;
}
bool Validator::IsValidationEnabled(const ast::AttributeList& attributes,
ast::DisabledValidation validation) const {
- return !IsValidationDisabled(attributes, validation);
+ return !IsValidationDisabled(attributes, validation);
}
-std::string Validator::VectorPretty(uint32_t size,
- const sem::Type* element_type) const {
- sem::Vector vec_type(element_type, size);
- return vec_type.FriendlyName(symbols_);
+std::string Validator::VectorPretty(uint32_t size, const sem::Type* element_type) const {
+ sem::Vector vec_type(element_type, size);
+ return vec_type.FriendlyName(symbols_);
}
} // namespace tint::resolver
diff --git a/src/tint/resolver/validator.h b/src/tint/resolver/validator.h
index 77e5172..9901dc9 100644
--- a/src/tint/resolver/validator.h
+++ b/src/tint/resolver/validator.h
@@ -66,384 +66,372 @@
/// also assume that sem changes have already been made. The validation checks
/// should not alter the AST or SEM trees.
class Validator {
- public:
- /// The valid type storage layouts typedef
- using ValidTypeStorageLayouts =
- std::set<std::pair<const sem::Type*, ast::StorageClass>>;
+ public:
+ /// The valid type storage layouts typedef
+ using ValidTypeStorageLayouts = std::set<std::pair<const sem::Type*, ast::StorageClass>>;
- /// Constructor
- /// @param builder the program builder
- /// @param helper the SEM helper to validate with
- Validator(ProgramBuilder* builder, SemHelper& helper);
- ~Validator();
+ /// Constructor
+ /// @param builder the program builder
+ /// @param helper the SEM helper to validate with
+ Validator(ProgramBuilder* builder, SemHelper& helper);
+ ~Validator();
- /// Adds the given error message to the diagnostics
- /// @param msg the error message
- /// @param source the error source
- void AddError(const std::string& msg, const Source& source) const;
+ /// Adds the given error message to the diagnostics
+ /// @param msg the error message
+ /// @param source the error source
+ void AddError(const std::string& msg, const Source& source) const;
- /// Adds the given warning message to the diagnostics
- /// @param msg the warning message
- /// @param source the warning source
- void AddWarning(const std::string& msg, const Source& source) const;
+ /// Adds the given warning message to the diagnostics
+ /// @param msg the warning message
+ /// @param source the warning source
+ void AddWarning(const std::string& msg, const Source& source) const;
- /// Adds the given note message to the diagnostics
- /// @param msg the note message
- /// @param source the note source
- void AddNote(const std::string& msg, const Source& source) const;
+ /// Adds the given note message to the diagnostics
+ /// @param msg the note message
+ /// @param source the note source
+ void AddNote(const std::string& msg, const Source& source) const;
- /// @param type the given type
- /// @returns true if the given type is a plain type
- bool IsPlain(const sem::Type* type) const;
+ /// @param type the given type
+ /// @returns true if the given type is a plain type
+ bool IsPlain(const sem::Type* type) const;
- /// @param type the given type
- /// @returns true if the given type is a fixed-footprint type
- bool IsFixedFootprint(const sem::Type* type) const;
+ /// @param type the given type
+ /// @returns true if the given type is a fixed-footprint type
+ bool IsFixedFootprint(const sem::Type* type) const;
- /// @param type the given type
- /// @returns true if the given type is storable
- bool IsStorable(const sem::Type* type) const;
+ /// @param type the given type
+ /// @returns true if the given type is storable
+ bool IsStorable(const sem::Type* type) const;
- /// @param type the given type
- /// @returns true if the given type is host-shareable
- bool IsHostShareable(const sem::Type* type) const;
+ /// @param type the given type
+ /// @returns true if the given type is host-shareable
+ bool IsHostShareable(const sem::Type* type) const;
- /// Validates pipeline stages
- /// @param entry_points the entry points to the module
- /// @returns true on success, false otherwise.
- bool PipelineStages(const std::vector<sem::Function*>& entry_points) const;
+ /// Validates pipeline stages
+ /// @param entry_points the entry points to the module
+ /// @returns true on success, false otherwise.
+ bool PipelineStages(const std::vector<sem::Function*>& entry_points) const;
- /// Validates aliases
- /// @param alias the alias to validate
- /// @returns true on success, false otherwise.
- bool Alias(const ast::Alias* alias) const;
+ /// Validates aliases
+ /// @param alias the alias to validate
+ /// @returns true on success, false otherwise.
+ bool Alias(const ast::Alias* alias) const;
- /// Validates the array
- /// @param arr the array to validate
- /// @param source the source of the array
- /// @returns true on success, false otherwise.
- bool Array(const sem::Array* arr, const Source& source) const;
+ /// Validates the array
+ /// @param arr the array to validate
+ /// @param source the source of the array
+ /// @returns true on success, false otherwise.
+ bool Array(const sem::Array* arr, const Source& source) const;
- /// Validates an array stride attribute
- /// @param attr the stride attribute to validate
- /// @param el_size the element size
- /// @param el_align the element alignment
- /// @param source the source of the attribute
- /// @returns true on success, false otherwise
- bool ArrayStrideAttribute(const ast::StrideAttribute* attr,
- uint32_t el_size,
- uint32_t el_align,
- const Source& source) const;
+ /// Validates an array stride attribute
+ /// @param attr the stride attribute to validate
+ /// @param el_size the element size
+ /// @param el_align the element alignment
+ /// @param source the source of the attribute
+ /// @returns true on success, false otherwise
+ bool ArrayStrideAttribute(const ast::StrideAttribute* attr,
+ uint32_t el_size,
+ uint32_t el_align,
+ const Source& source) const;
- /// Validates an atomic
- /// @param a the atomic ast node to validate
- /// @param s the atomic sem node
- /// @returns true on success, false otherwise.
- bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const;
+ /// Validates an atomic
+ /// @param a the atomic ast node to validate
+ /// @param s the atomic sem node
+ /// @returns true on success, false otherwise.
+ bool Atomic(const ast::Atomic* a, const sem::Atomic* s) const;
- /// Validates an atoic variable
- /// @param var the variable to validate
- /// @param atomic_composite_info store atomic information
- /// @returns true on success, false otherwise.
- bool AtomicVariable(const sem::Variable* var,
- std::unordered_map<const sem::Type*, const Source&>
- atomic_composite_info) const;
+ /// Validates an atoic variable
+ /// @param var the variable to validate
+ /// @param atomic_composite_info store atomic information
+ /// @returns true on success, false otherwise.
+ bool AtomicVariable(
+ const sem::Variable* var,
+ std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const;
- /// Validates an assignment
- /// @param a the assignment statement
- /// @param rhs_ty the type of the right hand side
- /// @returns true on success, false otherwise.
- bool Assignment(const ast::Statement* a, const sem::Type* rhs_ty) const;
+ /// Validates an assignment
+ /// @param a the assignment statement
+ /// @param rhs_ty the type of the right hand side
+ /// @returns true on success, false otherwise.
+ bool Assignment(const ast::Statement* a, const sem::Type* rhs_ty) const;
- /// Validates a bitcase
- /// @param cast the bitcast expression
- /// @param to the destination type
- /// @returns true on success, false otherwise
- bool Bitcast(const ast::BitcastExpression* cast, const sem::Type* to) const;
+ /// Validates a bitcase
+ /// @param cast the bitcast expression
+ /// @param to the destination type
+ /// @returns true on success, false otherwise
+ bool Bitcast(const ast::BitcastExpression* cast, const sem::Type* to) const;
- /// Validates a break statement
- /// @param stmt the break statement to validate
- /// @param current_statement the current statement being resolved
- /// @returns true on success, false otherwise.
- bool BreakStatement(const sem::Statement* stmt,
- sem::Statement* current_statement) const;
+ /// Validates a break statement
+ /// @param stmt the break statement to validate
+ /// @param current_statement the current statement being resolved
+ /// @returns true on success, false otherwise.
+ bool BreakStatement(const sem::Statement* stmt, sem::Statement* current_statement) const;
- /// Validates a builtin attribute
- /// @param attr the attribute to validate
- /// @param storage_type the attribute storage type
- /// @param stage the current pipeline stage
- /// @param is_input true if this is an input attribute
- /// @returns true on success, false otherwise.
- bool BuiltinAttribute(const ast::BuiltinAttribute* attr,
- const sem::Type* storage_type,
- ast::PipelineStage stage,
- const bool is_input) const;
+ /// Validates a builtin attribute
+ /// @param attr the attribute to validate
+ /// @param storage_type the attribute storage type
+ /// @param stage the current pipeline stage
+ /// @param is_input true if this is an input attribute
+ /// @returns true on success, false otherwise.
+ bool BuiltinAttribute(const ast::BuiltinAttribute* attr,
+ const sem::Type* storage_type,
+ ast::PipelineStage stage,
+ const bool is_input) const;
- /// Validates a continue statement
- /// @param stmt the continue statement to validate
- /// @param current_statement the current statement being resolved
- /// @returns true on success, false otherwise
- bool ContinueStatement(const sem::Statement* stmt,
- sem::Statement* current_statement) const;
+ /// Validates a continue statement
+ /// @param stmt the continue statement to validate
+ /// @param current_statement the current statement being resolved
+ /// @returns true on success, false otherwise
+ bool ContinueStatement(const sem::Statement* stmt, sem::Statement* current_statement) const;
- /// Validates a discard statement
- /// @param stmt the statement to validate
- /// @param current_statement the current statement being resolved
- /// @returns true on success, false otherwise
- bool DiscardStatement(const sem::Statement* stmt,
- sem::Statement* current_statement) const;
+ /// Validates a discard statement
+ /// @param stmt the statement to validate
+ /// @param current_statement the current statement being resolved
+ /// @returns true on success, false otherwise
+ bool DiscardStatement(const sem::Statement* stmt, sem::Statement* current_statement) const;
- /// Validates an entry point
- /// @param func the entry point function to validate
- /// @param stage the pipeline stage for the entry point
- /// @returns true on success, false otherwise
- bool EntryPoint(const sem::Function* func, ast::PipelineStage stage) const;
+ /// Validates an entry point
+ /// @param func the entry point function to validate
+ /// @param stage the pipeline stage for the entry point
+ /// @returns true on success, false otherwise
+ bool EntryPoint(const sem::Function* func, ast::PipelineStage stage) const;
- /// Validates a for loop
- /// @param stmt the for loop statement to validate
- /// @returns true on success, false otherwise
- bool ForLoopStatement(const sem::ForLoopStatement* stmt) const;
+ /// Validates a for loop
+ /// @param stmt the for loop statement to validate
+ /// @returns true on success, false otherwise
+ bool ForLoopStatement(const sem::ForLoopStatement* stmt) const;
- /// Validates a fallthrough statement
- /// @param stmt the fallthrough to validate
- /// @returns true on success, false otherwise
- bool FallthroughStatement(const sem::Statement* stmt) const;
+ /// Validates a fallthrough statement
+ /// @param stmt the fallthrough to validate
+ /// @returns true on success, false otherwise
+ bool FallthroughStatement(const sem::Statement* stmt) const;
- /// Validates a function
- /// @param func the function to validate
- /// @param stage the current pipeline stage
- /// @returns true on success, false otherwise.
- bool Function(const sem::Function* func, ast::PipelineStage stage) const;
+ /// Validates a function
+ /// @param func the function to validate
+ /// @param stage the current pipeline stage
+ /// @returns true on success, false otherwise.
+ bool Function(const sem::Function* func, ast::PipelineStage stage) const;
- /// Validates a function call
- /// @param call the function call to validate
- /// @param current_statement the current statement being resolved
- /// @returns true on success, false otherwise
- bool FunctionCall(const sem::Call* call,
- sem::Statement* current_statement) const;
+ /// Validates a function call
+ /// @param call the function call to validate
+ /// @param current_statement the current statement being resolved
+ /// @returns true on success, false otherwise
+ bool FunctionCall(const sem::Call* call, sem::Statement* current_statement) const;
- /// Validates a global variable
- /// @param var the global variable to validate
- /// @param constant_ids the set of constant ids in the module
- /// @param atomic_composite_info atomic composite info in the module
- /// @returns true on success, false otherwise
- bool GlobalVariable(
- const sem::Variable* var,
- std::unordered_map<uint32_t, const sem::Variable*> constant_ids,
- std::unordered_map<const sem::Type*, const Source&> atomic_composite_info)
- const;
+ /// Validates a global variable
+ /// @param var the global variable to validate
+ /// @param constant_ids the set of constant ids in the module
+ /// @param atomic_composite_info atomic composite info in the module
+ /// @returns true on success, false otherwise
+ bool GlobalVariable(
+ const sem::Variable* var,
+ std::unordered_map<uint32_t, const sem::Variable*> constant_ids,
+ std::unordered_map<const sem::Type*, const Source&> atomic_composite_info) const;
- /// Validates an if statement
- /// @param stmt the statement to validate
- /// @returns true on success, false otherwise
- bool IfStatement(const sem::IfStatement* stmt) const;
+ /// Validates an if statement
+ /// @param stmt the statement to validate
+ /// @returns true on success, false otherwise
+ bool IfStatement(const sem::IfStatement* stmt) const;
- /// Validates an increment or decrement statement
- /// @param stmt the statement to validate
- /// @returns true on success, false otherwise
- bool IncrementDecrementStatement(
- const ast::IncrementDecrementStatement* stmt) const;
+ /// Validates an increment or decrement statement
+ /// @param stmt the statement to validate
+ /// @returns true on success, false otherwise
+ bool IncrementDecrementStatement(const ast::IncrementDecrementStatement* stmt) const;
- /// Validates an interpolate attribute
- /// @param attr the interpolation attribute to validate
- /// @param storage_type the storage type of the attached variable
- /// @returns true on succes, false otherwise
- bool InterpolateAttribute(const ast::InterpolateAttribute* attr,
- const sem::Type* storage_type) const;
+ /// Validates an interpolate attribute
+ /// @param attr the interpolation attribute to validate
+ /// @param storage_type the storage type of the attached variable
+ /// @returns true on succes, false otherwise
+ bool InterpolateAttribute(const ast::InterpolateAttribute* attr,
+ const sem::Type* storage_type) const;
- /// Validates a builtin call
- /// @param call the builtin call to validate
- /// @returns true on success, false otherwise.
- bool BuiltinCall(const sem::Call* call) const;
+ /// Validates a builtin call
+ /// @param call the builtin call to validate
+ /// @returns true on success, false otherwise.
+ bool BuiltinCall(const sem::Call* call) const;
- /// Validates a location attribute
- /// @param location the location attribute to validate
- /// @param type the variable type
- /// @param locations the set of locations in the module
- /// @param stage the current pipeline stage
- /// @param source the source of the attribute
- /// @param is_input true if this is an input variable
- /// @returns true on success, false otherwise.
- bool LocationAttribute(const ast::LocationAttribute* location,
- const sem::Type* type,
- std::unordered_set<uint32_t>& locations,
- ast::PipelineStage stage,
- const Source& source,
- const bool is_input = false) const;
+ /// Validates a location attribute
+ /// @param location the location attribute to validate
+ /// @param type the variable type
+ /// @param locations the set of locations in the module
+ /// @param stage the current pipeline stage
+ /// @param source the source of the attribute
+ /// @param is_input true if this is an input variable
+ /// @returns true on success, false otherwise.
+ bool LocationAttribute(const ast::LocationAttribute* location,
+ const sem::Type* type,
+ std::unordered_set<uint32_t>& locations,
+ ast::PipelineStage stage,
+ const Source& source,
+ const bool is_input = false) const;
- /// Validates a loop statement
- /// @param stmt the loop statement
- /// @returns true on success, false otherwise.
- bool LoopStatement(const sem::LoopStatement* stmt) const;
+ /// Validates a loop statement
+ /// @param stmt the loop statement
+ /// @returns true on success, false otherwise.
+ bool LoopStatement(const sem::LoopStatement* stmt) const;
- /// Validates a matrix
- /// @param ty the matrix to validate
- /// @param source the source of the matrix
- /// @returns true on success, false otherwise
- bool Matrix(const sem::Matrix* ty, const Source& source) const;
+ /// Validates a matrix
+ /// @param ty the matrix to validate
+ /// @param source the source of the matrix
+ /// @returns true on success, false otherwise
+ bool Matrix(const sem::Matrix* ty, const Source& source) const;
- /// Validates a function parameter
- /// @param func the function the variable is for
- /// @param var the variable to validate
- /// @returns true on success, false otherwise
- bool FunctionParameter(const ast::Function* func,
- const sem::Variable* var) const;
+ /// Validates a function parameter
+ /// @param func the function the variable is for
+ /// @param var the variable to validate
+ /// @returns true on success, false otherwise
+ bool FunctionParameter(const ast::Function* func, const sem::Variable* var) const;
- /// Validates a return
- /// @param ret the return statement to validate
- /// @param func_type the return type of the curreunt function
- /// @param ret_type the return type
- /// @param current_statement the current statement being resolved
- /// @returns true on success, false otherwise
- bool Return(const ast::ReturnStatement* ret,
- const sem::Type* func_type,
- const sem::Type* ret_type,
- sem::Statement* current_statement) const;
+ /// Validates a return
+ /// @param ret the return statement to validate
+ /// @param func_type the return type of the curreunt function
+ /// @param ret_type the return type
+ /// @param current_statement the current statement being resolved
+ /// @returns true on success, false otherwise
+ bool Return(const ast::ReturnStatement* ret,
+ const sem::Type* func_type,
+ const sem::Type* ret_type,
+ sem::Statement* current_statement) const;
- /// Validates a list of statements
- /// @param stmts the statements to validate
- /// @returns true on success, false otherwise
- bool Statements(const ast::StatementList& stmts) const;
+ /// Validates a list of statements
+ /// @param stmts the statements to validate
+ /// @returns true on success, false otherwise
+ bool Statements(const ast::StatementList& stmts) const;
- /// Validates a storage texture
- /// @param t the texture to validate
- /// @returns true on success, false otherwise
- bool StorageTexture(const ast::StorageTexture* t) const;
+ /// Validates a storage texture
+ /// @param t the texture to validate
+ /// @returns true on success, false otherwise
+ bool StorageTexture(const ast::StorageTexture* t) const;
- /// Validates a structure
- /// @param str the structure to validate
- /// @param stage the current pipeline stage
- /// @returns true on success, false otherwise.
- bool Structure(const sem::Struct* str, ast::PipelineStage stage) const;
+ /// Validates a structure
+ /// @param str the structure to validate
+ /// @param stage the current pipeline stage
+ /// @returns true on success, false otherwise.
+ bool Structure(const sem::Struct* str, ast::PipelineStage stage) const;
- /// Validates a structure constructor or cast
- /// @param ctor the call expression to validate
- /// @param struct_type the type of the structure
- /// @returns true on success, false otherwise
- bool StructureConstructorOrCast(const ast::CallExpression* ctor,
- const sem::Struct* struct_type) const;
+ /// Validates a structure constructor or cast
+ /// @param ctor the call expression to validate
+ /// @param struct_type the type of the structure
+ /// @returns true on success, false otherwise
+ bool StructureConstructorOrCast(const ast::CallExpression* ctor,
+ const sem::Struct* struct_type) const;
- /// Validates a switch statement
- /// @param s the switch to validate
- /// @returns true on success, false otherwise
- bool SwitchStatement(const ast::SwitchStatement* s);
+ /// Validates a switch statement
+ /// @param s the switch to validate
+ /// @returns true on success, false otherwise
+ bool SwitchStatement(const ast::SwitchStatement* s);
- /// Validates a variable
- /// @param var the variable to validate
- /// @returns true on success, false otherwise.
- bool Variable(const sem::Variable* var) const;
+ /// Validates a variable
+ /// @param var the variable to validate
+ /// @returns true on success, false otherwise.
+ bool Variable(const sem::Variable* var) const;
- /// Validates a variable constructor or cast
- /// @param var the variable to validate
- /// @param storage_class the storage class of the variable
- /// @param storage_type the type of the storage
- /// @param rhs_type the right hand side of the expression
- /// @returns true on succes, false otherwise
- bool VariableConstructorOrCast(const ast::Variable* var,
- ast::StorageClass storage_class,
- const sem::Type* storage_type,
- const sem::Type* rhs_type) const;
+ /// Validates a variable constructor or cast
+ /// @param var the variable to validate
+ /// @param storage_class the storage class of the variable
+ /// @param storage_type the type of the storage
+ /// @param rhs_type the right hand side of the expression
+ /// @returns true on succes, false otherwise
+ bool VariableConstructorOrCast(const ast::Variable* var,
+ ast::StorageClass storage_class,
+ const sem::Type* storage_type,
+ const sem::Type* rhs_type) const;
- /// Validates a vector
- /// @param ty the vector to validate
- /// @param source the source of the vector
- /// @returns true on success, false otherwise
- bool Vector(const sem::Vector* ty, const Source& source) const;
+ /// Validates a vector
+ /// @param ty the vector to validate
+ /// @param source the source of the vector
+ /// @returns true on success, false otherwise
+ bool Vector(const sem::Vector* ty, const Source& source) const;
- /// Validates a vector constructor or cast
- /// @param ctor the call expression to validate
- /// @param vec_type the vector type
- /// @returns true on success, false otherwise
- bool VectorConstructorOrCast(const ast::CallExpression* ctor,
- const sem::Vector* vec_type) const;
+ /// Validates a vector constructor or cast
+ /// @param ctor the call expression to validate
+ /// @param vec_type the vector type
+ /// @returns true on success, false otherwise
+ bool VectorConstructorOrCast(const ast::CallExpression* ctor,
+ const sem::Vector* vec_type) const;
- /// Validates a matrix constructor or cast
- /// @param ctor the call expression to validate
- /// @param matrix_type the type of the matrix
- /// @returns true on success, false otherwise
- bool MatrixConstructorOrCast(const ast::CallExpression* ctor,
- const sem::Matrix* matrix_type) const;
+ /// Validates a matrix constructor or cast
+ /// @param ctor the call expression to validate
+ /// @param matrix_type the type of the matrix
+ /// @returns true on success, false otherwise
+ bool MatrixConstructorOrCast(const ast::CallExpression* ctor,
+ const sem::Matrix* matrix_type) const;
- /// Validates a scalar constructor or cast
- /// @param ctor the call expression to validate
- /// @param type the type of the scalar
- /// @returns true on success, false otherwise.
- bool ScalarConstructorOrCast(const ast::CallExpression* ctor,
- const sem::Type* type) const;
+ /// Validates a scalar constructor or cast
+ /// @param ctor the call expression to validate
+ /// @param type the type of the scalar
+ /// @returns true on success, false otherwise.
+ bool ScalarConstructorOrCast(const ast::CallExpression* ctor, const sem::Type* type) const;
- /// Validates an array constructor or cast
- /// @param ctor the call expresion to validate
- /// @param arr_type the type of the array
- /// @returns true on success, false otherwise
- bool ArrayConstructorOrCast(const ast::CallExpression* ctor,
- const sem::Array* arr_type) const;
+ /// Validates an array constructor or cast
+ /// @param ctor the call expresion to validate
+ /// @param arr_type the type of the array
+ /// @returns true on success, false otherwise
+ bool ArrayConstructorOrCast(const ast::CallExpression* ctor, const sem::Array* arr_type) const;
- /// Validates a texture builtin function
- /// @param call the builtin call to validate
- /// @returns true on success, false otherwise
- bool TextureBuiltinFunction(const sem::Call* call) const;
+ /// Validates a texture builtin function
+ /// @param call the builtin call to validate
+ /// @returns true on success, false otherwise
+ bool TextureBuiltinFunction(const sem::Call* call) const;
- /// Validates there are no duplicate attributes
- /// @param attributes the list of attributes to validate
- /// @returns true on success, false otherwise.
- bool NoDuplicateAttributes(const ast::AttributeList& attributes) const;
+ /// Validates there are no duplicate attributes
+ /// @param attributes the list of attributes to validate
+ /// @returns true on success, false otherwise.
+ bool NoDuplicateAttributes(const ast::AttributeList& attributes) const;
- /// Validates a storage class layout
- /// @param type the type to validate
- /// @param sc the storage class
- /// @param source the source of the type
- /// @param layouts previously validated storage layouts
- /// @returns true on success, false otherwise
- bool StorageClassLayout(const sem::Type* type,
- ast::StorageClass sc,
- Source source,
- ValidTypeStorageLayouts& layouts) const;
+ /// Validates a storage class layout
+ /// @param type the type to validate
+ /// @param sc the storage class
+ /// @param source the source of the type
+ /// @param layouts previously validated storage layouts
+ /// @returns true on success, false otherwise
+ bool StorageClassLayout(const sem::Type* type,
+ ast::StorageClass sc,
+ Source source,
+ ValidTypeStorageLayouts& layouts) const;
- /// Validates a storage class layout
- /// @param var the variable to validate
- /// @param layouts previously validated storage layouts
- /// @returns true on success, false otherwise.
- bool StorageClassLayout(const sem::Variable* var,
- ValidTypeStorageLayouts& layouts) const;
+ /// Validates a storage class layout
+ /// @param var the variable to validate
+ /// @param layouts previously validated storage layouts
+ /// @returns true on success, false otherwise.
+ bool StorageClassLayout(const sem::Variable* var, ValidTypeStorageLayouts& layouts) const;
- /// @returns true if the attribute list contains a
- /// ast::DisableValidationAttribute with the validation mode equal to
- /// `validation`
- /// @param attributes the attribute list to check
- /// @param validation the validation mode to check
- bool IsValidationDisabled(const ast::AttributeList& attributes,
- ast::DisabledValidation validation) const;
+ /// @returns true if the attribute list contains a
+ /// ast::DisableValidationAttribute with the validation mode equal to
+ /// `validation`
+ /// @param attributes the attribute list to check
+ /// @param validation the validation mode to check
+ bool IsValidationDisabled(const ast::AttributeList& attributes,
+ ast::DisabledValidation validation) const;
- /// @returns true if the attribute list does not contains a
- /// ast::DisableValidationAttribute with the validation mode equal to
- /// `validation`
- /// @param attributes the attribute list to check
- /// @param validation the validation mode to check
- bool IsValidationEnabled(const ast::AttributeList& attributes,
- ast::DisabledValidation validation) const;
+ /// @returns true if the attribute list does not contains a
+ /// ast::DisableValidationAttribute with the validation mode equal to
+ /// `validation`
+ /// @param attributes the attribute list to check
+ /// @param validation the validation mode to check
+ bool IsValidationEnabled(const ast::AttributeList& attributes,
+ ast::DisabledValidation validation) const;
- private:
- /// Searches the current statement and up through parents of the current
- /// statement looking for a loop or for-loop continuing statement.
- /// @returns the closest continuing statement to the current statement that
- /// (transitively) owns the current statement.
- /// @param stop_at_loop if true then the function will return nullptr if a
- /// loop or for-loop was found before the continuing.
- /// @param current_statement the current statement being resolved
- const ast::Statement* ClosestContinuing(
- bool stop_at_loop,
- sem::Statement* current_statement) const;
+ private:
+ /// Searches the current statement and up through parents of the current
+ /// statement looking for a loop or for-loop continuing statement.
+ /// @returns the closest continuing statement to the current statement that
+ /// (transitively) owns the current statement.
+ /// @param stop_at_loop if true then the function will return nullptr if a
+ /// loop or for-loop was found before the continuing.
+ /// @param current_statement the current statement being resolved
+ const ast::Statement* ClosestContinuing(bool stop_at_loop,
+ sem::Statement* current_statement) const;
- /// Returns a human-readable string representation of the vector type name
- /// with the given parameters.
- /// @param size the vector dimension
- /// @param element_type scalar vector sub-element type
- /// @return pretty string representation
- std::string VectorPretty(uint32_t size, const sem::Type* element_type) const;
+ /// Returns a human-readable string representation of the vector type name
+ /// with the given parameters.
+ /// @param size the vector dimension
+ /// @param element_type scalar vector sub-element type
+ /// @return pretty string representation
+ std::string VectorPretty(uint32_t size, const sem::Type* element_type) const;
- SymbolTable& symbols_;
- diag::List& diagnostics_;
- SemHelper& sem_;
+ SymbolTable& symbols_;
+ diag::List& diagnostics_;
+ SemHelper& sem_;
};
} // namespace tint::resolver
diff --git a/src/tint/resolver/validator_is_storeable_test.cc b/src/tint/resolver/validator_is_storeable_test.cc
index 3c82a72..a5f612c 100644
--- a/src/tint/resolver/validator_is_storeable_test.cc
+++ b/src/tint/resolver/validator_is_storeable_test.cc
@@ -24,62 +24,62 @@
using ValidatorIsStorableTest = ResolverTest;
TEST_F(ValidatorIsStorableTest, Void) {
- EXPECT_FALSE(v()->IsStorable(create<sem::Void>()));
+ EXPECT_FALSE(v()->IsStorable(create<sem::Void>()));
}
TEST_F(ValidatorIsStorableTest, Scalar) {
- EXPECT_TRUE(v()->IsStorable(create<sem::Bool>()));
- EXPECT_TRUE(v()->IsStorable(create<sem::I32>()));
- EXPECT_TRUE(v()->IsStorable(create<sem::U32>()));
- EXPECT_TRUE(v()->IsStorable(create<sem::F32>()));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Bool>()));
+ EXPECT_TRUE(v()->IsStorable(create<sem::I32>()));
+ EXPECT_TRUE(v()->IsStorable(create<sem::U32>()));
+ EXPECT_TRUE(v()->IsStorable(create<sem::F32>()));
}
TEST_F(ValidatorIsStorableTest, Vector) {
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::I32>(), 2u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::I32>(), 3u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::I32>(), 4u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::U32>(), 2u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::U32>(), 3u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::U32>(), 4u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 2u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 3u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 4u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::I32>(), 2u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::I32>(), 3u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::I32>(), 4u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::U32>(), 2u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::U32>(), 3u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::U32>(), 4u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 2u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 3u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Vector>(create<sem::F32>(), 4u)));
}
TEST_F(ValidatorIsStorableTest, Matrix) {
- auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
- auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
- auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 2u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 3u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 4u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 2u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 3u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 4u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 2u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 3u)));
- EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 4u)));
+ auto* vec2 = create<sem::Vector>(create<sem::F32>(), 2u);
+ auto* vec3 = create<sem::Vector>(create<sem::F32>(), 3u);
+ auto* vec4 = create<sem::Vector>(create<sem::F32>(), 4u);
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 2u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 3u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec2, 4u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 2u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 3u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec3, 4u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 2u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 3u)));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Matrix>(vec4, 4u)));
}
TEST_F(ValidatorIsStorableTest, Pointer) {
- auto* ptr = create<sem::Pointer>(
- create<sem::I32>(), ast::StorageClass::kPrivate, ast::Access::kReadWrite);
- EXPECT_FALSE(v()->IsStorable(ptr));
+ auto* ptr = create<sem::Pointer>(create<sem::I32>(), ast::StorageClass::kPrivate,
+ ast::Access::kReadWrite);
+ EXPECT_FALSE(v()->IsStorable(ptr));
}
TEST_F(ValidatorIsStorableTest, Atomic) {
- EXPECT_TRUE(v()->IsStorable(create<sem::Atomic>(create<sem::I32>())));
- EXPECT_TRUE(v()->IsStorable(create<sem::Atomic>(create<sem::U32>())));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Atomic>(create<sem::I32>())));
+ EXPECT_TRUE(v()->IsStorable(create<sem::Atomic>(create<sem::U32>())));
}
TEST_F(ValidatorIsStorableTest, ArraySizedOfStorable) {
- auto* arr = create<sem::Array>(create<sem::I32>(), 5u, 4u, 20u, 4u, 4u);
- EXPECT_TRUE(v()->IsStorable(arr));
+ auto* arr = create<sem::Array>(create<sem::I32>(), 5u, 4u, 20u, 4u, 4u);
+ EXPECT_TRUE(v()->IsStorable(arr));
}
TEST_F(ValidatorIsStorableTest, ArrayUnsizedOfStorable) {
- auto* arr = create<sem::Array>(create<sem::I32>(), 0u, 4u, 4u, 4u, 4u);
- EXPECT_TRUE(v()->IsStorable(arr));
+ auto* arr = create<sem::Array>(create<sem::I32>(), 0u, 4u, 4u, 4u, 4u);
+ EXPECT_TRUE(v()->IsStorable(arr));
}
} // namespace
diff --git a/src/tint/resolver/var_let_test.cc b/src/tint/resolver/var_let_test.cc
index 26f4cbc..4e6eaa7 100644
--- a/src/tint/resolver/var_let_test.cc
+++ b/src/tint/resolver/var_let_test.cc
@@ -21,674 +21,653 @@
namespace tint::resolver {
namespace {
-struct ResolverVarLetTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverVarLetTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverVarLetTest, VarDeclWithoutConstructor) {
- // struct S { i : i32; }
- // alias A = S;
- // fn F(){
- // var i : i32;
- // var u : u32;
- // var f : f32;
- // var b : bool;
- // var s : S;
- // var a : A;
- // }
+ // struct S { i : i32; }
+ // alias A = S;
+ // fn F(){
+ // var i : i32;
+ // var u : u32;
+ // var f : f32;
+ // var b : bool;
+ // var s : S;
+ // var a : A;
+ // }
- auto* S = Structure("S", {Member("i", ty.i32())});
- auto* A = Alias("A", ty.Of(S));
+ auto* S = Structure("S", {Member("i", ty.i32())});
+ auto* A = Alias("A", ty.Of(S));
- auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
- auto* u = Var("u", ty.u32(), ast::StorageClass::kNone);
- auto* f = Var("f", ty.f32(), ast::StorageClass::kNone);
- auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone);
- auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone);
- auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone);
+ auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
+ auto* u = Var("u", ty.u32(), ast::StorageClass::kNone);
+ auto* f = Var("f", ty.f32(), ast::StorageClass::kNone);
+ auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone);
+ auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone);
+ auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone);
- Func("F", {}, ty.void_(),
- {
- Decl(i),
- Decl(u),
- Decl(f),
- Decl(b),
- Decl(s),
- Decl(a),
- });
+ Func("F", {}, ty.void_(),
+ {
+ Decl(i),
+ Decl(u),
+ Decl(f),
+ Decl(b),
+ Decl(s),
+ Decl(a),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- // `var` declarations are always of reference type
- ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
+ // `var` declarations are always of reference type
+ ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
- EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+ EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+ EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
- EXPECT_EQ(Sem().Get(i)->Constructor(), nullptr);
- EXPECT_EQ(Sem().Get(u)->Constructor(), nullptr);
- EXPECT_EQ(Sem().Get(f)->Constructor(), nullptr);
- EXPECT_EQ(Sem().Get(b)->Constructor(), nullptr);
- EXPECT_EQ(Sem().Get(s)->Constructor(), nullptr);
- EXPECT_EQ(Sem().Get(a)->Constructor(), nullptr);
+ EXPECT_EQ(Sem().Get(i)->Constructor(), nullptr);
+ EXPECT_EQ(Sem().Get(u)->Constructor(), nullptr);
+ EXPECT_EQ(Sem().Get(f)->Constructor(), nullptr);
+ EXPECT_EQ(Sem().Get(b)->Constructor(), nullptr);
+ EXPECT_EQ(Sem().Get(s)->Constructor(), nullptr);
+ EXPECT_EQ(Sem().Get(a)->Constructor(), nullptr);
}
TEST_F(ResolverVarLetTest, VarDeclWithConstructor) {
- // struct S { i : i32; }
- // alias A = S;
- // fn F(){
- // var i : i32 = 1;
- // var u : u32 = 1u;
- // var f : f32 = 1.f;
- // var b : bool = true;
- // var s : S = S(1);
- // var a : A = A(1);
- // }
+ // struct S { i : i32; }
+ // alias A = S;
+ // fn F(){
+ // var i : i32 = 1;
+ // var u : u32 = 1u;
+ // var f : f32 = 1.f;
+ // var b : bool = true;
+ // var s : S = S(1);
+ // var a : A = A(1);
+ // }
- auto* S = Structure("S", {Member("i", ty.i32())});
- auto* A = Alias("A", ty.Of(S));
+ auto* S = Structure("S", {Member("i", ty.i32())});
+ auto* A = Alias("A", ty.Of(S));
- auto* i_c = Expr(1);
- auto* u_c = Expr(1u);
- auto* f_c = Expr(1.f);
- auto* b_c = Expr(true);
- auto* s_c = Construct(ty.Of(S), Expr(1));
- auto* a_c = Construct(ty.Of(A), Expr(1));
+ auto* i_c = Expr(1);
+ auto* u_c = Expr(1u);
+ auto* f_c = Expr(1.f);
+ auto* b_c = Expr(true);
+ auto* s_c = Construct(ty.Of(S), Expr(1));
+ auto* a_c = Construct(ty.Of(A), Expr(1));
- auto* i = Var("i", ty.i32(), ast::StorageClass::kNone, i_c);
- auto* u = Var("u", ty.u32(), ast::StorageClass::kNone, u_c);
- auto* f = Var("f", ty.f32(), ast::StorageClass::kNone, f_c);
- auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone, b_c);
- auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone, s_c);
- auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone, a_c);
+ auto* i = Var("i", ty.i32(), ast::StorageClass::kNone, i_c);
+ auto* u = Var("u", ty.u32(), ast::StorageClass::kNone, u_c);
+ auto* f = Var("f", ty.f32(), ast::StorageClass::kNone, f_c);
+ auto* b = Var("b", ty.bool_(), ast::StorageClass::kNone, b_c);
+ auto* s = Var("s", ty.Of(S), ast::StorageClass::kNone, s_c);
+ auto* a = Var("a", ty.Of(A), ast::StorageClass::kNone, a_c);
- Func("F", {}, ty.void_(),
- {
- Decl(i),
- Decl(u),
- Decl(f),
- Decl(b),
- Decl(s),
- Decl(a),
- });
+ Func("F", {}, ty.void_(),
+ {
+ Decl(i),
+ Decl(u),
+ Decl(f),
+ Decl(b),
+ Decl(s),
+ Decl(a),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- // `var` declarations are always of reference type
- ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
+ // `var` declarations are always of reference type
+ ASSERT_TRUE(TypeOf(i)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(u)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(f)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(b)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(s)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(a)->Is<sem::Reference>());
- EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
- EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
- EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
- EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
- EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
- EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+ EXPECT_TRUE(TypeOf(i)->As<sem::Reference>()->StoreType()->Is<sem::I32>());
+ EXPECT_TRUE(TypeOf(u)->As<sem::Reference>()->StoreType()->Is<sem::U32>());
+ EXPECT_TRUE(TypeOf(f)->As<sem::Reference>()->StoreType()->Is<sem::F32>());
+ EXPECT_TRUE(TypeOf(b)->As<sem::Reference>()->StoreType()->Is<sem::Bool>());
+ EXPECT_TRUE(TypeOf(s)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
+ EXPECT_TRUE(TypeOf(a)->As<sem::Reference>()->StoreType()->Is<sem::Struct>());
- EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
- EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
- EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
- EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
- EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
- EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
+ EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
+ EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
+ EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
+ EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
+ EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
+ EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
}
TEST_F(ResolverVarLetTest, LetDecl) {
- // struct S { i : i32; }
- // fn F(){
- // var v : i32;
- // let i : i32 = 1;
- // let u : u32 = 1u;
- // let f : f32 = 1.;
- // let b : bool = true;
- // let s : S = S(1);
- // let a : A = A(1);
- // let p : pointer<function, i32> = &v;
- // }
+ // struct S { i : i32; }
+ // fn F(){
+ // var v : i32;
+ // let i : i32 = 1;
+ // let u : u32 = 1u;
+ // let f : f32 = 1.;
+ // let b : bool = true;
+ // let s : S = S(1);
+ // let a : A = A(1);
+ // let p : pointer<function, i32> = &v;
+ // }
- auto* S = Structure("S", {Member("i", ty.i32())});
- auto* A = Alias("A", ty.Of(S));
- auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
+ auto* S = Structure("S", {Member("i", ty.i32())});
+ auto* A = Alias("A", ty.Of(S));
+ auto* v = Var("v", ty.i32(), ast::StorageClass::kNone);
- auto* i_c = Expr(1);
- auto* u_c = Expr(1u);
- auto* f_c = Expr(1.f);
- auto* b_c = Expr(true);
- auto* s_c = Construct(ty.Of(S), Expr(1));
- auto* a_c = Construct(ty.Of(A), Expr(1));
- auto* p_c = AddressOf(v);
+ auto* i_c = Expr(1);
+ auto* u_c = Expr(1u);
+ auto* f_c = Expr(1.f);
+ auto* b_c = Expr(true);
+ auto* s_c = Construct(ty.Of(S), Expr(1));
+ auto* a_c = Construct(ty.Of(A), Expr(1));
+ auto* p_c = AddressOf(v);
- auto* i = Let("i", ty.i32(), i_c);
- auto* u = Let("u", ty.u32(), u_c);
- auto* f = Let("f", ty.f32(), f_c);
- auto* b = Let("b", ty.bool_(), b_c);
- auto* s = Let("s", ty.Of(S), s_c);
- auto* a = Let("a", ty.Of(A), a_c);
- auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
+ auto* i = Let("i", ty.i32(), i_c);
+ auto* u = Let("u", ty.u32(), u_c);
+ auto* f = Let("f", ty.f32(), f_c);
+ auto* b = Let("b", ty.bool_(), b_c);
+ auto* s = Let("s", ty.Of(S), s_c);
+ auto* a = Let("a", ty.Of(A), a_c);
+ auto* p = Let("p", ty.pointer<i32>(ast::StorageClass::kFunction), p_c);
- Func("F", {}, ty.void_(),
- {
- Decl(v),
- Decl(i),
- Decl(u),
- Decl(f),
- Decl(b),
- Decl(s),
- Decl(a),
- Decl(p),
- });
+ Func("F", {}, ty.void_(),
+ {
+ Decl(v),
+ Decl(i),
+ Decl(u),
+ Decl(f),
+ Decl(b),
+ Decl(s),
+ Decl(a),
+ Decl(p),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- // `let` declarations are always of the storage type
- ASSERT_TRUE(TypeOf(i)->Is<sem::I32>());
- ASSERT_TRUE(TypeOf(u)->Is<sem::U32>());
- ASSERT_TRUE(TypeOf(f)->Is<sem::F32>());
- ASSERT_TRUE(TypeOf(b)->Is<sem::Bool>());
- ASSERT_TRUE(TypeOf(s)->Is<sem::Struct>());
- ASSERT_TRUE(TypeOf(a)->Is<sem::Struct>());
- ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
- ASSERT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
+ // `let` declarations are always of the storage type
+ ASSERT_TRUE(TypeOf(i)->Is<sem::I32>());
+ ASSERT_TRUE(TypeOf(u)->Is<sem::U32>());
+ ASSERT_TRUE(TypeOf(f)->Is<sem::F32>());
+ ASSERT_TRUE(TypeOf(b)->Is<sem::Bool>());
+ ASSERT_TRUE(TypeOf(s)->Is<sem::Struct>());
+ ASSERT_TRUE(TypeOf(a)->Is<sem::Struct>());
+ ASSERT_TRUE(TypeOf(p)->Is<sem::Pointer>());
+ ASSERT_TRUE(TypeOf(p)->As<sem::Pointer>()->StoreType()->Is<sem::I32>());
- EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
- EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
- EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
- EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
- EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
- EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
- EXPECT_EQ(Sem().Get(p)->Constructor()->Declaration(), p_c);
+ EXPECT_EQ(Sem().Get(i)->Constructor()->Declaration(), i_c);
+ EXPECT_EQ(Sem().Get(u)->Constructor()->Declaration(), u_c);
+ EXPECT_EQ(Sem().Get(f)->Constructor()->Declaration(), f_c);
+ EXPECT_EQ(Sem().Get(b)->Constructor()->Declaration(), b_c);
+ EXPECT_EQ(Sem().Get(s)->Constructor()->Declaration(), s_c);
+ EXPECT_EQ(Sem().Get(a)->Constructor()->Declaration(), a_c);
+ EXPECT_EQ(Sem().Get(p)->Constructor()->Declaration(), p_c);
}
TEST_F(ResolverVarLetTest, DefaultVarStorageClass) {
- // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
+ // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
- auto* buf = Structure("S", {Member("m", ty.i32())});
- auto* function = Var("f", ty.i32());
- auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
- auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* uniform = Global("ub", ty.Of(buf), ast::StorageClass::kUniform,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
- auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(0),
- });
- auto* handle = Global("h", ty.depth_texture(ast::TextureDimension::k2d),
- ast::AttributeList{
- create<ast::BindingAttribute>(2),
- create<ast::GroupAttribute>(0),
- });
+ auto* buf = Structure("S", {Member("m", ty.i32())});
+ auto* function = Var("f", ty.i32());
+ auto* private_ = Global("p", ty.i32(), ast::StorageClass::kPrivate);
+ auto* workgroup = Global("w", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* uniform = Global("ub", ty.Of(buf), ast::StorageClass::kUniform,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
+ auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(0),
+ });
+ auto* handle = Global("h", ty.depth_texture(ast::TextureDimension::k2d),
+ ast::AttributeList{
+ create<ast::BindingAttribute>(2),
+ create<ast::GroupAttribute>(0),
+ });
- WrapInFunction(function);
+ WrapInFunction(function);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(function)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(workgroup)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(uniform)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(handle)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(function)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(private_)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(workgroup)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(uniform)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(handle)->Is<sem::Reference>());
- EXPECT_EQ(TypeOf(function)->As<sem::Reference>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(private_)->As<sem::Reference>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(workgroup)->As<sem::Reference>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(uniform)->As<sem::Reference>()->Access(),
- ast::Access::kRead);
- EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(),
- ast::Access::kRead);
- EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
+ EXPECT_EQ(TypeOf(function)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(private_)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(workgroup)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(uniform)->As<sem::Reference>()->Access(), ast::Access::kRead);
+ EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), ast::Access::kRead);
+ EXPECT_EQ(TypeOf(handle)->As<sem::Reference>()->Access(), ast::Access::kRead);
}
TEST_F(ResolverVarLetTest, ExplicitVarStorageClass) {
- // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
+ // https://gpuweb.github.io/gpuweb/wgsl/#storage-class
- auto* buf = Structure("S", {Member("m", ty.i32())});
- auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(1),
- create<ast::GroupAttribute>(0),
- });
+ auto* buf = Structure("S", {Member("m", ty.i32())});
+ auto* storage = Global("sb", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(1),
+ create<ast::GroupAttribute>(0),
+ });
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(storage)->Is<sem::Reference>());
- EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(),
- ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(storage)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
}
TEST_F(ResolverVarLetTest, LetInheritsAccessFromOriginatingVariable) {
- // struct Inner {
- // arr: array<i32, 4>;
- // }
- // struct S {
- // inner: Inner;
- // }
- // @group(0) @binding(0) var<storage, read_write> s : S;
- // fn f() {
- // let p = &s.inner.arr[2];
- // }
- auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
- auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
- auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
- ast::Access::kReadWrite,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct Inner {
+ // arr: array<i32, 4>;
+ // }
+ // struct S {
+ // inner: Inner;
+ // }
+ // @group(0) @binding(0) var<storage, read_write> s : S;
+ // fn f() {
+ // let p = &s.inner.arr[2];
+ // }
+ auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
+ auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+ auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage, ast::Access::kReadWrite,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- auto* expr =
- IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
- auto* ptr = Let("p", nullptr, AddressOf(expr));
+ auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
+ auto* ptr = Let("p", nullptr, AddressOf(expr));
- WrapInFunction(ptr);
+ WrapInFunction(ptr);
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
- ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
+ ASSERT_TRUE(TypeOf(expr)->Is<sem::Reference>());
+ ASSERT_TRUE(TypeOf(ptr)->Is<sem::Pointer>());
- EXPECT_EQ(TypeOf(expr)->As<sem::Reference>()->Access(),
- ast::Access::kReadWrite);
- EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(expr)->As<sem::Reference>()->Access(), ast::Access::kReadWrite);
+ EXPECT_EQ(TypeOf(ptr)->As<sem::Pointer>()->Access(), ast::Access::kReadWrite);
}
TEST_F(ResolverVarLetTest, LocalShadowsAlias) {
- // type a = i32;
- //
- // fn X() {
- // var a = false;
- // }
- //
- // fn Y() {
- // let a = true;
- // }
+ // type a = i32;
+ //
+ // fn X() {
+ // var a = false;
+ // }
+ //
+ // fn Y() {
+ // let a = true;
+ // }
- auto* t = Alias("a", ty.i32());
- auto* v = Var("a", nullptr, Expr(false));
- auto* l = Let("a", nullptr, Expr(false));
- Func("X", {}, ty.void_(), {Decl(v)});
- Func("Y", {}, ty.void_(), {Decl(l)});
+ auto* t = Alias("a", ty.i32());
+ auto* v = Var("a", nullptr, Expr(false));
+ auto* l = Let("a", nullptr, Expr(false));
+ Func("X", {}, ty.void_(), {Decl(v)});
+ Func("Y", {}, ty.void_(), {Decl(l)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* type_t = Sem().Get(t);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* type_t = Sem().Get(t);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), type_t);
- EXPECT_EQ(local_l->Shadows(), type_t);
+ EXPECT_EQ(local_v->Shadows(), type_t);
+ EXPECT_EQ(local_l->Shadows(), type_t);
}
TEST_F(ResolverVarLetTest, LocalShadowsStruct) {
- // struct a {
- // m : i32;
- // };
- //
- // fn X() {
- // var a = true;
- // }
- //
- // fn Y() {
- // let a = false;
- // }
+ // struct a {
+ // m : i32;
+ // };
+ //
+ // fn X() {
+ // var a = true;
+ // }
+ //
+ // fn Y() {
+ // let a = false;
+ // }
- auto* t = Structure("a", {Member("m", ty.i32())});
- auto* v = Var("a", nullptr, Expr(false));
- auto* l = Let("a", nullptr, Expr(false));
- Func("X", {}, ty.void_(), {Decl(v)});
- Func("Y", {}, ty.void_(), {Decl(l)});
+ auto* t = Structure("a", {Member("m", ty.i32())});
+ auto* v = Var("a", nullptr, Expr(false));
+ auto* l = Let("a", nullptr, Expr(false));
+ Func("X", {}, ty.void_(), {Decl(v)});
+ Func("Y", {}, ty.void_(), {Decl(l)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* type_t = Sem().Get(t);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* type_t = Sem().Get(t);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), type_t);
- EXPECT_EQ(local_l->Shadows(), type_t);
+ EXPECT_EQ(local_v->Shadows(), type_t);
+ EXPECT_EQ(local_l->Shadows(), type_t);
}
TEST_F(ResolverVarLetTest, LocalShadowsFunction) {
- // fn a() {
- // var a = true;
- // }
- //
- // fn b() {
- // let b = false;
- // }
+ // fn a() {
+ // var a = true;
+ // }
+ //
+ // fn b() {
+ // let b = false;
+ // }
- auto* v = Var("a", nullptr, Expr(false));
- auto* l = Let("b", nullptr, Expr(false));
- auto* fa = Func("a", {}, ty.void_(), {Decl(v)});
- auto* fb = Func("b", {}, ty.void_(), {Decl(l)});
+ auto* v = Var("a", nullptr, Expr(false));
+ auto* l = Let("b", nullptr, Expr(false));
+ auto* fa = Func("a", {}, ty.void_(), {Decl(v)});
+ auto* fb = Func("b", {}, ty.void_(), {Decl(l)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
- auto* func_a = Sem().Get(fa);
- auto* func_b = Sem().Get(fb);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* func_a = Sem().Get(fa);
+ auto* func_b = Sem().Get(fb);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
- ASSERT_NE(func_a, nullptr);
- ASSERT_NE(func_b, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(func_a, nullptr);
+ ASSERT_NE(func_b, nullptr);
- EXPECT_EQ(local_v->Shadows(), func_a);
- EXPECT_EQ(local_l->Shadows(), func_b);
+ EXPECT_EQ(local_v->Shadows(), func_a);
+ EXPECT_EQ(local_l->Shadows(), func_b);
}
TEST_F(ResolverVarLetTest, LocalShadowsGlobalVar) {
- // var<private> a : i32;
- //
- // fn X() {
- // var a = a;
- // }
- //
- // fn Y() {
- // let a = a;
- // }
+ // var<private> a : i32;
+ //
+ // fn X() {
+ // var a = a;
+ // }
+ //
+ // fn Y() {
+ // let a = a;
+ // }
- auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
- auto* v = Var("a", nullptr, Expr("a"));
- auto* l = Let("a", nullptr, Expr("a"));
- Func("X", {}, ty.void_(), {Decl(v)});
- Func("Y", {}, ty.void_(), {Decl(l)});
+ auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+ auto* v = Var("a", nullptr, Expr("a"));
+ auto* l = Let("a", nullptr, Expr("a"));
+ Func("X", {}, ty.void_(), {Decl(v)});
+ Func("Y", {}, ty.void_(), {Decl(l)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* global = Sem().Get(g);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* global = Sem().Get(g);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), global);
- EXPECT_EQ(local_l->Shadows(), global);
+ EXPECT_EQ(local_v->Shadows(), global);
+ EXPECT_EQ(local_l->Shadows(), global);
- auto* user_v =
- Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
- auto* user_l =
- Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
+ auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+ auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
- ASSERT_NE(user_v, nullptr);
- ASSERT_NE(user_l, nullptr);
+ ASSERT_NE(user_v, nullptr);
+ ASSERT_NE(user_l, nullptr);
- EXPECT_EQ(user_v->Variable(), global);
- EXPECT_EQ(user_l->Variable(), global);
+ EXPECT_EQ(user_v->Variable(), global);
+ EXPECT_EQ(user_l->Variable(), global);
}
TEST_F(ResolverVarLetTest, LocalShadowsGlobalLet) {
- // let a : i32 = 1;
- //
- // fn X() {
- // var a = (a == 123);
- // }
- //
- // fn Y() {
- // let a = (a == 321);
- // }
+ // let a : i32 = 1;
+ //
+ // fn X() {
+ // var a = (a == 123);
+ // }
+ //
+ // fn Y() {
+ // let a = (a == 321);
+ // }
- auto* g = GlobalConst("a", ty.i32(), Expr(1));
- auto* v = Var("a", nullptr, Expr("a"));
- auto* l = Let("a", nullptr, Expr("a"));
- Func("X", {}, ty.void_(), {Decl(v)});
- Func("Y", {}, ty.void_(), {Decl(l)});
+ auto* g = GlobalConst("a", ty.i32(), Expr(1));
+ auto* v = Var("a", nullptr, Expr("a"));
+ auto* l = Let("a", nullptr, Expr("a"));
+ Func("X", {}, ty.void_(), {Decl(v)});
+ Func("Y", {}, ty.void_(), {Decl(l)});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* global = Sem().Get(g);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* global = Sem().Get(g);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), global);
- EXPECT_EQ(local_l->Shadows(), global);
+ EXPECT_EQ(local_v->Shadows(), global);
+ EXPECT_EQ(local_l->Shadows(), global);
- auto* user_v =
- Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
- auto* user_l =
- Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
+ auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+ auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
- ASSERT_NE(user_v, nullptr);
- ASSERT_NE(user_l, nullptr);
+ ASSERT_NE(user_v, nullptr);
+ ASSERT_NE(user_l, nullptr);
- EXPECT_EQ(user_v->Variable(), global);
- EXPECT_EQ(user_l->Variable(), global);
+ EXPECT_EQ(user_v->Variable(), global);
+ EXPECT_EQ(user_l->Variable(), global);
}
TEST_F(ResolverVarLetTest, LocalShadowsLocalVar) {
- // fn X() {
- // var a : i32;
- // {
- // var a = a;
- // }
- // {
- // let a = a;
- // }
- // }
+ // fn X() {
+ // var a : i32;
+ // {
+ // var a = a;
+ // }
+ // {
+ // let a = a;
+ // }
+ // }
- auto* s = Var("a", ty.i32(), Expr(1));
- auto* v = Var("a", nullptr, Expr("a"));
- auto* l = Let("a", nullptr, Expr("a"));
- Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
+ auto* s = Var("a", ty.i32(), Expr(1));
+ auto* v = Var("a", nullptr, Expr("a"));
+ auto* l = Let("a", nullptr, Expr("a"));
+ Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* local_s = Sem().Get<sem::LocalVariable>(s);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* local_s = Sem().Get<sem::LocalVariable>(s);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(local_s, nullptr);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(local_s, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), local_s);
- EXPECT_EQ(local_l->Shadows(), local_s);
+ EXPECT_EQ(local_v->Shadows(), local_s);
+ EXPECT_EQ(local_l->Shadows(), local_s);
- auto* user_v =
- Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
- auto* user_l =
- Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
+ auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+ auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
- ASSERT_NE(user_v, nullptr);
- ASSERT_NE(user_l, nullptr);
+ ASSERT_NE(user_v, nullptr);
+ ASSERT_NE(user_l, nullptr);
- EXPECT_EQ(user_v->Variable(), local_s);
- EXPECT_EQ(user_l->Variable(), local_s);
+ EXPECT_EQ(user_v->Variable(), local_s);
+ EXPECT_EQ(user_l->Variable(), local_s);
}
TEST_F(ResolverVarLetTest, LocalShadowsLocalLet) {
- // fn X() {
- // let a = 1;
- // {
- // var a = (a == 123);
- // }
- // {
- // let a = (a == 321);
- // }
- // }
+ // fn X() {
+ // let a = 1;
+ // {
+ // var a = (a == 123);
+ // }
+ // {
+ // let a = (a == 321);
+ // }
+ // }
- auto* s = Let("a", ty.i32(), Expr(1));
- auto* v = Var("a", nullptr, Expr("a"));
- auto* l = Let("a", nullptr, Expr("a"));
- Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
+ auto* s = Let("a", ty.i32(), Expr(1));
+ auto* v = Var("a", nullptr, Expr("a"));
+ auto* l = Let("a", nullptr, Expr("a"));
+ Func("X", {}, ty.void_(), {Decl(s), Block(Decl(v)), Block(Decl(l))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* local_s = Sem().Get<sem::LocalVariable>(s);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* local_s = Sem().Get<sem::LocalVariable>(s);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(local_s, nullptr);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(local_s, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), local_s);
- EXPECT_EQ(local_l->Shadows(), local_s);
+ EXPECT_EQ(local_v->Shadows(), local_s);
+ EXPECT_EQ(local_l->Shadows(), local_s);
- auto* user_v =
- Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
- auto* user_l =
- Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
+ auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+ auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
- ASSERT_NE(user_v, nullptr);
- ASSERT_NE(user_l, nullptr);
+ ASSERT_NE(user_v, nullptr);
+ ASSERT_NE(user_l, nullptr);
- EXPECT_EQ(user_v->Variable(), local_s);
- EXPECT_EQ(user_l->Variable(), local_s);
+ EXPECT_EQ(user_v->Variable(), local_s);
+ EXPECT_EQ(user_l->Variable(), local_s);
}
TEST_F(ResolverVarLetTest, LocalShadowsParam) {
- // fn F(a : i32) {
- // {
- // var a = a;
- // }
- // {
- // let a = a;
- // }
- // }
+ // fn F(a : i32) {
+ // {
+ // var a = a;
+ // }
+ // {
+ // let a = a;
+ // }
+ // }
- auto* p = Param("a", ty.i32());
- auto* v = Var("a", nullptr, Expr("a"));
- auto* l = Let("a", nullptr, Expr("a"));
- Func("X", {p}, ty.void_(), {Block(Decl(v)), Block(Decl(l))});
+ auto* p = Param("a", ty.i32());
+ auto* v = Var("a", nullptr, Expr("a"));
+ auto* l = Let("a", nullptr, Expr("a"));
+ Func("X", {p}, ty.void_(), {Block(Decl(v)), Block(Decl(l))});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* param = Sem().Get<sem::Parameter>(p);
- auto* local_v = Sem().Get<sem::LocalVariable>(v);
- auto* local_l = Sem().Get<sem::LocalVariable>(l);
+ auto* param = Sem().Get<sem::Parameter>(p);
+ auto* local_v = Sem().Get<sem::LocalVariable>(v);
+ auto* local_l = Sem().Get<sem::LocalVariable>(l);
- ASSERT_NE(param, nullptr);
- ASSERT_NE(local_v, nullptr);
- ASSERT_NE(local_l, nullptr);
+ ASSERT_NE(param, nullptr);
+ ASSERT_NE(local_v, nullptr);
+ ASSERT_NE(local_l, nullptr);
- EXPECT_EQ(local_v->Shadows(), param);
- EXPECT_EQ(local_l->Shadows(), param);
+ EXPECT_EQ(local_v->Shadows(), param);
+ EXPECT_EQ(local_l->Shadows(), param);
- auto* user_v =
- Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
- auto* user_l =
- Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
+ auto* user_v = Sem().Get<sem::VariableUser>(local_v->Declaration()->constructor);
+ auto* user_l = Sem().Get<sem::VariableUser>(local_l->Declaration()->constructor);
- ASSERT_NE(user_v, nullptr);
- ASSERT_NE(user_l, nullptr);
+ ASSERT_NE(user_v, nullptr);
+ ASSERT_NE(user_l, nullptr);
- EXPECT_EQ(user_v->Variable(), param);
- EXPECT_EQ(user_l->Variable(), param);
+ EXPECT_EQ(user_v->Variable(), param);
+ EXPECT_EQ(user_l->Variable(), param);
}
TEST_F(ResolverVarLetTest, ParamShadowsFunction) {
- // fn a(a : bool) {
- // }
+ // fn a(a : bool) {
+ // }
- auto* p = Param("a", ty.bool_());
- auto* f = Func("a", {p}, ty.void_(), {});
+ auto* p = Param("a", ty.bool_());
+ auto* f = Func("a", {p}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* func = Sem().Get(f);
- auto* param = Sem().Get<sem::Parameter>(p);
+ auto* func = Sem().Get(f);
+ auto* param = Sem().Get<sem::Parameter>(p);
- ASSERT_NE(func, nullptr);
- ASSERT_NE(param, nullptr);
+ ASSERT_NE(func, nullptr);
+ ASSERT_NE(param, nullptr);
- EXPECT_EQ(param->Shadows(), func);
+ EXPECT_EQ(param->Shadows(), func);
}
TEST_F(ResolverVarLetTest, ParamShadowsGlobalVar) {
- // var<private> a : i32;
- //
- // fn F(a : bool) {
- // }
+ // var<private> a : i32;
+ //
+ // fn F(a : bool) {
+ // }
- auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
- auto* p = Param("a", ty.bool_());
- Func("F", {p}, ty.void_(), {});
+ auto* g = Global("a", ty.i32(), ast::StorageClass::kPrivate);
+ auto* p = Param("a", ty.bool_());
+ Func("F", {p}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* global = Sem().Get(g);
- auto* param = Sem().Get<sem::Parameter>(p);
+ auto* global = Sem().Get(g);
+ auto* param = Sem().Get<sem::Parameter>(p);
- ASSERT_NE(global, nullptr);
- ASSERT_NE(param, nullptr);
+ ASSERT_NE(global, nullptr);
+ ASSERT_NE(param, nullptr);
- EXPECT_EQ(param->Shadows(), global);
+ EXPECT_EQ(param->Shadows(), global);
}
TEST_F(ResolverVarLetTest, ParamShadowsGlobalLet) {
- // let a : i32 = 1;
- //
- // fn F(a : bool) {
- // }
+ // let a : i32 = 1;
+ //
+ // fn F(a : bool) {
+ // }
- auto* g = GlobalConst("a", ty.i32(), Expr(1));
- auto* p = Param("a", ty.bool_());
- Func("F", {p}, ty.void_(), {});
+ auto* g = GlobalConst("a", ty.i32(), Expr(1));
+ auto* p = Param("a", ty.bool_());
+ Func("F", {p}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* global = Sem().Get(g);
- auto* param = Sem().Get<sem::Parameter>(p);
+ auto* global = Sem().Get(g);
+ auto* param = Sem().Get<sem::Parameter>(p);
- ASSERT_NE(global, nullptr);
- ASSERT_NE(param, nullptr);
+ ASSERT_NE(global, nullptr);
+ ASSERT_NE(param, nullptr);
- EXPECT_EQ(param->Shadows(), global);
+ EXPECT_EQ(param->Shadows(), global);
}
TEST_F(ResolverVarLetTest, ParamShadowsAlias) {
- // type a = i32;
- //
- // fn F(a : a) {
- // }
+ // type a = i32;
+ //
+ // fn F(a : a) {
+ // }
- auto* a = Alias("a", ty.i32());
- auto* p = Param("a", ty.type_name("a"));
- Func("F", {p}, ty.void_(), {});
+ auto* a = Alias("a", ty.i32());
+ auto* p = Param("a", ty.type_name("a"));
+ Func("F", {p}, ty.void_(), {});
- ASSERT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_TRUE(r()->Resolve()) << r()->error();
- auto* alias = Sem().Get(a);
- auto* param = Sem().Get<sem::Parameter>(p);
+ auto* alias = Sem().Get(a);
+ auto* param = Sem().Get<sem::Parameter>(p);
- ASSERT_NE(alias, nullptr);
- ASSERT_NE(param, nullptr);
+ ASSERT_NE(alias, nullptr);
+ ASSERT_NE(param, nullptr);
- EXPECT_EQ(param->Shadows(), alias);
- EXPECT_EQ(param->Type(), alias);
+ EXPECT_EQ(param->Shadows(), alias);
+ EXPECT_EQ(param->Type(), alias);
}
} // namespace
diff --git a/src/tint/resolver/var_let_validation_test.cc b/src/tint/resolver/var_let_validation_test.cc
index be41da8..caa47d0 100644
--- a/src/tint/resolver/var_let_validation_test.cc
+++ b/src/tint/resolver/var_let_validation_test.cc
@@ -20,324 +20,297 @@
namespace tint::resolver {
namespace {
-struct ResolverVarLetValidationTest : public resolver::TestHelper,
- public testing::Test {};
+struct ResolverVarLetValidationTest : public resolver::TestHelper, public testing::Test {};
TEST_F(ResolverVarLetValidationTest, LetNoInitializer) {
- // let a : i32;
- WrapInFunction(Let(Source{{12, 34}}, "a", ty.i32(), nullptr));
+ // let a : i32;
+ WrapInFunction(Let(Source{{12, 34}}, "a", ty.i32(), nullptr));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: let declaration must have an initializer");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: let declaration must have an initializer");
}
TEST_F(ResolverVarLetValidationTest, GlobalLetNoInitializer) {
- // let a : i32;
- GlobalConst(Source{{12, 34}}, "a", ty.i32(), nullptr);
+ // let a : i32;
+ GlobalConst(Source{{12, 34}}, "a", ty.i32(), nullptr);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: let declaration must have an initializer");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: let declaration must have an initializer");
}
TEST_F(ResolverVarLetValidationTest, VarNoInitializerNoType) {
- // var a;
- WrapInFunction(Var(Source{{12, 34}}, "a", nullptr));
+ // var a;
+ WrapInFunction(Var(Source{{12, 34}}, "a", nullptr));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function scope var declaration requires a type or "
- "initializer");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: function scope var declaration requires a type or "
+ "initializer");
}
TEST_F(ResolverVarLetValidationTest, GlobalVarNoInitializerNoType) {
- // var a;
- Global(Source{{12, 34}}, "a", nullptr);
+ // var a;
+ Global(Source{{12, 34}}, "a", nullptr);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: module scope var declaration requires a type and "
- "initializer");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: module scope var declaration requires a type and "
+ "initializer");
}
TEST_F(ResolverVarLetValidationTest, VarTypeNotStorable) {
- // var i : i32;
- // var p : pointer<function, i32> = &v;
- auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
- auto* p =
- Var(Source{{56, 78}}, "a", ty.pointer<i32>(ast::StorageClass::kFunction),
- ast::StorageClass::kNone, AddressOf(Source{{12, 34}}, "i"));
- WrapInFunction(i, p);
+ // var i : i32;
+ // var p : pointer<function, i32> = &v;
+ auto* i = Var("i", ty.i32(), ast::StorageClass::kNone);
+ auto* p = Var(Source{{56, 78}}, "a", ty.pointer<i32>(ast::StorageClass::kFunction),
+ ast::StorageClass::kNone, AddressOf(Source{{12, 34}}, "i"));
+ WrapInFunction(i, p);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: ptr<function, i32, read_write> cannot be used as the "
- "type of a var");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "56:78 error: ptr<function, i32, read_write> cannot be used as the "
+ "type of a var");
}
TEST_F(ResolverVarLetValidationTest, LetTypeNotConstructible) {
- // @group(0) @binding(0) var t1 : texture_2d<f32>;
- // let t2 : t1;
- auto* t1 =
- Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
- GroupAndBinding(0, 0));
- auto* t2 = Let(Source{{56, 78}}, "t2", nullptr, Expr(t1));
- WrapInFunction(t2);
+ // @group(0) @binding(0) var t1 : texture_2d<f32>;
+ // let t2 : t1;
+ auto* t1 = Global("t1", ty.sampled_texture(ast::TextureDimension::k2d, ty.f32()),
+ GroupAndBinding(0, 0));
+ auto* t2 = Let(Source{{56, 78}}, "t2", nullptr, Expr(t1));
+ WrapInFunction(t2);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "56:78 error: texture_2d<f32> cannot be used as the type of a let");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "56:78 error: texture_2d<f32> cannot be used as the type of a let");
}
TEST_F(ResolverVarLetValidationTest, LetConstructorWrongType) {
- // var v : i32 = 2u
- WrapInFunction(Let(Source{{3, 3}}, "v", ty.i32(), Expr(2u)));
+ // var v : i32 = 2u
+ WrapInFunction(Let(Source{{3, 3}}, "v", ty.i32(), Expr(2u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
}
TEST_F(ResolverVarLetValidationTest, VarConstructorWrongType) {
- // var v : i32 = 2u
- WrapInFunction(
- Var(Source{{3, 3}}, "v", ty.i32(), ast::StorageClass::kNone, Expr(2u)));
+ // var v : i32 = 2u
+ WrapInFunction(Var(Source{{3, 3}}, "v", ty.i32(), ast::StorageClass::kNone, Expr(2u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
}
TEST_F(ResolverVarLetValidationTest, LetConstructorWrongTypeViaAlias) {
- auto* a = Alias("I32", ty.i32());
- WrapInFunction(Let(Source{{3, 3}}, "v", ty.Of(a), Expr(2u)));
+ auto* a = Alias("I32", ty.i32());
+ WrapInFunction(Let(Source{{3, 3}}, "v", ty.Of(a), Expr(2u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(3:3 error: cannot initialize let of type 'i32' with value of type 'u32')");
}
TEST_F(ResolverVarLetValidationTest, VarConstructorWrongTypeViaAlias) {
- auto* a = Alias("I32", ty.i32());
- WrapInFunction(
- Var(Source{{3, 3}}, "v", ty.Of(a), ast::StorageClass::kNone, Expr(2u)));
+ auto* a = Alias("I32", ty.i32());
+ WrapInFunction(Var(Source{{3, 3}}, "v", ty.Of(a), ast::StorageClass::kNone, Expr(2u)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(3:3 error: cannot initialize var of type 'i32' with value of type 'u32')");
}
TEST_F(ResolverVarLetValidationTest, LetOfPtrConstructedWithRef) {
- // var a : f32;
- // let b : ptr<function,f32> = a;
- const auto priv = ast::StorageClass::kFunction;
- auto* var_a = Var("a", ty.f32(), priv);
- auto* var_b =
- Let(Source{{12, 34}}, "b", ty.pointer<float>(priv), Expr("a"), {});
- WrapInFunction(var_a, var_b);
+ // var a : f32;
+ // let b : ptr<function,f32> = a;
+ const auto priv = ast::StorageClass::kFunction;
+ auto* var_a = Var("a", ty.f32(), priv);
+ auto* var_b = Let(Source{{12, 34}}, "b", ty.pointer<float>(priv), Expr("a"), {});
+ WrapInFunction(var_a, var_b);
- ASSERT_FALSE(r()->Resolve());
+ ASSERT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: cannot initialize let of type 'ptr<function, f32, read_write>' with value of type 'f32')");
+ EXPECT_EQ(
+ r()->error(),
+ R"(12:34 error: cannot initialize let of type 'ptr<function, f32, read_write>' with value of type 'f32')");
}
TEST_F(ResolverVarLetValidationTest, LocalLetRedeclared) {
- // let l : f32 = 1.;
- // let l : i32 = 0;
- auto* l1 = Let("l", ty.f32(), Expr(1.f));
- auto* l2 = Let(Source{{12, 34}}, "l", ty.i32(), Expr(0));
- WrapInFunction(l1, l2);
+ // let l : f32 = 1.;
+ // let l : i32 = 0;
+ auto* l1 = Let("l", ty.f32(), Expr(1.f));
+ auto* l2 = Let(Source{{12, 34}}, "l", ty.i32(), Expr(0));
+ WrapInFunction(l1, l2);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "12:34 error: redeclaration of 'l'\nnote: 'l' previously declared here");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: redeclaration of 'l'\nnote: 'l' previously declared here");
}
TEST_F(ResolverVarLetValidationTest, GlobalVarRedeclaredAsLocal) {
- // var v : f32 = 2.1;
- // fn my_func() {
- // var v : f32 = 2.0;
- // return 0;
- // }
+ // var v : f32 = 2.1;
+ // fn my_func() {
+ // var v : f32 = 2.0;
+ // return 0;
+ // }
- Global("v", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
+ Global("v", ty.f32(), ast::StorageClass::kPrivate, Expr(2.1f));
- WrapInFunction(Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone,
- Expr(2.0f)));
+ WrapInFunction(Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone, Expr(2.0f)));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverVarLetValidationTest, VarRedeclaredInInnerBlock) {
- // {
- // var v : f32;
- // { var v : f32; }
- // }
- auto* var_outer = Var("v", ty.f32(), ast::StorageClass::kNone);
- auto* var_inner =
- Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone);
- auto* inner = Block(Decl(var_inner));
- auto* outer_body = Block(Decl(var_outer), inner);
+ // {
+ // var v : f32;
+ // { var v : f32; }
+ // }
+ auto* var_outer = Var("v", ty.f32(), ast::StorageClass::kNone);
+ auto* var_inner = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone);
+ auto* inner = Block(Decl(var_inner));
+ auto* outer_body = Block(Decl(var_outer), inner);
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverVarLetValidationTest, VarRedeclaredInIfBlock) {
- // {
- // var v : f32 = 3.14;
- // if (true) { var v : f32 = 2.0; }
- // }
- auto* var_a_float = Var("v", ty.f32(), ast::StorageClass::kNone, Expr(3.1f));
+ // {
+ // var v : f32 = 3.14;
+ // if (true) { var v : f32 = 2.0; }
+ // }
+ auto* var_a_float = Var("v", ty.f32(), ast::StorageClass::kNone, Expr(3.1f));
- auto* var = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone,
- Expr(2.0f));
+ auto* var = Var(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kNone, Expr(2.0f));
- auto* cond = Expr(true);
- auto* body = Block(Decl(var));
+ auto* cond = Expr(true);
+ auto* body = Block(Decl(var));
- auto* outer_body = Block(Decl(var_a_float), If(cond, body));
+ auto* outer_body = Block(Decl(var_a_float), If(cond, body));
- WrapInFunction(outer_body);
+ WrapInFunction(outer_body);
- EXPECT_TRUE(r()->Resolve()) << r()->error();
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
}
TEST_F(ResolverVarLetValidationTest, InferredPtrStorageAccessMismatch) {
- // struct Inner {
- // arr: array<i32, 4>;
- // }
- // struct S {
- // inner: Inner;
- // }
- // @group(0) @binding(0) var<storage> s : S;
- // fn f() {
- // let p : pointer<storage, i32, read_write> = &s.inner.arr[2];
- // }
- auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
- auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
- auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
- ast::AttributeList{
- create<ast::BindingAttribute>(0),
- create<ast::GroupAttribute>(0),
- });
+ // struct Inner {
+ // arr: array<i32, 4>;
+ // }
+ // struct S {
+ // inner: Inner;
+ // }
+ // @group(0) @binding(0) var<storage> s : S;
+ // fn f() {
+ // let p : pointer<storage, i32, read_write> = &s.inner.arr[2];
+ // }
+ auto* inner = Structure("Inner", {Member("arr", ty.array<i32, 4>())});
+ auto* buf = Structure("S", {Member("inner", ty.Of(inner))});
+ auto* storage = Global("s", ty.Of(buf), ast::StorageClass::kStorage,
+ ast::AttributeList{
+ create<ast::BindingAttribute>(0),
+ create<ast::GroupAttribute>(0),
+ });
- auto* expr =
- IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
- auto* ptr =
- Let(Source{{12, 34}}, "p",
- ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite),
- AddressOf(expr));
+ auto* expr = IndexAccessor(MemberAccessor(MemberAccessor(storage, "inner"), "arr"), 4);
+ auto* ptr =
+ Let(Source{{12, 34}}, "p",
+ ty.pointer<i32>(ast::StorageClass::kStorage, ast::Access::kReadWrite), AddressOf(expr));
- WrapInFunction(ptr);
+ WrapInFunction(ptr);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: cannot initialize let of type "
- "'ptr<storage, i32, read_write>' with value of type "
- "'ptr<storage, i32, read>'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: cannot initialize let of type "
+ "'ptr<storage, i32, read_write>' with value of type "
+ "'ptr<storage, i32, read>'");
}
TEST_F(ResolverVarLetValidationTest, NonConstructibleType_Atomic) {
- auto* v = Var("v", ty.atomic(Source{{12, 34}}, ty.i32()));
- WrapInFunction(v);
+ auto* v = Var("v", ty.atomic(Source{{12, 34}}, ty.i32()));
+ WrapInFunction(v);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function variable must have a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function variable must have a constructible type");
}
TEST_F(ResolverVarLetValidationTest, NonConstructibleType_RuntimeArray) {
- auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))});
- auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
- WrapInFunction(v);
+ auto* s = Structure("S", {Member(Source{{56, 78}}, "m", ty.array(ty.i32()))});
+ auto* v = Var(Source{{12, 34}}, "v", ty.Of(s));
+ WrapInFunction(v);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ R"(12:34 error: runtime-sized arrays can only be used in the <storage> storage class
56:78 note: while analysing structure member S.m
12:34 note: while instantiating variable v)");
}
TEST_F(ResolverVarLetValidationTest, NonConstructibleType_Struct_WithAtomic) {
- auto* s = Structure("S", {Member("m", ty.atomic(ty.i32()))});
- auto* v = Var("v", ty.Of(s));
- WrapInFunction(v);
+ auto* s = Structure("S", {Member("m", ty.atomic(ty.i32()))});
+ auto* v = Var("v", ty.Of(s));
+ WrapInFunction(v);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "error: function variable must have a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "error: function variable must have a constructible type");
}
TEST_F(ResolverVarLetValidationTest, NonConstructibleType_InferredType) {
- // @group(0) @binding(0) var s : sampler;
- // fn foo() {
- // var v = s;
- // }
- Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 0));
- auto* v = Var(Source{{12, 34}}, "v", nullptr, Expr("s"));
- WrapInFunction(v);
+ // @group(0) @binding(0) var s : sampler;
+ // fn foo() {
+ // var v = s;
+ // }
+ Global("s", ty.sampler(ast::SamplerKind::kSampler), GroupAndBinding(0, 0));
+ auto* v = Var(Source{{12, 34}}, "v", nullptr, Expr("s"));
+ WrapInFunction(v);
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: function variable must have a constructible type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: function variable must have a constructible type");
}
TEST_F(ResolverVarLetValidationTest, InvalidStorageClassForInitializer) {
- // var<workgroup> v : f32 = 1.23;
- Global(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kWorkgroup,
- Expr(1.23f));
+ // var<workgroup> v : f32 = 1.23;
+ Global(Source{{12, 34}}, "v", ty.f32(), ast::StorageClass::kWorkgroup, Expr(1.23f));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: var of storage class 'workgroup' cannot have "
- "an initializer. var initializers are only supported for the "
- "storage classes 'private' and 'function'");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: var of storage class 'workgroup' cannot have "
+ "an initializer. var initializers are only supported for the "
+ "storage classes 'private' and 'function'");
}
TEST_F(ResolverVarLetValidationTest, VectorLetNoType) {
- // let a : mat3x3 = mat3x3<f32>();
- WrapInFunction(
- Let("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3), vec3<f32>()));
+ // let a : mat3x3 = mat3x3<f32>();
+ WrapInFunction(Let("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3), vec3<f32>()));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
}
TEST_F(ResolverVarLetValidationTest, VectorVarNoType) {
- // var a : mat3x3;
- WrapInFunction(Var("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3)));
+ // var a : mat3x3;
+ WrapInFunction(Var("a", create<ast::Vector>(Source{{12, 34}}, nullptr, 3)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing vector element type");
}
TEST_F(ResolverVarLetValidationTest, MatrixLetNoType) {
- // let a : mat3x3 = mat3x3<f32>();
- WrapInFunction(Let("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3),
- mat3x3<f32>()));
+ // let a : mat3x3 = mat3x3<f32>();
+ WrapInFunction(Let("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3), mat3x3<f32>()));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
}
TEST_F(ResolverVarLetValidationTest, MatrixVarNoType) {
- // var a : mat3x3;
- WrapInFunction(
- Var("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3)));
+ // var a : mat3x3;
+ WrapInFunction(Var("a", create<ast::Matrix>(Source{{12, 34}}, nullptr, 3, 3)));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: missing matrix element type");
}
} // namespace