Resolver: Split tests into multiple files
Try and make sense of the huge number of tests we have.
Rename tests so they have a consistent naming style.
Change-Id: I0c089d5945778a8718480a1a2f854435e7b0e79a
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/44162
Reviewed-by: Antonio Maiorano <amaiorano@google.com>
Commit-Queue: Antonio Maiorano <amaiorano@google.com>
diff --git a/BUILD.gn b/BUILD.gn
index 7a3a7ca..1ac5dac 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -842,9 +842,11 @@
"src/intrinsic_table_test.cc",
"src/program_builder_test.cc",
"src/program_test.cc",
- "src/resolver/resolver_test.cc",
+ "src/resolver/intrinsic_test.cc",
"src/resolver/resolver_test_helper.cc",
"src/resolver/resolver_test_helper.h",
+ "src/resolver/resolver_test.cc",
+ "src/resolver/validation_test.cc",
"src/semantic/sem_intrinsic_test.cc",
"src/scope_stack_test.cc",
"src/symbol_table_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 684a9c4..d3e94d4 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -470,11 +470,13 @@
inspector/inspector_test.cc
intrinsic_table_test.cc
program_test.cc
- resolver/resolver_test.cc
+ resolver/intrinsic_test.cc
resolver/resolver_test_helper.cc
resolver/resolver_test_helper.h
- semantic/sem_intrinsic_test.cc
+ resolver/resolver_test.cc
+ resolver/validation_test.cc
scope_stack_test.cc
+ semantic/sem_intrinsic_test.cc
symbol_table_test.cc
symbol_test.cc
traits_test.cc
diff --git a/src/resolver/intrinsic_test.cc b/src/resolver/intrinsic_test.cc
new file mode 100644
index 0000000..b266b00
--- /dev/null
+++ b/src/resolver/intrinsic_test.cc
@@ -0,0 +1,1961 @@
+// 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/resolver/resolver.h"
+
+#include "gmock/gmock.h"
+#include "src/ast/assignment_statement.h"
+#include "src/ast/bitcast_expression.h"
+#include "src/ast/break_statement.h"
+#include "src/ast/call_statement.h"
+#include "src/ast/continue_statement.h"
+#include "src/ast/if_statement.h"
+#include "src/ast/intrinsic_texture_helper_test.h"
+#include "src/ast/loop_statement.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/stage_decoration.h"
+#include "src/ast/switch_statement.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/ast/variable_decl_statement.h"
+#include "src/resolver/resolver_test_helper.h"
+#include "src/semantic/call.h"
+#include "src/semantic/function.h"
+#include "src/semantic/member_accessor_expression.h"
+#include "src/semantic/statement.h"
+#include "src/semantic/variable.h"
+#include "src/type/access_control_type.h"
+#include "src/type/sampled_texture_type.h"
+
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+
+namespace tint {
+namespace resolver {
+namespace {
+
+using IntrinsicType = semantic::IntrinsicType;
+
+using ResolverIntrinsicTest = ResolverTest;
+
+using ResolverIntrinsicDerivativeTest = ResolverTestWithParam<std::string>;
+TEST_P(ResolverIntrinsicDerivativeTest, Scalar) {
+ auto name = GetParam();
+
+ Global("ident", ty.f32(), ast::StorageClass::kNone);
+
+ auto* expr = Call(name, "ident");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<type::F32>());
+}
+
+TEST_P(ResolverIntrinsicDerivativeTest, Vector) {
+ auto name = GetParam();
+ Global("ident", ty.vec4<f32>(), ast::StorageClass::kNone);
+
+ auto* expr = Call(name, "ident");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
+ EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 4u);
+}
+
+TEST_P(ResolverIntrinsicDerivativeTest, 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<f32>) -> vecN<f32>\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverIntrinsicDerivativeTest,
+ testing::Values("dpdx",
+ "dpdxCoarse",
+ "dpdxFine",
+ "dpdy",
+ "dpdyCoarse",
+ "dpdyFine",
+ "fwidth",
+ "fwidthCoarse",
+ "fwidthFine"));
+
+using ResolverIntrinsic = ResolverTestWithParam<std::string>;
+TEST_P(ResolverIntrinsic, Test) {
+ auto name = GetParam();
+
+ Global("my_var", ty.vec3<bool>(), ast::StorageClass::kNone);
+
+ auto* expr = Call(name, "my_var");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
+}
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverIntrinsic,
+ testing::Values("any", "all"));
+
+using ResolverIntrinsicTest_FloatMethod = ResolverTestWithParam<std::string>;
+TEST_P(ResolverIntrinsicTest_FloatMethod, Vector) {
+ auto name = GetParam();
+
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+
+ auto* expr = Call(name, "my_var");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(expr), nullptr);
+ ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::Bool>());
+ EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_FloatMethod, Scalar) {
+ auto name = GetParam();
+
+ Global("my_var", ty.f32(), ast::StorageClass::kNone);
+
+ auto* expr = Call(name, "my_var");
+ WrapInFunction(expr);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(expr), nullptr);
+ EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
+}
+
+TEST_P(ResolverIntrinsicTest_FloatMethod, MissingParam) {
+ auto name = GetParam();
+
+ Global("my_var", ty.f32(), ast::StorageClass::kNone);
+
+ 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) -> bool\n " + name +
+ "(vecN<f32>) -> vecN<bool>\n");
+}
+
+TEST_P(ResolverIntrinsicTest_FloatMethod, TooManyParams) {
+ auto name = GetParam();
+
+ Global("my_var", ty.f32(), ast::StorageClass::kNone);
+
+ auto* expr = Call(name, "my_var", 1.23f);
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(), "error: no matching call to " + name +
+ "(ptr<f32>, f32)\n\n"
+ "2 candidate functions:\n " +
+ name + "(f32) -> bool\n " + name +
+ "(vecN<f32>) -> vecN<bool>\n");
+}
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_FloatMethod,
+ testing::Values("isInf", "isNan", "isFinite", "isNormal"));
+
+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 {
+ type::TextureDimension dim;
+ Texture type = Texture::kF32;
+ type::ImageFormat format = type::ImageFormat::kR16Float;
+};
+inline std::ostream& operator<<(std::ostream& out, TextureTestParams data) {
+ out << data.dim << "_" << data.type;
+ return out;
+}
+
+class ResolverIntrinsicTest_TextureOperation
+ : public ResolverTestWithParam<TextureTestParams> {
+ public:
+ /// Gets an appropriate type for the coords parameter depending the the
+ /// dimensionality of the texture being sampled.
+ /// @param dim dimensionality of the texture being sampled
+ /// @param scalar the scalar type
+ /// @returns a pointer to a type appropriate for the coord param
+ type::Type* GetCoordsType(type::TextureDimension dim, type::Type* scalar) {
+ switch (dim) {
+ case type::TextureDimension::k1d:
+ return scalar;
+ case type::TextureDimension::k2d:
+ case type::TextureDimension::k2dArray:
+ return create<type::Vector>(scalar, 2);
+ case type::TextureDimension::k3d:
+ case type::TextureDimension::kCube:
+ case type::TextureDimension::kCubeArray:
+ return create<type::Vector>(scalar, 3);
+ default:
+ [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
+ }
+ return nullptr;
+ }
+
+ void add_call_param(std::string name,
+ type::Type* type,
+ ast::ExpressionList* call_params) {
+ Global(name, type, ast::StorageClass::kNone);
+ call_params->push_back(Expr(name));
+ }
+ type::Type* subtype(Texture type) {
+ if (type == Texture::kF32) {
+ return create<type::F32>();
+ }
+ if (type == Texture::kI32) {
+ return create<type::I32>();
+ }
+ return create<type::U32>();
+ }
+};
+
+using ResolverIntrinsicTest_StorageTextureOperation =
+ ResolverIntrinsicTest_TextureOperation;
+TEST_P(ResolverIntrinsicTest_StorageTextureOperation, TextureLoadRo) {
+ auto dim = GetParam().dim;
+ auto type = GetParam().type;
+ auto format = GetParam().format;
+
+ auto* coords_type = GetCoordsType(dim, ty.i32());
+
+ auto* subtype = type::StorageTexture::SubtypeFor(format, Types());
+ auto* texture_type = create<type::StorageTexture>(dim, format, subtype);
+ auto* ro_texture_type =
+ create<type::AccessControl>(ast::AccessControl::kReadOnly, texture_type);
+
+ ast::ExpressionList call_params;
+
+ add_call_param("texture", ro_texture_type, &call_params);
+ add_call_param("coords", coords_type, &call_params);
+
+ if (type::IsTextureArray(dim)) {
+ add_call_param("array_index", 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<type::Vector>());
+ if (type == Texture::kF32) {
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
+ } else if (type == Texture::kI32) {
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::I32>());
+ } else {
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::U32>());
+ }
+ EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 4u);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_StorageTextureOperation,
+ testing::Values(
+ TextureTestParams{type::TextureDimension::k1d, Texture::kF32,
+ type::ImageFormat::kR16Float},
+ TextureTestParams{type::TextureDimension::k1d, Texture::kI32,
+ type::ImageFormat::kR16Sint},
+ TextureTestParams{type::TextureDimension::k1d, Texture::kF32,
+ type::ImageFormat::kR8Unorm},
+ TextureTestParams{type::TextureDimension::k2d, Texture::kF32,
+ type::ImageFormat::kR16Float},
+ TextureTestParams{type::TextureDimension::k2d, Texture::kI32,
+ type::ImageFormat::kR16Sint},
+ TextureTestParams{type::TextureDimension::k2d, Texture::kF32,
+ type::ImageFormat::kR8Unorm},
+ TextureTestParams{type::TextureDimension::k2dArray, Texture::kF32,
+ type::ImageFormat::kR16Float},
+ TextureTestParams{type::TextureDimension::k2dArray, Texture::kI32,
+ type::ImageFormat::kR16Sint},
+ TextureTestParams{type::TextureDimension::k2dArray, Texture::kF32,
+ type::ImageFormat::kR8Unorm},
+ TextureTestParams{type::TextureDimension::k3d, Texture::kF32,
+ type::ImageFormat::kR16Float},
+ TextureTestParams{type::TextureDimension::k3d, Texture::kI32,
+ type::ImageFormat::kR16Sint},
+ TextureTestParams{type::TextureDimension::k3d, Texture::kF32,
+ type::ImageFormat::kR8Unorm}));
+
+using ResolverIntrinsicTest_SampledTextureOperation =
+ ResolverIntrinsicTest_TextureOperation;
+TEST_P(ResolverIntrinsicTest_SampledTextureOperation, TextureLoadSampled) {
+ auto dim = GetParam().dim;
+ auto type = GetParam().type;
+
+ type::Type* s = subtype(type);
+ auto* coords_type = GetCoordsType(dim, ty.i32());
+ auto* texture_type = create<type::SampledTexture>(dim, s);
+
+ ast::ExpressionList call_params;
+
+ add_call_param("texture", texture_type, &call_params);
+ add_call_param("coords", coords_type, &call_params);
+ if (dim == type::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<type::Vector>());
+ if (type == Texture::kF32) {
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
+ } else if (type == Texture::kI32) {
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::I32>());
+ } else {
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::U32>());
+ }
+ EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 4u);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_SampledTextureOperation,
+ testing::Values(TextureTestParams{type::TextureDimension::k1d},
+ TextureTestParams{type::TextureDimension::k2d},
+ TextureTestParams{type::TextureDimension::k2dArray},
+ TextureTestParams{type::TextureDimension::k3d}));
+
+TEST_F(ResolverIntrinsicTest, Dot_Vec2) {
+ Global("my_var", ty.vec2<f32>(), ast::StorageClass::kNone);
+
+ 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<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Dot_Vec3) {
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+
+ 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<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Dot_Vec4) {
+ Global("my_var", ty.vec4<f32>(), ast::StorageClass::kNone);
+
+ 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<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Dot_Error_Scalar) {
+ auto* expr = Call("dot", 1.0f, 1.0f);
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to dot(f32, f32)
+
+1 candidate function:
+ dot(vecN<f32>, vecN<f32>) -> f32
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Dot_Error_VectorInt) {
+ Global("my_var", ty.vec4<i32>(), ast::StorageClass::kNone);
+
+ auto* expr = Call("dot", "my_var", "my_var");
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to dot(ptr<vec4<i32>>, ptr<vec4<i32>>)
+
+1 candidate function:
+ dot(vecN<f32>, vecN<f32>) -> f32
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Select) {
+ Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
+
+ Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kNone);
+
+ 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<type::Vector>());
+ EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
+ EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Select_Error_NoParams) {
+ auto* expr = Call("select");
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select()
+
+2 candidate functions:
+ select(T, T, bool) -> T where: T is scalar
+ select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Select_Error_SelectorInt) {
+ auto* expr = Call("select", Expr(1), Expr(1), Expr(1));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(i32, i32, i32)
+
+2 candidate functions:
+ select(T, T, bool) -> T where: T is scalar
+ select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Select_Error_Matrix) {
+ auto* mat = mat2x2<float>(vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f));
+ auto* expr = Call("select", mat, mat, Expr(true));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
+
+2 candidate functions:
+ select(T, T, bool) -> T where: T is scalar
+ select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Select_Error_MismatchTypes) {
+ auto* expr = Call("select", 1.0f, vec2<float>(2.0f, 3.0f), Expr(true));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(f32, vec2<f32>, bool)
+
+2 candidate functions:
+ select(T, T, bool) -> T where: T is scalar
+ select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Select_Error_MismatchVectorSize) {
+ auto* expr = Call("select", vec2<float>(1.0f, 2.0f),
+ vec3<float>(3.0f, 4.0f, 5.0f), Expr(true));
+ WrapInFunction(expr);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
+
+2 candidate functions:
+ select(T, T, bool) -> T where: T is scalar
+ select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
+)");
+}
+
+struct IntrinsicData {
+ const char* name;
+ IntrinsicType intrinsic;
+};
+
+inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
+ out << data.name;
+ return out;
+}
+
+using ResolverIntrinsicTest_DataPacking = ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_DataPacking, InferType) {
+ auto param = GetParam();
+
+ bool pack4 = param.intrinsic == IntrinsicType::kPack4x8Snorm ||
+ param.intrinsic == IntrinsicType::kPack4x8Unorm;
+
+ auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f))
+ : Call(param.name, vec2<f32>(1.f, 2.f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
+}
+
+TEST_P(ResolverIntrinsicTest_DataPacking, Error_IncorrectParamType) {
+ auto param = GetParam();
+
+ bool pack4 = param.intrinsic == IntrinsicType::kPack4x8Snorm ||
+ param.intrinsic == IntrinsicType::kPack4x8Unorm;
+
+ auto* call = pack4 ? Call(param.name, vec4<i32>(1, 2, 3, 4))
+ : Call(param.name, vec2<i32>(1, 2));
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
+ std::string(param.name)));
+}
+
+TEST_P(ResolverIntrinsicTest_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(ResolverIntrinsicTest_DataPacking, Error_TooManyParams) {
+ auto param = GetParam();
+
+ bool pack4 = param.intrinsic == IntrinsicType::kPack4x8Snorm ||
+ param.intrinsic == IntrinsicType::kPack4x8Unorm;
+
+ auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f)
+ : Call(param.name, vec2<f32>(1.f, 2.f), 1.0f);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
+ std::string(param.name)));
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_DataPacking,
+ testing::Values(
+ IntrinsicData{"pack4x8snorm", IntrinsicType::kPack4x8Snorm},
+ IntrinsicData{"pack4x8unorm", IntrinsicType::kPack4x8Unorm},
+ IntrinsicData{"pack2x16snorm", IntrinsicType::kPack2x16Snorm},
+ IntrinsicData{"pack2x16unorm", IntrinsicType::kPack2x16Unorm},
+ IntrinsicData{"pack2x16float", IntrinsicType::kPack2x16Float}));
+
+using ResolverIntrinsicTest_DataUnpacking =
+ ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_DataUnpacking, InferType) {
+ auto param = GetParam();
+
+ bool pack4 = param.intrinsic == IntrinsicType::kUnpack4x8Snorm ||
+ param.intrinsic == IntrinsicType::kUnpack4x8Unorm;
+
+ auto* call = Call(param.name, 1u);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ if (pack4) {
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 4u);
+ } else {
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 2u);
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_DataUnpacking,
+ testing::Values(
+ IntrinsicData{"unpack4x8snorm", IntrinsicType::kUnpack4x8Snorm},
+ IntrinsicData{"unpack4x8unorm", IntrinsicType::kUnpack4x8Unorm},
+ IntrinsicData{"unpack2x16snorm", IntrinsicType::kUnpack2x16Snorm},
+ IntrinsicData{"unpack2x16unorm", IntrinsicType::kUnpack2x16Unorm},
+ IntrinsicData{"unpack2x16float", IntrinsicType::kUnpack2x16Float}));
+
+using ResolverIntrinsicTest_SingleParam = ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_SingleParam, Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1.f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam, Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
+ 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<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32) -> f32\n " +
+ std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam, Error_TooManyParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1, 2, 3);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "(i32, i32, i32)\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32) -> f32\n " +
+ std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_SingleParam,
+ testing::Values(IntrinsicData{"acos", IntrinsicType::kAcos},
+ IntrinsicData{"asin", IntrinsicType::kAsin},
+ IntrinsicData{"atan", IntrinsicType::kAtan},
+ IntrinsicData{"ceil", IntrinsicType::kCeil},
+ IntrinsicData{"cos", IntrinsicType::kCos},
+ IntrinsicData{"cosh", IntrinsicType::kCosh},
+ IntrinsicData{"exp", IntrinsicType::kExp},
+ IntrinsicData{"exp2", IntrinsicType::kExp2},
+ IntrinsicData{"floor", IntrinsicType::kFloor},
+ IntrinsicData{"fract", IntrinsicType::kFract},
+ IntrinsicData{"inverseSqrt", IntrinsicType::kInverseSqrt},
+ IntrinsicData{"log", IntrinsicType::kLog},
+ IntrinsicData{"log2", IntrinsicType::kLog2},
+ IntrinsicData{"round", IntrinsicType::kRound},
+ IntrinsicData{"sign", IntrinsicType::kSign},
+ IntrinsicData{"sin", IntrinsicType::kSin},
+ IntrinsicData{"sinh", IntrinsicType::kSinh},
+ IntrinsicData{"sqrt", IntrinsicType::kSqrt},
+ IntrinsicData{"tan", IntrinsicType::kTan},
+ IntrinsicData{"tanh", IntrinsicType::kTanh},
+ IntrinsicData{"trunc", IntrinsicType::kTrunc}));
+
+using ResolverIntrinsicDataTest = ResolverTest;
+
+TEST_F(ResolverIntrinsicDataTest, ArrayLength_Vector) {
+ Global("arr", ty.array<int>(), ast::StorageClass::kNone);
+ auto* call = Call("arrayLength", "arr");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
+}
+
+TEST_F(ResolverIntrinsicDataTest, ArrayLength_Error_ArraySized) {
+ Global("arr", ty.array<int, 4>(), ast::StorageClass::kNone);
+ auto* call = Call("arrayLength", "arr");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to arrayLength(ptr<array<i32, 4>>)\n\n"
+ "1 candidate function:\n"
+ " arrayLength(array<T>) -> u32\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Normalize_Vector) {
+ auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
+ 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<type::Vector>()->size(), 3u);
+}
+
+TEST_F(ResolverIntrinsicDataTest, Normalize_Error_NoParams) {
+ auto* call = Call("normalize");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to normalize()\n\n"
+ "1 candidate function:\n"
+ " normalize(vecN<f32>) -> vecN<f32>\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, FrexpScalar) {
+ Global("exp", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", 1.0f, "exp");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicDataTest, FrexpVector) {
+ Global("exp", ty.vec3<i32>(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", vec3<f32>(1.0f, 2.0f, 3.0f), "exp");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::Vector>());
+ EXPECT_TRUE(TypeOf(call)->As<type::Vector>()->type()->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicDataTest, Frexp_Error_FirstParamInt) {
+ Global("exp", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", 1, "exp");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to frexp(i32, ptr<workgroup, i32>)\n\n"
+ "2 candidate functions:\n"
+ " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
+ " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
+ "where: T is i32 or u32\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Frexp_Error_SecondParamFloatPtr) {
+ Global("exp", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", 1.0f, "exp");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to frexp(f32, ptr<workgroup, f32>)\n\n"
+ "2 candidate functions:\n"
+ " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
+ " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
+ "where: T is i32 or u32\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Frexp_Error_SecondParamNotAPointer) {
+ auto* call = Call("frexp", 1.0f, 1);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to frexp(f32, i32)\n\n"
+ "2 candidate functions:\n"
+ " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
+ " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
+ "where: T is i32 or u32\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Frexp_Error_VectorSizesDontMatch) {
+ Global("exp", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("frexp", vec2<f32>(1.0f, 2.0f), "exp");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to frexp(vec2<f32>, ptr<workgroup, "
+ "vec4<i32>>)\n\n"
+ "2 candidate functions:\n"
+ " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
+ " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
+ "where: T is i32 or u32\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, ModfScalar) {
+ Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", 1.0f, "whole");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicDataTest, ModfVector) {
+ Global("whole", ty.vec3<f32>(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", vec3<f32>(1.0f, 2.0f, 3.0f), "whole");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::Vector>());
+ EXPECT_TRUE(TypeOf(call)->As<type::Vector>()->type()->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicDataTest, Modf_Error_FirstParamInt) {
+ Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", 1, "whole");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to modf(i32, ptr<workgroup, f32>)\n\n"
+ "2 candidate functions:\n"
+ " modf(f32, ptr<f32>) -> f32\n"
+ " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Modf_Error_SecondParamIntPtr) {
+ Global("whole", ty.i32(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", 1.0f, "whole");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to modf(f32, ptr<workgroup, i32>)\n\n"
+ "2 candidate functions:\n"
+ " modf(f32, ptr<f32>) -> f32\n"
+ " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Modf_Error_SecondParamNotAPointer) {
+ auto* call = Call("modf", 1.0f, 1.0f);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to modf(f32, f32)\n\n"
+ "2 candidate functions:\n"
+ " modf(f32, ptr<f32>) -> f32\n"
+ " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
+}
+
+TEST_F(ResolverIntrinsicDataTest, Modf_Error_VectorSizesDontMatch) {
+ Global("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
+ auto* call = Call("modf", vec2<f32>(1.0f, 2.0f), "whole");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to modf(vec2<f32>, ptr<workgroup, "
+ "vec4<f32>>)\n\n"
+ "2 candidate functions:\n"
+ " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n"
+ " modf(f32, ptr<f32>) -> f32\n");
+}
+
+using ResolverIntrinsicTest_SingleParam_FloatOrInt =
+ ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Float_Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1.f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Float_Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
+ 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<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Sint_Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, -1);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Sint_Vector) {
+ auto param = GetParam();
+
+ ast::ExpressionList vals;
+ vals.push_back(Expr(1));
+ vals.push_back(Expr(1));
+ vals.push_back(Expr(3));
+
+ ast::ExpressionList params;
+ params.push_back(vec3<i32>(vals));
+
+ auto* call = Call(param.name, params);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Uint_Scalar) {
+ auto param = GetParam();
+
+ ast::ExpressionList params;
+ params.push_back(Expr(1u));
+
+ auto* call = Call(param.name, params);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Uint_Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_SingleParam_FloatOrInt, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) +
+ "(T) -> T where: T is f32, i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverIntrinsicTest_SingleParam_FloatOrInt,
+ testing::Values(IntrinsicData{"abs",
+ IntrinsicType::kAbs}));
+
+TEST_F(ResolverIntrinsicTest, Length_Scalar) {
+ auto* call = Call("length", 1.f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+TEST_F(ResolverIntrinsicTest, Length_FloatVector) {
+ ast::ExpressionList params;
+ params.push_back(vec3<f32>(1.0f, 1.0f, 3.0f));
+
+ auto* call = Call("length", params);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+using ResolverIntrinsicTest_TwoParam = ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_TwoParam, Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1.f, 1.f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+TEST_P(ResolverIntrinsicTest_TwoParam, Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
+ vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_TwoParam, Error_NoTooManyParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1, 2, 3);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "(i32, i32, i32)\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32, f32) -> f32\n " +
+ std::string(param.name) +
+ "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
+}
+
+TEST_P(ResolverIntrinsicTest_TwoParam, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32, f32) -> f32\n " +
+ std::string(param.name) +
+ "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_TwoParam,
+ testing::Values(IntrinsicData{"atan2", IntrinsicType::kAtan2},
+ IntrinsicData{"pow", IntrinsicType::kPow},
+ IntrinsicData{"step", IntrinsicType::kStep},
+ IntrinsicData{"reflect", IntrinsicType::kReflect}));
+
+TEST_F(ResolverIntrinsicTest, Distance_Scalar) {
+ 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_float_scalar());
+}
+
+TEST_F(ResolverIntrinsicTest, Distance_Vector) {
+ auto* call = Call("distance", vec3<f32>(1.0f, 1.0f, 3.0f),
+ vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Cross) {
+ auto* call =
+ Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_F(ResolverIntrinsicTest, 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<f32>, vec3<f32>) -> vec3<f32>
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Cross_Error_Scalar) {
+ auto* call = Call("cross", 1.0f, 1.0f);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(), R"(error: no matching call to cross(f32, f32)
+
+1 candidate function:
+ cross(vec3<f32>, vec3<f32>) -> vec3<f32>
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Cross_Error_Vec3Int) {
+ auto* call = Call("cross", vec3<i32>(1, 2, 3), vec3<i32>(1, 2, 3));
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to cross(vec3<i32>, vec3<i32>)
+
+1 candidate function:
+ cross(vec3<f32>, vec3<f32>) -> vec3<f32>
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Cross_Error_Vec4) {
+ auto* call = Call("cross", vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f),
+ vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
+
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to cross(vec4<f32>, vec4<f32>)
+
+1 candidate function:
+ cross(vec3<f32>, vec3<f32>) -> vec3<f32>
+)");
+}
+
+TEST_F(ResolverIntrinsicTest, Cross_Error_TooManyParams) {
+ auto* call = Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f),
+ vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
+
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ R"(error: no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
+
+1 candidate function:
+ cross(vec3<f32>, vec3<f32>) -> vec3<f32>
+)");
+}
+TEST_F(ResolverIntrinsicTest, Normalize) {
+ auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
+ 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<type::Vector>()->size(), 3u);
+}
+
+TEST_F(ResolverIntrinsicTest, Normalize_NoArgs) {
+ 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<f32>) -> vecN<f32>
+)");
+}
+
+using ResolverIntrinsicTest_ThreeParam = ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_ThreeParam, Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1.f, 1.f, 1.f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam, Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
+ vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+TEST_P(ResolverIntrinsicTest_ThreeParam, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) + "(f32, f32, f32) -> f32\n " +
+ std::string(param.name) +
+ "(vecN<f32>, vecN<f32>, vecN<f32>) -> vecN<f32>\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_ThreeParam,
+ testing::Values(IntrinsicData{"mix", IntrinsicType::kMix},
+ IntrinsicData{"smoothStep", IntrinsicType::kSmoothStep},
+ IntrinsicData{"fma", IntrinsicType::kFma},
+ IntrinsicData{"faceForward", IntrinsicType::kFaceForward}));
+
+using ResolverIntrinsicTest_ThreeParam_FloatOrInt =
+ ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Float_Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1.f, 1.f, 1.f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_scalar());
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Float_Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
+ vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Sint_Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1, 1, 1);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Sint_Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3),
+ vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Uint_Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1u, 1u, 1u);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Uint_Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u),
+ vec3<u32>(1u, 1u, 3u));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_ThreeParam_FloatOrInt, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) +
+ "(T, T, T) -> T where: T is f32, i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>, vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 "
+ "or u32\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(ResolverTest,
+ ResolverIntrinsicTest_ThreeParam_FloatOrInt,
+ testing::Values(IntrinsicData{"clamp",
+ IntrinsicType::kClamp}));
+
+using ResolverIntrinsicTest_Int_SingleParam =
+ ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_Int_SingleParam, Scalar) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_integer_scalar());
+}
+
+TEST_P(ResolverIntrinsicTest_Int_SingleParam, Vector) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_Int_SingleParam, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(), "error: no matching call to " +
+ std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) +
+ "(T) -> T where: T is i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>) -> vecN<T> where: T is i32 or u32\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_Int_SingleParam,
+ testing::Values(IntrinsicData{"countOneBits", IntrinsicType::kCountOneBits},
+ IntrinsicData{"reverseBits", IntrinsicType::kReverseBits}));
+
+using ResolverIntrinsicTest_FloatOrInt_TwoParam =
+ ResolverTestWithParam<IntrinsicData>;
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Scalar_Signed) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1, 1);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
+}
+
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Scalar_Unsigned) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1u, 1u);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
+}
+
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Scalar_Float) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, 1.0f, 1.0f);
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Vector_Signed) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Vector_Unsigned) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Vector_Float) {
+ auto param = GetParam();
+
+ auto* call =
+ Call(param.name, vec3<f32>(1.f, 1.f, 3.f), vec3<f32>(1.f, 1.f, 3.f));
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->is_float_vector());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
+}
+
+TEST_P(ResolverIntrinsicTest_FloatOrInt_TwoParam, Error_NoParams) {
+ auto param = GetParam();
+
+ auto* call = Call(param.name);
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to " + std::string(param.name) +
+ "()\n\n"
+ "2 candidate functions:\n " +
+ std::string(param.name) +
+ "(T, T) -> T where: T is f32, i32 or u32\n " +
+ std::string(param.name) +
+ "(vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_FloatOrInt_TwoParam,
+ testing::Values(IntrinsicData{"min", IntrinsicType::kMin},
+ IntrinsicData{"max", IntrinsicType::kMax}));
+
+TEST_F(ResolverIntrinsicTest, Determinant_2x2) {
+ Global("var", ty.mat2x2<f32>(), ast::StorageClass::kFunction);
+
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Determinant_3x3) {
+ Global("var", ty.mat3x3<f32>(), ast::StorageClass::kFunction);
+
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Determinant_4x4) {
+ Global("var", ty.mat4x4<f32>(), ast::StorageClass::kFunction);
+
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+
+ ASSERT_NE(TypeOf(call), nullptr);
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
+}
+
+TEST_F(ResolverIntrinsicTest, Determinant_NotSquare) {
+ Global("var", ty.mat2x3<f32>(), ast::StorageClass::kFunction);
+
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(
+ r()->error(),
+ "error: no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
+ "1 candidate function:\n"
+ " determinant(matNxN<f32>) -> f32\n");
+}
+
+TEST_F(ResolverIntrinsicTest, Determinant_NotMatrix) {
+ Global("var", ty.f32(), ast::StorageClass::kFunction);
+
+ auto* call = Call("determinant", "var");
+ WrapInFunction(call);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: no matching call to determinant(ptr<function, f32>)\n\n"
+ "1 candidate function:\n"
+ " determinant(matNxN<f32>) -> f32\n");
+}
+
+using ResolverIntrinsicTest_Texture =
+ ResolverTestWithParam<ast::intrinsic::test::TextureOverloadCase>;
+
+INSTANTIATE_TEST_SUITE_P(
+ ResolverTest,
+ ResolverIntrinsicTest_Texture,
+ testing::ValuesIn(ast::intrinsic::test::TextureOverloadCase::ValidCases()));
+
+std::string to_str(const std::string& function,
+ const semantic::ParameterList& params) {
+ std::stringstream out;
+ out << function << "(";
+ bool first = true;
+ for (auto& param : params) {
+ if (!first) {
+ out << ", ";
+ }
+ out << semantic::str(param.usage);
+ first = false;
+ }
+ out << ")";
+ return out.str();
+}
+
+const char* expected_texture_overload(
+ ast::intrinsic::test::ValidTextureOverload overload) {
+ using ValidTextureOverload = ast::intrinsic::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::kDimensionsMultisampled2dArray:
+ case ValidTextureOverload::kDimensionsDepth2d:
+ case ValidTextureOverload::kDimensionsDepth2dArray:
+ case ValidTextureOverload::kDimensionsDepthCube:
+ case ValidTextureOverload::kDimensionsDepthCubeArray:
+ case ValidTextureOverload::kDimensionsStorageRO1d:
+ case ValidTextureOverload::kDimensionsStorageRO2d:
+ case ValidTextureOverload::kDimensionsStorageRO2dArray:
+ case ValidTextureOverload::kDimensionsStorageRO3d:
+ case ValidTextureOverload::kDimensionsStorageWO1d:
+ case ValidTextureOverload::kDimensionsStorageWO2d:
+ case ValidTextureOverload::kDimensionsStorageWO2dArray:
+ case ValidTextureOverload::kDimensionsStorageWO3d:
+ return R"(textureDimensions(texture))";
+ case ValidTextureOverload::kNumLayers2dArray:
+ case ValidTextureOverload::kNumLayersCubeArray:
+ case ValidTextureOverload::kNumLayersMultisampled2dArray:
+ 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::kNumSamplesMultisampled2d:
+ case ValidTextureOverload::kNumSamplesMultisampled2dArray:
+ 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::kLoad1dLevelF32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad1dLevelU32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad1dLevelI32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad2dLevelF32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad2dLevelU32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad2dLevelI32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad2dArrayLevelF32:
+ return R"(textureLoad(texture, coords, array_index, level))";
+ case ValidTextureOverload::kLoad2dArrayLevelU32:
+ return R"(textureLoad(texture, coords, array_index, level))";
+ case ValidTextureOverload::kLoad2dArrayLevelI32:
+ return R"(textureLoad(texture, coords, array_index, level))";
+ case ValidTextureOverload::kLoad3dLevelF32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad3dLevelU32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoad3dLevelI32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoadMultisampled2dF32:
+ return R"(textureLoad(texture, coords, sample_index))";
+ case ValidTextureOverload::kLoadMultisampled2dU32:
+ return R"(textureLoad(texture, coords, sample_index))";
+ case ValidTextureOverload::kLoadMultisampled2dI32:
+ return R"(textureLoad(texture, coords, sample_index))";
+ case ValidTextureOverload::kLoadMultisampled2dArrayF32:
+ return R"(textureLoad(texture, coords, array_index, sample_index))";
+ case ValidTextureOverload::kLoadMultisampled2dArrayU32:
+ return R"(textureLoad(texture, coords, array_index, sample_index))";
+ case ValidTextureOverload::kLoadMultisampled2dArrayI32:
+ return R"(textureLoad(texture, coords, array_index, sample_index))";
+ case ValidTextureOverload::kLoadDepth2dLevelF32:
+ return R"(textureLoad(texture, coords, level))";
+ case ValidTextureOverload::kLoadDepth2dArrayLevelF32:
+ return R"(textureLoad(texture, coords, array_index, level))";
+ case ValidTextureOverload::kLoadStorageRO1dRgba32float:
+ return R"(textureLoad(texture, coords))";
+ case ValidTextureOverload::kLoadStorageRO2dRgba8unorm:
+ case ValidTextureOverload::kLoadStorageRO2dRgba8snorm:
+ case ValidTextureOverload::kLoadStorageRO2dRgba8uint:
+ case ValidTextureOverload::kLoadStorageRO2dRgba8sint:
+ case ValidTextureOverload::kLoadStorageRO2dRgba16uint:
+ case ValidTextureOverload::kLoadStorageRO2dRgba16sint:
+ case ValidTextureOverload::kLoadStorageRO2dRgba16float:
+ case ValidTextureOverload::kLoadStorageRO2dR32uint:
+ case ValidTextureOverload::kLoadStorageRO2dR32sint:
+ case ValidTextureOverload::kLoadStorageRO2dR32float:
+ case ValidTextureOverload::kLoadStorageRO2dRg32uint:
+ case ValidTextureOverload::kLoadStorageRO2dRg32sint:
+ case ValidTextureOverload::kLoadStorageRO2dRg32float:
+ case ValidTextureOverload::kLoadStorageRO2dRgba32uint:
+ case ValidTextureOverload::kLoadStorageRO2dRgba32sint:
+ case ValidTextureOverload::kLoadStorageRO2dRgba32float:
+ return R"(textureLoad(texture, coords))";
+ case ValidTextureOverload::kLoadStorageRO2dArrayRgba32float:
+ return R"(textureLoad(texture, coords, array_index))";
+ case ValidTextureOverload::kLoadStorageRO3dRgba32float:
+ return R"(textureLoad(texture, coords))";
+ case ValidTextureOverload::kStoreWO1dRgba32float:
+ return R"(textureStore(texture, coords, value))";
+ case ValidTextureOverload::kStoreWO2dRgba32float:
+ return R"(textureStore(texture, coords, value))";
+ case ValidTextureOverload::kStoreWO2dArrayRgba32float:
+ return R"(textureStore(texture, coords, array_index, value))";
+ case ValidTextureOverload::kStoreWO3dRgba32float:
+ return R"(textureStore(texture, coords, value))";
+ }
+ return "<unmatched texture overload>";
+}
+
+TEST_P(ResolverIntrinsicTest_Texture, Call) {
+ auto param = GetParam();
+
+ param.buildTextureVariable(this);
+ param.buildSamplerVariable(this);
+
+ auto* call = Call(param.function, param.args(this));
+ WrapInFunction(call);
+
+ 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 type::TextureDimension::k1d:
+ EXPECT_EQ(TypeOf(call)->type_name(), ty.i32()->type_name());
+ break;
+ case type::TextureDimension::k2d:
+ case type::TextureDimension::k2dArray:
+ EXPECT_EQ(TypeOf(call)->type_name(), ty.vec2<i32>()->type_name());
+ break;
+ case type::TextureDimension::k3d:
+ case type::TextureDimension::kCube:
+ case type::TextureDimension::kCubeArray:
+ EXPECT_EQ(TypeOf(call)->type_name(), ty.vec3<i32>()->type_name());
+ break;
+ }
+ } else if (std::string(param.function) == "textureNumLayers") {
+ EXPECT_EQ(TypeOf(call), ty.i32());
+ } else if (std::string(param.function) == "textureNumLevels") {
+ EXPECT_EQ(TypeOf(call), ty.i32());
+ } else if (std::string(param.function) == "textureNumSamples") {
+ EXPECT_EQ(TypeOf(call), ty.i32());
+ } else if (std::string(param.function) == "textureStore") {
+ EXPECT_EQ(TypeOf(call), ty.void_());
+ } else {
+ switch (param.texture_kind) {
+ case ast::intrinsic::test::TextureKind::kRegular:
+ case ast::intrinsic::test::TextureKind::kMultisampled:
+ case ast::intrinsic::test::TextureKind::kStorage: {
+ auto* datatype = param.resultVectorComponentType(this);
+ ASSERT_TRUE(TypeOf(call)->Is<type::Vector>());
+ EXPECT_EQ(TypeOf(call)->As<type::Vector>()->type(), datatype);
+ break;
+ }
+ case ast::intrinsic::test::TextureKind::kDepth: {
+ EXPECT_EQ(TypeOf(call), ty.f32());
+ break;
+ }
+ }
+ }
+
+ auto* call_sem = Sem().Get(call);
+ ASSERT_NE(call_sem, nullptr);
+ auto* target = call_sem->Target();
+ ASSERT_NE(target, nullptr);
+
+ auto got = resolver::to_str(param.function, target->Parameters());
+ auto* expected = expected_texture_overload(param.overload);
+ EXPECT_EQ(got, expected);
+}
+
+} // namespace
+} // namespace resolver
+} // namespace tint
diff --git a/src/resolver/resolver_test.cc b/src/resolver/resolver_test.cc
index 0e11556..057e142 100644
--- a/src/resolver/resolver_test.cc
+++ b/src/resolver/resolver_test.cc
@@ -44,46 +44,6 @@
namespace resolver {
namespace {
-using IntrinsicType = semantic::IntrinsicType;
-
-class FakeStmt : public ast::Statement {
- public:
- explicit FakeStmt(Source source) : ast::Statement(source) {}
- FakeStmt* Clone(CloneContext*) const override { return nullptr; }
- bool IsValid() const override { return true; }
- void to_str(const semantic::Info&, std::ostream& out, size_t) const override {
- out << "Fake";
- }
-};
-
-class FakeExpr : public ast::Expression {
- public:
- explicit FakeExpr(Source source) : ast::Expression(source) {}
- FakeExpr* Clone(CloneContext*) const override { return nullptr; }
- bool IsValid() const override { return true; }
- void to_str(const semantic::Info&, std::ostream&, size_t) const override {}
-};
-
-TEST_F(ResolverTest, Error_WithEmptySource) {
- auto* s = create<FakeStmt>();
- WrapInFunction(s);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: unknown statement type for type determination: Fake");
-}
-
-TEST_F(ResolverTest, Stmt_Error_Unknown) {
- auto* s = create<FakeStmt>(Source{Source::Location{2, 30}});
- WrapInFunction(s);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "2:30 error: unknown statement type for type determination: Fake");
-}
-
TEST_F(ResolverTest, Stmt_Assign) {
auto* lhs = Expr(2);
auto* rhs = Expr(2.3f);
@@ -239,244 +199,6 @@
EXPECT_TRUE(TypeOf(continuing_rhs)->Is<type::F32>());
}
-TEST_F(ResolverTest, Stmt_Loop_ContinueInLoopBodyBeforeDecl_UsageInContinuing) {
- // loop {
- // continue; // Bypasses z decl
- // var z : i32;
- //
- // continuing {
- // z = 2;
- // }
- // }
-
- auto error_loc = Source{Source::Location{12, 34}};
- auto* body = Block(create<ast::ContinueStatement>(),
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Assign(Expr(error_loc, "z"), Expr(2)));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: continue statement bypasses declaration of 'z' in "
- "continuing block");
-}
-
-TEST_F(ResolverTest,
- Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing) {
- // loop {
- // continue; // Bypasses z decl
- // var z : i32;
- // continue; // Ok
- //
- // continuing {
- // z = 2;
- // }
- // }
-
- auto error_loc = Source{Source::Location{12, 34}};
- auto* body = Block(create<ast::ContinueStatement>(),
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
- create<ast::ContinueStatement>());
- auto* continuing = Block(Assign(Expr(error_loc, "z"), Expr(2)));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: continue statement bypasses declaration of 'z' in "
- "continuing block");
-}
-
-TEST_F(ResolverTest,
- Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuing) {
- // loop {
- // if (true) {
- // continue; // Still bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // z = 2;
- // }
- // }
-
- auto error_loc = Source{Source::Location{12, 34}};
- auto* body = Block(If(Expr(true), Block(create<ast::ContinueStatement>())),
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Assign(Expr(error_loc, "z"), Expr(2)));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: continue statement bypasses declaration of 'z' in "
- "continuing block");
-}
-
-TEST_F(
- ResolverTest,
- Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingSubscope) {
- // loop {
- // if (true) {
- // continue; // Still bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // if (true) {
- // z = 2; // Must fail even if z is in a sub-scope
- // }
- // }
- // }
-
- auto error_loc = Source{Source::Location{12, 34}};
- auto* body = Block(If(Expr(true), Block(create<ast::ContinueStatement>())),
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
-
- auto* continuing =
- Block(If(Expr(true), Block(Assign(Expr(error_loc, "z"), Expr(2)))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: continue statement bypasses declaration of 'z' in "
- "continuing block");
-}
-
-TEST_F(ResolverTest,
- Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingLoop) {
- // loop {
- // if (true) {
- // continue; // Still bypasses z decl (if we reach here)
- // }
- // var z : i32;
- // continuing {
- // loop {
- // z = 2; // Must fail even if z is in a sub-scope
- // }
- // }
- // }
-
- auto error_loc = Source{Source::Location{12, 34}};
- auto* body = Block(If(Expr(true), Block(create<ast::ContinueStatement>())),
- Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
-
- auto* continuing = Block(Loop(Block(Assign(Expr(error_loc, "z"), Expr(2)))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_FALSE(r()->Resolve()) << r()->error();
- EXPECT_EQ(r()->error(),
- "12:34 error: continue statement bypasses declaration of 'z' in "
- "continuing block");
-}
-
-TEST_F(ResolverTest,
- Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
- // loop {
- // loop {
- // continue; // OK: not part of the outer loop
- // }
- // var z : i32;
- //
- // continuing {
- // z = 2;
- // }
- // }
-
- auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
- auto* body =
- Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Assign(Expr("z"), Expr(2)));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverTest,
- Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingSubscope) {
- // loop {
- // loop {
- // continue; // OK: not part of the outer loop
- // }
- // var z : i32;
- //
- // continuing {
- // if (true) {
- // z = 2;
- // }
- // }
- // }
-
- auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
- auto* body =
- Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(If(Expr(true), Block(Assign(Expr("z"), Expr(2)))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverTest,
- Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
- // loop {
- // loop {
- // continue; // OK: not part of the outer loop
- // }
- // var z : i32;
- //
- // continuing {
- // loop {
- // z = 2;
- // }
- // }
- // }
-
- auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
- auto* body =
- Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
- auto* continuing = Block(Loop(Block(Assign(Expr("z"), Expr(2)))));
- auto* loop_stmt = Loop(body, continuing);
- WrapInFunction(loop_stmt);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverTest, Stmt_ContinueInLoop) {
- WrapInFunction(Loop(Block(create<ast::ContinueStatement>(Source{{12, 34}}))));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverTest, Stmt_ContinueNotInLoop) {
- WrapInFunction(create<ast::ContinueStatement>(Source{{12, 34}}));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
-}
-
-TEST_F(ResolverTest, Stmt_BreakInLoop) {
- WrapInFunction(Loop(Block(create<ast::BreakStatement>(Source{{12, 34}}))));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverTest, Stmt_BreakInSwitch) {
- WrapInFunction(Loop(Block(create<ast::SwitchStatement>(
- Expr(1), ast::CaseStatementList{
- create<ast::CaseStatement>(
- ast::CaseSelectorList{Literal(1)},
- Block(create<ast::BreakStatement>(Source{{12, 34}}))),
- }))));
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-}
-
-TEST_F(ResolverTest, Stmt_BreakNotInLoopOrSwitch) {
- WrapInFunction(create<ast::BreakStatement>(Source{{12, 34}}));
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: break statement must be in a loop or switch case");
-}
-
TEST_F(ResolverTest, Stmt_Return) {
auto* cond = Expr(2);
@@ -540,55 +262,6 @@
EXPECT_EQ(StmtOf(expr), call);
}
-TEST_F(ResolverTest, Stmt_Call_undeclared) {
- // fn main() -> void {func(); return; }
- // fn func() -> void { return; }
-
- SetSource(Source::Location{12, 34});
- auto* call_expr = Call("func");
- ast::VariableList params0;
-
- Func("main", params0, ty.f32(),
- ast::StatementList{
- create<ast::CallStatement>(call_expr),
- create<ast::ReturnStatement>(),
- },
- ast::FunctionDecorationList{});
-
- Func("func", params0, ty.f32(),
- ast::StatementList{
- create<ast::ReturnStatement>(),
- },
- ast::FunctionDecorationList{});
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "12:34 error: v-0006: unable to find called function: func");
-}
-
-TEST_F(ResolverTest, Stmt_Call_recursive) {
- // fn main() -> void {main(); }
-
- SetSource(Source::Location{12, 34});
- auto* call_expr = Call("main");
- ast::VariableList params0;
-
- Func("main", params0, ty.f32(),
- ast::StatementList{
- create<ast::CallStatement>(call_expr),
- },
- ast::FunctionDecorationList{
- create<ast::StageDecoration>(ast::PipelineStage::kVertex),
- });
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "12:34 error: recursion is not permitted. 'main' attempted to call "
- "itself.");
-}
-
TEST_F(ResolverTest, Stmt_VariableDecl) {
auto* var = Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(2));
auto* init = var->constructor();
@@ -616,37 +289,6 @@
EXPECT_TRUE(TypeOf(init)->Is<type::I32>());
}
-TEST_F(ResolverTest, Stmt_VariableDecl_MismatchedTypeScalarConstructor) {
- u32 unsigned_value = 2u; // Type does not match variable type
- auto* var =
- Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(unsigned_value));
-
- auto* decl =
- create<ast::VariableDeclStatement>(Source{{{3, 3}, {3, 22}}}, var);
- WrapInFunction(decl);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:3 error: constructor expression type does not match variable type)");
-}
-
-TEST_F(ResolverTest, Stmt_VariableDecl_MismatchedTypeScalarConstructor_Alias) {
- auto* my_int = ty.alias("MyInt", ty.i32());
- u32 unsigned_value = 2u; // Type does not match variable type
- auto* var =
- Var("my_var", my_int, ast::StorageClass::kNone, Expr(unsigned_value));
-
- auto* decl =
- create<ast::VariableDeclStatement>(Source{{{3, 3}, {3, 22}}}, var);
- WrapInFunction(decl);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- R"(3:3 error: constructor expression type does not match variable type)");
-}
-
TEST_F(ResolverTest, Stmt_VariableDecl_ModuleScope) {
auto* init = Expr(2);
Global("my_var", ty.i32(), ast::StorageClass::kNone, init);
@@ -758,16 +400,6 @@
EXPECT_TRUE(CheckVarUsers(mod_f32, {fn_f32->constructor()}));
}
-TEST_F(ResolverTest, Expr_Error_Unknown) {
- FakeExpr e(Source{Source::Location{2, 30}});
- WrapInFunction(&e);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "2:30 error: unknown expression for type determination");
-}
-
TEST_F(ResolverTest, Expr_ArrayAccessor_Array) {
auto* idx = Expr(2);
Global("my_var", ty.array<f32, 3>(), ast::StorageClass::kFunction);
@@ -925,27 +557,6 @@
EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
}
-TEST_F(ResolverTest, Expr_DontCall_Function) {
- Func("func", {}, ty.void_(), {}, {});
- auto* ident = create<ast::IdentifierExpression>(
- Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
- Symbols().Register("func"));
- WrapInFunction(ident);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:8 error: missing '(' for function call");
-}
-
-TEST_F(ResolverTest, Expr_DontCall_Intrinsic) {
- auto* ident = create<ast::IdentifierExpression>(
- Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
- Symbols().Register("round"));
- WrapInFunction(ident);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:8 error: missing '(' for intrinsic call");
-}
-
TEST_F(ResolverTest, Expr_Cast) {
Global("name", ty.f32(), ast::StorageClass::kPrivate);
@@ -1104,39 +715,6 @@
EXPECT_FALSE(r()->Resolve());
}
-TEST_F(ResolverTest, UsingUndefinedVariable_Fail) {
- // b = 2;
-
- SetSource(Source{Source::Location{12, 34}});
- auto* lhs = Expr("b");
- auto* rhs = Expr(2);
- auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
- WrapInFunction(assign);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: v-0006: identifier must be declared before use: b");
-}
-
-TEST_F(ResolverTest, UsingUndefinedVariableInBlockStatement_Fail) {
- // {
- // b = 2;
- // }
-
- SetSource(Source{Source::Location{12, 34}});
- auto* lhs = Expr("b");
- auto* rhs = Expr(2);
-
- auto* body = create<ast::BlockStatement>(ast::StatementList{
- create<ast::AssignmentStatement>(lhs, rhs),
- });
- WrapInFunction(body);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(),
- "12:34 error: v-0006: identifier must be declared before use: b");
-}
-
TEST_F(ResolverTest, Function_RegisterInputOutputVariables) {
auto* in_var = Global("in_var", ty.f32(), ast::StorageClass::kInput);
auto* out_var = Global("out_var", ty.f32(), ast::StorageClass::kOutput);
@@ -1300,49 +878,6 @@
EXPECT_THAT(Sem().Get(mem)->Swizzle(), ElementsAre(2));
}
-TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
- Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
-
- auto* ident = create<ast::IdentifierExpression>(
- Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
- Symbols().Register("xyqz"));
-
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:5 error: invalid vector swizzle character");
-}
-
-TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
- Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
-
- auto* ident = create<ast::IdentifierExpression>(
- Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
- Symbols().Register("rgyw"));
-
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(
- r()->error(),
- "3:3 error: invalid mixing of vector swizzle characters rgba with xyzw");
-}
-
-TEST_F(ResolverTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
- Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
-
- auto* ident = create<ast::IdentifierExpression>(
- Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
- Symbols().Register("zzzzz"));
- auto* mem = MemberAccessor("my_vec", ident);
- WrapInFunction(mem);
-
- EXPECT_FALSE(r()->Resolve());
- EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle size");
-}
-
TEST_F(ResolverTest, Expr_Accessor_MultiLevel) {
// struct b {
// vec4<f32> foo
@@ -1654,484 +1189,6 @@
EXPECT_EQ(mat->columns(), 4u);
}
-using IntrinsicDerivativeTest = ResolverTestWithParam<std::string>;
-TEST_P(IntrinsicDerivativeTest, Scalar) {
- auto name = GetParam();
-
- Global("ident", ty.f32(), ast::StorageClass::kNone);
-
- auto* expr = Call(name, "ident");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<type::F32>());
-}
-
-TEST_P(IntrinsicDerivativeTest, Vector) {
- auto name = GetParam();
- Global("ident", ty.vec4<f32>(), ast::StorageClass::kNone);
-
- auto* expr = Call(name, "ident");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
- EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 4u);
-}
-
-TEST_P(IntrinsicDerivativeTest, 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<f32>) -> vecN<f32>\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
- IntrinsicDerivativeTest,
- testing::Values("dpdx",
- "dpdxCoarse",
- "dpdxFine",
- "dpdy",
- "dpdyCoarse",
- "dpdyFine",
- "fwidth",
- "fwidthCoarse",
- "fwidthFine"));
-
-using Intrinsic = ResolverTestWithParam<std::string>;
-TEST_P(Intrinsic, Test) {
- auto name = GetParam();
-
- Global("my_var", ty.vec3<bool>(), ast::StorageClass::kNone);
-
- auto* expr = Call(name, "my_var");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
-}
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
- Intrinsic,
- testing::Values("any", "all"));
-
-using Intrinsic_FloatMethod = ResolverTestWithParam<std::string>;
-TEST_P(Intrinsic_FloatMethod, Vector) {
- auto name = GetParam();
-
- Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
-
- auto* expr = Call(name, "my_var");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(expr), nullptr);
- ASSERT_TRUE(TypeOf(expr)->Is<type::Vector>());
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::Bool>());
- EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_FloatMethod, Scalar) {
- auto name = GetParam();
-
- Global("my_var", ty.f32(), ast::StorageClass::kNone);
-
- auto* expr = Call(name, "my_var");
- WrapInFunction(expr);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(expr), nullptr);
- EXPECT_TRUE(TypeOf(expr)->Is<type::Bool>());
-}
-
-TEST_P(Intrinsic_FloatMethod, MissingParam) {
- auto name = GetParam();
-
- Global("my_var", ty.f32(), ast::StorageClass::kNone);
-
- 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) -> bool\n " + name +
- "(vecN<f32>) -> vecN<bool>\n");
-}
-
-TEST_P(Intrinsic_FloatMethod, TooManyParams) {
- auto name = GetParam();
-
- Global("my_var", ty.f32(), ast::StorageClass::kNone);
-
- auto* expr = Call(name, "my_var", 1.23f);
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(), "error: no matching call to " + name +
- "(ptr<f32>, f32)\n\n"
- "2 candidate functions:\n " +
- name + "(f32) -> bool\n " + name +
- "(vecN<f32>) -> vecN<bool>\n");
-}
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_FloatMethod,
- testing::Values("isInf", "isNan", "isFinite", "isNormal"));
-
-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 {
- type::TextureDimension dim;
- Texture type = Texture::kF32;
- type::ImageFormat format = type::ImageFormat::kR16Float;
-};
-inline std::ostream& operator<<(std::ostream& out, TextureTestParams data) {
- out << data.dim << "_" << data.type;
- return out;
-}
-
-class Intrinsic_TextureOperation
- : public ResolverTestWithParam<TextureTestParams> {
- public:
- /// Gets an appropriate type for the coords parameter depending the the
- /// dimensionality of the texture being sampled.
- /// @param dim dimensionality of the texture being sampled
- /// @param scalar the scalar type
- /// @returns a pointer to a type appropriate for the coord param
- type::Type* GetCoordsType(type::TextureDimension dim, type::Type* scalar) {
- switch (dim) {
- case type::TextureDimension::k1d:
- return scalar;
- case type::TextureDimension::k2d:
- case type::TextureDimension::k2dArray:
- return create<type::Vector>(scalar, 2);
- case type::TextureDimension::k3d:
- case type::TextureDimension::kCube:
- case type::TextureDimension::kCubeArray:
- return create<type::Vector>(scalar, 3);
- default:
- [=]() { FAIL() << "Unsupported texture dimension: " << dim; }();
- }
- return nullptr;
- }
-
- void add_call_param(std::string name,
- type::Type* type,
- ast::ExpressionList* call_params) {
- Global(name, type, ast::StorageClass::kNone);
- call_params->push_back(Expr(name));
- }
- type::Type* subtype(Texture type) {
- if (type == Texture::kF32) {
- return create<type::F32>();
- }
- if (type == Texture::kI32) {
- return create<type::I32>();
- }
- return create<type::U32>();
- }
-};
-
-using Intrinsic_StorageTextureOperation = Intrinsic_TextureOperation;
-TEST_P(Intrinsic_StorageTextureOperation, TextureLoadRo) {
- auto dim = GetParam().dim;
- auto type = GetParam().type;
- auto format = GetParam().format;
-
- auto* coords_type = GetCoordsType(dim, ty.i32());
-
- auto* subtype = type::StorageTexture::SubtypeFor(format, Types());
- auto* texture_type = create<type::StorageTexture>(dim, format, subtype);
- auto* ro_texture_type =
- create<type::AccessControl>(ast::AccessControl::kReadOnly, texture_type);
-
- ast::ExpressionList call_params;
-
- add_call_param("texture", ro_texture_type, &call_params);
- add_call_param("coords", coords_type, &call_params);
-
- if (type::IsTextureArray(dim)) {
- add_call_param("array_index", 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<type::Vector>());
- if (type == Texture::kF32) {
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
- } else if (type == Texture::kI32) {
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::I32>());
- } else {
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::U32>());
- }
- EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 4u);
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_StorageTextureOperation,
- testing::Values(
- TextureTestParams{type::TextureDimension::k1d, Texture::kF32,
- type::ImageFormat::kR16Float},
- TextureTestParams{type::TextureDimension::k1d, Texture::kI32,
- type::ImageFormat::kR16Sint},
- TextureTestParams{type::TextureDimension::k1d, Texture::kF32,
- type::ImageFormat::kR8Unorm},
- TextureTestParams{type::TextureDimension::k2d, Texture::kF32,
- type::ImageFormat::kR16Float},
- TextureTestParams{type::TextureDimension::k2d, Texture::kI32,
- type::ImageFormat::kR16Sint},
- TextureTestParams{type::TextureDimension::k2d, Texture::kF32,
- type::ImageFormat::kR8Unorm},
- TextureTestParams{type::TextureDimension::k2dArray, Texture::kF32,
- type::ImageFormat::kR16Float},
- TextureTestParams{type::TextureDimension::k2dArray, Texture::kI32,
- type::ImageFormat::kR16Sint},
- TextureTestParams{type::TextureDimension::k2dArray, Texture::kF32,
- type::ImageFormat::kR8Unorm},
- TextureTestParams{type::TextureDimension::k3d, Texture::kF32,
- type::ImageFormat::kR16Float},
- TextureTestParams{type::TextureDimension::k3d, Texture::kI32,
- type::ImageFormat::kR16Sint},
- TextureTestParams{type::TextureDimension::k3d, Texture::kF32,
- type::ImageFormat::kR8Unorm}));
-
-using Intrinsic_SampledTextureOperation = Intrinsic_TextureOperation;
-TEST_P(Intrinsic_SampledTextureOperation, TextureLoadSampled) {
- auto dim = GetParam().dim;
- auto type = GetParam().type;
-
- type::Type* s = subtype(type);
- auto* coords_type = GetCoordsType(dim, ty.i32());
- auto* texture_type = create<type::SampledTexture>(dim, s);
-
- ast::ExpressionList call_params;
-
- add_call_param("texture", texture_type, &call_params);
- add_call_param("coords", coords_type, &call_params);
- if (dim == type::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<type::Vector>());
- if (type == Texture::kF32) {
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
- } else if (type == Texture::kI32) {
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::I32>());
- } else {
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::U32>());
- }
- EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 4u);
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_SampledTextureOperation,
- testing::Values(TextureTestParams{type::TextureDimension::k1d},
- TextureTestParams{type::TextureDimension::k2d},
- TextureTestParams{type::TextureDimension::k2dArray},
- TextureTestParams{type::TextureDimension::k3d}));
-
-TEST_F(ResolverTest, Intrinsic_Dot_Vec2) {
- Global("my_var", ty.vec2<f32>(), ast::StorageClass::kNone);
-
- 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<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Dot_Vec3) {
- Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
-
- 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<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Dot_Vec4) {
- Global("my_var", ty.vec4<f32>(), ast::StorageClass::kNone);
-
- 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<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Dot_Error_Scalar) {
- auto* expr = Call("dot", 1.0f, 1.0f);
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to dot(f32, f32)
-
-1 candidate function:
- dot(vecN<f32>, vecN<f32>) -> f32
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Dot_Error_VectorInt) {
- Global("my_var", ty.vec4<i32>(), ast::StorageClass::kNone);
-
- auto* expr = Call("dot", "my_var", "my_var");
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to dot(ptr<vec4<i32>>, ptr<vec4<i32>>)
-
-1 candidate function:
- dot(vecN<f32>, vecN<f32>) -> f32
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Select) {
- Global("my_var", ty.vec3<f32>(), ast::StorageClass::kNone);
-
- Global("bool_var", ty.vec3<bool>(), ast::StorageClass::kNone);
-
- 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<type::Vector>());
- EXPECT_EQ(TypeOf(expr)->As<type::Vector>()->size(), 3u);
- EXPECT_TRUE(TypeOf(expr)->As<type::Vector>()->type()->Is<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Select_Error_NoParams) {
- auto* expr = Call("select");
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select()
-
-2 candidate functions:
- select(T, T, bool) -> T where: T is scalar
- select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Select_Error_SelectorInt) {
- auto* expr = Call("select", Expr(1), Expr(1), Expr(1));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(i32, i32, i32)
-
-2 candidate functions:
- select(T, T, bool) -> T where: T is scalar
- select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Select_Error_Matrix) {
- auto* mat = mat2x2<float>(vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f));
- auto* expr = Call("select", mat, mat, Expr(true));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(mat2x2<f32>, mat2x2<f32>, bool)
-
-2 candidate functions:
- select(T, T, bool) -> T where: T is scalar
- select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Select_Error_MismatchTypes) {
- auto* expr = Call("select", 1.0f, vec2<float>(2.0f, 3.0f), Expr(true));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(f32, vec2<f32>, bool)
-
-2 candidate functions:
- select(T, T, bool) -> T where: T is scalar
- select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Select_Error_MismatchVectorSize) {
- auto* expr = Call("select", vec2<float>(1.0f, 2.0f),
- vec3<float>(3.0f, 4.0f, 5.0f), Expr(true));
- WrapInFunction(expr);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to select(vec2<f32>, vec3<f32>, bool)
-
-2 candidate functions:
- select(T, T, bool) -> T where: T is scalar
- select(vecN<T>, vecN<T>, vecN<bool>) -> vecN<T> where: T is scalar
-)");
-}
-
using UnaryOpExpressionTest = ResolverTestWithParam<ast::UnaryOp>;
TEST_P(UnaryOpExpressionTest, Expr_UnaryOp) {
auto op = GetParam();
@@ -2175,1125 +1232,6 @@
EXPECT_EQ(Sem().Get(var)->StorageClass(), ast::StorageClass::kNone);
}
-TEST_F(ResolverTest, StorageClass_NonFunctionClassError) {
- auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
-
- auto* stmt = create<ast::VariableDeclStatement>(var);
- Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
- ast::FunctionDecorationList{});
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: function variable has a non-function storage class");
-}
-
-struct IntrinsicData {
- const char* name;
- IntrinsicType intrinsic;
-};
-
-inline std::ostream& operator<<(std::ostream& out, IntrinsicData data) {
- out << data.name;
- return out;
-}
-
-using Intrinsic_DataPackingTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_DataPackingTest, InferType) {
- auto param = GetParam();
-
- bool pack4 = param.intrinsic == IntrinsicType::kPack4x8Snorm ||
- param.intrinsic == IntrinsicType::kPack4x8Unorm;
-
- auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f))
- : Call(param.name, vec2<f32>(1.f, 2.f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
-}
-
-TEST_P(Intrinsic_DataPackingTest, Error_IncorrectParamType) {
- auto param = GetParam();
-
- bool pack4 = param.intrinsic == IntrinsicType::kPack4x8Snorm ||
- param.intrinsic == IntrinsicType::kPack4x8Unorm;
-
- auto* call = pack4 ? Call(param.name, vec4<i32>(1, 2, 3, 4))
- : Call(param.name, vec2<i32>(1, 2));
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name)));
-}
-
-TEST_P(Intrinsic_DataPackingTest, 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(Intrinsic_DataPackingTest, Error_TooManyParams) {
- auto param = GetParam();
-
- bool pack4 = param.intrinsic == IntrinsicType::kPack4x8Snorm ||
- param.intrinsic == IntrinsicType::kPack4x8Unorm;
-
- auto* call = pack4 ? Call(param.name, vec4<f32>(1.f, 2.f, 3.f, 4.f), 1.0f)
- : Call(param.name, vec2<f32>(1.f, 2.f), 1.0f);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_THAT(r()->error(), HasSubstr("error: no matching call to " +
- std::string(param.name)));
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_DataPackingTest,
- testing::Values(
- IntrinsicData{"pack4x8snorm", IntrinsicType::kPack4x8Snorm},
- IntrinsicData{"pack4x8unorm", IntrinsicType::kPack4x8Unorm},
- IntrinsicData{"pack2x16snorm", IntrinsicType::kPack2x16Snorm},
- IntrinsicData{"pack2x16unorm", IntrinsicType::kPack2x16Unorm},
- IntrinsicData{"pack2x16float", IntrinsicType::kPack2x16Float}));
-
-using Intrinsic_DataUnpackingTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_DataUnpackingTest, InferType) {
- auto param = GetParam();
-
- bool pack4 = param.intrinsic == IntrinsicType::kUnpack4x8Snorm ||
- param.intrinsic == IntrinsicType::kUnpack4x8Unorm;
-
- auto* call = Call(param.name, 1u);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- if (pack4) {
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 4u);
- } else {
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 2u);
- }
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_DataUnpackingTest,
- testing::Values(
- IntrinsicData{"unpack4x8snorm", IntrinsicType::kUnpack4x8Snorm},
- IntrinsicData{"unpack4x8unorm", IntrinsicType::kUnpack4x8Unorm},
- IntrinsicData{"unpack2x16snorm", IntrinsicType::kUnpack2x16Snorm},
- IntrinsicData{"unpack2x16unorm", IntrinsicType::kUnpack2x16Unorm},
- IntrinsicData{"unpack2x16float", IntrinsicType::kUnpack2x16Float}));
-
-using Intrinsic_SingleParamTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_SingleParamTest, Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-TEST_P(Intrinsic_SingleParamTest, Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
- 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<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_SingleParamTest, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32) -> f32\n " +
- std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
-}
-
-TEST_P(Intrinsic_SingleParamTest, Error_TooManyParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 2, 3);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "(i32, i32, i32)\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32) -> f32\n " +
- std::string(param.name) + "(vecN<f32>) -> vecN<f32>\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_SingleParamTest,
- testing::Values(IntrinsicData{"acos", IntrinsicType::kAcos},
- IntrinsicData{"asin", IntrinsicType::kAsin},
- IntrinsicData{"atan", IntrinsicType::kAtan},
- IntrinsicData{"ceil", IntrinsicType::kCeil},
- IntrinsicData{"cos", IntrinsicType::kCos},
- IntrinsicData{"cosh", IntrinsicType::kCosh},
- IntrinsicData{"exp", IntrinsicType::kExp},
- IntrinsicData{"exp2", IntrinsicType::kExp2},
- IntrinsicData{"floor", IntrinsicType::kFloor},
- IntrinsicData{"fract", IntrinsicType::kFract},
- IntrinsicData{"inverseSqrt", IntrinsicType::kInverseSqrt},
- IntrinsicData{"log", IntrinsicType::kLog},
- IntrinsicData{"log2", IntrinsicType::kLog2},
- IntrinsicData{"round", IntrinsicType::kRound},
- IntrinsicData{"sign", IntrinsicType::kSign},
- IntrinsicData{"sin", IntrinsicType::kSin},
- IntrinsicData{"sinh", IntrinsicType::kSinh},
- IntrinsicData{"sqrt", IntrinsicType::kSqrt},
- IntrinsicData{"tan", IntrinsicType::kTan},
- IntrinsicData{"tanh", IntrinsicType::kTanh},
- IntrinsicData{"trunc", IntrinsicType::kTrunc}));
-
-using IntrinsicDataTest = ResolverTest;
-
-TEST_F(IntrinsicDataTest, ArrayLength_Vector) {
- Global("arr", ty.array<int>(), ast::StorageClass::kNone);
- auto* call = Call("arrayLength", "arr");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
-}
-
-TEST_F(IntrinsicDataTest, ArrayLength_Error_ArraySized) {
- Global("arr", ty.array<int, 4>(), ast::StorageClass::kNone);
- auto* call = Call("arrayLength", "arr");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to arrayLength(ptr<array<i32, 4>>)\n\n"
- "1 candidate function:\n"
- " arrayLength(array<T>) -> u32\n");
-}
-
-TEST_F(IntrinsicDataTest, Normalize_Vector) {
- auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
- 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<type::Vector>()->size(), 3u);
-}
-
-TEST_F(IntrinsicDataTest, Normalize_Error_NoParams) {
- auto* call = Call("normalize");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to normalize()\n\n"
- "1 candidate function:\n"
- " normalize(vecN<f32>) -> vecN<f32>\n");
-}
-
-TEST_F(IntrinsicDataTest, FrexpScalar) {
- Global("exp", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", 1.0f, "exp");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_F(IntrinsicDataTest, FrexpVector) {
- Global("exp", ty.vec3<i32>(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", vec3<f32>(1.0f, 2.0f, 3.0f), "exp");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::Vector>());
- EXPECT_TRUE(TypeOf(call)->As<type::Vector>()->type()->Is<type::F32>());
-}
-
-TEST_F(IntrinsicDataTest, Frexp_Error_FirstParamInt) {
- Global("exp", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", 1, "exp");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to frexp(i32, ptr<workgroup, i32>)\n\n"
- "2 candidate functions:\n"
- " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
- " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
- "where: T is i32 or u32\n");
-}
-
-TEST_F(IntrinsicDataTest, Frexp_Error_SecondParamFloatPtr) {
- Global("exp", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", 1.0f, "exp");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to frexp(f32, ptr<workgroup, f32>)\n\n"
- "2 candidate functions:\n"
- " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
- " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
- "where: T is i32 or u32\n");
-}
-
-TEST_F(IntrinsicDataTest, Frexp_Error_SecondParamNotAPointer) {
- auto* call = Call("frexp", 1.0f, 1);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to frexp(f32, i32)\n\n"
- "2 candidate functions:\n"
- " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
- " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
- "where: T is i32 or u32\n");
-}
-
-TEST_F(IntrinsicDataTest, Frexp_Error_VectorSizesDontMatch) {
- Global("exp", ty.vec4<i32>(), ast::StorageClass::kWorkgroup);
- auto* call = Call("frexp", vec2<f32>(1.0f, 2.0f), "exp");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to frexp(vec2<f32>, ptr<workgroup, "
- "vec4<i32>>)\n\n"
- "2 candidate functions:\n"
- " frexp(f32, ptr<T>) -> f32 where: T is i32 or u32\n"
- " frexp(vecN<f32>, ptr<vecN<T>>) -> vecN<f32> "
- "where: T is i32 or u32\n");
-}
-
-TEST_F(IntrinsicDataTest, ModfScalar) {
- Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", 1.0f, "whole");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_F(IntrinsicDataTest, ModfVector) {
- Global("whole", ty.vec3<f32>(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", vec3<f32>(1.0f, 2.0f, 3.0f), "whole");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::Vector>());
- EXPECT_TRUE(TypeOf(call)->As<type::Vector>()->type()->Is<type::F32>());
-}
-
-TEST_F(IntrinsicDataTest, Modf_Error_FirstParamInt) {
- Global("whole", ty.f32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", 1, "whole");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to modf(i32, ptr<workgroup, f32>)\n\n"
- "2 candidate functions:\n"
- " modf(f32, ptr<f32>) -> f32\n"
- " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
-}
-
-TEST_F(IntrinsicDataTest, Modf_Error_SecondParamIntPtr) {
- Global("whole", ty.i32(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", 1.0f, "whole");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to modf(f32, ptr<workgroup, i32>)\n\n"
- "2 candidate functions:\n"
- " modf(f32, ptr<f32>) -> f32\n"
- " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
-}
-
-TEST_F(IntrinsicDataTest, Modf_Error_SecondParamNotAPointer) {
- auto* call = Call("modf", 1.0f, 1.0f);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to modf(f32, f32)\n\n"
- "2 candidate functions:\n"
- " modf(f32, ptr<f32>) -> f32\n"
- " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n");
-}
-
-TEST_F(IntrinsicDataTest, Modf_Error_VectorSizesDontMatch) {
- Global("whole", ty.vec4<f32>(), ast::StorageClass::kWorkgroup);
- auto* call = Call("modf", vec2<f32>(1.0f, 2.0f), "whole");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to modf(vec2<f32>, ptr<workgroup, "
- "vec4<f32>>)\n\n"
- "2 candidate functions:\n"
- " modf(vecN<f32>, ptr<vecN<f32>>) -> vecN<f32>\n"
- " modf(f32, ptr<f32>) -> f32\n");
-}
-
-using Intrinsic_SingleParam_FloatOrInt_Test =
- ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Float_Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Float_Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f));
- 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<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Sint_Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, -1);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
-}
-
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Sint_Vector) {
- auto param = GetParam();
-
- ast::ExpressionList vals;
- vals.push_back(Expr(1));
- vals.push_back(Expr(1));
- vals.push_back(Expr(3));
-
- ast::ExpressionList params;
- params.push_back(vec3<i32>(vals));
-
- auto* call = Call(param.name, params);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Uint_Scalar) {
- auto param = GetParam();
-
- ast::ExpressionList params;
- params.push_back(Expr(1u));
-
- auto* call = Call(param.name, params);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
-}
-
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Uint_Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_SingleParam_FloatOrInt_Test, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T) -> T where: T is f32, i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
- Intrinsic_SingleParam_FloatOrInt_Test,
- testing::Values(IntrinsicData{"abs",
- IntrinsicType::kAbs}));
-
-TEST_F(ResolverTest, Intrinsic_Length_Scalar) {
- auto* call = Call("length", 1.f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-TEST_F(ResolverTest, Intrinsic_Length_FloatVector) {
- ast::ExpressionList params;
- params.push_back(vec3<f32>(1.0f, 1.0f, 3.0f));
-
- auto* call = Call("length", params);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-using Intrinsic_TwoParamTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_TwoParamTest, Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-TEST_P(Intrinsic_TwoParamTest, Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_TwoParamTest, Error_NoTooManyParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 2, 3);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "(i32, i32, i32)\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32, f32) -> f32\n " +
- std::string(param.name) +
- "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
-}
-
-TEST_P(Intrinsic_TwoParamTest, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32, f32) -> f32\n " +
- std::string(param.name) +
- "(vecN<f32>, vecN<f32>) -> vecN<f32>\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_TwoParamTest,
- testing::Values(IntrinsicData{"atan2", IntrinsicType::kAtan2},
- IntrinsicData{"pow", IntrinsicType::kPow},
- IntrinsicData{"step", IntrinsicType::kStep},
- IntrinsicData{"reflect", IntrinsicType::kReflect}));
-
-TEST_F(ResolverTest, Intrinsic_Distance_Scalar) {
- 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_float_scalar());
-}
-
-TEST_F(ResolverTest, Intrinsic_Distance_Vector) {
- auto* call = Call("distance", vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Cross) {
- auto* call =
- Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_F(ResolverTest, Intrinsic_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<f32>, vec3<f32>) -> vec3<f32>
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Cross_Error_Scalar) {
- auto* call = Call("cross", 1.0f, 1.0f);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(), R"(error: no matching call to cross(f32, f32)
-
-1 candidate function:
- cross(vec3<f32>, vec3<f32>) -> vec3<f32>
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Cross_Error_Vec3Int) {
- auto* call = Call("cross", vec3<i32>(1, 2, 3), vec3<i32>(1, 2, 3));
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to cross(vec3<i32>, vec3<i32>)
-
-1 candidate function:
- cross(vec3<f32>, vec3<f32>) -> vec3<f32>
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Cross_Error_Vec4) {
- auto* call = Call("cross", vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f),
- vec4<f32>(1.0f, 2.0f, 3.0f, 4.0f));
-
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to cross(vec4<f32>, vec4<f32>)
-
-1 candidate function:
- cross(vec3<f32>, vec3<f32>) -> vec3<f32>
-)");
-}
-
-TEST_F(ResolverTest, Intrinsic_Cross_Error_TooManyParams) {
- auto* call = Call("cross", vec3<f32>(1.0f, 2.0f, 3.0f),
- vec3<f32>(1.0f, 2.0f, 3.0f), vec3<f32>(1.0f, 2.0f, 3.0f));
-
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- R"(error: no matching call to cross(vec3<f32>, vec3<f32>, vec3<f32>)
-
-1 candidate function:
- cross(vec3<f32>, vec3<f32>) -> vec3<f32>
-)");
-}
-TEST_F(ResolverTest, Intrinsic_Normalize) {
- auto* call = Call("normalize", vec3<f32>(1.0f, 1.0f, 3.0f));
- 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<type::Vector>()->size(), 3u);
-}
-
-TEST_F(ResolverTest, Intrinsic_Normalize_NoArgs) {
- 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<f32>) -> vecN<f32>
-)");
-}
-
-using Intrinsic_ThreeParamTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_ThreeParamTest, Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-TEST_P(Intrinsic_ThreeParamTest, Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-TEST_P(Intrinsic_ThreeParamTest, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) + "(f32, f32, f32) -> f32\n " +
- std::string(param.name) +
- "(vecN<f32>, vecN<f32>, vecN<f32>) -> vecN<f32>\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_ThreeParamTest,
- testing::Values(IntrinsicData{"mix", IntrinsicType::kMix},
- IntrinsicData{"smoothStep", IntrinsicType::kSmoothStep},
- IntrinsicData{"fma", IntrinsicType::kFma},
- IntrinsicData{"faceForward", IntrinsicType::kFaceForward}));
-
-using Intrinsic_ThreeParam_FloatOrInt_Test =
- ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Float_Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_scalar());
-}
-
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Float_Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Sint_Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 1, 1);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
-}
-
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Sint_Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3),
- vec3<i32>(1, 1, 3));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Uint_Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1u, 1u, 1u);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
-}
-
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Uint_Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u),
- vec3<u32>(1u, 1u, 3u));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_ThreeParam_FloatOrInt_Test, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T, T, T) -> T where: T is f32, i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>, vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 "
- "or u32\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(ResolverTest,
- Intrinsic_ThreeParam_FloatOrInt_Test,
- testing::Values(IntrinsicData{"clamp",
- IntrinsicType::kClamp}));
-
-using Intrinsic_Int_SingleParamTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_Int_SingleParamTest, Scalar) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_integer_scalar());
-}
-
-TEST_P(Intrinsic_Int_SingleParamTest, Vector) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<i32>(1, 1, 3));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_Int_SingleParamTest, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(), "error: no matching call to " +
- std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T) -> T where: T is i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>) -> vecN<T> where: T is i32 or u32\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_Int_SingleParamTest,
- testing::Values(IntrinsicData{"countOneBits", IntrinsicType::kCountOneBits},
- IntrinsicData{"reverseBits", IntrinsicType::kReverseBits}));
-
-using Intrinsic_FloatOrInt_TwoParamTest = ResolverTestWithParam<IntrinsicData>;
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Scalar_Signed) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 1);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::I32>());
-}
-
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Scalar_Unsigned) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1u, 1u);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::U32>());
-}
-
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Scalar_Float) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.0f, 1.0f);
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Vector_Signed) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_signed_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Vector_Unsigned) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec3<u32>(1u, 1u, 3u), vec3<u32>(1u, 1u, 3u));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_unsigned_integer_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Vector_Float) {
- auto param = GetParam();
-
- auto* call =
- Call(param.name, vec3<f32>(1.f, 1.f, 3.f), vec3<f32>(1.f, 1.f, 3.f));
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->is_float_vector());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->size(), 3u);
-}
-
-TEST_P(Intrinsic_FloatOrInt_TwoParamTest, Error_NoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name);
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to " + std::string(param.name) +
- "()\n\n"
- "2 candidate functions:\n " +
- std::string(param.name) +
- "(T, T) -> T where: T is f32, i32 or u32\n " +
- std::string(param.name) +
- "(vecN<T>, vecN<T>) -> vecN<T> where: T is f32, i32 or u32\n");
-}
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- Intrinsic_FloatOrInt_TwoParamTest,
- testing::Values(IntrinsicData{"min", IntrinsicType::kMin},
- IntrinsicData{"max", IntrinsicType::kMax}));
-
-TEST_F(ResolverTest, Intrinsic_Determinant_2x2) {
- Global("var", ty.mat2x2<f32>(), ast::StorageClass::kFunction);
-
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Determinant_3x3) {
- Global("var", ty.mat3x3<f32>(), ast::StorageClass::kFunction);
-
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Determinant_4x4) {
- Global("var", ty.mat4x4<f32>(), ast::StorageClass::kFunction);
-
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
-
- EXPECT_TRUE(r()->Resolve()) << r()->error();
-
- ASSERT_NE(TypeOf(call), nullptr);
- EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
-}
-
-TEST_F(ResolverTest, Intrinsic_Determinant_NotSquare) {
- Global("var", ty.mat2x3<f32>(), ast::StorageClass::kFunction);
-
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(
- r()->error(),
- "error: no matching call to determinant(ptr<function, mat2x3<f32>>)\n\n"
- "1 candidate function:\n"
- " determinant(matNxN<f32>) -> f32\n");
-}
-
-TEST_F(ResolverTest, Intrinsic_Determinant_NotMatrix) {
- Global("var", ty.f32(), ast::StorageClass::kFunction);
-
- auto* call = Call("determinant", "var");
- WrapInFunction(call);
-
- EXPECT_FALSE(r()->Resolve());
-
- EXPECT_EQ(r()->error(),
- "error: no matching call to determinant(ptr<function, f32>)\n\n"
- "1 candidate function:\n"
- " determinant(matNxN<f32>) -> f32\n");
-}
-
TEST_F(ResolverTest, Function_EntryPoints_StageDecoration) {
// fn b() {}
// fn c() { b(); }
@@ -3427,325 +1365,6 @@
ASSERT_TRUE(r()->Resolve()) << r()->error();
}
-using ResolverTextureIntrinsicTest =
- ResolverTestWithParam<ast::intrinsic::test::TextureOverloadCase>;
-
-INSTANTIATE_TEST_SUITE_P(
- ResolverTest,
- ResolverTextureIntrinsicTest,
- testing::ValuesIn(ast::intrinsic::test::TextureOverloadCase::ValidCases()));
-
-std::string to_str(const std::string& function,
- const semantic::ParameterList& params) {
- std::stringstream out;
- out << function << "(";
- bool first = true;
- for (auto& param : params) {
- if (!first) {
- out << ", ";
- }
- out << semantic::str(param.usage);
- first = false;
- }
- out << ")";
- return out.str();
-}
-
-const char* expected_texture_overload(
- ast::intrinsic::test::ValidTextureOverload overload) {
- using ValidTextureOverload = ast::intrinsic::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::kDimensionsMultisampled2dArray:
- case ValidTextureOverload::kDimensionsDepth2d:
- case ValidTextureOverload::kDimensionsDepth2dArray:
- case ValidTextureOverload::kDimensionsDepthCube:
- case ValidTextureOverload::kDimensionsDepthCubeArray:
- case ValidTextureOverload::kDimensionsStorageRO1d:
- case ValidTextureOverload::kDimensionsStorageRO2d:
- case ValidTextureOverload::kDimensionsStorageRO2dArray:
- case ValidTextureOverload::kDimensionsStorageRO3d:
- case ValidTextureOverload::kDimensionsStorageWO1d:
- case ValidTextureOverload::kDimensionsStorageWO2d:
- case ValidTextureOverload::kDimensionsStorageWO2dArray:
- case ValidTextureOverload::kDimensionsStorageWO3d:
- return R"(textureDimensions(texture))";
- case ValidTextureOverload::kNumLayers2dArray:
- case ValidTextureOverload::kNumLayersCubeArray:
- case ValidTextureOverload::kNumLayersMultisampled2dArray:
- 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::kNumSamplesMultisampled2d:
- case ValidTextureOverload::kNumSamplesMultisampled2dArray:
- 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::kLoad1dLevelF32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad1dLevelU32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad1dLevelI32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad2dLevelF32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad2dLevelU32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad2dLevelI32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad2dArrayLevelF32:
- return R"(textureLoad(texture, coords, array_index, level))";
- case ValidTextureOverload::kLoad2dArrayLevelU32:
- return R"(textureLoad(texture, coords, array_index, level))";
- case ValidTextureOverload::kLoad2dArrayLevelI32:
- return R"(textureLoad(texture, coords, array_index, level))";
- case ValidTextureOverload::kLoad3dLevelF32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad3dLevelU32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoad3dLevelI32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoadMultisampled2dF32:
- return R"(textureLoad(texture, coords, sample_index))";
- case ValidTextureOverload::kLoadMultisampled2dU32:
- return R"(textureLoad(texture, coords, sample_index))";
- case ValidTextureOverload::kLoadMultisampled2dI32:
- return R"(textureLoad(texture, coords, sample_index))";
- case ValidTextureOverload::kLoadMultisampled2dArrayF32:
- return R"(textureLoad(texture, coords, array_index, sample_index))";
- case ValidTextureOverload::kLoadMultisampled2dArrayU32:
- return R"(textureLoad(texture, coords, array_index, sample_index))";
- case ValidTextureOverload::kLoadMultisampled2dArrayI32:
- return R"(textureLoad(texture, coords, array_index, sample_index))";
- case ValidTextureOverload::kLoadDepth2dLevelF32:
- return R"(textureLoad(texture, coords, level))";
- case ValidTextureOverload::kLoadDepth2dArrayLevelF32:
- return R"(textureLoad(texture, coords, array_index, level))";
- case ValidTextureOverload::kLoadStorageRO1dRgba32float:
- return R"(textureLoad(texture, coords))";
- case ValidTextureOverload::kLoadStorageRO2dRgba8unorm:
- case ValidTextureOverload::kLoadStorageRO2dRgba8snorm:
- case ValidTextureOverload::kLoadStorageRO2dRgba8uint:
- case ValidTextureOverload::kLoadStorageRO2dRgba8sint:
- case ValidTextureOverload::kLoadStorageRO2dRgba16uint:
- case ValidTextureOverload::kLoadStorageRO2dRgba16sint:
- case ValidTextureOverload::kLoadStorageRO2dRgba16float:
- case ValidTextureOverload::kLoadStorageRO2dR32uint:
- case ValidTextureOverload::kLoadStorageRO2dR32sint:
- case ValidTextureOverload::kLoadStorageRO2dR32float:
- case ValidTextureOverload::kLoadStorageRO2dRg32uint:
- case ValidTextureOverload::kLoadStorageRO2dRg32sint:
- case ValidTextureOverload::kLoadStorageRO2dRg32float:
- case ValidTextureOverload::kLoadStorageRO2dRgba32uint:
- case ValidTextureOverload::kLoadStorageRO2dRgba32sint:
- case ValidTextureOverload::kLoadStorageRO2dRgba32float:
- return R"(textureLoad(texture, coords))";
- case ValidTextureOverload::kLoadStorageRO2dArrayRgba32float:
- return R"(textureLoad(texture, coords, array_index))";
- case ValidTextureOverload::kLoadStorageRO3dRgba32float:
- return R"(textureLoad(texture, coords))";
- case ValidTextureOverload::kStoreWO1dRgba32float:
- return R"(textureStore(texture, coords, value))";
- case ValidTextureOverload::kStoreWO2dRgba32float:
- return R"(textureStore(texture, coords, value))";
- case ValidTextureOverload::kStoreWO2dArrayRgba32float:
- return R"(textureStore(texture, coords, array_index, value))";
- case ValidTextureOverload::kStoreWO3dRgba32float:
- return R"(textureStore(texture, coords, value))";
- }
- return "<unmatched texture overload>";
-}
-
-TEST_P(ResolverTextureIntrinsicTest, Call) {
- auto param = GetParam();
-
- param.buildTextureVariable(this);
- param.buildSamplerVariable(this);
-
- auto* call = Call(param.function, param.args(this));
- WrapInFunction(call);
-
- 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 type::TextureDimension::k1d:
- EXPECT_EQ(TypeOf(call)->type_name(), ty.i32()->type_name());
- break;
- case type::TextureDimension::k2d:
- case type::TextureDimension::k2dArray:
- EXPECT_EQ(TypeOf(call)->type_name(), ty.vec2<i32>()->type_name());
- break;
- case type::TextureDimension::k3d:
- case type::TextureDimension::kCube:
- case type::TextureDimension::kCubeArray:
- EXPECT_EQ(TypeOf(call)->type_name(), ty.vec3<i32>()->type_name());
- break;
- }
- } else if (std::string(param.function) == "textureNumLayers") {
- EXPECT_EQ(TypeOf(call), ty.i32());
- } else if (std::string(param.function) == "textureNumLevels") {
- EXPECT_EQ(TypeOf(call), ty.i32());
- } else if (std::string(param.function) == "textureNumSamples") {
- EXPECT_EQ(TypeOf(call), ty.i32());
- } else if (std::string(param.function) == "textureStore") {
- EXPECT_EQ(TypeOf(call), ty.void_());
- } else {
- switch (param.texture_kind) {
- case ast::intrinsic::test::TextureKind::kRegular:
- case ast::intrinsic::test::TextureKind::kMultisampled:
- case ast::intrinsic::test::TextureKind::kStorage: {
- auto* datatype = param.resultVectorComponentType(this);
- ASSERT_TRUE(TypeOf(call)->Is<type::Vector>());
- EXPECT_EQ(TypeOf(call)->As<type::Vector>()->type(), datatype);
- break;
- }
- case ast::intrinsic::test::TextureKind::kDepth: {
- EXPECT_EQ(TypeOf(call), ty.f32());
- break;
- }
- }
- }
-
- auto* call_sem = Sem().Get(call);
- ASSERT_NE(call_sem, nullptr);
- auto* target = call_sem->Target();
- ASSERT_NE(target, nullptr);
-
- auto got = resolver::to_str(param.function, target->Parameters());
- auto* expected = expected_texture_overload(param.overload);
- EXPECT_EQ(got, expected);
-}
-
} // namespace
} // namespace resolver
} // namespace tint
diff --git a/src/resolver/validation_test.cc b/src/resolver/validation_test.cc
new file mode 100644
index 0000000..1d22b05
--- /dev/null
+++ b/src/resolver/validation_test.cc
@@ -0,0 +1,530 @@
+// 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/resolver/resolver.h"
+
+#include "gmock/gmock.h"
+#include "src/ast/assignment_statement.h"
+#include "src/ast/bitcast_expression.h"
+#include "src/ast/break_statement.h"
+#include "src/ast/call_statement.h"
+#include "src/ast/continue_statement.h"
+#include "src/ast/if_statement.h"
+#include "src/ast/intrinsic_texture_helper_test.h"
+#include "src/ast/loop_statement.h"
+#include "src/ast/return_statement.h"
+#include "src/ast/stage_decoration.h"
+#include "src/ast/switch_statement.h"
+#include "src/ast/unary_op_expression.h"
+#include "src/ast/variable_decl_statement.h"
+#include "src/resolver/resolver_test_helper.h"
+#include "src/semantic/call.h"
+#include "src/semantic/function.h"
+#include "src/semantic/member_accessor_expression.h"
+#include "src/semantic/statement.h"
+#include "src/semantic/variable.h"
+#include "src/type/access_control_type.h"
+#include "src/type/sampled_texture_type.h"
+
+using ::testing::ElementsAre;
+using ::testing::HasSubstr;
+
+namespace tint {
+namespace resolver {
+namespace {
+
+using ResolverValidationTest = ResolverTest;
+
+class FakeStmt : public ast::Statement {
+ public:
+ explicit FakeStmt(Source source) : ast::Statement(source) {}
+ FakeStmt* Clone(CloneContext*) const override { return nullptr; }
+ bool IsValid() const override { return true; }
+ void to_str(const semantic::Info&, std::ostream& out, size_t) const override {
+ out << "Fake";
+ }
+};
+
+class FakeExpr : public ast::Expression {
+ public:
+ explicit FakeExpr(Source source) : ast::Expression(source) {}
+ FakeExpr* Clone(CloneContext*) const override { return nullptr; }
+ bool IsValid() const override { return true; }
+ void to_str(const semantic::Info&, std::ostream&, size_t) const override {}
+};
+
+TEST_F(ResolverValidationTest, Error_WithEmptySource) {
+ auto* s = create<FakeStmt>();
+ WrapInFunction(s);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: unknown statement type for type determination: Fake");
+}
+
+TEST_F(ResolverValidationTest, Stmt_Error_Unknown) {
+ auto* s = create<FakeStmt>(Source{Source::Location{2, 30}});
+ WrapInFunction(s);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "2:30 error: unknown statement type for type determination: Fake");
+}
+
+TEST_F(ResolverValidationTest, Stmt_Call_undeclared) {
+ // fn main() -> void {func(); return; }
+ // fn func() -> void { return; }
+
+ SetSource(Source::Location{12, 34});
+ auto* call_expr = Call("func");
+ ast::VariableList params0;
+
+ Func("main", params0, ty.f32(),
+ ast::StatementList{
+ create<ast::CallStatement>(call_expr),
+ create<ast::ReturnStatement>(),
+ },
+ ast::FunctionDecorationList{});
+
+ Func("func", params0, ty.f32(),
+ ast::StatementList{
+ create<ast::ReturnStatement>(),
+ },
+ ast::FunctionDecorationList{});
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "12:34 error: v-0006: unable to find called function: func");
+}
+
+TEST_F(ResolverValidationTest, Stmt_Call_recursive) {
+ // fn main() -> void {main(); }
+
+ SetSource(Source::Location{12, 34});
+ auto* call_expr = Call("main");
+ ast::VariableList params0;
+
+ Func("main", params0, ty.f32(),
+ ast::StatementList{
+ create<ast::CallStatement>(call_expr),
+ },
+ ast::FunctionDecorationList{
+ create<ast::StageDecoration>(ast::PipelineStage::kVertex),
+ });
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "12:34 error: recursion is not permitted. 'main' attempted to call "
+ "itself.");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_VariableDecl_MismatchedTypeScalarConstructor) {
+ u32 unsigned_value = 2u; // Type does not match variable type
+ auto* var =
+ Var("my_var", ty.i32(), ast::StorageClass::kNone, Expr(unsigned_value));
+
+ auto* decl =
+ create<ast::VariableDeclStatement>(Source{{{3, 3}, {3, 22}}}, var);
+ WrapInFunction(decl);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(3:3 error: constructor expression type does not match variable type)");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_VariableDecl_MismatchedTypeScalarConstructor_Alias) {
+ auto* my_int = ty.alias("MyInt", ty.i32());
+ u32 unsigned_value = 2u; // Type does not match variable type
+ auto* var =
+ Var("my_var", my_int, ast::StorageClass::kNone, Expr(unsigned_value));
+
+ auto* decl =
+ create<ast::VariableDeclStatement>(Source{{{3, 3}, {3, 22}}}, var);
+ WrapInFunction(decl);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ R"(3:3 error: constructor expression type does not match variable type)");
+}
+
+TEST_F(ResolverValidationTest, Expr_Error_Unknown) {
+ FakeExpr e(Source{Source::Location{2, 30}});
+ WrapInFunction(&e);
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "2:30 error: unknown expression for type determination");
+}
+
+TEST_F(ResolverValidationTest, Expr_DontCall_Function) {
+ Func("func", {}, ty.void_(), {}, {});
+ auto* ident = create<ast::IdentifierExpression>(
+ Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
+ Symbols().Register("func"));
+ WrapInFunction(ident);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:8 error: missing '(' for function call");
+}
+
+TEST_F(ResolverValidationTest, Expr_DontCall_Intrinsic) {
+ auto* ident = create<ast::IdentifierExpression>(
+ Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
+ Symbols().Register("round"));
+ WrapInFunction(ident);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:8 error: missing '(' for intrinsic call");
+}
+
+TEST_F(ResolverValidationTest, UsingUndefinedVariable_Fail) {
+ // b = 2;
+
+ SetSource(Source{Source::Location{12, 34}});
+ auto* lhs = Expr("b");
+ auto* rhs = Expr(2);
+ auto* assign = create<ast::AssignmentStatement>(lhs, rhs);
+ WrapInFunction(assign);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: v-0006: identifier must be declared before use: b");
+}
+
+TEST_F(ResolverValidationTest, UsingUndefinedVariableInBlockStatement_Fail) {
+ // {
+ // b = 2;
+ // }
+
+ SetSource(Source{Source::Location{12, 34}});
+ auto* lhs = Expr("b");
+ auto* rhs = Expr(2);
+
+ auto* body = create<ast::BlockStatement>(ast::StatementList{
+ create<ast::AssignmentStatement>(lhs, rhs),
+ });
+ WrapInFunction(body);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: v-0006: identifier must be declared before use: b");
+}
+
+TEST_F(ResolverValidationTest, StorageClass_NonFunctionClassError) {
+ auto* var = Var("var", ty.i32(), ast::StorageClass::kWorkgroup);
+
+ auto* stmt = create<ast::VariableDeclStatement>(var);
+ Func("func", ast::VariableList{}, ty.i32(), ast::StatementList{stmt},
+ ast::FunctionDecorationList{});
+
+ EXPECT_FALSE(r()->Resolve());
+
+ EXPECT_EQ(r()->error(),
+ "error: function variable has a non-function storage class");
+}
+
+TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadChar) {
+ Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+
+ auto* ident = create<ast::IdentifierExpression>(
+ Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
+ Symbols().Register("xyqz"));
+
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:5 error: invalid vector swizzle character");
+}
+
+TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_MixedChars) {
+ Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+
+ auto* ident = create<ast::IdentifierExpression>(
+ Source{{Source::Location{3, 3}, Source::Location{3, 7}}},
+ Symbols().Register("rgyw"));
+
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(
+ r()->error(),
+ "3:3 error: invalid mixing of vector swizzle characters rgba with xyzw");
+}
+
+TEST_F(ResolverValidationTest, Expr_MemberAccessor_VectorSwizzle_BadLength) {
+ Global("my_vec", ty.vec3<f32>(), ast::StorageClass::kNone);
+
+ auto* ident = create<ast::IdentifierExpression>(
+ Source{{Source::Location{3, 3}, Source::Location{3, 8}}},
+ Symbols().Register("zzzzz"));
+ auto* mem = MemberAccessor("my_vec", ident);
+ WrapInFunction(mem);
+
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "3:3 error: invalid vector swizzle size");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInLoopBodyBeforeDecl_UsageInContinuing) {
+ // loop {
+ // continue; // Bypasses z decl
+ // var z : i32;
+ //
+ // continuing {
+ // z = 2;
+ // }
+ // }
+
+ auto error_loc = Source{Source::Location{12, 34}};
+ auto* body = Block(create<ast::ContinueStatement>(),
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(Assign(Expr(error_loc, "z"), Expr(2)));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: continue statement bypasses declaration of 'z' in "
+ "continuing block");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInLoopBodyBeforeDeclAndAfterDecl_UsageInContinuing) {
+ // loop {
+ // continue; // Bypasses z decl
+ // var z : i32;
+ // continue; // Ok
+ //
+ // continuing {
+ // z = 2;
+ // }
+ // }
+
+ auto error_loc = Source{Source::Location{12, 34}};
+ auto* body = Block(create<ast::ContinueStatement>(),
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)),
+ create<ast::ContinueStatement>());
+ auto* continuing = Block(Assign(Expr(error_loc, "z"), Expr(2)));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: continue statement bypasses declaration of 'z' in "
+ "continuing block");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuing) {
+ // loop {
+ // if (true) {
+ // continue; // Still bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // z = 2;
+ // }
+ // }
+
+ auto error_loc = Source{Source::Location{12, 34}};
+ auto* body = Block(If(Expr(true), Block(create<ast::ContinueStatement>())),
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(Assign(Expr(error_loc, "z"), Expr(2)));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: continue statement bypasses declaration of 'z' in "
+ "continuing block");
+}
+
+TEST_F(
+ ResolverTest,
+ Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingSubscope) {
+ // loop {
+ // if (true) {
+ // continue; // Still bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // if (true) {
+ // z = 2; // Must fail even if z is in a sub-scope
+ // }
+ // }
+ // }
+
+ auto error_loc = Source{Source::Location{12, 34}};
+ auto* body = Block(If(Expr(true), Block(create<ast::ContinueStatement>())),
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+
+ auto* continuing =
+ Block(If(Expr(true), Block(Assign(Expr(error_loc, "z"), Expr(2)))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: continue statement bypasses declaration of 'z' in "
+ "continuing block");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInLoopBodySubscopeBeforeDecl_UsageInContinuingLoop) {
+ // loop {
+ // if (true) {
+ // continue; // Still bypasses z decl (if we reach here)
+ // }
+ // var z : i32;
+ // continuing {
+ // loop {
+ // z = 2; // Must fail even if z is in a sub-scope
+ // }
+ // }
+ // }
+
+ auto error_loc = Source{Source::Location{12, 34}};
+ auto* body = Block(If(Expr(true), Block(create<ast::ContinueStatement>())),
+ Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+
+ auto* continuing = Block(Loop(Block(Assign(Expr(error_loc, "z"), Expr(2)))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_FALSE(r()->Resolve()) << r()->error();
+ EXPECT_EQ(r()->error(),
+ "12:34 error: continue statement bypasses declaration of 'z' in "
+ "continuing block");
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuing) {
+ // loop {
+ // loop {
+ // continue; // OK: not part of the outer loop
+ // }
+ // var z : i32;
+ //
+ // continuing {
+ // z = 2;
+ // }
+ // }
+
+ auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
+ auto* body =
+ Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(Assign(Expr("z"), Expr(2)));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingSubscope) {
+ // loop {
+ // loop {
+ // continue; // OK: not part of the outer loop
+ // }
+ // var z : i32;
+ //
+ // continuing {
+ // if (true) {
+ // z = 2;
+ // }
+ // }
+ // }
+
+ auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
+ auto* body =
+ Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(If(Expr(true), Block(Assign(Expr("z"), Expr(2)))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest,
+ Stmt_Loop_ContinueInNestedLoopBodyBeforeDecl_UsageInContinuingLoop) {
+ // loop {
+ // loop {
+ // continue; // OK: not part of the outer loop
+ // }
+ // var z : i32;
+ //
+ // continuing {
+ // loop {
+ // z = 2;
+ // }
+ // }
+ // }
+
+ auto* inner_loop = Loop(Block(create<ast::ContinueStatement>()));
+ auto* body =
+ Block(inner_loop, Decl(Var("z", ty.i32(), ast::StorageClass::kNone)));
+ auto* continuing = Block(Loop(Block(Assign(Expr("z"), Expr(2)))));
+ auto* loop_stmt = Loop(body, continuing);
+ WrapInFunction(loop_stmt);
+
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest, Stmt_ContinueInLoop) {
+ WrapInFunction(Loop(Block(create<ast::ContinueStatement>(Source{{12, 34}}))));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest, Stmt_ContinueNotInLoop) {
+ WrapInFunction(create<ast::ContinueStatement>(Source{{12, 34}}));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(), "12:34 error: continue statement must be in a loop");
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakInLoop) {
+ WrapInFunction(Loop(Block(create<ast::BreakStatement>(Source{{12, 34}}))));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakInSwitch) {
+ WrapInFunction(Loop(Block(create<ast::SwitchStatement>(
+ Expr(1), ast::CaseStatementList{
+ create<ast::CaseStatement>(
+ ast::CaseSelectorList{Literal(1)},
+ Block(create<ast::BreakStatement>(Source{{12, 34}}))),
+ }))));
+ EXPECT_TRUE(r()->Resolve()) << r()->error();
+}
+
+TEST_F(ResolverValidationTest, Stmt_BreakNotInLoopOrSwitch) {
+ WrapInFunction(create<ast::BreakStatement>(Source{{12, 34}}));
+ EXPECT_FALSE(r()->Resolve());
+ EXPECT_EQ(r()->error(),
+ "12:34 error: break statement must be in a loop or switch case");
+}
+
+} // namespace
+} // namespace resolver
+} // namespace tint