// Copyright 2021 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/ast/call_statement.h" #include "src/resolver/resolver_test_helper.h" namespace tint { namespace { class ResolverBuiltinsValidationTest : public resolver::TestHelper, public testing::Test {}; TEST_F(ResolverBuiltinsValidationTest, PositionNotF32_Struct_Fail) { // struct MyInputs { // [[builtin(kPosition)]] p: vec4; // }; // [[stage(fragment)]] // fn fragShader(is_front: MyInputs) -> [[location(0)]] f32 { return 1.0; } auto* m = Member( "position", ty.vec4(), ast::DecorationList{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'"); } 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)}); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), "12:34 error: store type of builtin(position) must be 'vec4'"); } 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; } auto* m = Member( "frag_depth", ty.i32(), ast::DecorationList{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'"); } 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; } auto* s = Structure( "MyInputs", {Member("m", ty.f32(), ast::DecorationList{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'"); } 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)}); 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::DecorationList{ Builtin(Source{{12, 34}}, ast::Builtin::kSampleMask)}); Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)}, ast::DecorationList{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; } auto* s = Structure( "MyInputs", {Member("m", ty.f32(), ast::DecorationList{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'"); } 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::DecorationList{ Builtin(Source{{12, 34}}, ast::Builtin::kSampleIndex)}); Func("fs_main", ast::VariableList{arg}, ty.f32(), {Return(1.0f)}, ast::DecorationList{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, // ) -> [[location(0)]] f32 { return 1.0; } auto* p = Param( "p", ty.vec3(), ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kPosition)}); Func("fs_main", ast::VariableList{p}, ty.f32(), {Return(1.0f)}, ast::DecorationList{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'"); } TEST_F(ResolverBuiltinsValidationTest, FragDepthIsNotF32_Fail) { // [[stage(fragment)]] // fn fs_main( // [[builtin(kFragDepth)]] fd: f32, // ) -> [[location(0)]] f32 { return 1.0; } auto* fd = Param( "fd", ty.i32(), ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kFragDepth)}); Func("fs_main", ast::VariableList{fd}, ty.f32(), {Return(1.0f)}, ast::DecorationList{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'"); } TEST_F(ResolverBuiltinsValidationTest, VertexIndexIsNotU32_Fail) { // [[stage(vertex)]] // fn main( // [[builtin(kVertexIndex)]] vi : f32, // [[builtin(kPosition)]] p :vec4 // ) -> [[builtin(kPosition)]] vec4 { return vec4(); } auto* p = Param("p", ty.vec4(), ast::DecorationList{Builtin(ast::Builtin::kPosition)}); auto* vi = Param("vi", ty.f32(), ast::DecorationList{ Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)}); Func("main", ast::VariableList{vi, p}, ty.vec4(), {Return(Expr("p"))}, ast::DecorationList{Stage(ast::PipelineStage::kVertex)}, ast::DecorationList{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 // ) -> [[builtin(kPosition)]] vec4 { return vec4(); } auto* p = Param("p", ty.vec4(), ast::DecorationList{Builtin(ast::Builtin::kPosition)}); auto* ii = Param("ii", ty.f32(), ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)}); Func("main", ast::VariableList{ii, p}, ty.vec4(), {Return(Expr("p"))}, ast::DecorationList{Stage(ast::PipelineStage::kVertex)}, ast::DecorationList{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, // [[builtin(front_facing)]] ff: bool, // [[builtin(frag_depth)]] fd: f32, // [[builtin(sample_index)]] si: u32, // [[builtin(sample_mask)]] sm : u32 // ) -> [[location(0)]] f32 { return 1.0; } auto* p = Param("p", ty.vec4(), ast::DecorationList{Builtin(ast::Builtin::kPosition)}); auto* ff = Param("ff", ty.bool_(), ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)}); auto* fd = Param("fd", ty.f32(), ast::DecorationList{Builtin(ast::Builtin::kFragDepth)}); auto* si = Param("si", ty.u32(), ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)}); auto* sm = Param("sm", ty.u32(), ast::DecorationList{Builtin(ast::Builtin::kSampleMask)}); Func( "fs_main", ast::VariableList{p, ff, fd, si, sm}, ty.f32(), {Return(1.0f)}, ast::DecorationList{Stage(ast::PipelineStage::kFragment)}, {Location(0)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, VertexBuiltin_Pass) { // [[stage(vertex)]] // fn main( // [[builtin(kVertexIndex)]] vi : u32, // [[builtin(kInstanceIndex)]] ii : u32, // [[builtin(kPosition)]] p :vec4 // ) {} auto* vi = Param("vi", ty.u32(), ast::DecorationList{ Builtin(Source{{12, 34}}, ast::Builtin::kVertexIndex)}); auto* p = Param("p", ty.vec4(), ast::DecorationList{Builtin(ast::Builtin::kPosition)}); auto* ii = Param("ii", ty.u32(), ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kInstanceIndex)}); Func("main", ast::VariableList{vi, ii, p}, ty.vec4(), { Return(Expr(p)), }, ast::DecorationList{Stage(ast::PipelineStage::kVertex)}, ast::DecorationList{Builtin(ast::Builtin::kPosition)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_Pass) { // [[stage(compute)]] // fn main( // [[builtin(local_invocationId)]] li_id: vec3, // [[builtin(local_invocationIndex)]] li_index: u32, // [[builtin(global_invocationId)]] gi: vec3, // [[builtin(workgroup_id)]] wi: vec3, // ) {} auto* li_id = Param("li_id", ty.vec3(), ast::DecorationList{Builtin(ast::Builtin::kLocalInvocationId)}); auto* li_index = Param("li_index", ty.u32(), ast::DecorationList{Builtin(ast::Builtin::kLocalInvocationIndex)}); auto* gi = Param("gi", ty.vec3(), ast::DecorationList{Builtin(ast::Builtin::kGlobalInvocationId)}); auto* wi = Param("wi", ty.vec3(), ast::DecorationList{Builtin(ast::Builtin::kWorkgroupId)}); Func("main", ast::VariableList{li_id, li_index, gi, wi}, ty.void_(), {}, ast::DecorationList{ Stage(ast::PipelineStage::kCompute), WorkgroupSize(Expr(Source{Source::Location{12, 34}}, 2))}); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_WorkGroupIdNotVec3U32) { auto* wi = Param("wi", ty.f32(), ast::DecorationList{ Builtin(Source{{12, 34}}, ast::Builtin::kWorkgroupId)}); Func("main", ast::VariableList{wi}, ty.void_(), {}, ast::DecorationList{ 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'"); } TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_GlobalInvocationNotVec3U32) { auto* gi = Param("gi", ty.vec3(), ast::DecorationList{Builtin( Source{{12, 34}}, ast::Builtin::kGlobalInvocationId)}); Func("main", ast::VariableList{gi}, ty.void_(), {}, ast::DecorationList{ 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'"); } TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationIndexNotU32) { auto* li_index = Param("li_index", ty.vec3(), ast::DecorationList{Builtin(Source{{12, 34}}, ast::Builtin::kLocalInvocationIndex)}); Func("main", ast::VariableList{li_index}, ty.void_(), {}, ast::DecorationList{ 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'"); } TEST_F(ResolverBuiltinsValidationTest, ComputeBuiltin_LocalInvocationNotVec3U32) { auto* li_id = Param("li_id", ty.vec2(), ast::DecorationList{Builtin( Source{{12, 34}}, ast::Builtin::kLocalInvocationId)}); Func("main", ast::VariableList{li_id}, ty.void_(), {}, ast::DecorationList{ 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'"); } TEST_F(ResolverBuiltinsValidationTest, FragmentBuiltinStruct_Pass) { // Struct MyInputs { // [[builtin(kPosition)]] p: vec4; // [[builtin(front_facing)]] ff: bool; // [[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(), ast::DecorationList{Builtin(ast::Builtin::kPosition)}), Member("front_facing", ty.bool_(), ast::DecorationList{Builtin(ast::Builtin::kFrontFacing)}), Member("frag_depth", ty.f32(), ast::DecorationList{Builtin(ast::Builtin::kFragDepth)}), Member("sample_index", ty.u32(), ast::DecorationList{Builtin(ast::Builtin::kSampleIndex)}), Member("sample_mask", ty.u32(), ast::DecorationList{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; } auto* is_front = Param("is_front", ty.i32(), ast::DecorationList{Builtin( Source{{12, 34}}, ast::Builtin::kFrontFacing)}); Func("fs_main", ast::VariableList{is_front}, ty.f32(), {Return(1.0f)}, ast::DecorationList{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'"); } 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; } auto* s = Structure( "MyInputs", {Member("pos", ty.f32(), ast::DecorationList{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'"); } TEST_F(ResolverBuiltinsValidationTest, Length_Float_Scalar) { auto* builtin = Call("length", 1.0f); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Length_Float_Vec2) { auto* builtin = Call("length", vec2(1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Length_Float_Vec3) { auto* builtin = Call("length", vec3(1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Length_Float_Vec4) { auto* builtin = Call("length", vec4(1.0f, 1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Scalar) { auto* builtin = Call("distance", 1.0f, 1.0f); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Vec2) { auto* builtin = Call("distance", vec2(1.0f, 1.0f), vec2(1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Vec3) { auto* builtin = Call("distance", vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Distance_Float_Vec4) { auto* builtin = Call("distance", vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(1.0f, 1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Determinant_Mat2x2) { auto* builtin = Call( "determinant", mat2x2(vec2(1.0f, 1.0f), vec2(1.0f, 1.0f))); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Determinant_Mat3x3) { auto* builtin = Call("determinant", mat3x3(vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, 1.0f, 1.0f))); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Determinant_Mat4x4) { auto* builtin = Call("determinant", mat4x4(vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(1.0f, 1.0f, 1.0f, 1.0f))); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Frexp_Scalar) { auto* a = Var("a", ty.i32()); auto* builtin = Call("frexp", 1.0f, AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->Is()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Frexp_Vec2) { auto* a = Var("a", ty.vec2()); auto* builtin = Call("frexp", vec2(1.0f, 1.0f), AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Frexp_Vec3) { auto* a = Var("a", ty.vec3()); auto* builtin = Call("frexp", vec3(1.0f, 1.0f, 1.0f), AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Frexp_Vec4) { auto* a = Var("a", ty.vec4()); auto* builtin = Call("frexp", vec4(1.0f, 1.0f, 1.0f, 1.0f), AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Modf_Scalar) { auto* a = Var("a", ty.f32()); auto* builtin = Call("modf", 1.0f, AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->Is()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Modf_Vec2) { auto* a = Var("a", ty.vec2()); auto* builtin = Call("modf", vec2(1.0f, 1.0f), AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Modf_Vec3) { auto* a = Var("a", ty.vec3()); auto* builtin = Call("modf", vec3(1.0f, 1.0f, 1.0f), AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Modf_Vec4) { auto* a = Var("a", ty.vec4()); auto* builtin = Call("modf", vec4(1.0f, 1.0f, 1.0f, 1.0f), AddressOf(Expr("a"))); WrapInFunction(Decl(a), builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); EXPECT_TRUE(TypeOf(builtin->params()[1])->Is()); } TEST_F(ResolverBuiltinsValidationTest, Cross_Float_Vec3) { auto* builtin = Call("cross", vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Dot_Float_Vec2) { auto* builtin = Call("dot", vec2(1.0f, 1.0f), vec2(1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Dot_Float_Vec3) { auto* builtin = Call("dot", vec3(1.0f, 1.0f, 1.0f), vec3(1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Dot_Float_Vec4) { auto* builtin = Call("dot", vec4(1.0f, 1.0f, 1.0f, 1.0f), vec4(1.0f, 1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); 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); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Select_Integer_Scalar) { auto* builtin = Call("select", Expr(1), Expr(1), Expr(true)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Select_Boolean_Scalar) { auto* builtin = Call("select", Expr(true), Expr(true), Expr(true)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Select_Float_Vec2) { auto* builtin = Call("select", vec2(1.0f, 1.0f), vec2(1.0f, 1.0f), vec2(true, true)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Select_Integer_Vec2) { auto* builtin = Call("select", vec2(1, 1), vec2(1, 1), vec2(true, true)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } TEST_F(ResolverBuiltinsValidationTest, Select_Boolean_Vec2) { auto* builtin = Call("select", vec2(true, true), vec2(true, true), vec2(true, true)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } template class ResolverBuiltinsValidationTestWithParams : public resolver::TestHelper, public testing::TestWithParam {}; using FloatAllMatching = ResolverBuiltinsValidationTestWithParams>; TEST_P(FloatAllMatching, Scalar) { 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_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->Is()); } TEST_P(FloatAllMatching, Vec2) { 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(1.0f, 1.0f)); } auto* builtin = Call(name, params); Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec3(1.0f, 1.0f, 1.0f)); } auto* builtin = Call(name, params); Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec4(1.0f, 1.0f, 1.0f, 1.0f)); } auto* builtin = Call(name, params); Func("func", {}, ty.void_(), {Ignore(builtin)}, {create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->is_float_vector()); } INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest, FloatAllMatching, ::testing::Values(std::make_tuple("abs", 1), std::make_tuple("acos", 1), std::make_tuple("asin", 1), std::make_tuple("atan", 1), std::make_tuple("atan2", 2), std::make_tuple("ceil", 1), std::make_tuple("clamp", 3), std::make_tuple("cos", 1), std::make_tuple("cosh", 1), std::make_tuple("dpdx", 1), std::make_tuple("dpdxCoarse", 1), std::make_tuple("dpdxFine", 1), std::make_tuple("dpdy", 1), std::make_tuple("dpdyCoarse", 1), std::make_tuple("dpdyFine", 1), std::make_tuple("exp", 1), std::make_tuple("exp2", 1), std::make_tuple("faceForward", 3), std::make_tuple("floor", 1), std::make_tuple("fma", 3), std::make_tuple("fract", 1), std::make_tuple("fwidth", 1), std::make_tuple("fwidthCoarse", 1), std::make_tuple("fwidthFine", 1), std::make_tuple("inverseSqrt", 1), std::make_tuple("log", 1), std::make_tuple("log2", 1), std::make_tuple("max", 2), std::make_tuple("min", 2), std::make_tuple("mix", 3), std::make_tuple("pow", 2), std::make_tuple("reflect", 2), std::make_tuple("round", 1), std::make_tuple("sign", 1), std::make_tuple("sin", 1), std::make_tuple("sinh", 1), std::make_tuple("smoothStep", 3), std::make_tuple("sqrt", 1), std::make_tuple("step", 2), std::make_tuple("tan", 1), std::make_tuple("tanh", 1), std::make_tuple("trunc", 1))); using IntegerAllMatching = ResolverBuiltinsValidationTestWithParams>; TEST_P(IntegerAllMatching, ScalarUnsigned) { 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(1)); } auto* builtin = Call(name, params); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->Is()); } TEST_P(IntegerAllMatching, Vec2Unsigned) { 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(1u, 1u)); } auto* builtin = Call(name, params); WrapInFunction(builtin); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec3(1u, 1u, 1u)); } auto* builtin = Call(name, params); WrapInFunction(builtin); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec4(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()); } TEST_P(IntegerAllMatching, ScalarSigned) { 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(1)); } auto* builtin = Call(name, params); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); EXPECT_TRUE(TypeOf(builtin)->Is()); } TEST_P(IntegerAllMatching, Vec2Signed) { 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(1, 1)); } auto* builtin = Call(name, params); WrapInFunction(builtin); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec3(1, 1, 1)); } auto* builtin = Call(name, params); WrapInFunction(builtin); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec4(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()); } INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest, IntegerAllMatching, ::testing::Values(std::make_tuple("abs", 1), std::make_tuple("clamp", 3), std::make_tuple("countOneBits", 1), std::make_tuple("max", 2), std::make_tuple("min", 2), std::make_tuple("reverseBits", 1))); using BooleanVectorInput = ResolverBuiltinsValidationTestWithParams>; TEST_P(BooleanVectorInput, Vec2) { 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(true, true)); } auto* builtin = Call(name, params); WrapInFunction(builtin); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec3(true, true, true)); } auto* builtin = Call(name, params); WrapInFunction(builtin); 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()); ast::ExpressionList params; for (uint32_t i = 0; i < num_params; ++i) { params.push_back(vec4(true, true, true, true)); } auto* builtin = Call(name, params); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest, BooleanVectorInput, ::testing::Values(std::make_tuple("all", 1), std::make_tuple("any", 1))); using DataPacking4x8 = ResolverBuiltinsValidationTestWithParams; TEST_P(DataPacking4x8, Float_Vec4) { auto name = GetParam(); auto* builtin = Call(name, vec4(1.0f, 1.0f, 1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest, DataPacking4x8, ::testing::Values("pack4x8snorm", "pack4x8unorm")); using DataPacking2x16 = ResolverBuiltinsValidationTestWithParams; TEST_P(DataPacking2x16, Float_Vec2) { auto name = GetParam(); auto* builtin = Call(name, vec2(1.0f, 1.0f)); WrapInFunction(builtin); EXPECT_TRUE(r()->Resolve()) << r()->error(); } INSTANTIATE_TEST_SUITE_P(ResolverBuiltinsValidationTest, DataPacking2x16, ::testing::Values("pack2x16snorm", "pack2x16unorm", "pack2x16float")); } // namespace } // namespace tint