tint/resolver: Support error cases with const-eval builtin tests
Also: Print unrepresentable numbers with higher precision - otherwise values can round, and diagnostics can be very confusing. Improve diagnostic distinction between `( )` `[ ]` interval ranges. Change-Id: I9269fbf1738f0bce5f2ddb5a387687543fd5d0bb Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/108700 Commit-Queue: Antonio Maiorano <amaiorano@google.com> Kokoro: Kokoro <noreply+kokoro@google.com> Reviewed-by: Antonio Maiorano <amaiorano@google.com>
This commit is contained in:
parent
a39384133f
commit
b6903295a8
|
@ -15,6 +15,7 @@
|
||||||
#include "src/tint/resolver/const_eval.h"
|
#include "src/tint/resolver/const_eval.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <iomanip>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -187,14 +188,24 @@ inline bool IsPositiveZero(T value) {
|
||||||
template <typename NumberT>
|
template <typename NumberT>
|
||||||
std::string OverflowErrorMessage(NumberT lhs, const char* op, NumberT rhs) {
|
std::string OverflowErrorMessage(NumberT lhs, const char* op, NumberT rhs) {
|
||||||
std::stringstream ss;
|
std::stringstream ss;
|
||||||
|
ss << std::setprecision(20);
|
||||||
ss << "'" << lhs.value << " " << op << " " << rhs.value << "' cannot be represented as '"
|
ss << "'" << lhs.value << " " << op << " " << rhs.value << "' cannot be represented as '"
|
||||||
<< FriendlyName<NumberT>() << "'";
|
<< FriendlyName<NumberT>() << "'";
|
||||||
return ss.str();
|
return ss.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename VALUE_TY>
|
||||||
|
std::string OverflowErrorMessage(VALUE_TY value, std::string_view target_ty) {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << std::setprecision(20);
|
||||||
|
ss << "value " << value << " cannot be represented as "
|
||||||
|
<< "'" << target_ty << "'";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
/// @returns the number of consecutive leading bits in `@p e` set to `@p bit_value_to_count`.
|
/// @returns the number of consecutive leading bits in `@p e` set to `@p bit_value_to_count`.
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto CountLeadingBits(T e, T bit_value_to_count) -> std::make_unsigned_t<T> {
|
std::make_unsigned_t<T> CountLeadingBits(T e, T bit_value_to_count) {
|
||||||
using UT = std::make_unsigned_t<T>;
|
using UT = std::make_unsigned_t<T>;
|
||||||
constexpr UT kNumBits = sizeof(UT) * 8;
|
constexpr UT kNumBits = sizeof(UT) * 8;
|
||||||
constexpr UT kLeftMost = UT{1} << (kNumBits - 1);
|
constexpr UT kLeftMost = UT{1} << (kNumBits - 1);
|
||||||
|
@ -211,7 +222,7 @@ auto CountLeadingBits(T e, T bit_value_to_count) -> std::make_unsigned_t<T> {
|
||||||
|
|
||||||
/// @returns the number of consecutive trailing bits set to `@p bit_value_to_count` in `@p e`
|
/// @returns the number of consecutive trailing bits set to `@p bit_value_to_count` in `@p e`
|
||||||
template <typename T>
|
template <typename T>
|
||||||
auto CountTrailingBits(T e, T bit_value_to_count) -> std::make_unsigned_t<T> {
|
std::make_unsigned_t<T> CountTrailingBits(T e, T bit_value_to_count) {
|
||||||
using UT = std::make_unsigned_t<T>;
|
using UT = std::make_unsigned_t<T>;
|
||||||
constexpr UT kNumBits = sizeof(UT) * 8;
|
constexpr UT kNumBits = sizeof(UT) * 8;
|
||||||
constexpr UT kRightMost = UT{1};
|
constexpr UT kRightMost = UT{1};
|
||||||
|
@ -292,10 +303,9 @@ struct Element : ImplConstant {
|
||||||
// --- Below this point are the failure cases ---
|
// --- Below this point are the failure cases ---
|
||||||
} else if constexpr (IsAbstract<FROM>) {
|
} else if constexpr (IsAbstract<FROM>) {
|
||||||
// [abstract-numeric -> x] - materialization failure
|
// [abstract-numeric -> x] - materialization failure
|
||||||
std::stringstream ss;
|
builder.Diagnostics().add_error(
|
||||||
ss << "value " << value << " cannot be represented as ";
|
tint::diag::System::Resolver,
|
||||||
ss << "'" << builder.FriendlyName(target_ty) << "'";
|
OverflowErrorMessage(value, builder.FriendlyName(target_ty)), source);
|
||||||
builder.Diagnostics().add_error(tint::diag::System::Resolver, ss.str(), source);
|
|
||||||
return utils::Failure;
|
return utils::Failure;
|
||||||
} else if constexpr (IsFloatingPoint<TO>) {
|
} else if constexpr (IsFloatingPoint<TO>) {
|
||||||
// [x -> floating-point] - number not exactly representable
|
// [x -> floating-point] - number not exactly representable
|
||||||
|
@ -1602,7 +1612,7 @@ ConstEval::Result ConstEval::acos(const sem::Type* ty,
|
||||||
auto create = [&](auto i) -> ImplResult {
|
auto create = [&](auto i) -> ImplResult {
|
||||||
using NumberT = decltype(i);
|
using NumberT = decltype(i);
|
||||||
if (i < NumberT(-1.0) || i > NumberT(1.0)) {
|
if (i < NumberT(-1.0) || i > NumberT(1.0)) {
|
||||||
AddError("acos must be called with a value in the range [-1, 1]", source);
|
AddError("acos must be called with a value in the range [-1 .. 1] (inclusive)", source);
|
||||||
return utils::Failure;
|
return utils::Failure;
|
||||||
}
|
}
|
||||||
return CreateElement(builder, c0->Type(), NumberT(std::acos(i.value)));
|
return CreateElement(builder, c0->Type(), NumberT(std::acos(i.value)));
|
||||||
|
@ -1631,7 +1641,7 @@ ConstEval::Result ConstEval::asin(const sem::Type* ty,
|
||||||
auto create = [&](auto i) -> ImplResult {
|
auto create = [&](auto i) -> ImplResult {
|
||||||
using NumberT = decltype(i);
|
using NumberT = decltype(i);
|
||||||
if (i < NumberT(-1.0) || i > NumberT(1.0)) {
|
if (i < NumberT(-1.0) || i > NumberT(1.0)) {
|
||||||
AddError("asin must be called with a value in the range [-1, 1]", source);
|
AddError("asin must be called with a value in the range [-1 .. 1] (inclusive)", source);
|
||||||
return utils::Failure;
|
return utils::Failure;
|
||||||
}
|
}
|
||||||
return CreateElement(builder, c0->Type(), NumberT(std::asin(i.value)));
|
return CreateElement(builder, c0->Type(), NumberT(std::asin(i.value)));
|
||||||
|
@ -1677,7 +1687,7 @@ ConstEval::Result ConstEval::atanh(const sem::Type* ty,
|
||||||
auto create = [&](auto i) -> ImplResult {
|
auto create = [&](auto i) -> ImplResult {
|
||||||
using NumberT = decltype(i);
|
using NumberT = decltype(i);
|
||||||
if (i <= NumberT(-1.0) || i >= NumberT(1.0)) {
|
if (i <= NumberT(-1.0) || i >= NumberT(1.0)) {
|
||||||
AddError("atanh must be called with a value in the range (-1, 1)", source);
|
AddError("atanh must be called with a value in the range (-1 .. 1) (exclusive)", source);
|
||||||
return utils::Failure;
|
return utils::Failure;
|
||||||
}
|
}
|
||||||
return CreateElement(builder, c0->Type(), NumberT(std::atanh(i.value)));
|
return CreateElement(builder, c0->Type(), NumberT(std::atanh(i.value)));
|
||||||
|
|
|
@ -853,7 +853,7 @@ TEST_F(ResolverConstEvalTest, BinaryAbstractAddOverflow_AFloat) {
|
||||||
GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Highest()), AFloat::Highest()));
|
GlobalConst("c", Add(Source{{1, 1}}, Expr(AFloat::Highest()), AFloat::Highest()));
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"1:1 error: '1.79769e+308 + 1.79769e+308' cannot be represented as 'abstract-float'");
|
"1:1 error: '1.7976931348623157081e+308 + 1.7976931348623157081e+308' cannot be represented as 'abstract-float'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AFloat) {
|
TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AFloat) {
|
||||||
|
@ -861,7 +861,7 @@ TEST_F(ResolverConstEvalTest, BinaryAbstractAddUnderflow_AFloat) {
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(
|
EXPECT_EQ(
|
||||||
r()->error(),
|
r()->error(),
|
||||||
"1:1 error: '-1.79769e+308 + -1.79769e+308' cannot be represented as 'abstract-float'");
|
"1:1 error: '-1.7976931348623157081e+308 + -1.7976931348623157081e+308' cannot be represented as 'abstract-float'");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Mixed AInt and AFloat args to test implicit conversion to AFloat
|
// Mixed AInt and AFloat args to test implicit conversion to AFloat
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
|
|
||||||
#include "src/tint/resolver/const_eval_test.h"
|
#include "src/tint/resolver/const_eval_test.h"
|
||||||
|
|
||||||
|
#include "src/tint/utils/result.h"
|
||||||
|
|
||||||
using namespace tint::number_suffixes; // NOLINT
|
using namespace tint::number_suffixes; // NOLINT
|
||||||
|
|
||||||
namespace tint::resolver {
|
namespace tint::resolver {
|
||||||
|
@ -23,25 +25,39 @@ namespace {
|
||||||
using resolver::operator<<;
|
using resolver::operator<<;
|
||||||
|
|
||||||
struct Case {
|
struct Case {
|
||||||
Case(utils::VectorRef<Types> in_args, Types in_expected)
|
Case(utils::VectorRef<Types> in_args, Types expected_value)
|
||||||
: args(std::move(in_args)), expected(std::move(in_expected)) {}
|
: args(std::move(in_args)), expected(Success{std::move(expected_value), false, false}) {}
|
||||||
|
|
||||||
|
Case(utils::VectorRef<Types> in_args, const char* expected_err)
|
||||||
|
: args(std::move(in_args)), expected(Failure{expected_err}) {}
|
||||||
|
|
||||||
/// Expected value may be positive or negative
|
/// Expected value may be positive or negative
|
||||||
Case& PosOrNeg() {
|
Case& PosOrNeg() {
|
||||||
expected_pos_or_neg = true;
|
Success s = expected.Get();
|
||||||
|
s.pos_or_neg = true;
|
||||||
|
expected = s;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Expected value should be compared using FLOAT_EQ instead of EQ
|
/// Expected value should be compared using FLOAT_EQ instead of EQ
|
||||||
Case& FloatComp() {
|
Case& FloatComp() {
|
||||||
float_compare = true;
|
Success s = expected.Get();
|
||||||
|
s.float_compare = true;
|
||||||
|
expected = s;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
utils::Vector<Types, 8> args;
|
struct Success {
|
||||||
Types expected;
|
Types value;
|
||||||
bool expected_pos_or_neg = false;
|
bool pos_or_neg = false;
|
||||||
bool float_compare = false;
|
bool float_compare = false;
|
||||||
|
};
|
||||||
|
struct Failure {
|
||||||
|
const char* error = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
utils::Vector<Types, 8> args;
|
||||||
|
utils::Result<Success, Failure> expected;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::ostream& operator<<(std::ostream& o, const Case& c) {
|
static std::ostream& operator<<(std::ostream& o, const Case& c) {
|
||||||
|
@ -49,17 +65,24 @@ static std::ostream& operator<<(std::ostream& o, const Case& c) {
|
||||||
for (auto& a : c.args) {
|
for (auto& a : c.args) {
|
||||||
o << a << ", ";
|
o << a << ", ";
|
||||||
}
|
}
|
||||||
o << "expected: " << c.expected << ", expected_pos_or_neg: " << c.expected_pos_or_neg;
|
o << "expected: ";
|
||||||
|
if (c.expected) {
|
||||||
|
auto s = c.expected.Get();
|
||||||
|
o << s.value << ", pos_or_neg: " << s.pos_or_neg;
|
||||||
|
} else {
|
||||||
|
o << "[ERROR: " << c.expected.Failure().error << "]";
|
||||||
|
}
|
||||||
return o;
|
return o;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
|
||||||
|
|
||||||
/// Creates a Case with Values for args and result
|
/// Creates a Case with Values for args and result
|
||||||
static Case C(std::initializer_list<Types> args, Types result) {
|
static Case C(std::initializer_list<Types> args, Types result) {
|
||||||
return Case{utils::Vector<Types, 8>{args}, std::move(result)};
|
return Case{utils::Vector<Types, 8>{args}, std::move(result)};
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience overload that creates a Case with just scalars
|
/// Convenience overload that creates a Case with just scalars
|
||||||
using ScalarTypes = std::variant<AInt, AFloat, u32, i32, f32, f16>;
|
|
||||||
static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
|
static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
|
||||||
utils::Vector<Types, 8> args;
|
utils::Vector<Types, 8> args;
|
||||||
for (auto& sa : sargs) {
|
for (auto& sa : sargs) {
|
||||||
|
@ -70,6 +93,20 @@ static Case C(std::initializer_list<ScalarTypes> sargs, ScalarTypes sresult) {
|
||||||
return Case{std::move(args), std::move(result)};
|
return Case{std::move(args), std::move(result)};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Creates a Case with Values for args and expected error
|
||||||
|
static Case E(std::initializer_list<Types> args, const char* err) {
|
||||||
|
return Case{utils::Vector<Types, 8>{args}, err};
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Convenience overload that creates an expected-error Case with just scalars
|
||||||
|
static Case E(std::initializer_list<ScalarTypes> sargs, const char* err) {
|
||||||
|
utils::Vector<Types, 8> args;
|
||||||
|
for (auto& sa : sargs) {
|
||||||
|
std::visit([&](auto&& v) { return args.Push(Val(v)); }, sa);
|
||||||
|
}
|
||||||
|
return Case{std::move(args), err};
|
||||||
|
}
|
||||||
|
|
||||||
using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
|
using ResolverConstEvalBuiltinTest = ResolverTestWithParam<std::tuple<sem::BuiltinType, Case>>;
|
||||||
|
|
||||||
TEST_P(ResolverConstEvalBuiltinTest, Test) {
|
TEST_P(ResolverConstEvalBuiltinTest, Test) {
|
||||||
|
@ -83,11 +120,14 @@ TEST_P(ResolverConstEvalBuiltinTest, Test) {
|
||||||
std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
|
std::visit([&](auto&& v) { args.Push(v.Expr(*this)); }, a);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto* expected = ToValueBase(c.expected);
|
auto* expr = Call(Source{{12, 34}}, sem::str(builtin), std::move(args));
|
||||||
auto* expr = Call(sem::str(builtin), std::move(args));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
GlobalConst("C", expr);
|
||||||
auto* expected_expr = expected->Expr(*this);
|
|
||||||
|
if (c.expected) {
|
||||||
|
auto expected = c.expected.Get();
|
||||||
|
|
||||||
|
auto* expected_expr = ToValueBase(expected.value)->Expr(*this);
|
||||||
GlobalConst("E", expected_expr);
|
GlobalConst("E", expected_expr);
|
||||||
|
|
||||||
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
ASSERT_TRUE(r()->Resolve()) << r()->error();
|
||||||
|
@ -117,24 +157,28 @@ TEST_P(ResolverConstEvalBuiltinTest, Test) {
|
||||||
if (std::isnan(e)) {
|
if (std::isnan(e)) {
|
||||||
EXPECT_TRUE(std::isnan(v));
|
EXPECT_TRUE(std::isnan(v));
|
||||||
} else {
|
} else {
|
||||||
auto vf = (c.expected_pos_or_neg ? Abs(v) : v);
|
auto vf = (expected.pos_or_neg ? Abs(v) : v);
|
||||||
if (c.float_compare) {
|
if (expected.float_compare) {
|
||||||
EXPECT_FLOAT_EQ(vf, e);
|
EXPECT_FLOAT_EQ(vf, e);
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ(vf, e);
|
EXPECT_EQ(vf, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
EXPECT_EQ((c.expected_pos_or_neg ? Abs(v) : v), e);
|
EXPECT_EQ((expected.pos_or_neg ? Abs(v) : v), e);
|
||||||
// Check that the constant's integer doesn't contain unexpected
|
// Check that the constant's integer doesn't contain unexpected
|
||||||
// data in the MSBs that are outside of the bit-width of T.
|
// data in the MSBs that are outside of the bit-width of T.
|
||||||
EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
|
EXPECT_EQ(a->As<AInt>(), b->As<AInt>());
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
c.expected);
|
expected.value);
|
||||||
|
|
||||||
return HasFailure() ? Action::kStop : Action::kContinue;
|
return HasFailure() ? Action::kStop : Action::kContinue;
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
EXPECT_FALSE(r()->Resolve());
|
||||||
|
EXPECT_EQ(r()->error(), c.expected.Failure().error);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_SUITE_P( //
|
INSTANTIATE_TEST_SUITE_P( //
|
||||||
|
@ -374,6 +418,19 @@ std::vector<Case> AtanhCases() {
|
||||||
C({Vec(T(0.0), T(0.9), -T(0.9))}, Vec(T(0.0), T(1.4722193), -T(1.4722193))).FloatComp(),
|
C({Vec(T(0.0), T(0.9), -T(0.9))}, Vec(T(0.0), T(1.4722193), -T(1.4722193))).FloatComp(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConcatIntoIf<finite_only>( //
|
||||||
|
cases,
|
||||||
|
std::vector<Case>{
|
||||||
|
E({1.1_a},
|
||||||
|
"12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
|
||||||
|
E({-1.1_a},
|
||||||
|
"12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
|
||||||
|
E({T::Inf()},
|
||||||
|
"12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
|
||||||
|
E({-T::Inf()},
|
||||||
|
"12:34 error: atanh must be called with a value in the range (-1 .. 1) (exclusive)"),
|
||||||
|
});
|
||||||
|
|
||||||
ConcatIntoIf<!finite_only>( //
|
ConcatIntoIf<!finite_only>( //
|
||||||
cases, std::vector<Case>{
|
cases, std::vector<Case>{
|
||||||
// If i is NaN, NaN is returned
|
// If i is NaN, NaN is returned
|
||||||
|
@ -393,38 +450,6 @@ INSTANTIATE_TEST_SUITE_P( //
|
||||||
AtanhCases<f32, false>(),
|
AtanhCases<f32, false>(),
|
||||||
AtanhCases<f16, false>()))));
|
AtanhCases<f16, false>()))));
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Positive) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "atanh", Expr(1.0_a));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Negative) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "atanh", Negation(1.0_a));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Positive_INF) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "atanh", Expr(f32::Inf()));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Atanh_OutsideRange_Negative_INF) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "atanh", Negation(f32::Inf()));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: atanh must be called with a value in the range (-1, 1)");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, bool finite_only>
|
template <typename T, bool finite_only>
|
||||||
std::vector<Case> AcosCases() {
|
std::vector<Case> AcosCases() {
|
||||||
std::vector<Case> cases = {
|
std::vector<Case> cases = {
|
||||||
|
@ -438,6 +463,19 @@ std::vector<Case> AcosCases() {
|
||||||
C({Vec(T(1.0), -T(1.0))}, Vec(T(0), kPi<T>)).FloatComp(),
|
C({Vec(T(1.0), -T(1.0))}, Vec(T(0), kPi<T>)).FloatComp(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConcatIntoIf<finite_only>( //
|
||||||
|
cases,
|
||||||
|
std::vector<Case>{
|
||||||
|
E({1.1_a},
|
||||||
|
"12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
E({-1.1_a},
|
||||||
|
"12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
E({T::Inf()},
|
||||||
|
"12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
E({-T::Inf()},
|
||||||
|
"12:34 error: acos must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
});
|
||||||
|
|
||||||
ConcatIntoIf<!finite_only>( //
|
ConcatIntoIf<!finite_only>( //
|
||||||
cases, std::vector<Case>{
|
cases, std::vector<Case>{
|
||||||
// If i is NaN, NaN is returned
|
// If i is NaN, NaN is returned
|
||||||
|
@ -457,38 +495,6 @@ INSTANTIATE_TEST_SUITE_P( //
|
||||||
AcosCases<f32, false>(),
|
AcosCases<f32, false>(),
|
||||||
AcosCases<f16, false>()))));
|
AcosCases<f16, false>()))));
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Acos_OutsideRange_Positive) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "acos", Expr(1.1_a));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: acos must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Acos_OutsideRange_Negative) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "acos", Negation(1.1_a));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: acos must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Acos_OutsideRange_Positive_INF) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "acos", Expr(f32::Inf()));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: acos must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Acos_OutsideRange_Negative_INF) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "acos", Negation(f32::Inf()));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: acos must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, bool finite_only>
|
template <typename T, bool finite_only>
|
||||||
std::vector<Case> AsinCases() {
|
std::vector<Case> AsinCases() {
|
||||||
std::vector<Case> cases = {
|
std::vector<Case> cases = {
|
||||||
|
@ -503,6 +509,19 @@ std::vector<Case> AsinCases() {
|
||||||
C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver2<T>, -kPiOver2<T>)).FloatComp(),
|
C({Vec(T(0.0), T(1.0), -T(1.0))}, Vec(T(0.0), kPiOver2<T>, -kPiOver2<T>)).FloatComp(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ConcatIntoIf<finite_only>( //
|
||||||
|
cases,
|
||||||
|
std::vector<Case>{
|
||||||
|
E({1.1_a},
|
||||||
|
"12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
E({-1.1_a},
|
||||||
|
"12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
E({T::Inf()},
|
||||||
|
"12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
E({-T::Inf()},
|
||||||
|
"12:34 error: asin must be called with a value in the range [-1 .. 1] (inclusive)"),
|
||||||
|
});
|
||||||
|
|
||||||
ConcatIntoIf<!finite_only>( //
|
ConcatIntoIf<!finite_only>( //
|
||||||
cases, std::vector<Case>{
|
cases, std::vector<Case>{
|
||||||
// If i is NaN, NaN is returned
|
// If i is NaN, NaN is returned
|
||||||
|
@ -522,38 +541,6 @@ INSTANTIATE_TEST_SUITE_P( //
|
||||||
AsinCases<f32, false>(),
|
AsinCases<f32, false>(),
|
||||||
AsinCases<f16, false>()))));
|
AsinCases<f16, false>()))));
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Positive) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "asin", Expr(1.1_a));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Negative) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "asin", Negation(1.1_a));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Positive_INF) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "asin", Expr(f32::Inf()));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(ResolverConstEvalBuiltinTest, Asin_OutsideRange_Negative_INF) {
|
|
||||||
auto* expr = Call(Source{{12, 24}}, "asin", Negation(f32::Inf()));
|
|
||||||
|
|
||||||
GlobalConst("C", expr);
|
|
||||||
EXPECT_FALSE(r()->Resolve());
|
|
||||||
EXPECT_EQ(r()->error(), "12:24 error: asin must be called with a value in the range [-1, 1]");
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T, bool finite_only>
|
template <typename T, bool finite_only>
|
||||||
std::vector<Case> AsinhCases() {
|
std::vector<Case> AsinhCases() {
|
||||||
std::vector<Case> cases = {
|
std::vector<Case> cases = {
|
||||||
|
@ -980,12 +967,12 @@ using ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount =
|
||||||
ResolverTestWithParam<std::tuple<size_t, size_t>>;
|
ResolverTestWithParam<std::tuple<size_t, size_t>>;
|
||||||
TEST_P(ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount, Test) {
|
TEST_P(ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount, Test) {
|
||||||
auto& p = GetParam();
|
auto& p = GetParam();
|
||||||
auto* expr = Call(Source{{12, 24}}, sem::str(sem::BuiltinType::kInsertBits), Expr(1_u),
|
auto* expr = Call(Source{{12, 34}}, sem::str(sem::BuiltinType::kInsertBits), Expr(1_u),
|
||||||
Expr(1_u), Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p))));
|
Expr(1_u), Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p))));
|
||||||
GlobalConst("C", expr);
|
GlobalConst("C", expr);
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:24 error: 'offset + 'count' must be less than or equal to the bit width of 'e'");
|
"12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'");
|
||||||
}
|
}
|
||||||
INSTANTIATE_TEST_SUITE_P(InsertBits,
|
INSTANTIATE_TEST_SUITE_P(InsertBits,
|
||||||
ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount,
|
ResolverConstEvalBuiltinTest_InsertBits_InvalidOffsetAndCount,
|
||||||
|
@ -1083,12 +1070,12 @@ using ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount =
|
||||||
ResolverTestWithParam<std::tuple<size_t, size_t>>;
|
ResolverTestWithParam<std::tuple<size_t, size_t>>;
|
||||||
TEST_P(ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount, Test) {
|
TEST_P(ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount, Test) {
|
||||||
auto& p = GetParam();
|
auto& p = GetParam();
|
||||||
auto* expr = Call(Source{{12, 24}}, sem::str(sem::BuiltinType::kExtractBits), Expr(1_u),
|
auto* expr = Call(Source{{12, 34}}, sem::str(sem::BuiltinType::kExtractBits), Expr(1_u),
|
||||||
Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p))));
|
Expr(u32(std::get<0>(p))), Expr(u32(std::get<1>(p))));
|
||||||
GlobalConst("C", expr);
|
GlobalConst("C", expr);
|
||||||
EXPECT_FALSE(r()->Resolve());
|
EXPECT_FALSE(r()->Resolve());
|
||||||
EXPECT_EQ(r()->error(),
|
EXPECT_EQ(r()->error(),
|
||||||
"12:24 error: 'offset + 'count' must be less than or equal to the bit width of 'e'");
|
"12:34 error: 'offset + 'count' must be less than or equal to the bit width of 'e'");
|
||||||
}
|
}
|
||||||
INSTANTIATE_TEST_SUITE_P(ExtractBits,
|
INSTANTIATE_TEST_SUITE_P(ExtractBits,
|
||||||
ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount,
|
ResolverConstEvalBuiltinTest_ExtractBits_InvalidOffsetAndCount,
|
||||||
|
@ -1282,6 +1269,7 @@ INSTANTIATE_TEST_SUITE_P( //
|
||||||
StepCases<f16>()))));
|
StepCases<f16>()))));
|
||||||
|
|
||||||
std::vector<Case> QuantizeToF16Cases() {
|
std::vector<Case> QuantizeToF16Cases() {
|
||||||
|
(void)E({Vec(0_f, 0_f)}, ""); // Currently unused, but will be soon.
|
||||||
return {
|
return {
|
||||||
C({0_f}, 0_f), //
|
C({0_f}, 0_f), //
|
||||||
C({-0_f}, -0_f), //
|
C({-0_f}, -0_f), //
|
||||||
|
|
Loading…
Reference in New Issue