tint/reader/wgsl: Lex abstract floats
And remove lexer errors about float magnitudes been too small.
Also add tests for non-hex float literal overflow.
Bug: tint:1504
Bug: tint:1564
Change-Id: Ia26817d4f2a99af694e9935692b98ef91f97d2b3
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91428
Reviewed-by: David Neto <dneto@google.com>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@chromium.org>
diff --git a/src/tint/reader/wgsl/lexer.cc b/src/tint/reader/wgsl/lexer.cc
index fbaebf0..bfec1d1 100644
--- a/src/tint/reader/wgsl/lexer.cc
+++ b/src/tint/reader/wgsl/lexer.cc
@@ -358,31 +358,22 @@
advance(end - start);
end_source(source);
- double value = strtod(&at(start), nullptr);
+ double value = std::strtod(&at(start), nullptr);
if (has_f_suffix) {
if (auto f = CheckedConvert<f32>(AFloat(value))) {
- return {Token::Type::kFloatLiteral_F, source, value};
+ return {Token::Type::kFloatLiteral_F, source, static_cast<double>(f.Get())};
+ } else if (f.Failure() == ConversionFailure::kTooSmall) {
+ return {Token::Type::kFloatLiteral_F, source, 0.0};
} else {
- if (f.Failure() == ConversionFailure::kTooSmall) {
- return {Token::Type::kError, source,
- "value magnitude too small to be represented as 'f32'"};
- }
return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
}
}
- // TODO(crbug.com/tint/1504): Properly support abstract float:
- // Change `AbstractFloatType` to `double`, update errors to say 'abstract int'.
- using AbstractFloatType = f32;
- if (auto f = CheckedConvert<AbstractFloatType>(AFloat(value))) {
- return {Token::Type::kFloatLiteral, source, value};
+ if (value == HUGE_VAL || -value == HUGE_VAL) {
+ return {Token::Type::kError, source, "value cannot be represented as 'abstract-float'"};
} else {
- if (f.Failure() == ConversionFailure::kTooSmall) {
- return {Token::Type::kError, source,
- "value magnitude too small to be represented as 'f32'"};
- }
- return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
+ return {Token::Type::kFloatLiteral, source, value};
}
}
diff --git a/src/tint/reader/wgsl/lexer_test.cc b/src/tint/reader/wgsl/lexer_test.cc
index 52f5cb8..801b62c 100644
--- a/src/tint/reader/wgsl/lexer_test.cc
+++ b/src/tint/reader/wgsl/lexer_test.cc
@@ -362,12 +362,12 @@
FloatData{"-5.", -5.},
FloatData{"-.7", -.7},
// Non-zero with decimal and 'f' suffix
- FloatData{"5.7f", 5.7},
- FloatData{"5.f", 5.},
- FloatData{".7f", .7},
- FloatData{"-5.7f", -5.7},
- FloatData{"-5.f", -5.},
- FloatData{"-.7f", -.7},
+ FloatData{"5.7f", static_cast<double>(5.7f)},
+ FloatData{"5.f", static_cast<double>(5.f)},
+ FloatData{".7f", static_cast<double>(.7f)},
+ FloatData{"-5.7f", static_cast<double>(-5.7f)},
+ FloatData{"-5.f", static_cast<double>(-5.f)},
+ FloatData{"-.7f", static_cast<double>(-.7f)},
// No decimal, with exponent
FloatData{"1e5", 1e5},
@@ -375,10 +375,10 @@
FloatData{"1e-5", 1e-5},
FloatData{"1E-5", 1e-5},
// No decimal, with exponent and 'f' suffix
- FloatData{"1e5f", 1e5},
- FloatData{"1E5f", 1e5},
- FloatData{"1e-5f", 1e-5},
- FloatData{"1E-5f", 1e-5},
+ FloatData{"1e5f", static_cast<double>(1e5f)},
+ FloatData{"1E5f", static_cast<double>(1e5f)},
+ FloatData{"1e-5f", static_cast<double>(1e-5f)},
+ FloatData{"1E-5f", static_cast<double>(1e-5f)},
// With decimal and exponents
FloatData{"0.2e+12", 0.2e12},
FloatData{"1.2e-5", 1.2e-5},
@@ -386,11 +386,15 @@
FloatData{"2.5e+0", 2.5},
FloatData{"2.5e-0", 2.5},
// With decimal and exponents and 'f' suffix
- FloatData{"0.2e+12f", 0.2e12},
- FloatData{"1.2e-5f", 1.2e-5},
- FloatData{"2.57e23f", 2.57e23},
- FloatData{"2.5e+0f", 2.5},
- FloatData{"2.5e-0f", 2.5}));
+ FloatData{"0.2e+12f", static_cast<double>(0.2e12f)},
+ FloatData{"1.2e-5f", static_cast<double>(1.2e-5f)},
+ FloatData{"2.57e23f", static_cast<double>(2.57e23f)},
+ FloatData{"2.5e+0f", static_cast<double>(2.5f)},
+ FloatData{"2.5e-0f", static_cast<double>(2.5f)},
+ // Quantization
+ FloatData{"3.141592653589793", 3.141592653589793}, // no quantization
+ FloatData{"3.141592653589793f", 3.1415927410125732} // f32 quantized
+ ));
using FloatTest_Invalid = testing::TestWithParam<const char*>;
TEST_P(FloatTest_Invalid, Handles) {
@@ -415,11 +419,11 @@
".e+",
".e-",
// Overflow
- "2.5e+256",
- "-2.5e+127",
+ "2.5e+256f",
+ "-2.5e+127f",
// Magnitude smaller than smallest positive f32.
- "2.5e-300",
- "-2.5e-300",
+ "2.5e-300f",
+ "-2.5e-300f",
// Decimal exponent must immediately
// follow the 'e'.
"2.5e 12",
diff --git a/src/tint/reader/wgsl/parser_impl_const_literal_test.cc b/src/tint/reader/wgsl/parser_impl_const_literal_test.cc
index 5d882ca..8ce5178 100644
--- a/src/tint/reader/wgsl/parser_impl_const_literal_test.cc
+++ b/src/tint/reader/wgsl/parser_impl_const_literal_test.cc
@@ -117,34 +117,6 @@
ASSERT_EQ(c.value, nullptr);
}
-TEST_F(ParserImplTest, ConstLiteral_Float) {
- auto p = parser("234.e12");
- auto c = p->const_literal();
- EXPECT_TRUE(c.matched);
- 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, 234e12);
- EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
- ast::FloatLiteralExpression::Suffix::kNone);
- EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 8u}}));
-}
-
-TEST_F(ParserImplTest, ConstLiteral_FloatF) {
- auto p = parser("234.e12f");
- auto c = p->const_literal();
- EXPECT_TRUE(c.matched);
- 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, 234e12);
- EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
- ast::FloatLiteralExpression::Suffix::kF);
- EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 9u}}));
-}
-
TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_IncompleteExponent) {
auto p = parser("1.0e+");
auto c = p->const_literal();
@@ -154,33 +126,6 @@
ASSERT_EQ(c.value, nullptr);
}
-TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooSmallMagnitude) {
- auto p = parser("1e-256");
- auto c = p->const_literal();
- EXPECT_FALSE(c.matched);
- EXPECT_TRUE(c.errored);
- EXPECT_EQ(p->error(), "1:1: value magnitude too small to be represented as 'f32'");
- ASSERT_EQ(c.value, nullptr);
-}
-
-TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooLargeNegative) {
- auto p = parser("-1.2e+256");
- auto c = p->const_literal();
- EXPECT_FALSE(c.matched);
- EXPECT_TRUE(c.errored);
- EXPECT_EQ(p->error(), "1:1: value cannot be represented as 'f32'");
- ASSERT_EQ(c.value, nullptr);
-}
-
-TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooLargePositive) {
- auto p = parser("1.2e+256");
- auto c = p->const_literal();
- EXPECT_FALSE(c.matched);
- EXPECT_TRUE(c.errored);
- EXPECT_EQ(p->error(), "1:1: value cannot be represented as 'f32'");
- ASSERT_EQ(c.value, nullptr);
-}
-
struct FloatLiteralTestCase {
std::string input;
double expected;
@@ -217,26 +162,54 @@
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kNone);
}
+ EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 1u + params.input.size()}}));
}
using FloatLiteralTestCaseList = std::vector<FloatLiteralTestCase>;
-FloatLiteralTestCaseList DecimalFloatCases() {
- return FloatLiteralTestCaseList{
- {"0.0", 0.0}, // Zero
- {"1.0", 1.0}, // One
- {"-1.0", -1.0}, // MinusOne
- {"1000000000.0", 1e9}, // Billion
- {"-0.0", std::copysign(0.0, -5.0)}, // NegativeZero
- {"0.0", MakeDouble(0, 0, 0)}, // Zero
- {"-0.0", MakeDouble(1, 0, 0)}, // NegativeZero
- {"1.0", MakeDouble(0, 1023, 0)}, // One
- {"-1.0", MakeDouble(1, 1023, 0)}, // NegativeOne
- };
-}
-
INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_Float,
ParserImplFloatLiteralTest,
- testing::ValuesIn(DecimalFloatCases()));
+ testing::ValuesIn(FloatLiteralTestCaseList{
+ {"0.0", 0.0}, // Zero
+ {"1.0", 1.0}, // One
+ {"-1.0", -1.0}, // MinusOne
+ {"1000000000.0", 1e9}, // Billion
+ {"-0.0", std::copysign(0.0, -5.0)}, // NegativeZero
+ {"0.0", MakeDouble(0, 0, 0)}, // Zero
+ {"-0.0", MakeDouble(1, 0, 0)}, // NegativeZero
+ {"1.0", MakeDouble(0, 1023, 0)}, // One
+ {"-1.0", MakeDouble(1, 1023, 0)}, // NegativeOne
+
+ {"234.e12", 234.e12},
+ {"234.e12f", static_cast<double>(234.e12f)},
+
+ // Tiny cases
+ {"1e-5000", 0.0},
+ {"-1e-5000", 0.0},
+ {"1e-5000f", 0.0},
+ {"-1e-5000f", 0.0},
+ {"1e-50f", 0.0},
+ {"-1e-50f", 0.0},
+
+ // Nearly overflow
+ {"1.e308", 1.e308},
+ {"-1.e308", -1.e308},
+ {"1.8e307", 1.8e307},
+ {"-1.8e307", -1.8e307},
+ {"1.798e307", 1.798e307},
+ {"-1.798e307", -1.798e307},
+ {"1.7977e307", 1.7977e307},
+ {"-1.7977e307", -1.7977e307},
+
+ // Nearly overflow
+ {"1e38f", static_cast<double>(1e38f)},
+ {"-1e38f", static_cast<double>(-1e38f)},
+ {"4.0e37f", static_cast<double>(4.0e37f)},
+ {"-4.0e37f", static_cast<double>(-4.0e37f)},
+ {"3.5e37f", static_cast<double>(3.5e37f)},
+ {"-3.5e37f", static_cast<double>(-3.5e37f)},
+ {"3.403e37f", static_cast<double>(3.403e37f)},
+ {"-3.403e37f", static_cast<double>(-3.403e37f)},
+ }));
const double NegInf = MakeDouble(1, 0x7FF, 0);
const double PosInf = MakeDouble(0, 0x7FF, 0);
@@ -535,7 +508,7 @@
})));
INSTANTIATE_TEST_SUITE_P(
- NaNAFloat,
+ HexNaNAFloat,
ParserImplInvalidLiteralTest,
testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
testing::ValuesIn(std::vector<const char*>{
@@ -552,7 +525,7 @@
})));
INSTANTIATE_TEST_SUITE_P(
- NaNF32,
+ HexNaNF32,
ParserImplInvalidLiteralTest,
testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
testing::ValuesIn(std::vector<const char*>{
@@ -569,7 +542,7 @@
})));
INSTANTIATE_TEST_SUITE_P(
- OverflowAFloat,
+ HexOverflowAFloat,
ParserImplInvalidLiteralTest,
testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
testing::ValuesIn(std::vector<const char*>{
@@ -588,7 +561,7 @@
})));
INSTANTIATE_TEST_SUITE_P(
- OverflowF32,
+ HexOverflowF32,
ParserImplInvalidLiteralTest,
testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
testing::ValuesIn(std::vector<const char*>{
@@ -604,6 +577,40 @@
"-0x32p+500f",
})));
+INSTANTIATE_TEST_SUITE_P(
+ DecOverflowAFloat,
+ ParserImplInvalidLiteralTest,
+ testing::Combine(testing::Values("1:1: value cannot be represented as 'abstract-float'"),
+ testing::ValuesIn(std::vector<const char*>{
+ "1.e309",
+ "-1.e309",
+ "1.8e308",
+ "-1.8e308",
+ "1.798e308",
+ "-1.798e308",
+ "1.7977e308",
+ "-1.7977e308",
+ "1.2e+5000",
+ "-1.2e+5000",
+ })));
+
+INSTANTIATE_TEST_SUITE_P(
+ DecOverflowF32,
+ ParserImplInvalidLiteralTest,
+ testing::Combine(testing::Values("1:1: value cannot be represented as 'f32'"),
+ testing::ValuesIn(std::vector<const char*>{
+ "1e39f",
+ "-1e39f",
+ "4.0e38f",
+ "-4.0e38f",
+ "3.5e38f",
+ "-3.5e38f",
+ "3.403e38f",
+ "-3.403e38f",
+ "1.2e+256f",
+ "-1.2e+256f",
+ })));
+
TEST_F(ParserImplTest, ConstLiteral_FloatHighest) {
const auto highest = std::numeric_limits<float>::max();
const auto expected_highest = 340282346638528859811704183484516925440.0f;