Refactor unnecessary builtin checks
* Move unnecessary builtin checks out of type determination and into
validation
* Type determination now uses a bare minimum of information for most
builtins
* Validation now does majority of checking of builtins
* Added const qualifier to type accessors
Change-Id: Id11b739770af904a9b7afe0b1c2de50e1428a165
Reviewed-on: https://dawn-review.googlesource.com/c/tint/+/39540
Commit-Queue: Alan Baker <alanbaker@google.com>
Reviewed-by: dan sinclair <dsinclair@chromium.org>
diff --git a/BUILD.gn b/BUILD.gn
index 079bbd1..23d89b1 100644
--- a/BUILD.gn
+++ b/BUILD.gn
@@ -371,10 +371,10 @@
"src/inspector/scalar.h",
"src/namer.cc",
"src/namer.h",
- "src/program_builder.cc",
- "src/program_builder.h",
"src/program.cc",
"src/program.h",
+ "src/program_builder.cc",
+ "src/program_builder.h",
"src/reader/reader.cc",
"src/reader/reader.h",
"src/scope_stack.h",
@@ -404,10 +404,6 @@
"src/transform/transform.h",
"src/transform/vertex_pulling.cc",
"src/transform/vertex_pulling.h",
- "src/type_determiner.cc",
- "src/type_determiner.h",
- "src/validator/validator.cc",
- "src/validator/validator.h",
"src/type/access_control_type.cc",
"src/type/access_control_type.h",
"src/type/alias_type.cc",
@@ -448,6 +444,10 @@
"src/type/vector_type.h",
"src/type/void_type.cc",
"src/type/void_type.h",
+ "src/type_determiner.cc",
+ "src/type_determiner.h",
+ "src/validator/validator.cc",
+ "src/validator/validator.h",
"src/validator/validator_impl.cc",
"src/validator/validator_impl.h",
"src/validator/validator_test_helper.cc",
@@ -824,8 +824,8 @@
"src/diagnostic/printer_test.cc",
"src/inspector/inspector_test.cc",
"src/namer_test.cc",
- "src/program_test.cc",
"src/program_builder_test.cc",
+ "src/program_test.cc",
"src/scope_stack_test.cc",
"src/symbol_table_test.cc",
"src/symbol_test.cc",
@@ -835,7 +835,6 @@
"src/transform/first_index_offset_test.cc",
"src/transform/test_helper.h",
"src/transform/vertex_pulling_test.cc",
- "src/type_determiner_test.cc",
"src/type/access_control_type_test.cc",
"src/type/alias_type_test.cc",
"src/type/array_type_test.cc",
@@ -854,6 +853,8 @@
"src/type/type_manager_test.cc",
"src/type/u32_type_test.cc",
"src/type/vector_type_test.cc",
+ "src/type_determiner_test.cc",
+ "src/validator/validator_builtins_test.cc",
"src/validator/validator_control_block_test.cc",
"src/validator/validator_function_test.cc",
"src/validator/validator_test.cc",
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 465e16f..2ab4a49 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -478,6 +478,7 @@
type/type_manager_test.cc
type/u32_type_test.cc
type/vector_type_test.cc
+ validator/validator_builtins_test.cc
validator/validator_control_block_test.cc
validator/validator_function_test.cc
validator/validator_test.cc
diff --git a/src/type/type.cc b/src/type/type.cc
index 623d076..3dfdeb0 100644
--- a/src/type/type.cc
+++ b/src/type/type.cc
@@ -75,47 +75,47 @@
return 0;
}
-bool Type::is_scalar() {
+bool Type::is_scalar() const {
return is_float_scalar() || is_integer_scalar() || Is<Bool>();
}
-bool Type::is_float_scalar() {
+bool Type::is_float_scalar() const {
return Is<F32>();
}
-bool Type::is_float_matrix() {
+bool Type::is_float_matrix() const {
return Is<Matrix>() && As<Matrix>()->type()->is_float_scalar();
}
-bool Type::is_float_vector() {
+bool Type::is_float_vector() const {
return Is<Vector>() && As<Vector>()->type()->is_float_scalar();
}
-bool Type::is_float_scalar_or_vector() {
+bool Type::is_float_scalar_or_vector() const {
return is_float_scalar() || is_float_vector();
}
-bool Type::is_integer_scalar() {
+bool Type::is_integer_scalar() const {
return Is<U32>() || Is<I32>();
}
-bool Type::is_unsigned_integer_vector() {
+bool Type::is_unsigned_integer_vector() const {
return Is<Vector>() && As<Vector>()->type()->Is<U32>();
}
-bool Type::is_signed_integer_vector() {
+bool Type::is_signed_integer_vector() const {
return Is<Vector>() && As<Vector>()->type()->Is<I32>();
}
-bool Type::is_unsigned_scalar_or_vector() {
+bool Type::is_unsigned_scalar_or_vector() const {
return Is<U32>() || (Is<Vector>() && As<Vector>()->type()->Is<U32>());
}
-bool Type::is_signed_scalar_or_vector() {
+bool Type::is_signed_scalar_or_vector() const {
return Is<I32>() || (Is<Vector>() && As<Vector>()->type()->Is<I32>());
}
-bool Type::is_integer_scalar_or_vector() {
+bool Type::is_integer_scalar_or_vector() const {
return is_unsigned_scalar_or_vector() || is_signed_scalar_or_vector();
}
diff --git a/src/type/type.h b/src/type/type.h
index ae8d245..1264865 100644
--- a/src/type/type.h
+++ b/src/type/type.h
@@ -74,27 +74,27 @@
Type* UnwrapAll();
/// @returns true if this type is a scalar
- bool is_scalar();
+ bool is_scalar() const;
/// @returns true if this type is a float scalar
- bool is_float_scalar();
+ bool is_float_scalar() const;
/// @returns true if this type is a float matrix
- bool is_float_matrix();
+ bool is_float_matrix() const;
/// @returns true if this type is a float vector
- bool is_float_vector();
+ bool is_float_vector() const;
/// @returns true if this type is a float scalar or vector
- bool is_float_scalar_or_vector();
- /// @returns ture if this type is an integer scalar
- bool is_integer_scalar();
+ bool is_float_scalar_or_vector() const;
+ /// @returns true if this type is an integer scalar
+ bool is_integer_scalar() const;
/// @returns true if this type is a signed integer vector
- bool is_signed_integer_vector();
+ bool is_signed_integer_vector() const;
/// @returns true if this type is an unsigned vector
- bool is_unsigned_integer_vector();
+ bool is_unsigned_integer_vector() const;
/// @returns true if this type is an unsigned scalar or vector
- bool is_unsigned_scalar_or_vector();
+ bool is_unsigned_scalar_or_vector() const;
/// @returns true if this type is a signed scalar or vector
- bool is_signed_scalar_or_vector();
+ bool is_signed_scalar_or_vector() const;
/// @returns true if this type is an integer scalar or vector
- bool is_integer_scalar_or_vector();
+ bool is_integer_scalar_or_vector() const;
protected:
Type();
diff --git a/src/type_determiner.cc b/src/type_determiner.cc
index 4256651..32df870 100644
--- a/src/type_determiner.cc
+++ b/src/type_determiner.cc
@@ -448,70 +448,65 @@
namespace {
enum class IntrinsicDataType {
- kFloatOrIntScalarOrVector,
- kFloatScalarOrVector,
- kIntScalarOrVector,
- kFloatVector,
- kMatrix,
+ kDependent,
+ kSignedInteger,
+ kUnsignedInteger,
+ kFloat,
};
+
struct IntrinsicData {
ast::Intrinsic intrinsic;
- uint8_t param_count;
- IntrinsicDataType data_type;
- uint8_t vector_size;
+ IntrinsicDataType result_type;
+ uint8_t result_vector_width;
+ uint8_t param_for_result_type;
};
// Note, this isn't all the intrinsics. Some are handled specially before
// we get to the generic code. See the DetermineIntrinsic code below.
constexpr const IntrinsicData kIntrinsicData[] = {
- {ast::Intrinsic::kAbs, 1, IntrinsicDataType::kFloatOrIntScalarOrVector, 0},
- {ast::Intrinsic::kAcos, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kAsin, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kAtan, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kAtan2, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kCeil, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kClamp, 3, IntrinsicDataType::kFloatOrIntScalarOrVector,
- 0},
- {ast::Intrinsic::kCos, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kCosh, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kCountOneBits, 1, IntrinsicDataType::kIntScalarOrVector,
- 0},
- {ast::Intrinsic::kCross, 2, IntrinsicDataType::kFloatVector, 3},
- {ast::Intrinsic::kDeterminant, 1, IntrinsicDataType::kMatrix, 0},
- {ast::Intrinsic::kDistance, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kExp, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kExp2, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kFaceForward, 3, IntrinsicDataType::kFloatScalarOrVector,
- 0},
- {ast::Intrinsic::kFloor, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kFma, 3, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kFract, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kFrexp, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kInverseSqrt, 1, IntrinsicDataType::kFloatScalarOrVector,
- 0},
- {ast::Intrinsic::kLdexp, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kLength, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kLog, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kLog2, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kMax, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0},
- {ast::Intrinsic::kMin, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0},
- {ast::Intrinsic::kMix, 3, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kModf, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kNormalize, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kPow, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kReflect, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kReverseBits, 1, IntrinsicDataType::kIntScalarOrVector, 0},
- {ast::Intrinsic::kRound, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kSign, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kSin, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kSinh, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kSmoothStep, 3, IntrinsicDataType::kFloatScalarOrVector,
- 0},
- {ast::Intrinsic::kSqrt, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kStep, 2, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kTan, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kTanh, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
- {ast::Intrinsic::kTrunc, 1, IntrinsicDataType::kFloatScalarOrVector, 0},
+ {ast::Intrinsic::kAbs, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kAcos, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kAsin, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kAtan, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kAtan2, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kCeil, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kClamp, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kCos, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kCosh, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kCountOneBits, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kCross, IntrinsicDataType::kFloat, 3, 0},
+ {ast::Intrinsic::kDeterminant, IntrinsicDataType::kFloat, 1, 0},
+ {ast::Intrinsic::kDistance, IntrinsicDataType::kFloat, 1, 0},
+ {ast::Intrinsic::kExp, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kExp2, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kFaceForward, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kFloor, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kFma, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kFract, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kFrexp, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kInverseSqrt, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kLdexp, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kLength, IntrinsicDataType::kFloat, 1, 0},
+ {ast::Intrinsic::kLog, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kLog2, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kMax, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kMin, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kMix, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kModf, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kNormalize, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kPow, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kReflect, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kReverseBits, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kRound, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kSign, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kSin, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kSinh, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kSmoothStep, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kSqrt, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kStep, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kTan, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kTanh, IntrinsicDataType::kDependent, 0, 0},
+ {ast::Intrinsic::kTrunc, IntrinsicDataType::kDependent, 0, 0},
};
constexpr const uint32_t kIntrinsicDataCount =
@@ -780,105 +775,42 @@
return false;
}
- if (expr->params().size() != data->param_count) {
- set_error(expr->source(), "incorrect number of parameters for " +
- builder_->Symbols().NameFor(ident->symbol()) +
- ". Expected " +
- std::to_string(data->param_count) + " got " +
- std::to_string(expr->params().size()));
- return false;
- }
-
- std::vector<type::Type*> result_types;
- for (uint32_t i = 0; i < data->param_count; ++i) {
- result_types.push_back(TypeOf(expr->params()[i])->UnwrapPtrIfNeeded());
-
- switch (data->data_type) {
- case IntrinsicDataType::kFloatOrIntScalarOrVector:
- if (!result_types.back()->is_float_scalar_or_vector() &&
- !result_types.back()->is_integer_scalar_or_vector()) {
- set_error(expr->source(),
- "incorrect type for " +
- builder_->Symbols().NameFor(ident->symbol()) + ". " +
- "Requires float or int, scalar or vector values");
- return false;
- }
- break;
- case IntrinsicDataType::kFloatScalarOrVector:
- if (!result_types.back()->is_float_scalar_or_vector()) {
- set_error(expr->source(),
- "incorrect type for " +
- builder_->Symbols().NameFor(ident->symbol()) + ". " +
- "Requires float scalar or float vector values");
- return false;
- }
-
- break;
- case IntrinsicDataType::kIntScalarOrVector:
- if (!result_types.back()->is_integer_scalar_or_vector()) {
- set_error(expr->source(),
- "incorrect type for " +
- builder_->Symbols().NameFor(ident->symbol()) + ". " +
- "Requires integer scalar or integer vector values");
- return false;
- }
- break;
- case IntrinsicDataType::kFloatVector:
- if (!result_types.back()->is_float_vector()) {
- set_error(expr->source(),
- "incorrect type for " +
- builder_->Symbols().NameFor(ident->symbol()) + ". " +
- "Requires float vector values");
- return false;
- }
- if (data->vector_size > 0 &&
- result_types.back()->As<type::Vector>()->size() !=
- data->vector_size) {
- set_error(expr->source(),
- "incorrect vector size for " +
- builder_->Symbols().NameFor(ident->symbol()) + ". " +
- "Requires " + std::to_string(data->vector_size) +
- " elements");
- return false;
- }
- break;
- case IntrinsicDataType::kMatrix:
- if (!result_types.back()->Is<type::Matrix>()) {
- set_error(expr->source(),
- "incorrect type for " +
- builder_->Symbols().NameFor(ident->symbol()) +
- ". Requires matrix value");
- return false;
- }
- break;
- }
- }
-
- // Verify all the parameter types match
- for (size_t i = 1; i < data->param_count; ++i) {
- if (result_types[0] != result_types[i]) {
+ if (data->result_type == IntrinsicDataType::kDependent) {
+ const auto param_idx = data->param_for_result_type;
+ if (expr->params().size() <= param_idx) {
set_error(expr->source(),
- "mismatched parameter types for " +
+ "missing parameter " + std::to_string(param_idx) +
+ " required for type determination in builtin " +
builder_->Symbols().NameFor(ident->symbol()));
return false;
}
+ SetType(expr->func(),
+ TypeOf(expr->params()[param_idx])->UnwrapPtrIfNeeded());
+ } else {
+ // The result type is not dependent on the parameter types.
+ type::Type* type = nullptr;
+ switch (data->result_type) {
+ case IntrinsicDataType::kSignedInteger:
+ type = builder_->create<type::I32>();
+ break;
+ case IntrinsicDataType::kUnsignedInteger:
+ type = builder_->create<type::U32>();
+ break;
+ case IntrinsicDataType::kFloat:
+ type = builder_->create<type::F32>();
+ break;
+ default:
+ error_ = "unhandled intrinsic data type for " +
+ builder_->Symbols().NameFor(ident->symbol());
+ return false;
+ }
+
+ if (data->result_vector_width > 1) {
+ type = builder_->create<type::Vector>(type, data->result_vector_width);
+ }
+ SetType(expr->func(), type);
}
- // Handle functions which aways return the type, even if a vector is
- // provided.
- if (ident->intrinsic() == ast::Intrinsic::kLength ||
- ident->intrinsic() == ast::Intrinsic::kDistance) {
- SetType(expr->func(), result_types[0]->is_float_scalar()
- ? result_types[0]
- : result_types[0]->As<type::Vector>()->type());
- return true;
- }
- // The determinant returns the component type of the columns
- if (ident->intrinsic() == ast::Intrinsic::kDeterminant) {
- SetType(expr->func(), result_types[0]->As<type::Matrix>()->type());
- return true;
- }
- SetType(expr->func(), result_types[0]);
return true;
}
diff --git a/src/type_determiner_test.cc b/src/type_determiner_test.cc
index 29daee7..3fe802a 100644
--- a/src/type_determiner_test.cc
+++ b/src/type_determiner_test.cc
@@ -1735,35 +1735,15 @@
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-TEST_P(ImportData_SingleParamTest, Error_Integer) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires float scalar or float vector values");
-}
-
TEST_P(ImportData_SingleParamTest, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 0");
-}
-
-TEST_P(ImportData_SingleParamTest, Error_MultipleParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 3");
+ EXPECT_EQ(td()->error(),
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
INSTANTIATE_TEST_SUITE_P(
@@ -1874,35 +1854,15 @@
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-TEST_P(ImportData_SingleParam_FloatOrInt_Test, Error_Bool) {
- auto param = GetParam();
-
- auto* call = Call(param.name, false);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires float or int, scalar or vector values");
-}
-
TEST_P(ImportData_SingleParam_FloatOrInt_Test, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 0");
-}
-
-TEST_P(ImportData_SingleParam_FloatOrInt_Test, Error_MultipleParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 3");
+ EXPECT_EQ(td()->error(),
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
@@ -1933,36 +1893,6 @@
EXPECT_TRUE(TypeOf(ident)->is_float_scalar());
}
-TEST_F(TypeDeterminerTest, ImportData_Length_Error_Integer) {
- ast::ExpressionList params;
- params.push_back(Expr(1));
-
- auto* call = Call("length", params);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect type for length. Requires float scalar or float vector "
- "values");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Length_Error_NoParams) {
- ast::ExpressionList params;
-
- auto* call = Call("length");
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for length. Expected 1 got 0");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Length_Error_MultipleParams) {
- auto* call = Call("length", 1.f, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for length. Expected 1 got 3");
-}
-
using ImportData_TwoParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
TEST_P(ImportData_TwoParamTest, Scalar) {
auto param = GetParam();
@@ -1987,69 +1917,16 @@
EXPECT_TRUE(TypeOf(ident)->is_float_vector());
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-
-TEST_P(ImportData_TwoParamTest, Error_Integer) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 2);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires float scalar or float vector values");
-}
-
TEST_P(ImportData_TwoParamTest, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 2 got 0");
-}
-
-TEST_P(ImportData_TwoParamTest, Error_OneParam) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 2 got 1");
-}
-
-TEST_P(ImportData_TwoParamTest, Error_MismatchedParamCount) {
- auto param = GetParam();
-
- auto* call =
- Call(param.name, vec2<f32>(1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
-
-TEST_P(ImportData_TwoParamTest, Error_MismatchedParamType) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.0f, vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_TwoParamTest, Error_TooManyParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 2 got 3");
-}
-
INSTANTIATE_TEST_SUITE_P(
TypeDeterminerTest,
ImportData_TwoParamTest,
@@ -2079,54 +1956,6 @@
EXPECT_TRUE(TypeOf(ident)->Is<type::F32>());
}
-TEST_F(TypeDeterminerTest, ImportData_Distance_Error_Integer) {
- auto* call = Call("distance", 1, 2);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect type for distance. Requires float scalar or float "
- "vector values");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Distance_Error_NoParams) {
- auto* call = Call("distance");
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for distance. Expected 2 got 0");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Distance_Error_OneParam) {
- auto* call = Call("distance", 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for distance. Expected 2 got 1");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Distance_Error_MismatchedParamCount) {
- auto* call =
- Call("distance", vec2<f32>(1.0f, 1.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), "mismatched parameter types for distance");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Distance_Error_MismatchedParamType) {
- auto* call = Call("distance", Expr(1.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), "mismatched parameter types for distance");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Distance_Error_TooManyParams) {
- auto* call = Call("distance", Expr(1.f), Expr(1.f), Expr(1.f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for distance. Expected 2 got 3");
-}
-
TEST_F(TypeDeterminerTest, ImportData_Cross) {
auto* ident = Expr("cross");
@@ -2139,45 +1968,15 @@
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-TEST_F(TypeDeterminerTest, ImportData_Cross_Error_Scalar) {
- auto* call = Call("cross", 1.0f, 1.0f);
+TEST_F(TypeDeterminerTest, ImportData_Cross_AutoType) {
+ auto* ident = Expr("cross");
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect type for cross. Requires float vector values");
-}
+ auto* call = Call(ident);
-TEST_F(TypeDeterminerTest, ImportData_Cross_Error_IntType) {
- auto* call = Call("cross", vec3<i32>(1, 1, 3), vec3<i32>(1, 1, 3));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect type for cross. Requires float vector values");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Cross_Error_MissingParams) {
- auto* call = Call("cross");
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for cross. Expected 2 got 0");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Cross_Error_TooFewParams) {
- auto* call = Call("cross", vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for cross. Expected 2 got 1");
-}
-
-TEST_F(TypeDeterminerTest, ImportData_Cross_Error_TooManyParams) {
- auto* call = Call("cross", vec3<f32>(1.0f, 1.0f, 3.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- "incorrect number of parameters for cross. Expected 2 got 3");
+ EXPECT_TRUE(td()->DetermineResultType(call)) << td()->error();
+ ASSERT_NE(TypeOf(ident), nullptr);
+ EXPECT_TRUE(TypeOf(ident)->is_float_vector());
+ EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
using ImportData_ThreeParamTest = TypeDeterminerTestWithParam<IntrinsicData>;
@@ -2204,77 +2003,15 @@
EXPECT_TRUE(TypeOf(ident)->is_float_vector());
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-
-TEST_P(ImportData_ThreeParamTest, Error_Integer) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 2, 3);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires float scalar or float vector values");
-}
-
TEST_P(ImportData_ThreeParamTest, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 0");
-}
-
-TEST_P(ImportData_ThreeParamTest, Error_OneParam) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 1");
-}
-
-TEST_P(ImportData_ThreeParamTest, Error_TwoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 2");
-}
-
-TEST_P(ImportData_ThreeParamTest, Error_MismatchedParamCount) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec2<f32>(1.0f, 1.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_ThreeParamTest, Error_MismatchedParamType) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.0f, 1.0f, vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_ThreeParamTest, Error_TooManyParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 4");
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
INSTANTIATE_TEST_SUITE_P(
@@ -2360,76 +2097,15 @@
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_Bool) {
- auto param = GetParam();
-
- auto* call = Call(param.name, true, false, true);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires float or int, scalar or vector values");
-}
-
TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 0");
-}
-
-TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_OneParam) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 1");
-}
-
-TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_TwoParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 2");
-}
-
-TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_MismatchedParamCount) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec2<f32>(1.0f, 1.0f),
- vec3<f32>(1.0f, 1.0f, 3.0f), vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_MismatchedParamType) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.0f, 1.0f, vec3<f32>(1.0f, 1.0f, 3.0f));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_ThreeParam_FloatOrInt_Test, Error_TooManyParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f, 1.f, 1.f, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 3 got 4");
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
@@ -2462,35 +2138,15 @@
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-TEST_P(ImportData_Int_SingleParamTest, Error_Float) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1.f);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires integer scalar or integer vector values");
-}
-
TEST_P(ImportData_Int_SingleParamTest, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 0");
-}
-
-TEST_P(ImportData_Int_SingleParamTest, Error_MultipleParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 1, 1);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 3");
+ EXPECT_EQ(td()->error(),
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
INSTANTIATE_TEST_SUITE_P(
@@ -2571,65 +2227,15 @@
EXPECT_EQ(TypeOf(ident)->As<type::Vector>()->size(), 3u);
}
-TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_Bool) {
- auto param = GetParam();
-
- auto* call = Call(param.name, true, false);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("incorrect type for ") + param.name +
- ". Requires float or int, scalar or vector values");
-}
-
TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 2 got 0");
-}
-
-TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_OneParam) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 2 got 1");
-}
-
-TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_MismatchedParamCount) {
- auto param = GetParam();
-
- auto* call = Call(param.name, vec2<i32>(1, 1), vec3<i32>(1, 1, 3));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_MismatchedParamType) {
- auto param = GetParam();
-
- auto* call = Call(param.name, Expr(1), vec3<i32>(1, 1, 3));
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(),
- std::string("mismatched parameter types for ") + param.name);
-}
-
-TEST_P(ImportData_FloatOrInt_TwoParamTest, Error_TooManyParams) {
- auto param = GetParam();
-
- auto* call = Call(param.name, 1, 1, 1);
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 2 got 3");
+ "missing parameter 0 required for type determination in builtin " +
+ std::string(param.name));
}
INSTANTIATE_TEST_SUITE_P(
@@ -2655,45 +2261,16 @@
using ImportData_Matrix_OneParam_Test =
TypeDeterminerTestWithParam<IntrinsicData>;
-TEST_P(ImportData_Matrix_OneParam_Test, Error_Float) {
- auto param = GetParam();
-
- auto* var = Var("var", ast::StorageClass::kFunction, ty.f32());
- AST().AddGlobalVariable(var);
-
- ASSERT_TRUE(td()->Determine()) << td()->error();
-
- auto* call = Call(param.name, "var");
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect type for ") + param.name +
- ". Requires matrix value");
-}
TEST_P(ImportData_Matrix_OneParam_Test, NoParams) {
auto param = GetParam();
auto* call = Call(param.name);
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 0");
+ EXPECT_TRUE(td()->DetermineResultType(call)) << td()->error();
+ EXPECT_TRUE(TypeOf(call)->Is<type::F32>());
}
-TEST_P(ImportData_Matrix_OneParam_Test, TooManyParams) {
- auto param = GetParam();
-
- auto* var = Var("var", ast::StorageClass::kFunction, ty.mat3x3<f32>());
- AST().AddGlobalVariable(var);
-
- ASSERT_TRUE(td()->Determine()) << td()->error();
-
- auto* call = Call(param.name, "var", "var");
-
- EXPECT_FALSE(td()->DetermineResultType(call));
- EXPECT_EQ(td()->error(), std::string("incorrect number of parameters for ") +
- param.name + ". Expected 1 got 2");
-}
INSTANTIATE_TEST_SUITE_P(TypeDeterminerTest,
ImportData_Matrix_OneParam_Test,
testing::Values(IntrinsicData{
diff --git a/src/validator/validator_builtins_test.cc b/src/validator/validator_builtins_test.cc
new file mode 100644
index 0000000..392a7ac
--- /dev/null
+++ b/src/validator/validator_builtins_test.cc
@@ -0,0 +1,1128 @@
+// 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 "gtest/gtest.h"
+#include "src/ast/array_accessor_expression.h"
+#include "src/ast/struct.h"
+#include "src/ast/struct_block_decoration.h"
+#include "src/ast/struct_member.h"
+#include "src/ast/struct_member_decoration.h"
+#include "src/ast/type_constructor_expression.h"
+#include "src/ast/variable_decl_statement.h"
+#include "src/type/alias_type.h"
+#include "src/type/array_type.h"
+#include "src/type/f32_type.h"
+#include "src/type/i32_type.h"
+#include "src/type/struct_type.h"
+#include "src/validator/validator_impl.h"
+#include "src/validator/validator_test_helper.h"
+
+namespace tint {
+
+class ValidatorBuiltinsTest : public ValidatorTestHelper,
+ public testing::Test {};
+
+TEST_F(ValidatorBuiltinsTest, Length_Float_Scalar) {
+ auto* builtin = Call("length", 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_Float_Vec2) {
+ auto* builtin = Call("length", vec2<float>(1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_Float_Vec3) {
+ auto* builtin = Call("length", vec3<float>(1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_Float_Vec4) {
+ auto* builtin = Call("length", vec4<float>(1.0f, 1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_Integer_Scalar) {
+ auto* builtin = Call("length", 1);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect type for length. Requires float scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_Integer_Vec2) {
+ auto* builtin = Call("length", vec2<uint32_t>(1, 1));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect type for length. Requires float scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_TooManyParams) {
+ auto* builtin = Call("length", 1.0f, 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for length expected 1 got 2");
+}
+
+TEST_F(ValidatorBuiltinsTest, Length_TooFewParams) {
+ auto* builtin = Call("length");
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for length expected 1 got 0");
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_Float_Scalar) {
+ auto* builtin = Call("distance", 1.0f, 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_Float_Vec2) {
+ auto* builtin =
+ Call("distance", vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_Float_Vec3) {
+ auto* builtin = Call("distance", vec3<float>(1.0f, 1.0f, 1.0f),
+ vec3<float>(1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_Float_Vec4) {
+ auto* builtin = Call("distance", vec4<float>(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4<float>(1.0f, 1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_Integer_Scalar) {
+ auto* builtin = Call("distance", 1, 1);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "incorrect type for distance. Requires float scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_Integer_Vec2) {
+ auto* builtin = Call("distance", vec2<uint32_t>(1, 1), vec2<uint32_t>(1, 1));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "incorrect type for distance. Requires float scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_TooManyParams) {
+ auto* builtin = Call("distance", 1.0f, 1.0f, 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for distance expected 2 got 3");
+}
+
+TEST_F(ValidatorBuiltinsTest, Distance_TooFewParams) {
+ auto* builtin = Call("distance", 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for distance expected 2 got 1");
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Mat2x2) {
+ auto* builtin = Call("determinant", mat2x2<float>(vec2<float>(1.0f, 1.0f),
+ vec2<float>(1.0f, 1.0f)));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Mat3x3) {
+ auto* builtin =
+ Call("determinant", mat3x3<float>(vec3<float>(1.0f, 1.0f, 1.0f),
+ vec3<float>(1.0f, 1.0f, 1.0f),
+ vec3<float>(1.0f, 1.0f, 1.0f)));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Mat4x4) {
+ auto* builtin =
+ Call("determinant", mat4x4<float>(vec4<float>(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4<float>(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4<float>(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4<float>(1.0f, 1.0f, 1.0f, 1.0f)));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Mat3x2) {
+ auto* builtin = Call("determinant", mat3x2<float>(vec2<float>(1.0f, 1.0f),
+ vec2<float>(1.0f, 1.0f),
+ vec2<float>(1.0f, 1.0f)));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect type for determinant. Requires a square matrix");
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Float_Vec2) {
+ auto* builtin = Call("determinant", vec2<float>(1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for determinant. Requires matrix value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Float_Scalar) {
+ auto* builtin = Call("determinant", 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for determinant. Requires matrix value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_Integer_Scalar) {
+ auto* builtin = Call("determinant", 1);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for determinant. Requires matrix value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_TooManyParams) {
+ auto* builtin =
+ Call("determinant",
+ mat2x2<float>(vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f)),
+ mat2x2<float>(vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f)));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for determinant expected 1 got 2");
+}
+
+TEST_F(ValidatorBuiltinsTest, Determinant_TooFewParams) {
+ auto* builtin = Call("determinant");
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for determinant expected 1 got 0");
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Scalar) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.i32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<int>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", 1.0f, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Vec2) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec2<int>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.i32(), 2),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", vec2<float>(1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Vec3) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec3<int>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.i32(), 3),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", vec3<float>(1.0f, 1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Vec4) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec4<int>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.i32(), 4),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", vec4<float>(1.0f, 1.0f, 1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Integer_FirstParam) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.i32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<int>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", 1, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_FALSE(TypeOf(builtin)->Is<type::F32>());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect type for frexp. Requires float scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Float_SecondParam) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.f32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<float>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", 1.0f, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect type for frexp. Requires int scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_NotAPointer) {
+ auto* builtin = Call("frexp", 1.0f, 1);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for frexp. Requires pointer value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Frexp_Scalar_Vector) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec2<int>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.i32(), 2),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("frexp", 1.0f, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect types for frexp. Parameters must be matched scalars or "
+ "vectors");
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Scalar) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.f32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<float>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", 1.0f, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Vec2) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec2<float>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.f32(), 2),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", vec2<float>(1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Vec3) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec3<float>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.f32(), 3),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", vec3<float>(1.0f, 1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Vec4) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec4<float>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.f32(), 4),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", vec4<float>(1.0f, 1.0f, 1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Integer_FirstParam) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.f32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<float>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", 1, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_FALSE(TypeOf(builtin)->Is<type::F32>());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect type for modf. Requires float scalar or vector value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Integer_SecondParam) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.i32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<int>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", 1.0f, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 1's unwrapped type to match result type for modf");
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_NotAPointer) {
+ auto* builtin = Call("modf", 1.0f, 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for modf. Requires pointer value");
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Scalar_Vector) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec2<float>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.f32(), 2),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", 1.0f, Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 1's unwrapped type to match result type for modf");
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Vector_Scalar) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.f32());
+ auto* b =
+ Const("b", ast::StorageClass::kWorkgroup,
+ ty.pointer<int>(ast::StorageClass::kWorkgroup), Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", vec2<float>(1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 1's unwrapped type to match result type for modf");
+}
+
+TEST_F(ValidatorBuiltinsTest, Modf_Vector_Vector_MismatchedSize) {
+ auto* a = Var("a", ast::StorageClass::kWorkgroup, ty.vec2<float>());
+ auto* b = Const("b", ast::StorageClass::kWorkgroup,
+ create<type::Pointer>(create<type::Vector>(ty.f32(), 2),
+ ast::StorageClass::kWorkgroup),
+ Expr("a"), {});
+ RegisterVariable(a);
+ RegisterVariable(b);
+ auto* builtin = Call("modf", vec3<float>(1.0f, 1.0f, 1.0f), Expr("b"));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ EXPECT_TRUE(TypeOf(builtin->params()[1])->Is<type::Pointer>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 1's unwrapped type to match result type for modf");
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_Float_Vec3) {
+ auto* builtin = Call("cross", vec3<float>(1.0f, 1.0f, 1.0f),
+ vec3<float>(1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_Integer_Vec3) {
+ auto* builtin = Call("cross", vec3<int>(1, 1, 1), vec3<int>(1, 1, 1));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 0's unwrapped type to match result type for cross");
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_Float_Vec4) {
+ auto* builtin = Call("cross", vec4<float>(1.0f, 1.0f, 1.0f, 1.0f),
+ vec4<float>(1.0f, 1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 0's unwrapped type to match result type for cross");
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_Float_Vec2) {
+ auto* builtin =
+ Call("cross", vec2<float>(1.0f, 1.0f), vec2<float>(1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 0's unwrapped type to match result type for cross");
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_Float_Scalar) {
+ auto* builtin = Call("cross", 1.0f, 1.0f);
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(
+ v.error(),
+ "expected parameter 0's unwrapped type to match result type for cross");
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_TooManyParams) {
+ auto* builtin =
+ Call("cross", vec3<float>(1.0f, 1.0f, 1.0f),
+ vec3<float>(1.0f, 1.0f, 1.0f), vec3<float>(1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for cross expected 2 got 3");
+}
+
+TEST_F(ValidatorBuiltinsTest, Cross_TooFewParams) {
+ auto* builtin = Call("cross", vec3<float>(1.0f, 1.0f, 1.0f));
+
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "incorrect number of parameters for cross expected 2 got 1");
+}
+
+template <typename T>
+class ValidatorBuiltinsTestWithParams : public ValidatorTestHelper,
+ public testing::TestWithParam<T> {};
+
+using FloatAllMatching =
+ ValidatorBuiltinsTestWithParams<std::tuple<std::string, uint32_t>>;
+
+TEST_P(FloatAllMatching, Scalar) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(FloatAllMatching, Vec2) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<float>(1.0f, 1.0f));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(FloatAllMatching, Vec3) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<float>(1.0f, 1.0f, 1.0f));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(FloatAllMatching, Vec4) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<float>(1.0f, 1.0f, 1.0f, 1.0f));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_float_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(FloatAllMatching, Param_TooManyParams) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params + 1; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect number of parameters for " + name +
+ " expected " + std::to_string(num_params) + " got " +
+ std::to_string(num_params + 1));
+}
+
+TEST_P(FloatAllMatching, Param_TooFewParams) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ auto* builtin = Call(name, params);
+ // Most intrinsics require a parameter to determine the type so expect type
+ // determination to fail at zero parameters.
+ if (num_params > 1) {
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect number of parameters for " + name +
+ " expected " + std::to_string(num_params) +
+ " got " + std::to_string(num_params - 1));
+ } else {
+ EXPECT_FALSE(td()->DetermineResultType(builtin));
+ }
+}
+
+TEST_P(FloatAllMatching, Param_Mismatch_Scalar) {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ // Can't mismatch single parameter types.
+ if (num_params > 1) {
+ std::string name = std::get<0>(GetParam());
+ params.push_back(Expr(1));
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "expected parameter " + std::to_string(num_params - 1) +
+ "'s unwrapped type to match result type for " + name);
+ }
+}
+
+TEST_P(FloatAllMatching, Param_Mismatch_Vector) {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ // Can't mismatch single parameter types.
+ if (num_params > 1) {
+ std::string name = std::get<0>(GetParam());
+ params.push_back(vec2<float>(1.0f, 1.0f));
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "expected parameter " + std::to_string(num_params - 1) +
+ "'s unwrapped type to match result type for " + name);
+ }
+}
+
+TEST_P(FloatAllMatching, Param_Integer) {
+ std::string name = std::get<0>(GetParam());
+ // abs, clamp, max and min also support integers.
+ if (name != "abs" && name != "clamp" && name != "max" && name != "min") {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Expr(1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_FALSE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for " + name +
+ ". Requires float scalar or vector value");
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidatorBuiltinsTest,
+ FloatAllMatching,
+ ::testing::Values(std::make_tuple("abs", 1),
+ std::make_tuple("acos", 1),
+ std::make_tuple("asin", 1),
+ std::make_tuple("atan", 1),
+ std::make_tuple("atan2", 2),
+ std::make_tuple("ceil", 1),
+ std::make_tuple("clamp", 3),
+ std::make_tuple("cos", 1),
+ std::make_tuple("cosh", 1),
+ std::make_tuple("exp", 1),
+ std::make_tuple("exp2", 1),
+ std::make_tuple("faceForward", 3),
+ std::make_tuple("floor", 1),
+ std::make_tuple("fma", 3),
+ std::make_tuple("fract", 1),
+ std::make_tuple("inverseSqrt", 1),
+ std::make_tuple("ldexp", 2),
+ std::make_tuple("log", 1),
+ std::make_tuple("log2", 1),
+ std::make_tuple("max", 2),
+ std::make_tuple("min", 2),
+ std::make_tuple("mix", 3),
+ std::make_tuple("pow", 2),
+ std::make_tuple("reflect", 2),
+ std::make_tuple("round", 1),
+ std::make_tuple("sign", 1),
+ std::make_tuple("sin", 1),
+ std::make_tuple("sinh", 1),
+ std::make_tuple("smoothStep", 3),
+ std::make_tuple("sqrt", 1),
+ std::make_tuple("step", 2),
+ std::make_tuple("tan", 1),
+ std::make_tuple("tanh", 1),
+ std::make_tuple("trunc", 1)));
+
+using IntegerAllMatching =
+ ValidatorBuiltinsTestWithParams<std::tuple<std::string, uint32_t>>;
+
+TEST_P(IntegerAllMatching, ScalarUnsigned) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::U32>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Vec2Unsigned) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<uint32_t>(1, 1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Vec3Unsigned) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<uint32_t>(1, 1, 1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Vec4Unsigned) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<uint32_t>(1, 1, 1, 1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_unsigned_integer_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, ScalarSigned) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Construct<int32_t>(1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::I32>());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Vec2Signed) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec2<int32_t>(1, 1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Vec3Signed) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec3<int32_t>(1, 1, 1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Vec4Signed) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(vec4<int32_t>(1, 1, 1, 1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->is_signed_integer_vector());
+ ValidatorImpl& v = Build();
+ EXPECT_TRUE(v.ValidateCallExpr(builtin)) << v.error();
+}
+
+TEST_P(IntegerAllMatching, Param_TooManyParams) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params + 1; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::U32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect number of parameters for " + name +
+ " expected " + std::to_string(num_params) + " got " +
+ std::to_string(num_params + 1));
+}
+
+TEST_P(IntegerAllMatching, Param_TooFewParams) {
+ std::string name = std::get<0>(GetParam());
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ auto* builtin = Call(name, params);
+ // Most intrinsics require a parameter to determine the type so expect type
+ // determination to fail at zero parameters.
+ if (num_params > 1) {
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::U32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect number of parameters for " + name +
+ " expected " + std::to_string(num_params) +
+ " got " + std::to_string(num_params - 1));
+ } else {
+ EXPECT_FALSE(td()->DetermineResultType(builtin));
+ }
+}
+
+TEST_P(IntegerAllMatching, Param_Mismatch_Scalar) {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ // Can't mismatch single parameter types.
+ if (num_params > 1) {
+ std::string name = std::get<0>(GetParam());
+ params.push_back(Expr(1));
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::U32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "expected parameter " + std::to_string(num_params - 1) +
+ "'s unwrapped type to match result type for " + name);
+ }
+}
+
+TEST_P(IntegerAllMatching, Param_Mismatch_Vector) {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ // Can't mismatch single parameter types.
+ if (num_params > 1) {
+ std::string name = std::get<0>(GetParam());
+ params.push_back(vec2<uint32_t>(1, 1));
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::U32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "expected parameter " + std::to_string(num_params - 1) +
+ "'s unwrapped type to match result type for " + name);
+ }
+}
+
+TEST_P(IntegerAllMatching, Param_Mismatch_Sign) {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params - 1; ++i) {
+ params.push_back(Construct<uint32_t>(1));
+ }
+ // Can't mismatch single parameter types.
+ if (num_params > 1) {
+ std::string name = std::get<0>(GetParam());
+ params.push_back(Construct<int32_t>(1));
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::U32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(),
+ "expected parameter " + std::to_string(num_params - 1) +
+ "'s unwrapped type to match result type for " + name);
+ }
+}
+
+TEST_P(IntegerAllMatching, Param_Float) {
+ std::string name = std::get<0>(GetParam());
+ // abs, clamp, max and min also support integers.
+ if (name != "abs" && name != "clamp" && name != "max" && name != "min") {
+ uint32_t num_params = std::get<1>(GetParam());
+
+ ast::ExpressionList params;
+ for (uint32_t i = 0; i < num_params; ++i) {
+ params.push_back(Expr(1.0f));
+ }
+ auto* builtin = Call(name, params);
+ EXPECT_TRUE(td()->DetermineResultType(builtin)) << td()->error();
+ EXPECT_TRUE(TypeOf(builtin)->Is<type::F32>());
+ ValidatorImpl& v = Build();
+ EXPECT_FALSE(v.ValidateCallExpr(builtin));
+ EXPECT_EQ(v.error(), "incorrect type for " + name +
+ ". Requires int scalar or vector value");
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(ValidatorBuiltinsTest,
+ IntegerAllMatching,
+ ::testing::Values(std::make_tuple("abs", 1),
+ std::make_tuple("clamp", 3),
+ std::make_tuple("countOneBits", 1),
+ std::make_tuple("max", 2),
+ std::make_tuple("min", 2),
+ std::make_tuple("reverseBits", 1)));
+} // namespace tint
diff --git a/src/validator/validator_impl.cc b/src/validator/validator_impl.cc
index 818ac50..b3ce8f2 100644
--- a/src/validator/validator_impl.cc
+++ b/src/validator/validator_impl.cc
@@ -33,6 +33,7 @@
#include "src/semantic/expression.h"
#include "src/type/alias_type.h"
#include "src/type/array_type.h"
+#include "src/type/f32_type.h"
#include "src/type/i32_type.h"
#include "src/type/matrix_type.h"
#include "src/type/pointer_type.h"
@@ -43,6 +44,175 @@
namespace tint {
+namespace {
+
+enum class IntrinsicDataType {
+ kMixed,
+ kFloatOrIntScalarOrVector,
+ kFloatScalarOrVector,
+ kIntScalarOrVector,
+ kFloatVector,
+ kFloatScalar,
+ kMatrix,
+};
+
+struct IntrinsicData {
+ ast::Intrinsic intrinsic;
+ uint32_t param_count;
+ IntrinsicDataType data_type;
+ uint32_t vector_size;
+ bool all_types_match;
+};
+
+// Note, this isn't all the intrinsics. Some are handled specially before
+// we get to the generic code. See the ValidateCallExpr code below.
+constexpr const IntrinsicData kIntrinsicData[] = {
+ {ast::Intrinsic::kAbs, 1, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kAcos, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kAsin, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kAtan, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kAtan2, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kCeil, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kClamp, 3, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kCos, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kCosh, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kCountOneBits, 1, IntrinsicDataType::kIntScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kCross, 2, IntrinsicDataType::kFloatVector, 3, true},
+ {ast::Intrinsic::kDeterminant, 1, IntrinsicDataType::kMatrix, 0, false},
+ {ast::Intrinsic::kDistance, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
+ false},
+ {ast::Intrinsic::kExp, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kExp2, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kFaceForward, 3, IntrinsicDataType::kFloatScalarOrVector,
+ 0, true},
+ {ast::Intrinsic::kFloor, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kFma, 3, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kFract, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kFrexp, 2, IntrinsicDataType::kMixed, 0, false},
+ {ast::Intrinsic::kInverseSqrt, 1, IntrinsicDataType::kFloatScalarOrVector,
+ 0, true},
+ {ast::Intrinsic::kLdexp, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kLength, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ false},
+ {ast::Intrinsic::kLog, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kLog2, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kMax, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kMin, 2, IntrinsicDataType::kFloatOrIntScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kMix, 3, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kModf, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kNormalize, 1, IntrinsicDataType::kFloatVector, 0, true},
+ {ast::Intrinsic::kPow, 2, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kReflect, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kReverseBits, 1, IntrinsicDataType::kIntScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kRound, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kSign, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kSin, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kSinh, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kSmoothStep, 3, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kSqrt, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kStep, 2, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kTan, 1, IntrinsicDataType::kFloatScalarOrVector, 0, true},
+ {ast::Intrinsic::kTanh, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+ {ast::Intrinsic::kTrunc, 1, IntrinsicDataType::kFloatScalarOrVector, 0,
+ true},
+};
+
+constexpr const uint32_t kIntrinsicDataCount =
+ sizeof(kIntrinsicData) / sizeof(IntrinsicData);
+
+bool IsValidType(type::Type* type,
+ const Source& source,
+ const std::string& name,
+ const IntrinsicDataType& data_type,
+ uint32_t vector_size,
+ ValidatorImpl* impl) {
+ type = type->UnwrapPtrIfNeeded();
+ switch (data_type) {
+ case IntrinsicDataType::kFloatOrIntScalarOrVector:
+ if (!type->is_float_scalar_or_vector() &&
+ !type->is_integer_scalar_or_vector()) {
+ impl->add_error(source,
+ "incorrect type for " + name +
+ ". Requires int or float, scalar or vector value");
+ return false;
+ }
+ break;
+ case IntrinsicDataType::kFloatScalarOrVector:
+ if (!type->is_float_scalar_or_vector()) {
+ impl->add_error(source, "incorrect type for " + name +
+ ". Requires float scalar or vector value");
+ return false;
+ }
+ break;
+ case IntrinsicDataType::kIntScalarOrVector:
+ if (!type->is_integer_scalar_or_vector()) {
+ impl->add_error(source, "incorrect type for " + name +
+ ". Requires int scalar or vector value");
+ return false;
+ }
+ break;
+ case IntrinsicDataType::kFloatVector:
+ if (!type->is_float_vector()) {
+ impl->add_error(source, "incorrect type for " + name +
+ ". Requires float vector value");
+ return false;
+ }
+ if (vector_size > 0 && vector_size != type->As<type::Vector>()->size()) {
+ impl->add_error(source, "incorrect vector size for " + name +
+ ". Requires " +
+ std::to_string(vector_size) + " elements");
+ return false;
+ }
+ break;
+ case IntrinsicDataType::kFloatScalar:
+ if (!type->Is<type::F32>()) {
+ impl->add_error(source, "incorrect type for " + name +
+ ". Requires float scalar value");
+ return false;
+ }
+ break;
+ case IntrinsicDataType::kMatrix:
+ if (!type->Is<type::Matrix>()) {
+ impl->add_error(
+ source, "incorrect type for " + name + ". Requires matrix value");
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
+} // namespace
+
ValidatorImpl::ValidatorImpl(const Program* program) : program_(program) {}
ValidatorImpl::~ValidatorImpl() = default;
@@ -406,10 +576,145 @@
}
if (auto* ident = expr->func()->As<ast::IdentifierExpression>()) {
+ auto symbol = ident->symbol();
if (ident->IsIntrinsic()) {
- // TODO(sarahM0): validate intrinsics - tied with type-determiner
+ const IntrinsicData* data = nullptr;
+ for (uint32_t i = 0; i < kIntrinsicDataCount; ++i) {
+ if (ident->intrinsic() == kIntrinsicData[i].intrinsic) {
+ data = &kIntrinsicData[i];
+ break;
+ }
+ }
+
+ if (data != nullptr) {
+ const auto builtin = program_->Symbols().NameFor(symbol);
+ if (expr->params().size() != data->param_count) {
+ add_error(expr->source(),
+ "incorrect number of parameters for " + builtin +
+ " expected " + std::to_string(data->param_count) +
+ " got " + std::to_string(expr->params().size()));
+ return false;
+ }
+
+ if (data->all_types_match) {
+ // Check that the type is an acceptable one.
+ if (!IsValidType(program_->TypeOf(expr->func()), expr->source(),
+ builtin, data->data_type, data->vector_size, this)) {
+ return false;
+ }
+
+ // Check that all params match the result type.
+ for (uint32_t i = 0; i < data->param_count; ++i) {
+ if (program_->TypeOf(expr->func())->UnwrapPtrIfNeeded() !=
+ program_->TypeOf(expr->params()[i])->UnwrapPtrIfNeeded()) {
+ add_error(expr->params()[i]->source(),
+ "expected parameter " + std::to_string(i) +
+ "'s unwrapped type to match result type for " +
+ builtin);
+ return false;
+ }
+ }
+ } else {
+ if (data->data_type != IntrinsicDataType::kMixed) {
+ auto* p0 = expr->params()[0];
+ if (!IsValidType(program_->TypeOf(p0), p0->source(), builtin,
+ data->data_type, data->vector_size, this)) {
+ return false;
+ }
+
+ // Check that parameters are valid types.
+ for (uint32_t i = 1; i < expr->params().size(); ++i) {
+ if (program_->TypeOf(p0)->UnwrapPtrIfNeeded() !=
+ program_->TypeOf(expr->params()[i])->UnwrapPtrIfNeeded()) {
+ add_error(
+ expr->source(),
+ "parameter " + std::to_string(i) +
+ "'s unwrapped type must match parameter 0's type");
+ return false;
+ }
+ }
+ } else {
+ // Special cases.
+ if (data->intrinsic == ast::Intrinsic::kFrexp) {
+ auto* p0 = expr->params()[0];
+ auto* p1 = expr->params()[1];
+ auto* t0 = program_->TypeOf(p0)->UnwrapPtrIfNeeded();
+ auto* t1 = program_->TypeOf(p1)->UnwrapPtrIfNeeded();
+ if (!IsValidType(t0, p0->source(), builtin,
+ IntrinsicDataType::kFloatScalarOrVector, 0,
+ this)) {
+ return false;
+ }
+ if (!IsValidType(t1, p1->source(), builtin,
+ IntrinsicDataType::kIntScalarOrVector, 0,
+ this)) {
+ return false;
+ }
+
+ if (t0->is_scalar()) {
+ if (!t1->is_scalar()) {
+ add_error(
+ expr->source(),
+ "incorrect types for " + builtin +
+ ". Parameters must be matched scalars or vectors");
+ return false;
+ }
+ } else {
+ if (t1->is_integer_scalar()) {
+ add_error(
+ expr->source(),
+ "incorrect types for " + builtin +
+ ". Parameters must be matched scalars or vectors");
+ return false;
+ }
+ const auto* v0 = t0->As<type::Vector>();
+ const auto* v1 = t1->As<type::Vector>();
+ if (v0->size() != v1->size()) {
+ add_error(expr->source(),
+ "incorrect types for " + builtin +
+ ". Parameter vector sizes must match");
+ return false;
+ }
+ }
+ }
+ }
+
+ // Result types don't match parameter types.
+ if (data->intrinsic == ast::Intrinsic::kLength ||
+ data->intrinsic == ast::Intrinsic::kDistance ||
+ data->intrinsic == ast::Intrinsic::kDeterminant) {
+ if (!IsValidType(program_->TypeOf(expr->func()), expr->source(),
+ builtin, IntrinsicDataType::kFloatScalar, 0,
+ this)) {
+ return false;
+ }
+ }
+
+ // Must be a square matrix.
+ if (data->intrinsic == ast::Intrinsic::kDeterminant) {
+ const auto* matrix =
+ program_->TypeOf(expr->params()[0])->As<type::Matrix>();
+ if (matrix->rows() != matrix->columns()) {
+ add_error(expr->params()[0]->source(),
+ "incorrect type for " + builtin +
+ ". Requires a square matrix");
+ return false;
+ }
+ }
+ }
+
+ // Last parameter must be a pointer.
+ if (data->intrinsic == ast::Intrinsic::kFrexp ||
+ data->intrinsic == ast::Intrinsic::kModf) {
+ auto* last_param = expr->params()[data->param_count - 1];
+ if (!program_->TypeOf(last_param)->Is<type::Pointer>()) {
+ add_error(last_param->source(), "incorrect type for " + builtin +
+ ". Requires pointer value");
+ return false;
+ }
+ }
+ }
} else {
- auto symbol = ident->symbol();
if (!function_stack_.has(symbol)) {
add_error(expr->source(), "v-0005",
"function must be declared before use: '" +