tint/reader/wgsl: Use CheckedConvert() for lexing
And simplify diagnostic messages. Bug: tint:1504 Change-Id: Ib649602a828760f434ea9c8de5c482d2a0459757 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/91362 Reviewed-by: David Neto <dneto@google.com> Commit-Queue: Ben Clayton <bclayton@google.com> Kokoro: Kokoro <noreply+kokoro@google.com>
This commit is contained in:
parent
c2eccfc887
commit
ef702af6c8
|
@ -24,6 +24,7 @@
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "src/tint/debug.h"
|
#include "src/tint/debug.h"
|
||||||
|
#include "src/tint/number.h"
|
||||||
#include "src/tint/text/unicode.h"
|
#include "src/tint/text/unicode.h"
|
||||||
|
|
||||||
namespace tint::reader::wgsl {
|
namespace tint::reader::wgsl {
|
||||||
|
@ -80,42 +81,6 @@ uint32_t hex_value(char c) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// LimitCheck is the enumerator result of check_limits().
|
|
||||||
enum class LimitCheck {
|
|
||||||
/// The value was within the limits of the data type.
|
|
||||||
kWithinLimits,
|
|
||||||
/// The value was too small to fit within the data type.
|
|
||||||
kTooSmall,
|
|
||||||
/// The value was too large to fit within the data type.
|
|
||||||
kTooLarge,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Checks whether the value fits within the integer type `T`
|
|
||||||
template <typename T>
|
|
||||||
LimitCheck check_limits(int64_t value) {
|
|
||||||
static_assert(std::is_integral_v<T>, "T must be an integer");
|
|
||||||
if (value < static_cast<int64_t>(std::numeric_limits<T>::lowest())) {
|
|
||||||
return LimitCheck::kTooSmall;
|
|
||||||
}
|
|
||||||
if (value > static_cast<int64_t>(std::numeric_limits<T>::max())) {
|
|
||||||
return LimitCheck::kTooLarge;
|
|
||||||
}
|
|
||||||
return LimitCheck::kWithinLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Checks whether the value fits within the floating point type `T`
|
|
||||||
template <typename T>
|
|
||||||
LimitCheck check_limits(double value) {
|
|
||||||
static_assert(std::is_floating_point_v<T>, "T must be a floating point");
|
|
||||||
if (value < static_cast<double>(std::numeric_limits<T>::lowest())) {
|
|
||||||
return LimitCheck::kTooSmall;
|
|
||||||
}
|
|
||||||
if (value > static_cast<double>(std::numeric_limits<T>::max())) {
|
|
||||||
return LimitCheck::kTooLarge;
|
|
||||||
}
|
|
||||||
return LimitCheck::kWithinLimits;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Lexer::Lexer(const Source::File* file) : file_(file), location_{1, 1} {}
|
Lexer::Lexer(const Source::File* file) : file_(file), location_{1, 1} {}
|
||||||
|
@ -394,38 +359,30 @@ Token Lexer::try_float() {
|
||||||
end_source(source);
|
end_source(source);
|
||||||
|
|
||||||
double value = strtod(&at(start), nullptr);
|
double value = strtod(&at(start), nullptr);
|
||||||
const double magnitude = std::abs(value);
|
|
||||||
|
|
||||||
if (has_f_suffix) {
|
if (has_f_suffix) {
|
||||||
// This errors out if a non-zero magnitude is too small to represent in a
|
if (auto f = CheckedConvert<f32>(AFloat(value))) {
|
||||||
// float. It can't be represented faithfully in an f32.
|
return {Token::Type::kFloatLiteral_F, source, value};
|
||||||
if (0.0 < magnitude && magnitude < static_cast<double>(std::numeric_limits<float>::min())) {
|
} else {
|
||||||
return {Token::Type::kError, source, "magnitude too small to be represented as f32"};
|
if (f.Failure() == ConversionFailure::kTooSmall) {
|
||||||
}
|
return {Token::Type::kError, source,
|
||||||
switch (check_limits<float>(value)) {
|
"value magnitude too small to be represented as 'f32'"};
|
||||||
case LimitCheck::kTooSmall:
|
}
|
||||||
return {Token::Type::kError, source, "value too small for f32"};
|
return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
|
||||||
case LimitCheck::kTooLarge:
|
|
||||||
return {Token::Type::kError, source, "value too large for f32"};
|
|
||||||
default:
|
|
||||||
return {Token::Type::kFloatLiteral_F, source, value};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/tint/1504): Properly support abstract float:
|
// TODO(crbug.com/tint/1504): Properly support abstract float:
|
||||||
// Change `AbstractFloatType` to `double`, update errors to say 'abstract int'.
|
// Change `AbstractFloatType` to `double`, update errors to say 'abstract int'.
|
||||||
using AbstractFloatType = float;
|
using AbstractFloatType = f32;
|
||||||
if (0.0 < magnitude &&
|
if (auto f = CheckedConvert<AbstractFloatType>(AFloat(value))) {
|
||||||
magnitude < static_cast<double>(std::numeric_limits<AbstractFloatType>::min())) {
|
return {Token::Type::kFloatLiteral, source, value};
|
||||||
return {Token::Type::kError, source, "magnitude too small to be represented as f32"};
|
} else {
|
||||||
}
|
if (f.Failure() == ConversionFailure::kTooSmall) {
|
||||||
switch (check_limits<AbstractFloatType>(value)) {
|
return {Token::Type::kError, source,
|
||||||
case LimitCheck::kTooSmall:
|
"value magnitude too small to be represented as 'f32'"};
|
||||||
return {Token::Type::kError, source, "value too small for f32"};
|
}
|
||||||
case LimitCheck::kTooLarge:
|
return {Token::Type::kError, source, "value cannot be represented as 'f32'"};
|
||||||
return {Token::Type::kError, source, "value too large for f32"};
|
|
||||||
default:
|
|
||||||
return {Token::Type::kFloatLiteral, source, value};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,44 +682,31 @@ Token Lexer::build_token_from_int_if_possible(Source source, size_t start, int32
|
||||||
int64_t res = strtoll(&at(start), nullptr, base);
|
int64_t res = strtoll(&at(start), nullptr, base);
|
||||||
|
|
||||||
if (matches(pos(), "u")) {
|
if (matches(pos(), "u")) {
|
||||||
switch (check_limits<uint32_t>(res)) {
|
if (CheckedConvert<u32>(AInt(res))) {
|
||||||
case LimitCheck::kTooSmall:
|
advance(1);
|
||||||
return {Token::Type::kError, source, "unsigned literal cannot be negative"};
|
end_source(source);
|
||||||
case LimitCheck::kTooLarge:
|
return {Token::Type::kIntLiteral_U, source, res};
|
||||||
return {Token::Type::kError, source, "value too large for u32"};
|
|
||||||
default:
|
|
||||||
advance(1);
|
|
||||||
end_source(source);
|
|
||||||
return {Token::Type::kIntLiteral_U, source, res};
|
|
||||||
}
|
}
|
||||||
|
return {Token::Type::kError, source, "value cannot be represented as 'u32'"};
|
||||||
}
|
}
|
||||||
|
|
||||||
if (matches(pos(), "i")) {
|
if (matches(pos(), "i")) {
|
||||||
switch (check_limits<int32_t>(res)) {
|
if (CheckedConvert<i32>(AInt(res))) {
|
||||||
case LimitCheck::kTooSmall:
|
advance(1);
|
||||||
return {Token::Type::kError, source, "value too small for i32"};
|
end_source(source);
|
||||||
case LimitCheck::kTooLarge:
|
return {Token::Type::kIntLiteral_I, source, res};
|
||||||
return {Token::Type::kError, source, "value too large for i32"};
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
advance(1);
|
return {Token::Type::kError, source, "value cannot be represented as 'i32'"};
|
||||||
end_source(source);
|
|
||||||
return {Token::Type::kIntLiteral_I, source, res};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(crbug.com/tint/1504): Properly support abstract int:
|
// TODO(crbug.com/tint/1504): Properly support abstract int:
|
||||||
// Change `AbstractIntType` to `int64_t`, update errors to say 'abstract int'.
|
// Change `AbstractIntType` to `int64_t`, update errors to say 'abstract int'.
|
||||||
using AbstractIntType = int32_t;
|
using AbstractIntType = i32;
|
||||||
switch (check_limits<AbstractIntType>(res)) {
|
if (CheckedConvert<AbstractIntType>(AInt(res))) {
|
||||||
case LimitCheck::kTooSmall:
|
end_source(source);
|
||||||
return {Token::Type::kError, source, "value too small for i32"};
|
return {Token::Type::kIntLiteral, source, res};
|
||||||
case LimitCheck::kTooLarge:
|
|
||||||
return {Token::Type::kError, source, "value too large for i32"};
|
|
||||||
default:
|
|
||||||
end_source(source);
|
|
||||||
return {Token::Type::kIntLiteral, source, res};
|
|
||||||
}
|
}
|
||||||
|
return {Token::Type::kError, source, "value cannot be represented as 'i32'"};
|
||||||
}
|
}
|
||||||
|
|
||||||
Token Lexer::try_hex_integer() {
|
Token Lexer::try_hex_integer() {
|
||||||
|
|
|
@ -680,7 +680,7 @@ TEST_F(LexerTest, IntegerTest_HexSignedTooLarge) {
|
||||||
|
|
||||||
auto t = l.next();
|
auto t = l.next();
|
||||||
ASSERT_TRUE(t.Is(Token::Type::kError));
|
ASSERT_TRUE(t.Is(Token::Type::kError));
|
||||||
EXPECT_EQ(t.to_str(), "value too large for i32");
|
EXPECT_EQ(t.to_str(), "value cannot be represented as 'i32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) {
|
TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) {
|
||||||
|
@ -689,7 +689,7 @@ TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) {
|
||||||
|
|
||||||
auto t = l.next();
|
auto t = l.next();
|
||||||
ASSERT_TRUE(t.Is(Token::Type::kError));
|
ASSERT_TRUE(t.Is(Token::Type::kError));
|
||||||
EXPECT_EQ(t.to_str(), "value too small for i32");
|
EXPECT_EQ(t.to_str(), "value cannot be represented as 'i32'");
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(LexerTest, IntegerTest_HexSignedTooManyDigits) {
|
TEST_F(LexerTest, IntegerTest_HexSignedTooManyDigits) {
|
||||||
|
|
|
@ -133,7 +133,7 @@ TEST_F(ParserImplTest, ConstLiteral_Uint_Negative) {
|
||||||
auto c = p->const_literal();
|
auto c = p->const_literal();
|
||||||
EXPECT_FALSE(c.matched);
|
EXPECT_FALSE(c.matched);
|
||||||
EXPECT_TRUE(c.errored);
|
EXPECT_TRUE(c.errored);
|
||||||
EXPECT_EQ(p->error(), "1:1: unsigned literal cannot be negative");
|
EXPECT_EQ(p->error(), "1:1: value cannot be represented as 'u32'");
|
||||||
ASSERT_EQ(c.value, nullptr);
|
ASSERT_EQ(c.value, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +179,7 @@ TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooSmallMagnitude) {
|
||||||
auto c = p->const_literal();
|
auto c = p->const_literal();
|
||||||
EXPECT_FALSE(c.matched);
|
EXPECT_FALSE(c.matched);
|
||||||
EXPECT_TRUE(c.errored);
|
EXPECT_TRUE(c.errored);
|
||||||
EXPECT_EQ(p->error(), "1:1: magnitude too small to be represented as f32");
|
EXPECT_EQ(p->error(), "1:1: value magnitude too small to be represented as 'f32'");
|
||||||
ASSERT_EQ(c.value, nullptr);
|
ASSERT_EQ(c.value, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -188,7 +188,7 @@ TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooLargeNegative) {
|
||||||
auto c = p->const_literal();
|
auto c = p->const_literal();
|
||||||
EXPECT_FALSE(c.matched);
|
EXPECT_FALSE(c.matched);
|
||||||
EXPECT_TRUE(c.errored);
|
EXPECT_TRUE(c.errored);
|
||||||
EXPECT_EQ(p->error(), "1:1: value too small for f32");
|
EXPECT_EQ(p->error(), "1:1: value cannot be represented as 'f32'");
|
||||||
ASSERT_EQ(c.value, nullptr);
|
ASSERT_EQ(c.value, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +197,7 @@ TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_TooLargePositive) {
|
||||||
auto c = p->const_literal();
|
auto c = p->const_literal();
|
||||||
EXPECT_FALSE(c.matched);
|
EXPECT_FALSE(c.matched);
|
||||||
EXPECT_TRUE(c.errored);
|
EXPECT_TRUE(c.errored);
|
||||||
EXPECT_EQ(p->error(), "1:1: value too large for f32");
|
EXPECT_EQ(p->error(), "1:1: value cannot be represented as 'f32'");
|
||||||
ASSERT_EQ(c.value, nullptr);
|
ASSERT_EQ(c.value, nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue