tint/reader/wgsl: Lex abstract hex floats
Scale up the hex float parsing to support doubles. Also make it an error if the hex float resolves to an inf or NaN. See: https://github.com/gpuweb/gpuweb/issues/2953 Bug: tint:1504 Change-Id: Ie923bb5f660285fb7563cfe8dafa07d87b898a7e Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91426 Kokoro: Kokoro <noreply+kokoro@google.com> Commit-Queue: Ben Clayton <bclayton@chromium.org> Reviewed-by: David Neto <dneto@google.com>
This commit is contained in:
parent
f45180a428
commit
30f01c1790
|
@ -387,17 +387,18 @@ Token Lexer::try_float() {
|
|||
}
|
||||
|
||||
Token Lexer::try_hex_float() {
|
||||
constexpr uint32_t kExponentBits = 8;
|
||||
constexpr uint32_t kMantissaBits = 23;
|
||||
constexpr uint32_t kTotalBits = 1 + kExponentBits + kMantissaBits;
|
||||
constexpr uint32_t kTotalMsb = kTotalBits - 1;
|
||||
constexpr uint32_t kMantissaMsb = kMantissaBits - 1;
|
||||
constexpr uint32_t kMantissaShiftRight = kTotalBits - kMantissaBits;
|
||||
constexpr int32_t kExponentBias = 127;
|
||||
constexpr uint32_t kExponentMask = (1 << kExponentBits) - 1;
|
||||
constexpr int32_t kExponentMax = kExponentMask; // Including NaN / inf
|
||||
constexpr uint32_t kExponentLeftShift = kMantissaBits;
|
||||
constexpr uint32_t kSignBit = kTotalBits - 1;
|
||||
constexpr uint64_t kExponentBits = 11;
|
||||
constexpr uint64_t kMantissaBits = 52;
|
||||
constexpr uint64_t kTotalBits = 1 + kExponentBits + kMantissaBits;
|
||||
constexpr uint64_t kTotalMsb = kTotalBits - 1;
|
||||
constexpr uint64_t kMantissaMsb = kMantissaBits - 1;
|
||||
constexpr uint64_t kMantissaShiftRight = kTotalBits - kMantissaBits;
|
||||
constexpr int64_t kExponentBias = 1023;
|
||||
constexpr uint64_t kExponentMask = (1 << kExponentBits) - 1;
|
||||
constexpr int64_t kExponentMax = kExponentMask; // Including NaN / inf
|
||||
constexpr uint64_t kExponentLeftShift = kMantissaBits;
|
||||
constexpr uint64_t kSignBit = kTotalBits - 1;
|
||||
constexpr uint64_t kOne = 1;
|
||||
|
||||
auto start = pos();
|
||||
auto end = pos();
|
||||
|
@ -409,7 +410,7 @@ Token Lexer::try_hex_float() {
|
|||
// clang-format on
|
||||
|
||||
// -?
|
||||
int32_t sign_bit = 0;
|
||||
int64_t sign_bit = 0;
|
||||
if (matches(end, "-")) {
|
||||
sign_bit = 1;
|
||||
end++;
|
||||
|
@ -421,8 +422,8 @@ Token Lexer::try_hex_float() {
|
|||
return {};
|
||||
}
|
||||
|
||||
uint32_t mantissa = 0;
|
||||
uint32_t exponent = 0;
|
||||
uint64_t mantissa = 0;
|
||||
uint64_t exponent = 0;
|
||||
|
||||
// TODO(dneto): Values in the normal range for the format do not explicitly
|
||||
// store the most significant bit. The algorithm here works hard to eliminate
|
||||
|
@ -435,7 +436,7 @@ Token Lexer::try_hex_float() {
|
|||
// `set_next_mantissa_bit_to` sets next `mantissa` bit starting from msb to
|
||||
// lsb to value 1 if `set` is true, 0 otherwise. Returns true on success, i.e.
|
||||
// when the bit can be accommodated in the available space.
|
||||
uint32_t mantissa_next_bit = kTotalMsb;
|
||||
uint64_t mantissa_next_bit = kTotalMsb;
|
||||
auto set_next_mantissa_bit_to = [&](bool set, bool integer_part) -> bool {
|
||||
// If adding bits for the integer part, we can overflow whether we set the
|
||||
// bit or not. For the fractional part, we can only overflow when setting
|
||||
|
@ -447,7 +448,7 @@ Token Lexer::try_hex_float() {
|
|||
return false; // Overflowed mantissa
|
||||
}
|
||||
if (set) {
|
||||
mantissa |= (1 << mantissa_next_bit);
|
||||
mantissa |= (kOne << mantissa_next_bit);
|
||||
}
|
||||
--mantissa_next_bit;
|
||||
return true;
|
||||
|
@ -503,7 +504,7 @@ Token Lexer::try_hex_float() {
|
|||
has_zero_integer = false;
|
||||
}
|
||||
|
||||
for (int32_t bit = 3; bit >= 0; --bit) {
|
||||
for (int bit = 3; bit >= 0; --bit) {
|
||||
auto v = 1 & (nibble >> bit);
|
||||
|
||||
// Skip leading 0s and the first 1
|
||||
|
@ -524,7 +525,7 @@ Token Lexer::try_hex_float() {
|
|||
// [0-9a-fA-F]*
|
||||
for (auto i = fractional_range.first; i < fractional_range.second; ++i) {
|
||||
auto nibble = hex_value(at(i));
|
||||
for (int32_t bit = 3; bit >= 0; --bit) {
|
||||
for (int bit = 3; bit >= 0; --bit) {
|
||||
auto v = 1 & (nibble >> bit);
|
||||
|
||||
if (v == 1) {
|
||||
|
@ -552,8 +553,8 @@ Token Lexer::try_hex_float() {
|
|||
|
||||
// Parse the optional exponent.
|
||||
// ((p|P)(\+|-)?[0-9]+)?
|
||||
uint32_t input_exponent = 0; // Defaults to 0 if not present
|
||||
int32_t exponent_sign = 1;
|
||||
uint64_t input_exponent = 0; // Defaults to 0 if not present
|
||||
int64_t exponent_sign = 1;
|
||||
// If the 'p' part is present, the rest of the exponent must exist.
|
||||
bool has_f_suffix = false;
|
||||
if (has_exponent) {
|
||||
|
@ -568,7 +569,7 @@ Token Lexer::try_hex_float() {
|
|||
|
||||
// Parse exponent from input
|
||||
// [0-9]+
|
||||
// Allow overflow (in uint32_t) when the floating point value magnitude is
|
||||
// Allow overflow (in uint64_t) when the floating point value magnitude is
|
||||
// zero.
|
||||
bool has_exponent_digits = false;
|
||||
while (end < length() && isdigit(at(end))) {
|
||||
|
@ -605,14 +606,14 @@ Token Lexer::try_hex_float() {
|
|||
} else {
|
||||
// Ensure input exponent is not too large; i.e. that it won't overflow when
|
||||
// adding the exponent bias.
|
||||
const uint32_t kIntMax = static_cast<uint32_t>(std::numeric_limits<int32_t>::max());
|
||||
const uint32_t kMaxInputExponent = kIntMax - kExponentBias;
|
||||
const uint64_t kIntMax = static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
|
||||
const uint64_t kMaxInputExponent = kIntMax - kExponentBias;
|
||||
if (input_exponent > kMaxInputExponent) {
|
||||
return {Token::Type::kError, source, "exponent is too large for hex float"};
|
||||
}
|
||||
|
||||
// Compute exponent so far
|
||||
exponent += static_cast<uint32_t>(static_cast<int32_t>(input_exponent) * exponent_sign);
|
||||
exponent += static_cast<uint64_t>(static_cast<int64_t>(input_exponent) * exponent_sign);
|
||||
|
||||
// Bias exponent if non-zero
|
||||
// After this, if exponent is <= 0, our value is a denormal
|
||||
|
@ -631,7 +632,7 @@ Token Lexer::try_hex_float() {
|
|||
|
||||
// We can now safely work with exponent as a signed quantity, as there's no
|
||||
// chance to overflow
|
||||
int32_t signed_exponent = static_cast<int32_t>(exponent);
|
||||
int64_t signed_exponent = static_cast<int64_t>(exponent);
|
||||
|
||||
// Shift mantissa to occupy the low 23 bits
|
||||
mantissa >>= kMantissaShiftRight;
|
||||
|
@ -642,7 +643,7 @@ Token Lexer::try_hex_float() {
|
|||
// then shift the mantissa to make exponent zero.
|
||||
if (signed_exponent <= 0) {
|
||||
mantissa >>= 1;
|
||||
mantissa |= (1 << kMantissaMsb);
|
||||
mantissa |= (kOne << kMantissaMsb);
|
||||
}
|
||||
|
||||
while (signed_exponent < 0) {
|
||||
|
@ -656,24 +657,30 @@ Token Lexer::try_hex_float() {
|
|||
}
|
||||
}
|
||||
|
||||
if (signed_exponent > kExponentMax) {
|
||||
// Overflow: set to infinity
|
||||
signed_exponent = kExponentMax;
|
||||
mantissa = 0;
|
||||
} else if (signed_exponent == kExponentMax && mantissa != 0) {
|
||||
// NaN: set to infinity
|
||||
mantissa = 0;
|
||||
if (signed_exponent >= kExponentMax || (signed_exponent == kExponentMax && mantissa != 0)) {
|
||||
std::string type = has_f_suffix ? "f32" : "abstract-float";
|
||||
return {Token::Type::kError, source, "value cannot be represented as '" + type + "'"};
|
||||
}
|
||||
|
||||
// Combine sign, mantissa, and exponent
|
||||
uint32_t result_u32 = sign_bit << kSignBit;
|
||||
result_u32 |= mantissa;
|
||||
result_u32 |= (static_cast<uint32_t>(signed_exponent) & kExponentMask) << kExponentLeftShift;
|
||||
uint64_t result_u64 = sign_bit << kSignBit;
|
||||
result_u64 |= mantissa;
|
||||
result_u64 |= (static_cast<uint64_t>(signed_exponent) & kExponentMask) << kExponentLeftShift;
|
||||
|
||||
// Reinterpret as float and return
|
||||
float result_f32;
|
||||
std::memcpy(&result_f32, &result_u32, sizeof(result_f32));
|
||||
double result_f64 = static_cast<double>(result_f32);
|
||||
double result_f64;
|
||||
std::memcpy(&result_f64, &result_u64, 8);
|
||||
|
||||
if (has_f_suffix) {
|
||||
// Quantize to f32
|
||||
// TODO(crbug.com/tint/1564): If the hex-float value is not exactly representable then we
|
||||
// should be erroring here.
|
||||
result_f64 = static_cast<double>(static_cast<float>(result_f64));
|
||||
if (std::isinf(result_f64)) {
|
||||
return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
|
||||
}
|
||||
}
|
||||
|
||||
return {has_f_suffix ? Token::Type::kFloatLiteral_F : Token::Type::kFloatLiteral, source,
|
||||
result_f64};
|
||||
}
|
||||
|
|
|
@ -224,8 +224,12 @@ TEST_P(ParserImplFloatLiteralTest, Parse) {
|
|||
EXPECT_FALSE(c.errored);
|
||||
EXPECT_FALSE(p->has_error()) << p->error();
|
||||
ASSERT_NE(c.value, nullptr);
|
||||
ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>());
|
||||
EXPECT_DOUBLE_EQ(c->As<ast::FloatLiteralExpression>()->value, params.expected);
|
||||
auto* literal = c->As<ast::FloatLiteralExpression>();
|
||||
ASSERT_NE(literal, nullptr);
|
||||
EXPECT_DOUBLE_EQ(literal->value, params.expected)
|
||||
<< "\n"
|
||||
<< "got: " << std::hexfloat << literal->value << "\n"
|
||||
<< "expected: " << std::hexfloat << params.expected;
|
||||
if (params.input.back() == 'f') {
|
||||
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
|
||||
ast::FloatLiteralExpression::Suffix::kF);
|
||||
|
@ -290,27 +294,60 @@ FloatLiteralTestCaseList HexFloatCases() {
|
|||
{"-0x1.02p-3", 1.0 / -1024.0 - 1.0 / 8.0},
|
||||
|
||||
// Near lowest non-denorm
|
||||
{"0x1p-124", std::ldexp(1.0 * 8.0, -127)},
|
||||
{"0x1p-125", std::ldexp(1.0 * 4.0, -127)},
|
||||
{"-0x1p-124", -std::ldexp(1.0 * 8.0, -127)},
|
||||
{"-0x1p-125", -std::ldexp(1.0 * 4.0, -127)},
|
||||
{"0x1p-1020", std::ldexp(1.0 * 8.0, -1023)},
|
||||
{"0x1p-1021", std::ldexp(1.0 * 4.0, -1023)},
|
||||
{"-0x1p-1020", -std::ldexp(1.0 * 8.0, -1023)},
|
||||
{"-0x1p-1021", -std::ldexp(1.0 * 4.0, -1023)},
|
||||
|
||||
{"0x1p-124f", std::ldexp(1.0 * 8.0, -127)},
|
||||
{"0x1p-125f", std::ldexp(1.0 * 4.0, -127)},
|
||||
{"-0x1p-124f", -std::ldexp(1.0 * 8.0, -127)},
|
||||
{"-0x1p-125f", -std::ldexp(1.0 * 4.0, -127)},
|
||||
|
||||
// Lowest non-denorm
|
||||
{"0x1p-126", std::ldexp(1.0 * 2.0, -127)},
|
||||
{"-0x1p-126", -std::ldexp(1.0 * 2.0, -127)},
|
||||
{"0x1p-1022", std::ldexp(1.0 * 2.0, -1023)},
|
||||
{"-0x1p-1022", -std::ldexp(1.0 * 2.0, -1023)},
|
||||
|
||||
{"0x1p-126f", std::ldexp(1.0 * 2.0, -127)},
|
||||
{"-0x1p-126f", -std::ldexp(1.0 * 2.0, -127)},
|
||||
|
||||
// Denormalized values
|
||||
{"0x1p-127", std::ldexp(1.0, -127)},
|
||||
{"0x1p-128", std::ldexp(1.0 / 2.0, -127)},
|
||||
{"0x1p-129", std::ldexp(1.0 / 4.0, -127)},
|
||||
{"0x1p-130", std::ldexp(1.0 / 8.0, -127)},
|
||||
{"-0x1p-127", -std::ldexp(1.0, -127)},
|
||||
{"-0x1p-128", -std::ldexp(1.0 / 2.0, -127)},
|
||||
{"-0x1p-129", -std::ldexp(1.0 / 4.0, -127)},
|
||||
{"-0x1p-130", -std::ldexp(1.0 / 8.0, -127)},
|
||||
{"0x1p-1023", std::ldexp(1.0, -1023)},
|
||||
{"0x1p-1024", std::ldexp(1.0 / 2.0, -1023)},
|
||||
{"0x1p-1025", std::ldexp(1.0 / 4.0, -1023)},
|
||||
{"0x1p-1026", std::ldexp(1.0 / 8.0, -1023)},
|
||||
{"-0x1p-1023", -std::ldexp(1.0, -1023)},
|
||||
{"-0x1p-1024", -std::ldexp(1.0 / 2.0, -1023)},
|
||||
{"-0x1p-1025", -std::ldexp(1.0 / 4.0, -1023)},
|
||||
{"-0x1p-1026", -std::ldexp(1.0 / 8.0, -1023)},
|
||||
{"0x1.8p-1023", std::ldexp(1.0, -1023) + (std::ldexp(1.0, -1023) / 2.0)},
|
||||
{"0x1.8p-1024", std::ldexp(1.0, -1023) / 2.0 + (std::ldexp(1.0, -1023) / 4.0)},
|
||||
|
||||
{"0x1.8p-127", std::ldexp(1.0, -127) + (std::ldexp(1.0, -127) / 2.0)},
|
||||
{"0x1.8p-128", std::ldexp(1.0, -127) / 2.0 + (std::ldexp(1.0, -127) / 4.0)},
|
||||
{"0x1p-127f", std::ldexp(1.0, -127)},
|
||||
{"0x1p-128f", std::ldexp(1.0 / 2.0, -127)},
|
||||
{"0x1p-129f", std::ldexp(1.0 / 4.0, -127)},
|
||||
{"0x1p-130f", std::ldexp(1.0 / 8.0, -127)},
|
||||
{"-0x1p-127f", -std::ldexp(1.0, -127)},
|
||||
{"-0x1p-128f", -std::ldexp(1.0 / 2.0, -127)},
|
||||
{"-0x1p-129f", -std::ldexp(1.0 / 4.0, -127)},
|
||||
{"-0x1p-130f", -std::ldexp(1.0 / 8.0, -127)},
|
||||
{"0x1.8p-127f", std::ldexp(1.0, -127) + (std::ldexp(1.0, -127) / 2.0)},
|
||||
{"0x1.8p-128f", std::ldexp(1.0, -127) / 2.0 + (std::ldexp(1.0, -127) / 4.0)},
|
||||
|
||||
// F64 extremities
|
||||
{"0x1p-1074", MakeDouble(0, 0, 1)}, // +SmallestDenormal
|
||||
{"0x1p-1073", MakeDouble(0, 0, 2)}, // +BiggerDenormal
|
||||
{"0x1.ffffffffffffp-1027", MakeDouble(0, 0, 0xffffffffffff)}, // +LargestDenormal
|
||||
{"-0x1p-1074", MakeDouble(1, 0, 1)}, // -SmallestDenormal
|
||||
{"-0x1p-1073", MakeDouble(1, 0, 2)}, // -BiggerDenormal
|
||||
{"-0x1.ffffffffffffp-1027", MakeDouble(1, 0, 0xffffffffffff)}, // -LargestDenormal
|
||||
|
||||
{"0x0.cafebeeff000dp-1022", MakeDouble(0, 0, 0xcafebeeff000d)}, // +Subnormal
|
||||
{"-0x0.cafebeeff000dp-1022", MakeDouble(1, 0, 0xcafebeeff000d)}, // -Subnormal
|
||||
{"0x1.2bfaf8p-1052", MakeDouble(0, 0, 0x4afebe)}, // +Subnormal
|
||||
{"-0x1.2bfaf8p-1052", MakeDouble(1, 0, 0x4afebe)}, // +Subnormal
|
||||
{"0x1.55554p-1055", MakeDouble(0, 0, 0xaaaaa)}, // +Subnormal
|
||||
{"-0x1.55554p-1055", MakeDouble(1, 0, 0xaaaaa)}, // -Subnormal
|
||||
|
||||
// F32 extremities
|
||||
{"0x1p-149", static_cast<double>(MakeFloat(0, 0, 1))}, // +SmallestDenormal
|
||||
|
@ -320,46 +357,32 @@ FloatLiteralTestCaseList HexFloatCases() {
|
|||
{"-0x1p-148", static_cast<double>(MakeFloat(1, 0, 2))}, // -BiggerDenormal
|
||||
{"-0x1.fffffcp-127", static_cast<double>(MakeFloat(1, 0, 0x7fffff))}, // -LargestDenormal
|
||||
|
||||
{"0x1.2bfaf8p-127", static_cast<double>(MakeFloat(0, 0, 0xcafebe))}, // +Subnormal
|
||||
{"-0x1.2bfaf8p-127", static_cast<double>(MakeFloat(1, 0, 0xcafebe))}, // -Subnormal
|
||||
{"0x0.cafebp-129", static_cast<double>(MakeFloat(0, 0, 0xcafeb))}, // +Subnormal
|
||||
{"-0x0.cafebp-129", static_cast<double>(MakeFloat(1, 0, 0xcafeb))}, // -Subnormal
|
||||
{"0x1.2bfaf8p-127", static_cast<double>(MakeFloat(0, 0, 0x4afebe))}, // +Subnormal
|
||||
{"-0x1.2bfaf8p-127", static_cast<double>(MakeFloat(1, 0, 0x4afebe))}, // -Subnormal
|
||||
{"0x1.55554p-130", static_cast<double>(MakeFloat(0, 0, 0xaaaaa))}, // +Subnormal
|
||||
{"-0x1.55554p-130", static_cast<double>(MakeFloat(1, 0, 0xaaaaa))}, // -Subnormal
|
||||
|
||||
// Nan -> Infinity
|
||||
{"0x1.8p+128", PosInf},
|
||||
{"0x1.0002p+128", PosInf},
|
||||
{"0x1.0018p+128", PosInf},
|
||||
{"0x1.01ep+128", PosInf},
|
||||
{"0x1.fffffep+128", PosInf},
|
||||
{"-0x1.8p+128", NegInf},
|
||||
{"-0x1.0002p+128", NegInf},
|
||||
{"-0x1.0018p+128", NegInf},
|
||||
{"-0x1.01ep+128", NegInf},
|
||||
{"-0x1.fffffep+128", NegInf},
|
||||
|
||||
// Infinity
|
||||
{"0x1p+128", PosInf},
|
||||
{"-0x1p+128", NegInf},
|
||||
{"0x32p+127", PosInf},
|
||||
{"0x32p+500", PosInf},
|
||||
{"-0x32p+127", NegInf},
|
||||
{"-0x32p+500", NegInf},
|
||||
|
||||
// Overflow -> Infinity
|
||||
{"0x1p+129", PosInf},
|
||||
{"0x1.1p+128", PosInf},
|
||||
{"-0x1p+129", NegInf},
|
||||
{"-0x1.1p+128", NegInf},
|
||||
{"0x1.0p2147483520", PosInf}, // INT_MAX - 127 (largest valid exponent)
|
||||
|
||||
// Underflow -> Zero
|
||||
{"0x1p-500", 0.0}, // Exponent underflows
|
||||
{"-0x1p-500", -0.0},
|
||||
{"0x0.00000000001p-126", 0.0}, // Fraction causes underflow
|
||||
{"-0x0.0000000001p-127", -0.0},
|
||||
{"0x0.01p-142", 0.0},
|
||||
{"-0x0.01p-142", -0.0}, // Fraction causes additional underflow
|
||||
{"0x1.0p-2147483520", 0}, // -(INT_MAX - 127) (smallest valid exponent)
|
||||
{"0x1p-1074", 0.0}, // Exponent underflows
|
||||
{"-0x1p-1074", 0.0},
|
||||
{"0x1p-5000", 0.0},
|
||||
{"-0x1p-5000", 0.0},
|
||||
{"0x0.00000000000000000000001p-1022", 0.0}, // Fraction causes underflow
|
||||
{"-0x0.0000000000000000000001p-1023", -0.0},
|
||||
{"0x0.01p-1073", -0.0},
|
||||
{"-0x0.01p-1073", -0.0}, // Fraction causes additional underflow
|
||||
|
||||
{"0x1p-150f", 0.0}, // Exponent underflows
|
||||
{"-0x1p-150f", 0.0},
|
||||
{"0x1p-500f", 0.0},
|
||||
{"-0x1p-500f", -0.0},
|
||||
{"0x0.00000000001p-126f", 0.0}, // Fraction causes underflow
|
||||
{"-0x0.0000000001p-127f", -0.0},
|
||||
{"0x0.01p-142f", 0.0},
|
||||
{"-0x0.01p-142f", -0.0}, // Fraction causes additional underflow
|
||||
{"0x1.0p-9223372036854774784", 0}, // -(INT64_MAX - 1023) (smallest valid exponent)
|
||||
|
||||
// Zero with non-zero exponent -> Zero
|
||||
{"0x0p+0", 0.0},
|
||||
|
@ -369,22 +392,22 @@ FloatLiteralTestCaseList HexFloatCases() {
|
|||
{"0x0p-9999999999", 0.0},
|
||||
// Same, but with very large positive exponents that would cause overflow
|
||||
// if the mantissa were non-zero.
|
||||
{"0x0p+4000000000", 0.0}, // 4 billion:
|
||||
{"0x0p+40000000000", 0.0}, // 40 billion
|
||||
{"-0x0p+40000000000", 0.0}, // As above 2, but negative mantissa
|
||||
{"-0x0p+400000000000", 0.0},
|
||||
{"0x0.00p+4000000000", 0.0}, // As above 4, but with fractional part
|
||||
{"0x0.00p+40000000000", 0.0},
|
||||
{"-0x0.00p+40000000000", 0.0},
|
||||
{"-0x0.00p+400000000000", 0.0},
|
||||
{"0x0p-4000000000", 0.0}, // As above 8, but with negative exponents
|
||||
{"0x0p-40000000000", 0.0},
|
||||
{"-0x0p-40000000000", 0.0},
|
||||
{"-0x0p-400000000000", 0.0},
|
||||
{"0x0.00p-4000000000", 0.0},
|
||||
{"0x0.00p-40000000000", 0.0},
|
||||
{"-0x0.00p-40000000000", 0.0},
|
||||
{"-0x0.00p-400000000000", 0.0},
|
||||
{"0x0p+10000000000000000000", 0.0}, // 10 quintillion (10,000,000,000,000,000,000)
|
||||
{"0x0p+100000000000000000000", 0.0}, // 100 quintillion (100,000,000,000,000,000,000)
|
||||
{"-0x0p+100000000000000000000", 0.0}, // As above 2, but negative mantissa
|
||||
{"-0x0p+1000000000000000000000", 0.0},
|
||||
{"0x0.00p+10000000000000000000", 0.0}, // As above 4, but with fractional part
|
||||
{"0x0.00p+100000000000000000000", 0.0},
|
||||
{"-0x0.00p+100000000000000000000", 0.0},
|
||||
{"-0x0.00p+1000000000000000000000", 0.0},
|
||||
{"0x0p-10000000000000000000", 0.0}, // As above 8, but with negative exponents
|
||||
{"0x0p-100000000000000000000", 0.0},
|
||||
{"-0x0p-100000000000000000000", 0.0},
|
||||
{"-0x0p-1000000000000000000000", 0.0},
|
||||
{"0x0.00p-10000000000000000000", 0.0},
|
||||
{"0x0.00p-100000000000000000000", 0.0},
|
||||
{"-0x0.00p-100000000000000000000", 0.0},
|
||||
{"-0x0.00p-1000000000000000000000", 0.0},
|
||||
|
||||
// Test parsing
|
||||
{"0x0p0", 0.0},
|
||||
|
@ -462,65 +485,144 @@ INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_HexFloat_UpperCase0X,
|
|||
ParserImplFloatLiteralTest,
|
||||
testing::ValuesIn(UpperCase0X(HexFloatCases())));
|
||||
|
||||
struct InvalidLiteralTestCase {
|
||||
const char* input;
|
||||
const char* error_msg;
|
||||
};
|
||||
// <error, source>
|
||||
using InvalidLiteralTestCase = std::tuple<const char*, const char*>;
|
||||
|
||||
class ParserImplInvalidLiteralTest : public ParserImplTestWithParam<InvalidLiteralTestCase> {};
|
||||
TEST_P(ParserImplInvalidLiteralTest, Parse) {
|
||||
auto params = GetParam();
|
||||
SCOPED_TRACE(params.input);
|
||||
auto p = parser(params.input);
|
||||
auto* error = std::get<0>(GetParam());
|
||||
auto* source = std::get<1>(GetParam());
|
||||
auto p = parser(source);
|
||||
auto c = p->const_literal();
|
||||
EXPECT_FALSE(c.matched);
|
||||
EXPECT_TRUE(c.errored);
|
||||
EXPECT_EQ(p->error(), params.error_msg);
|
||||
EXPECT_EQ(p->error(), std::string(error));
|
||||
ASSERT_EQ(c.value, nullptr);
|
||||
}
|
||||
|
||||
InvalidLiteralTestCase invalid_hexfloat_mantissa_too_large_cases[] = {
|
||||
{"0x1.ffffffff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1f.fffffff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1ff.ffffff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1fff.fffff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1ffff.ffff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1fffff.fff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1ffffff.ff8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1fffffff.f8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1ffffffff.8p0", "1:1: mantissa is too large for hex float"},
|
||||
{"0x1ffffffff8.p0", "1:1: mantissa is too large for hex float"},
|
||||
};
|
||||
INSTANTIATE_TEST_SUITE_P(ParserImplInvalidLiteralTest_HexFloatMantissaTooLarge,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::ValuesIn(invalid_hexfloat_mantissa_too_large_cases));
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
HexFloatMantissaTooLarge,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: mantissa is too large for hex float"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
"0x1.ffffffffffffffff8p0",
|
||||
"0x1f.fffffffffffffff8p0",
|
||||
"0x1ff.ffffffffffffff8p0",
|
||||
"0x1fff.fffffffffffff8p0",
|
||||
"0x1ffff.ffffffffffff8p0",
|
||||
"0x1fffff.fffffffffff8p0",
|
||||
"0x1ffffff.ffffffffff8p0",
|
||||
"0x1fffffff.fffffffff8p0",
|
||||
"0x1ffffffff.ffffffff8p0",
|
||||
"0x1fffffffff.fffffff8p0",
|
||||
"0x1ffffffffff.ffffff8p0",
|
||||
"0x1fffffffffff.fffff8p0",
|
||||
"0x1ffffffffffff.ffff8p0",
|
||||
"0x1fffffffffffff.fff8p0",
|
||||
"0x1ffffffffffffff.ff8p0",
|
||||
"0x1ffffffffffffffff.8p0",
|
||||
"0x1ffffffffffffffff8.p0",
|
||||
})));
|
||||
|
||||
InvalidLiteralTestCase invalid_hexfloat_exponent_too_large_cases[] = {
|
||||
{"0x1p+2147483521", "1:1: exponent is too large for hex float"},
|
||||
{"0x1p-2147483521", "1:1: exponent is too large for hex float"},
|
||||
{"0x1p+4294967296", "1:1: exponent is too large for hex float"},
|
||||
{"0x1p-4294967296", "1:1: exponent is too large for hex float"},
|
||||
};
|
||||
INSTANTIATE_TEST_SUITE_P(ParserImplInvalidLiteralTest_HexFloatExponentTooLarge,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::ValuesIn(invalid_hexfloat_exponent_too_large_cases));
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
HexFloatExponentTooLarge,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: exponent is too large for hex float"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
"0x1p+9223372036854774785",
|
||||
"0x1p-9223372036854774785",
|
||||
"0x1p+18446744073709551616",
|
||||
"0x1p-18446744073709551616",
|
||||
})));
|
||||
|
||||
InvalidLiteralTestCase invalid_hexfloat_exponent_missing_cases[] = {
|
||||
// Lower case p
|
||||
{"0x0p", "1:1: expected an exponent value for hex float"},
|
||||
{"0x0p+", "1:1: expected an exponent value for hex float"},
|
||||
{"0x0p-", "1:1: expected an exponent value for hex float"},
|
||||
{"0x1.0p", "1:1: expected an exponent value for hex float"},
|
||||
{"0x0.1p", "1:1: expected an exponent value for hex float"},
|
||||
// Upper case p
|
||||
{"0x0P", "1:1: expected an exponent value for hex float"},
|
||||
{"0x0P+", "1:1: expected an exponent value for hex float"},
|
||||
{"0x0P-", "1:1: expected an exponent value for hex float"},
|
||||
{"0x1.0P", "1:1: expected an exponent value for hex float"},
|
||||
{"0x0.1P", "1:1: expected an exponent value for hex float"},
|
||||
};
|
||||
INSTANTIATE_TEST_SUITE_P(ParserImplInvalidLiteralTest_HexFloatExponentMissing,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::ValuesIn(invalid_hexfloat_exponent_missing_cases));
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
HexFloatMissingExponent,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: expected an exponent value for hex float"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
// Lower case p
|
||||
"0x0p",
|
||||
"0x0p+",
|
||||
"0x0p-",
|
||||
"0x1.0p",
|
||||
"0x0.1p",
|
||||
// Upper case p
|
||||
"0x0P",
|
||||
"0x0P+",
|
||||
"0x0P-",
|
||||
"0x1.0P",
|
||||
"0x0.1P",
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NaNAFloat,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
"0x1.8p+1024",
|
||||
"0x1.0002p+1024",
|
||||
"0x1.0018p+1024",
|
||||
"0x1.01ep+1024",
|
||||
"0x1.fffffep+1024",
|
||||
"-0x1.8p+1024",
|
||||
"-0x1.0002p+1024",
|
||||
"-0x1.0018p+1024",
|
||||
"-0x1.01ep+1024",
|
||||
"-0x1.fffffep+1024",
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
NaNF32,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
"0x1.8p+128f",
|
||||
"0x1.0002p+128f",
|
||||
"0x1.0018p+128f",
|
||||
"0x1.01ep+128f",
|
||||
"0x1.fffffep+128f",
|
||||
"-0x1.8p+128f",
|
||||
"-0x1.0002p+128f",
|
||||
"-0x1.0018p+128f",
|
||||
"-0x1.01ep+128f",
|
||||
"-0x1.fffffep+128f",
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
OverflowAFloat,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
"0x1p+1024",
|
||||
"-0x1p+1024",
|
||||
"0x1.1p+1024",
|
||||
"-0x1.1p+1024",
|
||||
"0x1p+1025",
|
||||
"-0x1p+1025",
|
||||
"0x32p+1023",
|
||||
"-0x32p+1023",
|
||||
"0x32p+5000",
|
||||
"-0x32p+5000",
|
||||
"0x1.0p9223372036854774784",
|
||||
"-0x1.0p9223372036854774784",
|
||||
})));
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
OverflowF32,
|
||||
ParserImplInvalidLiteralTest,
|
||||
testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
|
||||
testing::ValuesIn(std::vector<const char*>{
|
||||
"0x1p+128f",
|
||||
"-0x1p+128f",
|
||||
"0x1.1p+128f",
|
||||
"-0x1.1p+128f",
|
||||
"0x1p+129f",
|
||||
"-0x1p+129f",
|
||||
"0x32p+127f",
|
||||
"-0x32p+127f",
|
||||
"0x32p+500f",
|
||||
"-0x32p+500f",
|
||||
})));
|
||||
|
||||
TEST_F(ParserImplTest, ConstLiteral_FloatHighest) {
|
||||
const auto highest = std::numeric_limits<float>::max();
|
||||
|
|
|
@ -82,7 +82,7 @@ foobar
|
|||
TEST_F(ParserImplTest, HandlesBadToken_InMiddle) {
|
||||
auto p = parser(R"(
|
||||
fn main() {
|
||||
let f = 0x1p500000000000; // Exponent too big for hex float
|
||||
let f = 0x1p10000000000000000000; // Exponent too big for hex float
|
||||
return;
|
||||
})");
|
||||
|
||||
|
@ -96,7 +96,7 @@ TEST_F(ParserImplTest, HandlesBadToken_AtModuleScope) {
|
|||
fn main() {
|
||||
return;
|
||||
}
|
||||
0x1p5000000000000
|
||||
0x1p10000000000000000000
|
||||
)");
|
||||
|
||||
ASSERT_FALSE(p->Parse());
|
||||
|
|
Loading…
Reference in New Issue