// Copyright 2021 The Tint Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "src/tint/resolver/resolver.h" #include "gmock/gmock.h" #include "src/tint/ast/assignment_statement.h" #include "src/tint/ast/bitcast_expression.h" #include "src/tint/ast/break_statement.h" #include "src/tint/ast/builtin_texture_helper_test.h" #include "src/tint/ast/call_statement.h" #include "src/tint/ast/continue_statement.h" #include "src/tint/ast/if_statement.h" #include "src/tint/ast/loop_statement.h" #include "src/tint/ast/return_statement.h" #include "src/tint/ast/stage_attribute.h" #include "src/tint/ast/switch_statement.h" #include "src/tint/ast/unary_op_expression.h" #include "src/tint/ast/variable_decl_statement.h" #include "src/tint/resolver/resolver_test_helper.h" #include "src/tint/sem/call.h" #include "src/tint/sem/function.h" #include "src/tint/sem/member_accessor_expression.h" #include "src/tint/sem/sampled_texture.h" #include "src/tint/sem/statement.h" #include "src/tint/sem/variable.h" using ::testing::ElementsAre; using ::testing::HasSubstr; using namespace tint::number_suffixes; // NOLINT namespace tint::resolver { namespace { using ExpressionList = utils::Vector; using BuiltinType = sem::BuiltinType; using ResolverBuiltinTest = ResolverTest; struct BuiltinData { const char* name; BuiltinType builtin; }; inline std::ostream& operator<<(std::ostream& out, BuiltinData data) { out << data.name; return out; } TEST_F(ResolverBuiltinTest, ModuleScopeUsage) { GlobalConst("c", ty.f32(), Call(Source{{12, 34}}, "abs", 1._f)); EXPECT_FALSE(r()->Resolve()); // TODO(crbug.com/tint/1581): Once 'abs' is implemented as @const, this will no longer be an // error. EXPECT_EQ(r()->error(), R"(12:34 error: 'const' initializer must be constant expression)"); } // Tests for Logical builtins namespace logical_builtin_tests { using ResolverBuiltinTest_BoolMethod = ResolverTestWithParam; TEST_P(ResolverBuiltinTest_BoolMethod, Scalar) { auto name = GetParam(); GlobalVar("my_var", ty.bool_(), ast::StorageClass::kPrivate); auto* expr = Call(name, "my_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); } TEST_P(ResolverBuiltinTest_BoolMethod, Vector) { auto name = GetParam(); GlobalVar("my_var", ty.vec3(), ast::StorageClass::kPrivate); auto* expr = Call(name, "my_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); } INSTANTIATE_TEST_SUITE_P(ResolverTest, ResolverBuiltinTest_BoolMethod, testing::Values("any", "all")); TEST_F(ResolverBuiltinTest, Select) { GlobalVar("my_var", ty.vec3(), ast::StorageClass::kPrivate); GlobalVar("bool_var", ty.vec3(), ast::StorageClass::kPrivate); auto* expr = Call("select", "my_var", "my_var", "bool_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); EXPECT_EQ(TypeOf(expr)->As()->Width(), 3u); EXPECT_TRUE(TypeOf(expr)->As()->type()->Is()); } TEST_F(ResolverBuiltinTest, Select_Error_NoParams) { auto* expr = Call("select"); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to select() 3 candidate functions: select(T, T, bool) -> T where: T is f32, f16, i32, u32 or bool select(vecN, vecN, bool) -> vecN where: T is f32, f16, i32, u32 or bool select(vecN, vecN, vecN) -> vecN where: T is f32, f16, i32, u32 or bool )"); } TEST_F(ResolverBuiltinTest, Select_Error_SelectorInt) { auto* expr = Call("select", 1_i, 1_i, 1_i); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); 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, f16, i32, u32 or bool select(vecN, vecN, bool) -> vecN where: T is f32, f16, i32, u32 or bool select(vecN, vecN, vecN) -> vecN where: T is f32, f16, i32, u32 or bool )"); } TEST_F(ResolverBuiltinTest, Select_Error_Matrix) { auto* expr = Call("select", mat2x2(vec2(1_f, 1_f), vec2(1_f, 1_f)), mat2x2(vec2(1_f, 1_f), vec2(1_f, 1_f)), Expr(true)); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to select(mat2x2, mat2x2, bool) 3 candidate functions: select(T, T, bool) -> T where: T is f32, f16, i32, u32 or bool select(vecN, vecN, bool) -> vecN where: T is f32, f16, i32, u32 or bool select(vecN, vecN, vecN) -> vecN where: T is f32, f16, i32, u32 or bool )"); } TEST_F(ResolverBuiltinTest, Select_Error_MismatchTypes) { auto* expr = Call("select", 1_f, vec2(2_f, 3_f), Expr(true)); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to select(f32, vec2, bool) 3 candidate functions: select(T, T, bool) -> T where: T is f32, f16, i32, u32 or bool select(vecN, vecN, bool) -> vecN where: T is f32, f16, i32, u32 or bool select(vecN, vecN, vecN) -> vecN where: T is f32, f16, i32, u32 or bool )"); } TEST_F(ResolverBuiltinTest, Select_Error_MismatchVectorSize) { auto* expr = Call("select", vec2(1_f, 2_f), vec3(3_f, 4_f, 5_f), Expr(true)); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to select(vec2, vec3, bool) 3 candidate functions: select(T, T, bool) -> T where: T is f32, f16, i32, u32 or bool select(vecN, vecN, bool) -> vecN where: T is f32, f16, i32, u32 or bool select(vecN, vecN, vecN) -> vecN where: T is f32, f16, i32, u32 or bool )"); } } // namespace logical_builtin_tests // Tests for Array builtins namespace array_builtin_tests { using ResolverBuiltinArrayTest = ResolverTest; TEST_F(ResolverBuiltinArrayTest, ArrayLength_Vector) { auto* ary = ty.array(); auto* str = Structure("S", utils::Vector{Member("x", ary)}); GlobalVar("a", ty.Of(str), ast::StorageClass::kStorage, ast::Access::kRead, utils::Vector{ create(0u), create(0u), }); auto* call = Call("arrayLength", AddressOf(MemberAccessor("a", "x"))); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinArrayTest, ArrayLength_Error_ArraySized) { GlobalVar("arr", ty.array(), ast::StorageClass::kPrivate); auto* call = Call("arrayLength", AddressOf("arr")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to arrayLength(ptr, read_write>) 1 candidate function: arrayLength(ptr, A>) -> u32 )"); } } // namespace array_builtin_tests // Tests for Numeric builtins with float parameter namespace float_builtin_tests { // Testcase parameters for float built-in having signature of (T, ...) -> T and (vecN, ...) -> // vecN struct BuiltinDataWithParamNum { uint32_t args_number; const char* name; BuiltinType builtin; }; inline std::ostream& operator<<(std::ostream& out, BuiltinDataWithParamNum data) { out << data.name; return out; } // Tests for float built-ins that has signiture (T, ...) -> T and (vecN, ...) -> vecN using ResolverBuiltinTest_FloatBuiltin_IdenticalType = ResolverTestWithParam; TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, Error_NoParams) { auto param = GetParam(); auto* call = Call(param.name); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "()")); } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, OneParam_Scalar_f32) { auto param = GetParam(); auto* call = Call(param.name, 1_f); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f32)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, OneParam_Vector_f32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_f, 1_f, 3_f)); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, TwoParams_Scalar_f32) { auto param = GetParam(); auto* call = Call(param.name, 1_f, 1_f); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f32, f32)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, TwoParams_Vector_f32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f)); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, ThreeParams_Scalar_f32) { auto param = GetParam(); auto* call = Call(param.name, 1_f, 1_f, 1_f); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f32, f32, f32)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, ThreeParams_Vector_f32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f)); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, FourParams_Scalar_f32) { auto param = GetParam(); auto* call = Call(param.name, 1_f, 1_f, 1_f, 1_f); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f32, f32, f32, f32)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, FourParams_Vector_f32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f)); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, OneParam_Scalar_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, 1_h); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f16)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, OneParam_Vector_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, vec3(1_h, 1_h, 3_h)); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, TwoParams_Scalar_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, 1_h, 1_h); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f16, f16)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, TwoParams_Vector_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h)); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, ThreeParams_Scalar_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, 1_h, 1_h, 1_h); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f16, f16, f16)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, ThreeParams_Vector_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h)); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, FourParams_Scalar_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, 1_h, 1_h, 1_h, 1_h); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(f16, f16, f16, f16)")); } } TEST_P(ResolverBuiltinTest_FloatBuiltin_IdenticalType, FourParams_Vector_f16) { auto param = GetParam(); Enable(ast::Extension::kF16); auto* call = Call(param.name, vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h)); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3, vec3)")); } } INSTANTIATE_TEST_SUITE_P( ResolverTest, ResolverBuiltinTest_FloatBuiltin_IdenticalType, testing::Values(BuiltinDataWithParamNum{1, "abs", BuiltinType::kAbs}, BuiltinDataWithParamNum{1, "acos", BuiltinType::kAcos}, BuiltinDataWithParamNum{1, "acosh", BuiltinType::kAcos}, BuiltinDataWithParamNum{1, "asin", BuiltinType::kAsin}, BuiltinDataWithParamNum{1, "asinh", BuiltinType::kAsin}, BuiltinDataWithParamNum{1, "atan", BuiltinType::kAtan}, BuiltinDataWithParamNum{1, "atanh", BuiltinType::kAtan}, BuiltinDataWithParamNum{2, "atan2", BuiltinType::kAtan2}, BuiltinDataWithParamNum{1, "ceil", BuiltinType::kCeil}, BuiltinDataWithParamNum{3, "clamp", BuiltinType::kClamp}, BuiltinDataWithParamNum{1, "cos", BuiltinType::kCos}, BuiltinDataWithParamNum{1, "cosh", BuiltinType::kCosh}, // cross: (vec3, vec3) -> vec3 BuiltinDataWithParamNum{1, "degrees", BuiltinType::kDegrees}, // distance: (T, T) -> T, (vecN, vecN) -> T BuiltinDataWithParamNum{1, "exp", BuiltinType::kExp}, BuiltinDataWithParamNum{1, "exp2", BuiltinType::kExp2}, // faceForward: (vecN, vecN, vecN) -> vecN BuiltinDataWithParamNum{1, "floor", BuiltinType::kFloor}, BuiltinDataWithParamNum{3, "fma", BuiltinType::kFma}, BuiltinDataWithParamNum{1, "fract", BuiltinType::kFract}, // frexp BuiltinDataWithParamNum{1, "inverseSqrt", BuiltinType::kInverseSqrt}, // ldexp: (T, i32) -> T, (vecN, vecN) -> vecN // length: (vecN) -> T BuiltinDataWithParamNum{1, "log", BuiltinType::kLog}, BuiltinDataWithParamNum{1, "log2", BuiltinType::kLog2}, BuiltinDataWithParamNum{2, "max", BuiltinType::kMax}, BuiltinDataWithParamNum{2, "min", BuiltinType::kMin}, // Note that `mix(vecN, vecN, f32) -> vecN` is not tested here. BuiltinDataWithParamNum{3, "mix", BuiltinType::kMix}, // modf // normalize: (vecN) -> vecN BuiltinDataWithParamNum{2, "pow", BuiltinType::kPow}, // quantizeToF16 is not implemented yet. BuiltinDataWithParamNum{1, "radians", BuiltinType::kRadians}, // reflect: (vecN, vecN) -> vecN // refract: (vecN, vecN, T) -> vecN BuiltinDataWithParamNum{1, "round", BuiltinType::kRound}, // saturate not implemented yet. BuiltinDataWithParamNum{1, "sign", BuiltinType::kSign}, BuiltinDataWithParamNum{1, "sin", BuiltinType::kSin}, BuiltinDataWithParamNum{1, "sinh", BuiltinType::kSinh}, BuiltinDataWithParamNum{3, "smoothstep", BuiltinType::kSmoothstep}, BuiltinDataWithParamNum{1, "sqrt", BuiltinType::kSqrt}, BuiltinDataWithParamNum{2, "step", BuiltinType::kStep}, BuiltinDataWithParamNum{1, "tan", BuiltinType::kTan}, BuiltinDataWithParamNum{1, "tanh", BuiltinType::kTanh}, BuiltinDataWithParamNum{1, "trunc", BuiltinType::kTrunc})); using ResolverBuiltinFloatTest = ResolverTest; // cross: (vec3, vec3) -> vec3 TEST_F(ResolverBuiltinFloatTest, Cross_f32) { auto* call = Call("cross", vec3(1_f, 2_f, 3_f), vec3(1_f, 2_f, 3_f)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } TEST_F(ResolverBuiltinFloatTest, Cross_f16) { Enable(ast::Extension::kF16); auto* call = Call("cross", vec3(1_h, 2_h, 3_h), vec3(1_h, 2_h, 3_h)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } TEST_F(ResolverBuiltinFloatTest, Cross_Error_NoArgs) { auto* call = Call("cross"); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to cross() 1 candidate function: cross(vec3, vec3) -> vec3 where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Cross_Error_Scalar) { auto* call = Call("cross", 1_f, 1_f); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to cross(f32, f32) 1 candidate function: cross(vec3, vec3) -> vec3 where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Cross_Error_Vec3Int) { auto* call = Call("cross", vec3(1_i, 2_i, 3_i), vec3(1_i, 2_i, 3_i)); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to cross(vec3, vec3) 1 candidate function: cross(vec3, vec3) -> vec3 where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Cross_Error_Vec4) { auto* call = Call("cross", vec4(1_f, 2_f, 3_f, 4_f), vec4(1_f, 2_f, 3_f, 4_f)); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to cross(vec4, vec4) 1 candidate function: cross(vec3, vec3) -> vec3 where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Cross_Error_TooManyParams) { auto* call = Call("cross", vec3(1_f, 2_f, 3_f), vec3(1_f, 2_f, 3_f), vec3(1_f, 2_f, 3_f)); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to cross(vec3, vec3, vec3) 1 candidate function: cross(vec3, vec3) -> vec3 where: T is f32 or f16 )"); } // distance: (T, T) -> T, (vecN, vecN) -> T TEST_F(ResolverBuiltinFloatTest, Distance_Scalar_f32) { auto* call = Call("distance", 1_f, 1_f); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Distance_Scalar_f16) { Enable(ast::Extension::kF16); auto* call = Call("distance", 1_h, 1_h); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Distance_Vector_f32) { auto* call = Call("distance", vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Distance_Vector_f16) { Enable(ast::Extension::kF16); auto* call = Call("distance", vec3(1_h, 1_h, 3_h), vec3(1_h, 1_h, 3_h)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Distance_TooManyParams) { auto* call = Call("distance", vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f)); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to distance(vec3, vec3, vec3) 2 candidate functions: distance(T, T) -> T where: T is f32 or f16 distance(vecN, vecN) -> T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Distance_TooFewParams) { auto* call = Call("distance", vec3(1_f, 1_f, 3_f)); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to distance(vec3) 2 candidate functions: distance(T, T) -> T where: T is f32 or f16 distance(vecN, vecN) -> T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Distance_NoParams) { auto* call = Call("distance"); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to distance() 2 candidate functions: distance(T, T) -> T where: T is f32 or f16 distance(vecN, vecN) -> T where: T is f32 or f16 )"); } // frexp: (f32) -> __frexp_result, (vecN) -> __frexp_result_vecN, (f16) -> __frexp_result_16, // (vecN) -> __frexp_result_vecN_f16 TEST_F(ResolverBuiltinFloatTest, FrexpScalar_f32) { auto* call = Call("frexp", 1_f); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* sig = ty->Members()[0]; EXPECT_TRUE(sig->Type()->Is()); 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()); 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); } TEST_F(ResolverBuiltinFloatTest, FrexpScalar_f16) { Enable(ast::Extension::kF16); auto* call = Call("frexp", 1_h); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* sig = ty->Members()[0]; EXPECT_TRUE(sig->Type()->Is()); EXPECT_EQ(sig->Offset(), 0u); EXPECT_EQ(sig->Size(), 2u); EXPECT_EQ(sig->Align(), 2u); EXPECT_EQ(sig->Name(), Sym("sig")); auto* exp = ty->Members()[1]; EXPECT_TRUE(exp->Type()->Is()); 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); } TEST_F(ResolverBuiltinFloatTest, FrexpVector_f32) { auto* call = Call("frexp", vec3()); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* sig = ty->Members()[0]; ASSERT_TRUE(sig->Type()->Is()); EXPECT_EQ(sig->Type()->As()->Width(), 3u); EXPECT_TRUE(sig->Type()->As()->type()->Is()); 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()); EXPECT_EQ(exp->Type()->As()->Width(), 3u); EXPECT_TRUE(exp->Type()->As()->type()->Is()); 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); } TEST_F(ResolverBuiltinFloatTest, FrexpVector_f16) { Enable(ast::Extension::kF16); auto* call = Call("frexp", vec3()); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* sig = ty->Members()[0]; ASSERT_TRUE(sig->Type()->Is()); EXPECT_EQ(sig->Type()->As()->Width(), 3u); EXPECT_TRUE(sig->Type()->As()->type()->Is()); EXPECT_EQ(sig->Offset(), 0u); EXPECT_EQ(sig->Size(), 6u); EXPECT_EQ(sig->Align(), 8u); EXPECT_EQ(sig->Name(), Sym("sig")); auto* exp = ty->Members()[1]; ASSERT_TRUE(exp->Type()->Is()); EXPECT_EQ(exp->Type()->As()->Width(), 3u); EXPECT_TRUE(exp->Type()->As()->type()->Is()); 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); } TEST_F(ResolverBuiltinFloatTest, Frexp_Error_FirstParamInt) { GlobalVar("v", ty.i32(), ast::StorageClass::kWorkgroup); auto* call = Call("frexp", 1_i, AddressOf("v")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(i32, ptr) 2 candidate functions: frexp(T) -> __frexp_result_T where: T is f32 or f16 frexp(vecN) -> __frexp_result_vecN_T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamFloatPtr) { GlobalVar("v", ty.f32(), ast::StorageClass::kWorkgroup); auto* call = Call("frexp", 1_f, AddressOf("v")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(f32, ptr) 2 candidate functions: frexp(T) -> __frexp_result_T where: T is f32 or f16 frexp(vecN) -> __frexp_result_vecN_T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Frexp_Error_SecondParamNotAPointer) { auto* call = Call("frexp", 1_f, 1_i); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(f32, i32) 2 candidate functions: frexp(T) -> __frexp_result_T where: T is f32 or f16 frexp(vecN) -> __frexp_result_vecN_T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Frexp_Error_VectorSizesDontMatch) { GlobalVar("v", ty.vec4(), ast::StorageClass::kWorkgroup); auto* call = Call("frexp", vec2(1_f, 2_f), AddressOf("v")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to frexp(vec2, ptr, read_write>) 2 candidate functions: frexp(T) -> __frexp_result_T where: T is f32 or f16 frexp(vecN) -> __frexp_result_vecN_T where: T is f32 or f16 )"); } // length: (T) -> T, (vecN) -> T TEST_F(ResolverBuiltinFloatTest, Length_Scalar_f32) { auto* call = Call("length", 1_f); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Length_Scalar_f16) { Enable(ast::Extension::kF16); auto* call = Call("length", 1_h); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Length_FloatVector_f32) { auto* call = Call("length", vec3(1_f, 1_f, 3_f)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Length_FloatVector_f16) { Enable(ast::Extension::kF16); auto* call = Call("length", vec3(1_h, 1_h, 3_h)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinFloatTest, Length_NoParams) { auto* call = Call("length"); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to length() 2 candidate functions: length(T) -> T where: T is f32 or f16 length(vecN) -> T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Length_TooManyParams) { auto* call = Call("length", 1_f, 2_f); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to length(f32, f32) 2 candidate functions: length(T) -> T where: T is f32 or f16 length(vecN) -> T where: T is f32 or f16 )"); } // mix(vecN, vecN, T) -> vecN. Other overloads are tested in // ResolverBuiltinTest_FloatBuiltin_IdenticalType above. TEST_F(ResolverBuiltinFloatTest, Mix_VectorScalar_f32) { auto* call = Call("mix", vec3(1_f, 1_f, 3_f), vec3(1_f, 1_f, 3_f), 4_f); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } TEST_F(ResolverBuiltinFloatTest, Mix_VectorScalar_f16) { Enable(ast::Extension::kF16); auto* call = Call("mix", vec3(1_h, 1_h, 1_h), vec3(1_h, 1_h, 1_h), 4_h); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } // modf: (f32) -> __modf_result, (vecN) -> __modf_result_vecN, (f16) -> __modf_result_f16, // (vecN) -> __modf_result_vecN_f16 TEST_F(ResolverBuiltinFloatTest, ModfScalar_f32) { auto* call = Call("modf", 1_f); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* fract = ty->Members()[0]; EXPECT_TRUE(fract->Type()->Is()); 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()); 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); } TEST_F(ResolverBuiltinFloatTest, ModfScalar_f16) { Enable(ast::Extension::kF16); auto* call = Call("modf", 1_h); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* fract = ty->Members()[0]; EXPECT_TRUE(fract->Type()->Is()); EXPECT_EQ(fract->Offset(), 0u); EXPECT_EQ(fract->Size(), 2u); EXPECT_EQ(fract->Align(), 2u); EXPECT_EQ(fract->Name(), Sym("fract")); auto* whole = ty->Members()[1]; EXPECT_TRUE(whole->Type()->Is()); EXPECT_EQ(whole->Offset(), 2u); EXPECT_EQ(whole->Size(), 2u); EXPECT_EQ(whole->Align(), 2u); EXPECT_EQ(whole->Name(), Sym("whole")); EXPECT_EQ(ty->Size(), 4u); EXPECT_EQ(ty->SizeNoPadding(), 4u); } TEST_F(ResolverBuiltinFloatTest, ModfVector_f32) { auto* call = Call("modf", vec3()); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* fract = ty->Members()[0]; ASSERT_TRUE(fract->Type()->Is()); EXPECT_EQ(fract->Type()->As()->Width(), 3u); EXPECT_TRUE(fract->Type()->As()->type()->Is()); 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()); EXPECT_EQ(whole->Type()->As()->Width(), 3u); EXPECT_TRUE(whole->Type()->As()->type()->Is()); 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); } TEST_F(ResolverBuiltinFloatTest, ModfVector_f16) { Enable(ast::Extension::kF16); auto* call = Call("modf", vec3()); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); auto* ty = TypeOf(call)->As(); ASSERT_NE(ty, nullptr); ASSERT_EQ(ty->Members().size(), 2u); auto* fract = ty->Members()[0]; ASSERT_TRUE(fract->Type()->Is()); EXPECT_EQ(fract->Type()->As()->Width(), 3u); EXPECT_TRUE(fract->Type()->As()->type()->Is()); EXPECT_EQ(fract->Offset(), 0u); EXPECT_EQ(fract->Size(), 6u); EXPECT_EQ(fract->Align(), 8u); EXPECT_EQ(fract->Name(), Sym("fract")); auto* whole = ty->Members()[1]; ASSERT_TRUE(whole->Type()->Is()); EXPECT_EQ(whole->Type()->As()->Width(), 3u); EXPECT_TRUE(whole->Type()->As()->type()->Is()); EXPECT_EQ(whole->Offset(), 8u); EXPECT_EQ(whole->Size(), 6u); EXPECT_EQ(whole->Align(), 8u); EXPECT_EQ(whole->Name(), Sym("whole")); EXPECT_EQ(ty->Size(), 16u); EXPECT_EQ(ty->SizeNoPadding(), 14u); } TEST_F(ResolverBuiltinFloatTest, Modf_Error_FirstParamInt) { GlobalVar("whole", ty.f32(), ast::StorageClass::kWorkgroup); auto* call = Call("modf", 1_i, AddressOf("whole")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to modf(i32, ptr) 2 candidate functions: modf(T) -> __modf_result_T where: T is f32 or f16 modf(vecN) -> __modf_result_vecN_T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Modf_Error_SecondParamIntPtr) { GlobalVar("whole", ty.i32(), ast::StorageClass::kWorkgroup); auto* call = Call("modf", 1_f, AddressOf("whole")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to modf(f32, ptr) 2 candidate functions: modf(T) -> __modf_result_T where: T is f32 or f16 modf(vecN) -> __modf_result_vecN_T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Modf_Error_SecondParamNotAPointer) { auto* call = Call("modf", 1_f, 1_f); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to modf(f32, f32) 2 candidate functions: modf(T) -> __modf_result_T where: T is f32 or f16 modf(vecN) -> __modf_result_vecN_T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinFloatTest, Modf_Error_VectorSizesDontMatch) { GlobalVar("whole", ty.vec4(), ast::StorageClass::kWorkgroup); auto* call = Call("modf", vec2(1_f, 2_f), AddressOf("whole")); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to modf(vec2, ptr, read_write>) 2 candidate functions: modf(T) -> __modf_result_T where: T is f32 or f16 modf(vecN) -> __modf_result_vecN_T where: T is f32 or f16 )"); } // normalize: (vecN) -> vecN TEST_F(ResolverBuiltinFloatTest, Normalize_Vector_f32) { auto* call = Call("normalize", vec3(1_f, 1_f, 3_f)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } TEST_F(ResolverBuiltinFloatTest, Normalize_Vector_f16) { Enable(ast::Extension::kF16); auto* call = Call("normalize", vec3(1_h, 1_h, 3_h)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_float_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } TEST_F(ResolverBuiltinFloatTest, Normalize_Error_NoParams) { auto* call = Call("normalize"); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to normalize() 1 candidate function: normalize(vecN) -> vecN where: T is f32 or f16 )"); } } // namespace float_builtin_tests // Tests for Numeric builtins with all integer parameter namespace integer_builtin_tests { // Testcase parameters for integer built-in having signature of (T, ...) -> T and (vecN, ...) -> // vecN, where T is i32 and u32 struct BuiltinDataWithParamNum { uint32_t args_number; const char* name; BuiltinType builtin; }; inline std::ostream& operator<<(std::ostream& out, BuiltinDataWithParamNum data) { out << data.name; return out; } // Tests for integer built-ins that has signiture (T, ...) -> T and (vecN, ...) -> vecN using ResolverBuiltinTest_IntegerBuiltin_IdenticalType = ResolverTestWithParam; TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, Error_NoParams) { auto param = GetParam(); auto* call = Call(param.name); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "()")); } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, OneParams_Scalar_i32) { auto param = GetParam(); auto* call = Call(param.name, 1_i); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(i32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, OneParams_Vector_i32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_i, 1_i, 3_i)); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, OneParams_Scalar_u32) { auto param = GetParam(); auto* call = Call(param.name, 1_u); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(u32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, OneParams_Vector_u32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_u, 1_u, 3_u)); WrapInFunction(call); if (param.args_number == 1u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, TwoParams_Scalar_i32) { auto param = GetParam(); auto* call = Call(param.name, 1_i, 1_i); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(i32, i32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, TwoParams_Vector_i32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_i, 1_i, 3_i), vec3(1_i, 1_i, 3_i)); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, TwoParams_Scalar_u32) { auto param = GetParam(); auto* call = Call(param.name, 1_u, 1_u); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(u32, u32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, TwoParams_Vector_u32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_u, 1_u, 3_u), vec3(1_u, 1_u, 3_u)); WrapInFunction(call); if (param.args_number == 2u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, ThreeParams_Scalar_i32) { auto param = GetParam(); auto* call = Call(param.name, 1_i, 1_i, 1_i); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(i32, i32, i32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, ThreeParams_Vector_i32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_i, 1_i, 3_i), vec3(1_i, 1_i, 3_i), vec3(1_i, 1_i, 3_i)); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, ThreeParams_Scalar_u32) { auto param = GetParam(); auto* call = Call(param.name, 1_u, 1_u, 1_u); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(u32, u32, u32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, ThreeParams_Vector_u32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_u, 1_u, 3_u), vec3(1_u, 1_u, 3_u), vec3(1_u, 1_u, 3_u)); WrapInFunction(call); if (param.args_number == 3u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, FourParams_Scalar_i32) { auto param = GetParam(); auto* call = Call(param.name, 1_i, 1_i, 1_i, 1_i); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(i32, i32, i32, i32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, FourParams_Vector_i32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_i, 1_i, 3_i), vec3(1_i, 1_i, 3_i), vec3(1_i, 1_i, 3_i), vec3(1_i, 1_i, 3_i)); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3, vec3)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, FourParams_Scalar_u32) { auto param = GetParam(); auto* call = Call(param.name, 1_u, 1_u, 1_u, 1_u); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(u32, u32, u32, u32)")); } } TEST_P(ResolverBuiltinTest_IntegerBuiltin_IdenticalType, FourParams_Vector_u32) { auto param = GetParam(); auto* call = Call(param.name, vec3(1_u, 1_u, 3_u), vec3(1_u, 1_u, 3_u), vec3(1_u, 1_u, 3_u), vec3(1_u, 1_u, 3_u)); WrapInFunction(call); if (param.args_number == 4u) { // Parameter count matched. EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector()); EXPECT_EQ(TypeOf(call)->As()->Width(), 3u); ASSERT_NE(TypeOf(call)->As()->type(), nullptr); EXPECT_TRUE(TypeOf(call)->As()->type()->Is()); } else { // Invalid parameter count. EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name) + "(vec3, vec3, vec3, vec3)")); } } INSTANTIATE_TEST_SUITE_P( ResolverTest, ResolverBuiltinTest_IntegerBuiltin_IdenticalType, testing::Values( BuiltinDataWithParamNum{1, "abs", BuiltinType::kAbs}, BuiltinDataWithParamNum{3, "clamp", BuiltinType::kClamp}, BuiltinDataWithParamNum{1, "countLeadingZeros", BuiltinType::kCountLeadingZeros}, BuiltinDataWithParamNum{1, "countOneBits", BuiltinType::kCountOneBits}, BuiltinDataWithParamNum{1, "countTrailingZeros", BuiltinType::kCountTrailingZeros}, // extractBits: (T, u32, u32) -> T BuiltinDataWithParamNum{1, "firstLeadingBit", BuiltinType::kFirstLeadingBit}, BuiltinDataWithParamNum{1, "firstTrailingBit", BuiltinType::kFirstTrailingBit}, // insertBits: (T, T, u32, u32) -> T BuiltinDataWithParamNum{2, "max", BuiltinType::kMax}, BuiltinDataWithParamNum{2, "min", BuiltinType::kMin}, BuiltinDataWithParamNum{1, "reverseBits", BuiltinType::kReverseBits})); } // namespace integer_builtin_tests // Tests for Numeric builtins with matrix parameter, i.e. "determinant" and "transpose" namespace matrix_builtin_tests { TEST_F(ResolverBuiltinTest, Determinant_2x2_f32) { GlobalVar("var", ty.mat2x2(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinTest, Determinant_2x2_f16) { Enable(ast::Extension::kF16); GlobalVar("var", ty.mat2x2(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinTest, Determinant_3x3_f32) { GlobalVar("var", ty.mat3x3(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinTest, Determinant_3x3_f16) { Enable(ast::Extension::kF16); GlobalVar("var", ty.mat3x3(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinTest, Determinant_4x4_f32) { GlobalVar("var", ty.mat4x4(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinTest, Determinant_4x4_f16) { Enable(ast::Extension::kF16); GlobalVar("var", ty.mat4x4(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_F(ResolverBuiltinTest, Determinant_NotSquare) { GlobalVar("var", ty.mat2x3(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(mat2x3) 1 candidate function: determinant(matNxN) -> T where: T is f32 or f16 )"); } TEST_F(ResolverBuiltinTest, Determinant_NotMatrix) { GlobalVar("var", ty.f32(), ast::StorageClass::kPrivate); auto* call = Call("determinant", "var"); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to determinant(f32) 1 candidate function: determinant(matNxN) -> T where: T is f32 or f16 )"); } } // namespace matrix_builtin_tests // Tests for Numeric builtins with float and integer vector parameter, i.e. "dot" namespace vector_builtin_tests { TEST_F(ResolverBuiltinTest, Dot_Vec2_f32) { GlobalVar("my_var", ty.vec2(), ast::StorageClass::kPrivate); auto* expr = Call("dot", "my_var", "my_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); } TEST_F(ResolverBuiltinTest, Dot_Vec2_f16) { Enable(ast::Extension::kF16); GlobalVar("my_var", ty.vec2(), ast::StorageClass::kPrivate); auto* expr = Call("dot", "my_var", "my_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); } TEST_F(ResolverBuiltinTest, Dot_Vec3_i32) { GlobalVar("my_var", ty.vec3(), ast::StorageClass::kPrivate); auto* expr = Call("dot", "my_var", "my_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); } TEST_F(ResolverBuiltinTest, Dot_Vec4_u32) { GlobalVar("my_var", ty.vec4(), ast::StorageClass::kPrivate); auto* expr = Call("dot", "my_var", "my_var"); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); EXPECT_TRUE(TypeOf(expr)->Is()); } TEST_F(ResolverBuiltinTest, Dot_Error_Scalar) { auto* expr = Call("dot", 1_f, 1_f); WrapInFunction(expr); EXPECT_FALSE(r()->Resolve()); EXPECT_EQ(r()->error(), R"(error: no matching call to dot(f32, f32) 1 candidate function: dot(vecN, vecN) -> T where: T is f32, i32, u32 or f16 )"); } } // namespace vector_builtin_tests // Tests for Derivative builtins namespace derivative_builtin_tests { using ResolverBuiltinDerivativeTest = ResolverTestWithParam; TEST_P(ResolverBuiltinDerivativeTest, Scalar) { auto name = GetParam(); GlobalVar("ident", ty.f32(), ast::StorageClass::kPrivate); auto* expr = Call(name, "ident"); Func("func", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)}, utils::Vector{create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); ASSERT_TRUE(TypeOf(expr)->Is()); } TEST_P(ResolverBuiltinDerivativeTest, Vector) { auto name = GetParam(); GlobalVar("ident", ty.vec4(), ast::StorageClass::kPrivate); auto* expr = Call(name, "ident"); Func("func", utils::Empty, ty.void_(), utils::Vector{Ignore(expr)}, utils::Vector{create(ast::PipelineStage::kFragment)}); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); ASSERT_TRUE(TypeOf(expr)->Is()); EXPECT_TRUE(TypeOf(expr)->As()->type()->Is()); EXPECT_EQ(TypeOf(expr)->As()->Width(), 4u); } TEST_P(ResolverBuiltinDerivativeTest, MissingParam) { auto name = GetParam(); auto* expr = Call(name); WrapInFunction(expr); 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) -> vecN\n"); } INSTANTIATE_TEST_SUITE_P(ResolverTest, ResolverBuiltinDerivativeTest, testing::Values("dpdx", "dpdxCoarse", "dpdxFine", "dpdy", "dpdyCoarse", "dpdyFine", "fwidth", "fwidthCoarse", "fwidthFine")); } // namespace derivative_builtin_tests // Tests for Texture builtins namespace texture_builtin_tests { 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; } struct TextureTestParams { 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; } class ResolverBuiltinTest_TextureOperation : public ResolverTestWithParam { 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, ExpressionList* call_params) { if (type->IsAnyOf()) { GlobalVar(name, type, utils::Vector{ create(0u), create(0u), }); } else { GlobalVar(name, type, ast::StorageClass::kPrivate); } call_params->Push(Expr(name)); } const ast::Type* subtype(Texture type) { if (type == Texture::kF32) { return ty.f32(); } if (type == Texture::kI32) { return ty.i32(); } return ty.u32(); } }; using ResolverBuiltinTest_SampledTextureOperation = ResolverBuiltinTest_TextureOperation; TEST_P(ResolverBuiltinTest_SampledTextureOperation, TextureLoadSampled) { 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); 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); auto* expr = Call("textureLoad", call_params); WrapInFunction(expr); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(expr), nullptr); ASSERT_TRUE(TypeOf(expr)->Is()); if (type == Texture::kF32) { EXPECT_TRUE(TypeOf(expr)->As()->type()->Is()); } else if (type == Texture::kI32) { EXPECT_TRUE(TypeOf(expr)->As()->type()->Is()); } else { EXPECT_TRUE(TypeOf(expr)->As()->type()->Is()); } EXPECT_EQ(TypeOf(expr)->As()->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})); using ResolverBuiltinTest_Texture = ResolverTestWithParam; INSTANTIATE_TEST_SUITE_P(ResolverTest, ResolverBuiltinTest_Texture, testing::ValuesIn(ast::builtin::test::TextureOverloadCase::ValidCases())); static std::string to_str(const std::string& function, utils::VectorRef params) { std::stringstream out; out << function << "("; bool first = true; for (auto* param : params) { if (!first) { out << ", "; } out << sem::str(param->Usage()); first = false; } out << ")"; return out.str(); } static 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 ""; } TEST_P(ResolverBuiltinTest_Texture, Call) { auto param = GetParam(); param.BuildTextureVariable(this); param.BuildSamplerVariable(this); auto* call = Call(param.function, param.args(this)); auto* stmt = CallStmt(call); Func("func", utils::Empty, ty.void_(), utils::Vector{stmt}, utils::Vector{Stage(ast::PipelineStage::kFragment)}); 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()); break; case ast::TextureDimension::k2d: case ast::TextureDimension::k2dArray: case ast::TextureDimension::kCube: case ast::TextureDimension::kCubeArray: { auto* vec = As(TypeOf(call)); ASSERT_NE(vec, nullptr); EXPECT_EQ(vec->Width(), 2u); EXPECT_TRUE(vec->type()->Is()); break; } case ast::TextureDimension::k3d: { auto* vec = As(TypeOf(call)); ASSERT_NE(vec, nullptr); EXPECT_EQ(vec->Width(), 3u); EXPECT_TRUE(vec->type()->Is()); break; } } } else if (std::string(param.function) == "textureNumLayers") { EXPECT_TRUE(TypeOf(call)->Is()); } else if (std::string(param.function) == "textureNumLevels") { EXPECT_TRUE(TypeOf(call)->Is()); } else if (std::string(param.function) == "textureNumSamples") { EXPECT_TRUE(TypeOf(call)->Is()); } else if (std::string(param.function) == "textureStore") { EXPECT_TRUE(TypeOf(call)->Is()); } else if (std::string(param.function) == "textureGather") { auto* vec = As(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()); break; case ast::builtin::test::TextureDataType::kU32: EXPECT_TRUE(vec->type()->Is()); break; case ast::builtin::test::TextureDataType::kI32: EXPECT_TRUE(vec->type()->Is()); break; } } else if (std::string(param.function) == "textureGatherCompare") { auto* vec = As(TypeOf(call)); ASSERT_NE(vec, nullptr); EXPECT_EQ(vec->Width(), 4u); EXPECT_TRUE(vec->type()->Is()); } 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(); ASSERT_NE(vec, nullptr); switch (param.texture_data_type) { case ast::builtin::test::TextureDataType::kF32: EXPECT_TRUE(vec->type()->Is()); break; case ast::builtin::test::TextureDataType::kU32: EXPECT_TRUE(vec->type()->Is()); break; case ast::builtin::test::TextureDataType::kI32: EXPECT_TRUE(vec->type()->Is()); break; } break; } case ast::builtin::test::TextureKind::kDepth: case ast::builtin::test::TextureKind::kDepthMultisampled: { EXPECT_TRUE(TypeOf(call)->Is()); break; } } } auto* call_sem = Sem().Get(call); ASSERT_NE(call_sem, nullptr); auto* target = call_sem->Target(); ASSERT_NE(target, nullptr); auto got = texture_builtin_tests::to_str(param.function, target->Parameters()); auto* expected = expected_texture_overload(param.overload); EXPECT_EQ(got, expected); } } // namespace texture_builtin_tests // Tests for Data Packing builtins namespace data_packing_builtin_tests { using ResolverBuiltinTest_DataPacking = ResolverTestWithParam; TEST_P(ResolverBuiltinTest_DataPacking, InferType) { auto param = GetParam(); bool pack4 = param.builtin == BuiltinType::kPack4x8snorm || param.builtin == BuiltinType::kPack4x8unorm; auto* call = pack4 ? Call(param.name, vec4(1_f, 2_f, 3_f, 4_f)) : Call(param.name, vec2(1_f, 2_f)); WrapInFunction(call); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_P(ResolverBuiltinTest_DataPacking, Error_IncorrectParamType) { auto param = GetParam(); bool pack4 = param.builtin == BuiltinType::kPack4x8snorm || param.builtin == BuiltinType::kPack4x8unorm; auto* call = pack4 ? Call(param.name, vec4(1_i, 2_i, 3_i, 4_i)) : Call(param.name, vec2(1_i, 2_i)); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name))); } TEST_P(ResolverBuiltinTest_DataPacking, Error_NoParams) { auto param = GetParam(); auto* call = Call(param.name); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " + std::string(param.name))); } TEST_P(ResolverBuiltinTest_DataPacking, Error_TooManyParams) { auto param = GetParam(); bool pack4 = param.builtin == BuiltinType::kPack4x8snorm || param.builtin == BuiltinType::kPack4x8unorm; auto* call = pack4 ? Call(param.name, vec4(1_f, 2_f, 3_f, 4_f), 1_f) : Call(param.name, vec2(1_f, 2_f), 1_f); WrapInFunction(call); EXPECT_FALSE(r()->Resolve()); 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})); } // namespace data_packing_builtin_tests // Tests for Data Unpacking builtins namespace data_unpacking_builtin_tests { using ResolverBuiltinTest_DataUnpacking = ResolverTestWithParam; TEST_P(ResolverBuiltinTest_DataUnpacking, InferType) { auto param = GetParam(); bool pack4 = param.builtin == BuiltinType::kUnpack4x8snorm || param.builtin == BuiltinType::kUnpack4x8unorm; auto* call = Call(param.name, 1_u); 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()->Width(), 4u); } else { EXPECT_EQ(TypeOf(call)->As()->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})); } // namespace data_unpacking_builtin_tests // Tests for Synchronization builtins namespace synchronization_builtin_tests { using ResolverBuiltinTest_Barrier = ResolverTestWithParam; TEST_P(ResolverBuiltinTest_Barrier, InferType) { auto param = GetParam(); auto* call = Call(param.name); WrapInFunction(CallStmt(call)); EXPECT_TRUE(r()->Resolve()) << r()->error(); ASSERT_NE(TypeOf(call), nullptr); EXPECT_TRUE(TypeOf(call)->Is()); } TEST_P(ResolverBuiltinTest_Barrier, Error_TooManyParams) { auto param = GetParam(); auto* call = Call(param.name, vec4(1_f, 2_f, 3_f, 4_f), 1_f); WrapInFunction(CallStmt(call)); EXPECT_FALSE(r()->Resolve()); 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})); } // namespace synchronization_builtin_tests } // namespace } // namespace tint::resolver