tint: Lex different tokens for float suffixes

Generate different tokens for:
• 'f' suffixed float literals
• unsuffixed integer literals

'f' and unsuffixed are currently both treated as f32 by the resolver,
but this is the first step to supporting abstract floats.

Bug: tint:1504
Change-Id: Id3b1fe420b6eb8901f88d6a5de06ef4f54aa3edf
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/89031
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Ben Clayton <bclayton@google.com>
Reviewed-by: Dan Sinclair <dsinclair@chromium.org>
This commit is contained in:
Ben Clayton 2022-05-10 14:55:34 +00:00 committed by Dawn LUCI CQ
parent dd5947ff76
commit 41285aa578
31 changed files with 513 additions and 327 deletions

View File

@ -22,15 +22,18 @@ TINT_INSTANTIATE_TYPEINFO(tint::ast::FloatLiteralExpression);
namespace tint::ast { namespace tint::ast {
FloatLiteralExpression::FloatLiteralExpression(ProgramID pid, const Source& src, float val) FloatLiteralExpression::FloatLiteralExpression(ProgramID pid,
: Base(pid, src), value(val) {} const Source& src,
double val,
Suffix suf)
: Base(pid, src), value(val), suffix(suf) {}
FloatLiteralExpression::~FloatLiteralExpression() = default; FloatLiteralExpression::~FloatLiteralExpression() = default;
const FloatLiteralExpression* FloatLiteralExpression::Clone(CloneContext* ctx) const { const FloatLiteralExpression* FloatLiteralExpression::Clone(CloneContext* ctx) const {
// Clone arguments outside of create() call to have deterministic ordering // Clone arguments outside of create() call to have deterministic ordering
auto src = ctx->Clone(source); auto src = ctx->Clone(source);
return ctx->dst->create<FloatLiteralExpression>(src, value); return ctx->dst->create<FloatLiteralExpression>(src, value, suffix);
} }
} // namespace tint::ast } // namespace tint::ast

View File

@ -24,11 +24,20 @@ namespace tint::ast {
/// A float literal /// A float literal
class FloatLiteralExpression final : public Castable<FloatLiteralExpression, LiteralExpression> { class FloatLiteralExpression final : public Castable<FloatLiteralExpression, LiteralExpression> {
public: public:
/// Literal suffix
enum class Suffix {
/// No suffix
kNone,
/// 'f' suffix (f32)
kF,
};
/// Constructor /// Constructor
/// @param pid the identifier of the program that owns this node /// @param pid the identifier of the program that owns this node
/// @param src the source of this node /// @param src the source of this node
/// @param value the float literals value /// @param val the literal value
FloatLiteralExpression(ProgramID pid, const Source& src, float value); /// @param suf the literal suffix
FloatLiteralExpression(ProgramID pid, const Source& src, double val, Suffix suf);
~FloatLiteralExpression() override; ~FloatLiteralExpression() override;
/// Clones this node and all transitive child nodes using the `CloneContext` /// Clones this node and all transitive child nodes using the `CloneContext`
@ -37,8 +46,11 @@ class FloatLiteralExpression final : public Castable<FloatLiteralExpression, Lit
/// @return the newly cloned node /// @return the newly cloned node
const FloatLiteralExpression* Clone(CloneContext* ctx) const override; const FloatLiteralExpression* Clone(CloneContext* ctx) const override;
/// The float literal value /// The literal value
const float value; const double value;
/// The literal suffix
const Suffix suffix;
}; };
} // namespace tint::ast } // namespace tint::ast

View File

@ -19,10 +19,18 @@ namespace {
using FloatLiteralExpressionTest = TestHelper; using FloatLiteralExpressionTest = TestHelper;
TEST_F(FloatLiteralExpressionTest, Value) { TEST_F(FloatLiteralExpressionTest, SuffixNone) {
auto* f = create<FloatLiteralExpression>(47.2f); auto* i = create<FloatLiteralExpression>(42.0, FloatLiteralExpression::Suffix::kNone);
ASSERT_TRUE(f->Is<FloatLiteralExpression>()); ASSERT_TRUE(i->Is<FloatLiteralExpression>());
EXPECT_EQ(f->value, 47.2f); EXPECT_EQ(i->value, 42);
EXPECT_EQ(i->suffix, FloatLiteralExpression::Suffix::kNone);
}
TEST_F(FloatLiteralExpressionTest, SuffixF) {
auto* i = create<FloatLiteralExpression>(42.0, FloatLiteralExpression::Suffix::kF);
ASSERT_TRUE(i->Is<FloatLiteralExpression>());
EXPECT_EQ(i->value, 42);
EXPECT_EQ(i->suffix, FloatLiteralExpression::Suffix::kF);
} }
} // namespace } // namespace

View File

@ -259,7 +259,7 @@ std::map<uint32_t, Scalar> Inspector::GetConstantIDs() {
} }
if (auto* l = literal->As<ast::FloatLiteralExpression>()) { if (auto* l = literal->As<ast::FloatLiteralExpression>()) {
result[constant_id] = Scalar(l->value); result[constant_id] = Scalar(static_cast<float>(l->value));
continue; continue;
} }

View File

@ -1027,15 +1027,15 @@ TEST_F(InspectorGetConstantIDsTest, Float) {
ASSERT_TRUE(result.find(20) != result.end()); ASSERT_TRUE(result.find(20) != result.end());
EXPECT_TRUE(result[20].IsFloat()); EXPECT_TRUE(result[20].IsFloat());
EXPECT_FLOAT_EQ(0.0, result[20].AsFloat()); EXPECT_FLOAT_EQ(0.0f, result[20].AsFloat());
ASSERT_TRUE(result.find(300) != result.end()); ASSERT_TRUE(result.find(300) != result.end());
EXPECT_TRUE(result[300].IsFloat()); EXPECT_TRUE(result[300].IsFloat());
EXPECT_FLOAT_EQ(-10.0, result[300].AsFloat()); EXPECT_FLOAT_EQ(-10.0f, result[300].AsFloat());
ASSERT_TRUE(result.find(4000) != result.end()); ASSERT_TRUE(result.find(4000) != result.end());
EXPECT_TRUE(result[4000].IsFloat()); EXPECT_TRUE(result[4000].IsFloat());
EXPECT_FLOAT_EQ(15.0, result[4000].AsFloat()); EXPECT_FLOAT_EQ(15.0f, result[4000].AsFloat());
} }
TEST_F(InspectorGetConstantNameToIdMapTest, WithAndWithoutIds) { TEST_F(InspectorGetConstantNameToIdMapTest, WithAndWithoutIds) {

View File

@ -16,6 +16,7 @@
#define SRC_TINT_NUMBER_H_ #define SRC_TINT_NUMBER_H_
#include <stdint.h> #include <stdint.h>
#include <functional>
namespace tint { namespace tint {
@ -27,7 +28,13 @@ struct Number {
/// Constructor. /// Constructor.
/// @param v the value to initialize this Number to /// @param v the value to initialize this Number to
explicit Number(T v) : value(v) {} template <typename U>
explicit Number(U v) : value(static_cast<T>(v)) {}
/// Constructor.
/// @param v the value to initialize this Number to
template <typename U>
explicit Number(Number<U> v) : value(static_cast<T>(v.value)) {}
/// Conversion operator /// Conversion operator
/// @returns the value as T /// @returns the value as T
@ -47,25 +54,26 @@ struct Number {
template <typename A, typename B> template <typename A, typename B>
bool operator==(Number<A> a, Number<B> b) { bool operator==(Number<A> a, Number<B> b) {
return a.value == b.value; using T = decltype(a.value + b.value);
return std::equal_to<T>()(a.value, b.value);
} }
template <typename A, typename B> template <typename A, typename B>
bool operator==(Number<A> a, B b) { bool operator==(Number<A> a, B b) {
return a.value == b; return a == Number<B>(b);
} }
template <typename A, typename B> template <typename A, typename B>
bool operator==(A a, Number<B> b) { bool operator==(A a, Number<B> b) {
return a == b.value; return Number<A>(a) == b;
} }
/// `i32` is a type alias to `Number<int32_t>`. /// `i32` is a type alias to `Number<int32_t>`.
using i32 = Number<int32_t>; using i32 = Number<int32_t>;
/// `u32` is a type alias to `Number<uint32_t>`. /// `u32` is a type alias to `Number<uint32_t>`.
using u32 = Number<uint32_t>; using u32 = Number<uint32_t>;
/// `f32` is a type alias to `float` /// `f32` is a type alias to `Number<float>`
using f32 = float; using f32 = Number<float>;
} // namespace tint } // namespace tint
@ -81,6 +89,16 @@ inline u32 operator"" _u(unsigned long long int value) { // NOLINT
return u32(static_cast<uint32_t>(value)); return u32(static_cast<uint32_t>(value));
} }
/// Literal suffix for f32 literals
inline f32 operator"" _f(long double value) { // NOLINT
return f32(static_cast<double>(value));
}
/// Literal suffix for f32 literals
inline f32 operator"" _f(unsigned long long int value) { // NOLINT
return f32(static_cast<double>(value));
}
} // namespace tint::number_suffixes } // namespace tint::number_suffixes
#endif // SRC_TINT_NUMBER_H_ #endif // SRC_TINT_NUMBER_H_

View File

@ -986,41 +986,58 @@ class ProgramBuilder {
/// @param source the source information /// @param source the source information
/// @param value the float value /// @param value the float value
/// @return a Scalar constructor for the given value /// @return a unsuffixed FloatLiteralExpression for the float value
const ast::FloatLiteralExpression* Expr(const Source& source, f32 value) { const ast::FloatLiteralExpression* Expr(const Source& source, float value) {
return create<ast::FloatLiteralExpression>(source, value); return create<ast::FloatLiteralExpression>(source, static_cast<double>(value),
ast::FloatLiteralExpression::Suffix::kNone);
} }
/// @param value the float value /// @param value the float value
/// @return a Scalar constructor for the given value /// @return a unsuffixed FloatLiteralExpression for the float value
const ast::FloatLiteralExpression* Expr(float value) {
return create<ast::FloatLiteralExpression>(static_cast<double>(value),
ast::FloatLiteralExpression::Suffix::kNone);
}
/// @param source the source information
/// @param value the float value
/// @return a 'f'-suffixed FloatLiteralExpression for the f32 value
const ast::FloatLiteralExpression* Expr(const Source& source, f32 value) {
return create<ast::FloatLiteralExpression>(source, static_cast<double>(value.value),
ast::FloatLiteralExpression::Suffix::kF);
}
/// @param value the float value
/// @return a 'f'-suffixed FloatLiteralExpression for the f32 value
const ast::FloatLiteralExpression* Expr(f32 value) { const ast::FloatLiteralExpression* Expr(f32 value) {
return create<ast::FloatLiteralExpression>(value); return create<ast::FloatLiteralExpression>(static_cast<double>(value.value),
ast::FloatLiteralExpression::Suffix::kF);
} }
/// @param source the source information /// @param source the source information
/// @param value the integer value /// @param value the integer value
/// @return a 'i'-suffixed IntLiteralExpression for the given value /// @return a signed 'i'-suffixed IntLiteralExpression for the i32 value
const ast::IntLiteralExpression* Expr(const Source& source, i32 value) { const ast::IntLiteralExpression* Expr(const Source& source, i32 value) {
return create<ast::IntLiteralExpression>(source, value, return create<ast::IntLiteralExpression>(source, value,
ast::IntLiteralExpression::Suffix::kI); ast::IntLiteralExpression::Suffix::kI);
} }
/// @param value the integer value /// @param value the integer value
/// @return a 'i'-suffixed IntLiteralExpression for the given value /// @return a signed 'i'-suffixed IntLiteralExpression for the i32 value
const ast::IntLiteralExpression* Expr(i32 value) { const ast::IntLiteralExpression* Expr(i32 value) {
return create<ast::IntLiteralExpression>(value, ast::IntLiteralExpression::Suffix::kI); return create<ast::IntLiteralExpression>(value, ast::IntLiteralExpression::Suffix::kI);
} }
/// @param source the source information /// @param source the source information
/// @param value the unsigned int value /// @param value the unsigned int value
/// @return a 'u'-suffixed IntLiteralExpression for the given value /// @return an unsigned 'u'-suffixed IntLiteralExpression for the u32 value
const ast::IntLiteralExpression* Expr(const Source& source, u32 value) { const ast::IntLiteralExpression* Expr(const Source& source, u32 value) {
return create<ast::IntLiteralExpression>(source, value, return create<ast::IntLiteralExpression>(source, value,
ast::IntLiteralExpression::Suffix::kU); ast::IntLiteralExpression::Suffix::kU);
} }
/// @param value the unsigned int value /// @param value the unsigned int value
/// @return a 'u'-suffixed IntLiteralExpression for the given value /// @return an unsigned 'u'-suffixed IntLiteralExpression for the u32 value
const ast::IntLiteralExpression* Expr(u32 value) { const ast::IntLiteralExpression* Expr(u32 value) {
return create<ast::IntLiteralExpression>(value, ast::IntLiteralExpression::Suffix::kU); return create<ast::IntLiteralExpression>(value, ast::IntLiteralExpression::Suffix::kU);
} }

View File

@ -2499,7 +2499,8 @@ TypedExpression FunctionEmitter::MakeExpression(uint32_t id) {
return source_expr; return source_expr;
} }
case SkipReason::kPointSizeBuiltinValue: { case SkipReason::kPointSizeBuiltinValue: {
return {ty_.F32(), create<ast::FloatLiteralExpression>(Source{}, 1.0f)}; return {ty_.F32(), create<ast::FloatLiteralExpression>(
Source{}, 1.0, ast::FloatLiteralExpression::Suffix::kF)};
} }
case SkipReason::kPointSizeBuiltinPointer: case SkipReason::kPointSizeBuiltinPointer:
Fail() << "unhandled use of a pointer to the PointSize builtin, with ID: " << id; Fail() << "unhandled use of a pointer to the PointSize builtin, with ID: " << id;
@ -3982,13 +3983,13 @@ TypedExpression FunctionEmitter::EmitGlslStd450ExtInst(const spvtools::opt::Inst
return {}; return {};
} }
const Type* f32 = eta.type; const Type* f32 = eta.type;
return {f32, return {f32, builder_.MemberAccessor(
builder_.MemberAccessor( builder_.Call(
builder_.Call(Source{}, "refract", Source{}, "refract",
ast::ExpressionList{ ast::ExpressionList{
builder_.vec2<float>(incident.expr, 0.0f), builder_.vec2<tint::f32>(incident.expr, 0.0f),
builder_.vec2<float>(normal.expr, 0.0f), eta.expr}), builder_.vec2<tint::f32>(normal.expr, 0.0f), eta.expr}),
"x")}; "x")};
} }
default: default:
break; break;

View File

@ -1343,7 +1343,9 @@ bool ParserImpl::EmitScalarSpecConstants() {
float float_value; float float_value;
// Copy the bits so we can read them as a float. // Copy the bits so we can read them as a float.
std::memcpy(&float_value, &literal_value, sizeof(float_value)); std::memcpy(&float_value, &literal_value, sizeof(float_value));
return create<ast::FloatLiteralExpression>(Source{}, float_value); return create<ast::FloatLiteralExpression>(
Source{}, static_cast<double>(float_value),
ast::FloatLiteralExpression::Suffix::kF);
}); });
if (ast_expr == nullptr) { if (ast_expr == nullptr) {
return Fail() << " invalid result type for OpSpecConstant " return Fail() << " invalid result type for OpSpecConstant "
@ -1905,18 +1907,22 @@ TypedExpression ParserImpl::MakeConstantExpressionForScalarSpirvConstant(
return Switch( return Switch(
ast_type, ast_type,
[&](const I32*) { [&](const I32*) {
return TypedExpression{ty_.I32(), create<ast::IntLiteralExpression>( return TypedExpression{ty_.I32(),
source, spirv_const->GetS32(), create<ast::IntLiteralExpression>(
ast::IntLiteralExpression::Suffix::kI)}; source, static_cast<int64_t>(spirv_const->GetS32()),
ast::IntLiteralExpression::Suffix::kI)};
}, },
[&](const U32*) { [&](const U32*) {
return TypedExpression{ty_.U32(), create<ast::IntLiteralExpression>( return TypedExpression{ty_.U32(),
source, spirv_const->GetU32(), create<ast::IntLiteralExpression>(
ast::IntLiteralExpression::Suffix::kU)}; source, static_cast<int64_t>(spirv_const->GetU32()),
ast::IntLiteralExpression::Suffix::kU)};
}, },
[&](const F32*) { [&](const F32*) {
return TypedExpression{ return TypedExpression{ty_.F32(),
ty_.F32(), create<ast::FloatLiteralExpression>(source, spirv_const->GetFloat())}; create<ast::FloatLiteralExpression>(
source, static_cast<double>(spirv_const->GetFloat()),
ast::FloatLiteralExpression::Suffix::kF)};
}, },
[&](const Bool*) { [&](const Bool*) {
const bool value = const bool value =
@ -1953,7 +1959,10 @@ const ast::Expression* ParserImpl::MakeNullValue(const Type* type) {
return create<ast::IntLiteralExpression>(Source{}, 0, return create<ast::IntLiteralExpression>(Source{}, 0,
ast::IntLiteralExpression::Suffix::kU); ast::IntLiteralExpression::Suffix::kU);
}, },
[&](const F32*) { return create<ast::FloatLiteralExpression>(Source{}, 0.0f); }, [&](const F32*) {
return create<ast::FloatLiteralExpression>(Source{}, 0,
ast::FloatLiteralExpression::Suffix::kF);
},
[&](const Vector*) { return builder_.Construct(Source{}, type->Build(builder_)); }, [&](const Vector*) { return builder_.Construct(Source{}, type->Build(builder_)); },
[&](const Matrix*) { return builder_.Construct(Source{}, type->Build(builder_)); }, [&](const Matrix*) { return builder_.Construct(Source{}, type->Build(builder_)); },
[&](const Array*) { return builder_.Construct(Source{}, type->Build(builder_)); }, [&](const Array*) { return builder_.Construct(Source{}, type->Build(builder_)); },

View File

@ -94,7 +94,7 @@ enum class LimitCheck {
template <typename T> template <typename T>
LimitCheck check_limits(int64_t value) { LimitCheck check_limits(int64_t value) {
static_assert(std::is_integral_v<T>, "T must be an integer"); static_assert(std::is_integral_v<T>, "T must be an integer");
if (value < static_cast<int64_t>(std::numeric_limits<T>::min())) { if (value < static_cast<int64_t>(std::numeric_limits<T>::lowest())) {
return LimitCheck::kTooSmall; return LimitCheck::kTooSmall;
} }
if (value > static_cast<int64_t>(std::numeric_limits<T>::max())) { if (value > static_cast<int64_t>(std::numeric_limits<T>::max())) {
@ -103,6 +103,19 @@ LimitCheck check_limits(int64_t value) {
return LimitCheck::kWithinLimits; 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} {}
@ -378,23 +391,40 @@ Token Lexer::try_float() {
advance(end - start); advance(end - start);
end_source(source); end_source(source);
auto res = strtod(&at(start), nullptr); double value = strtod(&at(start), nullptr);
// This errors out if a non-zero magnitude is too small to represent in a const double magnitude = std::abs(value);
// float. It can't be represented faithfully in an f32.
const auto magnitude = std::fabs(res); if (has_f_suffix) {
if (0.0 < magnitude && magnitude < static_cast<double>(std::numeric_limits<float>::min())) { // This errors out if a non-zero magnitude is too small to represent in a
return {Token::Type::kError, source, // float. It can't be represented faithfully in an f32.
"f32 (" + str + ") magnitude too small, not representable"}; if (0.0 < magnitude && magnitude < static_cast<double>(std::numeric_limits<float>::min())) {
} return {Token::Type::kError, source, "magnitude too small to be represented as f32"};
// This handles if the number is really large negative number }
if (res < static_cast<double>(std::numeric_limits<float>::lowest())) { switch (check_limits<float>(value)) {
return {Token::Type::kError, source, "f32 (" + str + ") too large (negative)"}; case LimitCheck::kTooSmall:
} return {Token::Type::kError, source, "value too small for f32"};
if (res > static_cast<double>(std::numeric_limits<float>::max())) { case LimitCheck::kTooLarge:
return {Token::Type::kError, source, "f32 (" + str + ") too large (positive)"}; return {Token::Type::kError, source, "value too large for f32"};
default:
return {Token::Type::kFloatFLiteral, source, value};
}
} }
return {source, static_cast<float>(res)}; // TODO(crbug.com/tint/1504): Properly support abstract float:
// Change `AbstractFloatType` to `double`, update errors to say 'abstract int'.
using AbstractFloatType = float;
if (0.0 < magnitude &&
magnitude < static_cast<double>(std::numeric_limits<AbstractFloatType>::min())) {
return {Token::Type::kError, source, "magnitude too small to be represented as f32"};
}
switch (check_limits<AbstractFloatType>(value)) {
case LimitCheck::kTooSmall:
return {Token::Type::kError, source, "value too small for f32"};
case LimitCheck::kTooLarge:
return {Token::Type::kError, source, "value too large for f32"};
default:
return {Token::Type::kFloatLiteral, source, value};
}
} }
Token Lexer::try_hex_float() { Token Lexer::try_hex_float() {
@ -566,6 +596,7 @@ Token Lexer::try_hex_float() {
uint32_t input_exponent = 0; // Defaults to 0 if not present uint32_t input_exponent = 0; // Defaults to 0 if not present
int32_t exponent_sign = 1; int32_t exponent_sign = 1;
// If the 'p' part is present, the rest of the exponent must exist. // If the 'p' part is present, the rest of the exponent must exist.
bool has_f_suffix = false;
if (has_exponent) { if (has_exponent) {
// Parse the rest of the exponent. // Parse the rest of the exponent.
// (+|-)? // (+|-)?
@ -597,6 +628,7 @@ Token Lexer::try_hex_float() {
// when the exponent is present. Otherwise it will look like // when the exponent is present. Otherwise it will look like
// one of the mantissa digits. // one of the mantissa digits.
if (end < length() && matches(end, "f")) { if (end < length() && matches(end, "f")) {
has_f_suffix = true;
end++; end++;
} }
@ -680,25 +712,22 @@ Token Lexer::try_hex_float() {
result_u32 |= (static_cast<uint32_t>(signed_exponent) & kExponentMask) << kExponentLeftShift; result_u32 |= (static_cast<uint32_t>(signed_exponent) & kExponentMask) << kExponentLeftShift;
// Reinterpret as float and return // Reinterpret as float and return
float result; float result_f32;
std::memcpy(&result, &result_u32, sizeof(result)); std::memcpy(&result_f32, &result_u32, sizeof(result_f32));
return {source, static_cast<float>(result)}; double result_f64 = static_cast<double>(result_f32);
return {has_f_suffix ? Token::Type::kFloatFLiteral : Token::Type::kFloatLiteral, source,
result_f64};
} }
Token Lexer::build_token_from_int_if_possible(Source source, Token Lexer::build_token_from_int_if_possible(Source source, size_t start, int32_t base) {
size_t start,
size_t end,
int32_t base) {
int64_t res = strtoll(&at(start), nullptr, base); int64_t res = strtoll(&at(start), nullptr, base);
auto str = [&] { return std::string{substr(start, end - start)}; };
if (matches(pos(), "u")) { if (matches(pos(), "u")) {
switch (check_limits<uint32_t>(res)) { switch (check_limits<uint32_t>(res)) {
case LimitCheck::kTooSmall: case LimitCheck::kTooSmall:
return {Token::Type::kError, source, "unsigned literal cannot be negative"}; return {Token::Type::kError, source, "unsigned literal cannot be negative"};
case LimitCheck::kTooLarge: case LimitCheck::kTooLarge:
return {Token::Type::kError, source, str() + " too large for u32"}; return {Token::Type::kError, source, "value too large for u32"};
default: default:
advance(1); advance(1);
end_source(source); end_source(source);
@ -709,9 +738,9 @@ Token Lexer::build_token_from_int_if_possible(Source source,
if (matches(pos(), "i")) { if (matches(pos(), "i")) {
switch (check_limits<int32_t>(res)) { switch (check_limits<int32_t>(res)) {
case LimitCheck::kTooSmall: case LimitCheck::kTooSmall:
return {Token::Type::kError, source, str() + " too small for i32"}; return {Token::Type::kError, source, "value too small for i32"};
case LimitCheck::kTooLarge: case LimitCheck::kTooLarge:
return {Token::Type::kError, source, str() + " too large for i32"}; return {Token::Type::kError, source, "value too large for i32"};
default: default:
break; break;
} }
@ -725,9 +754,9 @@ Token Lexer::build_token_from_int_if_possible(Source source,
using AbstractIntType = int32_t; using AbstractIntType = int32_t;
switch (check_limits<AbstractIntType>(res)) { switch (check_limits<AbstractIntType>(res)) {
case LimitCheck::kTooSmall: case LimitCheck::kTooSmall:
return {Token::Type::kError, source, str() + " too small for i32"}; return {Token::Type::kError, source, "value too small for i32"};
case LimitCheck::kTooLarge: case LimitCheck::kTooLarge:
return {Token::Type::kError, source, str() + " too large for i32"}; return {Token::Type::kError, source, "value too large for i32"};
default: default:
end_source(source); end_source(source);
return {Token::Type::kIntLiteral, source, res}; return {Token::Type::kIntLiteral, source, res};
@ -769,7 +798,7 @@ Token Lexer::try_hex_integer() {
advance(end - start); advance(end - start);
return build_token_from_int_if_possible(source, start, end, 16); return build_token_from_int_if_possible(source, start, 16);
} }
Token Lexer::try_integer() { Token Lexer::try_integer() {
@ -812,7 +841,7 @@ Token Lexer::try_integer() {
advance(end - start); advance(end - start);
return build_token_from_int_if_possible(source, start, end, 10); return build_token_from_int_if_possible(source, start, 10);
} }
Token Lexer::try_ident() { Token Lexer::try_ident() {

View File

@ -43,7 +43,8 @@ class Lexer {
/// @returns uninitialized token on success, or error /// @returns uninitialized token on success, or error
Token skip_comment(); Token skip_comment();
Token build_token_from_int_if_possible(Source source, size_t start, size_t end, int32_t base); Token build_token_from_int_if_possible(Source source, size_t start, int32_t base);
Token check_keyword(const Source&, std::string_view); Token check_keyword(const Source&, std::string_view);
/// The try_* methods have the following in common: /// The try_* methods have the following in common:

View File

@ -303,7 +303,7 @@ TEST_F(LexerTest, Null_InIdentifier_IsError) {
struct FloatData { struct FloatData {
const char* input; const char* input;
float result; double result;
}; };
inline std::ostream& operator<<(std::ostream& out, FloatData data) { inline std::ostream& operator<<(std::ostream& out, FloatData data) {
out << std::string(data.input); out << std::string(data.input);
@ -316,8 +316,12 @@ TEST_P(FloatTest, Parse) {
Lexer l(&file); Lexer l(&file);
auto t = l.next(); auto t = l.next();
EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral)); if (std::string(params.input).back() == 'f') {
EXPECT_EQ(t.to_f32(), params.result); EXPECT_TRUE(t.Is(Token::Type::kFloatFLiteral));
} else {
EXPECT_TRUE(t.Is(Token::Type::kFloatLiteral));
}
EXPECT_EQ(t.to_f64(), params.result);
EXPECT_EQ(t.source().range.begin.line, 1u); EXPECT_EQ(t.source().range.begin.line, 1u);
EXPECT_EQ(t.source().range.begin.column, 1u); EXPECT_EQ(t.source().range.begin.column, 1u);
EXPECT_EQ(t.source().range.end.line, 1u); EXPECT_EQ(t.source().range.end.line, 1u);
@ -330,63 +334,63 @@ INSTANTIATE_TEST_SUITE_P(LexerTest,
FloatTest, FloatTest,
testing::Values( testing::Values(
// No decimal, with 'f' suffix // No decimal, with 'f' suffix
FloatData{"0f", 0.0f}, FloatData{"0f", 0.0},
FloatData{"1f", 1.0f}, FloatData{"1f", 1.0},
FloatData{"-0f", 0.0f}, FloatData{"-0f", 0.0},
FloatData{"-1f", -1.0f}, FloatData{"-1f", -1.0},
// Zero, with decimal. // Zero, with decimal.
FloatData{"0.0", 0.0f}, FloatData{"0.0", 0.0},
FloatData{"0.", 0.0f}, FloatData{"0.", 0.0},
FloatData{".0", 0.0f}, FloatData{".0", 0.0},
FloatData{"-0.0", 0.0f}, FloatData{"-0.0", 0.0},
FloatData{"-0.", 0.0f}, FloatData{"-0.", 0.0},
FloatData{"-.0", 0.0f}, FloatData{"-.0", 0.0},
// Zero, with decimal and 'f' suffix // Zero, with decimal and 'f' suffix
FloatData{"0.0f", 0.0f}, FloatData{"0.0f", 0.0},
FloatData{"0.f", 0.0f}, FloatData{"0.f", 0.0},
FloatData{".0f", 0.0f}, FloatData{".0f", 0.0},
FloatData{"-0.0f", 0.0f}, FloatData{"-0.0f", 0.0},
FloatData{"-0.f", 0.0f}, FloatData{"-0.f", 0.0},
FloatData{"-.0", 0.0f}, FloatData{"-.0", 0.0},
// Non-zero with decimal // Non-zero with decimal
FloatData{"5.7", 5.7f}, FloatData{"5.7", 5.7},
FloatData{"5.", 5.f}, FloatData{"5.", 5.},
FloatData{".7", .7f}, FloatData{".7", .7},
FloatData{"-5.7", -5.7f}, FloatData{"-5.7", -5.7},
FloatData{"-5.", -5.f}, FloatData{"-5.", -5.},
FloatData{"-.7", -.7f}, FloatData{"-.7", -.7},
// Non-zero with decimal and 'f' suffix // Non-zero with decimal and 'f' suffix
FloatData{"5.7f", 5.7f}, FloatData{"5.7f", 5.7},
FloatData{"5.f", 5.f}, FloatData{"5.f", 5.},
FloatData{".7f", .7f}, FloatData{".7f", .7},
FloatData{"-5.7f", -5.7f}, FloatData{"-5.7f", -5.7},
FloatData{"-5.f", -5.f}, FloatData{"-5.f", -5.},
FloatData{"-.7f", -.7f}, FloatData{"-.7f", -.7},
// No decimal, with exponent // No decimal, with exponent
FloatData{"1e5", 1e5f}, FloatData{"1e5", 1e5},
FloatData{"1E5", 1e5f}, FloatData{"1E5", 1e5},
FloatData{"1e-5", 1e-5f}, FloatData{"1e-5", 1e-5},
FloatData{"1E-5", 1e-5f}, FloatData{"1E-5", 1e-5},
// No decimal, with exponent and 'f' suffix // No decimal, with exponent and 'f' suffix
FloatData{"1e5f", 1e5f}, FloatData{"1e5f", 1e5},
FloatData{"1E5f", 1e5f}, FloatData{"1E5f", 1e5},
FloatData{"1e-5f", 1e-5f}, FloatData{"1e-5f", 1e-5},
FloatData{"1E-5f", 1e-5f}, FloatData{"1E-5f", 1e-5},
// With decimal and exponents // With decimal and exponents
FloatData{"0.2e+12", 0.2e12f}, FloatData{"0.2e+12", 0.2e12},
FloatData{"1.2e-5", 1.2e-5f}, FloatData{"1.2e-5", 1.2e-5},
FloatData{"2.57e23", 2.57e23f}, FloatData{"2.57e23", 2.57e23},
FloatData{"2.5e+0", 2.5f}, FloatData{"2.5e+0", 2.5},
FloatData{"2.5e-0", 2.5f}, FloatData{"2.5e-0", 2.5},
// With decimal and exponents and 'f' suffix // With decimal and exponents and 'f' suffix
FloatData{"0.2e+12f", 0.2e12f}, FloatData{"0.2e+12f", 0.2e12},
FloatData{"1.2e-5f", 1.2e-5f}, FloatData{"1.2e-5f", 1.2e-5},
FloatData{"2.57e23f", 2.57e23f}, FloatData{"2.57e23f", 2.57e23},
FloatData{"2.5e+0f", 2.5f}, FloatData{"2.5e+0f", 2.5},
FloatData{"2.5e-0f", 2.5f})); FloatData{"2.5e-0f", 2.5}));
using FloatTest_Invalid = testing::TestWithParam<const char*>; using FloatTest_Invalid = testing::TestWithParam<const char*>;
TEST_P(FloatTest_Invalid, Handles) { TEST_P(FloatTest_Invalid, Handles) {
@ -676,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(), "0x80000000 too large for i32"); EXPECT_EQ(t.to_str(), "value too large for i32");
} }
TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) { TEST_F(LexerTest, IntegerTest_HexSignedTooSmall) {
@ -685,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(), "-0x8000000F too small for i32"); EXPECT_EQ(t.to_str(), "value too small for i32");
} }
TEST_F(LexerTest, IntegerTest_HexSignedTooManyDigits) { TEST_F(LexerTest, IntegerTest_HexSignedTooManyDigits) {

View File

@ -2790,7 +2790,12 @@ Maybe<const ast::LiteralExpression*> ParserImpl::const_literal() {
ast::IntLiteralExpression::Suffix::kU); ast::IntLiteralExpression::Suffix::kU);
} }
if (match(Token::Type::kFloatLiteral)) { if (match(Token::Type::kFloatLiteral)) {
return create<ast::FloatLiteralExpression>(t.source(), t.to_f32()); return create<ast::FloatLiteralExpression>(t.source(), t.to_f64(),
ast::FloatLiteralExpression::Suffix::kNone);
}
if (match(Token::Type::kFloatFLiteral)) {
return create<ast::FloatLiteralExpression>(t.source(), t.to_f64(),
ast::FloatLiteralExpression::Suffix::kF);
} }
if (match(Token::Type::kTrue)) { if (match(Token::Type::kTrue)) {
return create<ast::BoolLiteralExpression>(t.source(), true); return create<ast::BoolLiteralExpression>(t.source(), true);

View File

@ -31,10 +31,14 @@ TEST_F(ParserImplTest, ConstExpr_TypeDecl) {
ASSERT_EQ(t->args.size(), 2u); ASSERT_EQ(t->args.size(), 2u);
ASSERT_TRUE(t->args[0]->Is<ast::FloatLiteralExpression>()); ASSERT_TRUE(t->args[0]->Is<ast::FloatLiteralExpression>());
EXPECT_FLOAT_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->value, 1.); EXPECT_DOUBLE_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->value, 1.);
EXPECT_EQ(t->args[0]->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kNone);
ASSERT_TRUE(t->args[1]->Is<ast::FloatLiteralExpression>()); ASSERT_TRUE(t->args[1]->Is<ast::FloatLiteralExpression>());
EXPECT_FLOAT_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->value, 2.); EXPECT_DOUBLE_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->value, 2.);
EXPECT_EQ(t->args[1]->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kNone);
} }
TEST_F(ParserImplTest, ConstExpr_TypeDecl_Empty) { TEST_F(ParserImplTest, ConstExpr_TypeDecl_Empty) {

View File

@ -26,7 +26,7 @@ namespace {
// - 0 sign if sign is 0, 1 otherwise // - 0 sign if sign is 0, 1 otherwise
// - 'exponent_bits' is placed in the exponent space. // - 'exponent_bits' is placed in the exponent space.
// So, the exponent bias must already be included. // So, the exponent bias must already be included.
float MakeFloat(int sign, int biased_exponent, int mantissa) { float MakeFloat(uint32_t sign, uint32_t biased_exponent, uint32_t mantissa) {
const uint32_t sign_bit = sign ? 0x80000000u : 0u; const uint32_t sign_bit = sign ? 0x80000000u : 0u;
// The binary32 exponent is 8 bits, just below the sign. // The binary32 exponent is 8 bits, just below the sign.
const uint32_t exponent_bits = (biased_exponent & 0xffu) << 23; const uint32_t exponent_bits = (biased_exponent & 0xffu) << 23;
@ -41,6 +41,25 @@ float MakeFloat(int sign, int biased_exponent, int mantissa) {
return result; return result;
} }
// Makes an IEEE 754 binary64 floating point number with
// - 0 sign if sign is 0, 1 otherwise
// - 'exponent_bits' is placed in the exponent space.
// So, the exponent bias must already be included.
double MakeDouble(uint64_t sign, uint64_t biased_exponent, uint64_t mantissa) {
const uint64_t sign_bit = sign ? 0x8000000000000000u : 0u;
// The binary64 exponent is 11 bits, just below the sign.
const uint64_t exponent_bits = (biased_exponent & 0x7FFull) << 52;
// The mantissa is the bottom 52 bits.
const uint64_t mantissa_bits = (mantissa & 0xFFFFFFFFFFFFFull);
uint64_t bits = sign_bit | exponent_bits | mantissa_bits;
double result = 0.0;
static_assert(sizeof(result) == sizeof(bits),
"expected double and uint64_t to be the same size");
std::memcpy(&result, &bits, sizeof(bits));
return result;
}
TEST_F(ParserImplTest, ConstLiteral_Int) { TEST_F(ParserImplTest, ConstLiteral_Int) {
{ {
auto p = parser("234"); auto p = parser("234");
@ -126,10 +145,26 @@ TEST_F(ParserImplTest, ConstLiteral_Float) {
EXPECT_FALSE(p->has_error()) << p->error(); EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(c.value, nullptr); ASSERT_NE(c.value, nullptr);
ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, 234e12f); 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}})); 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) { TEST_F(ParserImplTest, ConstLiteral_InvalidFloat_IncompleteExponent) {
auto p = parser("1.0e+"); auto p = parser("1.0e+");
auto c = p->const_literal(); auto c = p->const_literal();
@ -144,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: f32 (1e-256) magnitude too small, not representable"); EXPECT_EQ(p->error(), "1:1: magnitude too small to be represented as f32");
ASSERT_EQ(c.value, nullptr); ASSERT_EQ(c.value, nullptr);
} }
@ -153,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: f32 (-1.2e+256) too large (negative)"); EXPECT_EQ(p->error(), "1:1: value too small for f32");
ASSERT_EQ(c.value, nullptr); ASSERT_EQ(c.value, nullptr);
} }
@ -162,22 +197,15 @@ 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: f32 (1.2e+256) too large (positive)"); EXPECT_EQ(p->error(), "1:1: value too large for f32");
ASSERT_EQ(c.value, nullptr); ASSERT_EQ(c.value, nullptr);
} }
// Returns true if the given non-Nan float numbers are equal.
bool FloatEqual(float a, float b) {
// Avoid Clang complaining about equality test on float.
// -Wfloat-equal.
return (a <= b) && (a >= b);
}
struct FloatLiteralTestCase { struct FloatLiteralTestCase {
std::string input; std::string input;
float expected; double expected;
bool operator==(const FloatLiteralTestCase& other) const { bool operator==(const FloatLiteralTestCase& other) const {
return (input == other.input) && FloatEqual(expected, other.expected); return (input == other.input) && std::equal_to<double>()(expected, other.expected);
} }
}; };
@ -197,22 +225,28 @@ TEST_P(ParserImplFloatLiteralTest, Parse) {
EXPECT_FALSE(p->has_error()) << p->error(); EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(c.value, nullptr); ASSERT_NE(c.value, nullptr);
ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, params.expected); EXPECT_DOUBLE_EQ(c->As<ast::FloatLiteralExpression>()->value, params.expected);
if (params.input.back() == 'f') {
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kF);
} else {
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kNone);
}
} }
using FloatLiteralTestCaseList = std::vector<FloatLiteralTestCase>; using FloatLiteralTestCaseList = std::vector<FloatLiteralTestCase>;
FloatLiteralTestCaseList DecimalFloatCases() { FloatLiteralTestCaseList DecimalFloatCases() {
return FloatLiteralTestCaseList{ return FloatLiteralTestCaseList{
{"0.0", 0.0f}, // Zero {"0.0", 0.0}, // Zero
{"1.0", 1.0f}, // One {"1.0", 1.0}, // One
{"-1.0", -1.0f}, // MinusOne {"-1.0", -1.0}, // MinusOne
{"1000000000.0", 1e9f}, // Billion {"1000000000.0", 1e9}, // Billion
{"-0.0", std::copysign(0.0f, -5.0f)}, // NegativeZero {"-0.0", std::copysign(0.0, -5.0)}, // NegativeZero
{"0.0", MakeFloat(0, 0, 0)}, // Zero {"0.0", MakeDouble(0, 0, 0)}, // Zero
{"-0.0", MakeFloat(1, 0, 0)}, // NegativeZero {"-0.0", MakeDouble(1, 0, 0)}, // NegativeZero
{"1.0", MakeFloat(0, 127, 0)}, // One {"1.0", MakeDouble(0, 1023, 0)}, // One
{"-1.0", MakeFloat(1, 127, 0)}, // NegativeOne {"-1.0", MakeDouble(1, 1023, 0)}, // NegativeOne
}; };
} }
@ -220,75 +254,76 @@ INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_Float,
ParserImplFloatLiteralTest, ParserImplFloatLiteralTest,
testing::ValuesIn(DecimalFloatCases())); testing::ValuesIn(DecimalFloatCases()));
const float NegInf = MakeFloat(1, 255, 0); const double NegInf = MakeDouble(1, 0x7FF, 0);
const float PosInf = MakeFloat(0, 255, 0); const double PosInf = MakeDouble(0, 0x7FF, 0);
FloatLiteralTestCaseList HexFloatCases() { FloatLiteralTestCaseList HexFloatCases() {
return FloatLiteralTestCaseList{ return FloatLiteralTestCaseList{
// Regular numbers // Regular numbers
{"0x0p+0", 0.f}, {"0x0p+0", 0.0},
{"0x1p+0", 1.f}, {"0x1p+0", 1.0},
{"0x1p+1", 2.f}, {"0x1p+1", 2.0},
{"0x1.8p+1", 3.f}, {"0x1.8p+1", 3.0},
{"0x1.99999ap-4", 0.1f}, {"0x1.99999ap-4", 0.10000000149011612},
{"0x1p-1", 0.5f}, {"0x1p-1", 0.5},
{"0x1p-2", 0.25f}, {"0x1p-2", 0.25},
{"0x1.8p-1", 0.75f}, {"0x1.8p-1", 0.75},
{"-0x0p+0", -0.f}, {"-0x0p+0", -0.0},
{"-0x1p+0", -1.f}, {"-0x1p+0", -1.0},
{"-0x1p-1", -0.5f}, {"-0x1p-1", -0.5},
{"-0x1p-2", -0.25f}, {"-0x1p-2", -0.25},
{"-0x1.8p-1", -0.75f}, {"-0x1.8p-1", -0.75},
// Large numbers // Large numbers
{"0x1p+9", 512.f}, {"0x1p+9", 512.0},
{"0x1p+10", 1024.f}, {"0x1p+10", 1024.0},
{"0x1.02p+10", 1024.f + 8.f}, {"0x1.02p+10", 1024.0 + 8.0},
{"-0x1p+9", -512.f}, {"-0x1p+9", -512.0},
{"-0x1p+10", -1024.f}, {"-0x1p+10", -1024.0},
{"-0x1.02p+10", -1024.f - 8.f}, {"-0x1.02p+10", -1024.0 - 8.0},
// Small numbers // Small numbers
{"0x1p-9", 1.0f / 512.f}, {"0x1p-9", 1.0 / 512.0},
{"0x1p-10", 1.0f / 1024.f}, {"0x1p-10", 1.0 / 1024.0},
{"0x1.02p-3", 1.0f / 1024.f + 1.0f / 8.f}, {"0x1.02p-3", 1.0 / 1024.0 + 1.0 / 8.0},
{"-0x1p-9", 1.0f / -512.f}, {"-0x1p-9", 1.0 / -512.0},
{"-0x1p-10", 1.0f / -1024.f}, {"-0x1p-10", 1.0 / -1024.0},
{"-0x1.02p-3", 1.0f / -1024.f - 1.0f / 8.f}, {"-0x1.02p-3", 1.0 / -1024.0 - 1.0 / 8.0},
// Near lowest non-denorm // Near lowest non-denorm
{"0x1p-124", std::ldexp(1.f * 8.f, -127)}, {"0x1p-124", std::ldexp(1.0 * 8.0, -127)},
{"0x1p-125", std::ldexp(1.f * 4.f, -127)}, {"0x1p-125", std::ldexp(1.0 * 4.0, -127)},
{"-0x1p-124", -std::ldexp(1.f * 8.f, -127)}, {"-0x1p-124", -std::ldexp(1.0 * 8.0, -127)},
{"-0x1p-125", -std::ldexp(1.f * 4.f, -127)}, {"-0x1p-125", -std::ldexp(1.0 * 4.0, -127)},
// Lowest non-denorm // Lowest non-denorm
{"0x1p-126", std::ldexp(1.f * 2.f, -127)}, {"0x1p-126", std::ldexp(1.0 * 2.0, -127)},
{"-0x1p-126", -std::ldexp(1.f * 2.f, -127)}, {"-0x1p-126", -std::ldexp(1.0 * 2.0, -127)},
// Denormalized values // Denormalized values
{"0x1p-127", std::ldexp(1.f, -127)}, {"0x1p-127", std::ldexp(1.0, -127)},
{"0x1p-128", std::ldexp(1.f / 2.f, -127)}, {"0x1p-128", std::ldexp(1.0 / 2.0, -127)},
{"0x1p-129", std::ldexp(1.f / 4.f, -127)}, {"0x1p-129", std::ldexp(1.0 / 4.0, -127)},
{"0x1p-130", std::ldexp(1.f / 8.f, -127)}, {"0x1p-130", std::ldexp(1.0 / 8.0, -127)},
{"-0x1p-127", -std::ldexp(1.f, -127)}, {"-0x1p-127", -std::ldexp(1.0, -127)},
{"-0x1p-128", -std::ldexp(1.f / 2.f, -127)}, {"-0x1p-128", -std::ldexp(1.0 / 2.0, -127)},
{"-0x1p-129", -std::ldexp(1.f / 4.f, -127)}, {"-0x1p-129", -std::ldexp(1.0 / 4.0, -127)},
{"-0x1p-130", -std::ldexp(1.f / 8.f, -127)}, {"-0x1p-130", -std::ldexp(1.0 / 8.0, -127)},
{"0x1.8p-127", std::ldexp(1.f, -127) + (std::ldexp(1.f, -127) / 2.f)}, {"0x1.8p-127", std::ldexp(1.0, -127) + (std::ldexp(1.0, -127) / 2.0)},
{"0x1.8p-128", std::ldexp(1.f, -127) / 2.f + (std::ldexp(1.f, -127) / 4.f)}, {"0x1.8p-128", std::ldexp(1.0, -127) / 2.0 + (std::ldexp(1.0, -127) / 4.0)},
{"0x1p-149", MakeFloat(0, 0, 1)}, // +SmallestDenormal // F32 extremities
{"0x1p-148", MakeFloat(0, 0, 2)}, // +BiggerDenormal {"0x1p-149", static_cast<double>(MakeFloat(0, 0, 1))}, // +SmallestDenormal
{"0x1.fffffcp-127", MakeFloat(0, 0, 0x7fffff)}, // +LargestDenormal {"0x1p-148", static_cast<double>(MakeFloat(0, 0, 2))}, // +BiggerDenormal
{"-0x1p-149", MakeFloat(1, 0, 1)}, // -SmallestDenormal {"0x1.fffffcp-127", static_cast<double>(MakeFloat(0, 0, 0x7fffff))}, // +LargestDenormal
{"-0x1p-148", MakeFloat(1, 0, 2)}, // -BiggerDenormal {"-0x1p-149", static_cast<double>(MakeFloat(1, 0, 1))}, // -SmallestDenormal
{"-0x1.fffffcp-127", MakeFloat(1, 0, 0x7fffff)}, // -LargestDenormal {"-0x1p-148", static_cast<double>(MakeFloat(1, 0, 2))}, // -BiggerDenormal
{"-0x1.fffffcp-127", static_cast<double>(MakeFloat(1, 0, 0x7fffff))}, // -LargestDenormal
{"0x1.2bfaf8p-127", MakeFloat(0, 0, 0xcafebe)}, // +Subnormal {"0x1.2bfaf8p-127", static_cast<double>(MakeFloat(0, 0, 0xcafebe))}, // +Subnormal
{"-0x1.2bfaf8p-127", MakeFloat(1, 0, 0xcafebe)}, // -Subnormal {"-0x1.2bfaf8p-127", static_cast<double>(MakeFloat(1, 0, 0xcafebe))}, // -Subnormal
{"0x1.55554p-130", MakeFloat(0, 0, 0xaaaaa)}, // +Subnormal {"0x1.55554p-130", static_cast<double>(MakeFloat(0, 0, 0xaaaaa))}, // +Subnormal
{"-0x1.55554p-130", MakeFloat(1, 0, 0xaaaaa)}, // -Subnormal {"-0x1.55554p-130", static_cast<double>(MakeFloat(1, 0, 0xaaaaa))}, // -Subnormal
// Nan -> Infinity // Nan -> Infinity
{"0x1.8p+128", PosInf}, {"0x1.8p+128", PosInf},
@ -318,76 +353,76 @@ FloatLiteralTestCaseList HexFloatCases() {
{"0x1.0p2147483520", PosInf}, // INT_MAX - 127 (largest valid exponent) {"0x1.0p2147483520", PosInf}, // INT_MAX - 127 (largest valid exponent)
// Underflow -> Zero // Underflow -> Zero
{"0x1p-500", 0.f}, // Exponent underflows {"0x1p-500", 0.0}, // Exponent underflows
{"-0x1p-500", -0.f}, {"-0x1p-500", -0.0},
{"0x0.00000000001p-126", 0.f}, // Fraction causes underflow {"0x0.00000000001p-126", 0.0}, // Fraction causes underflow
{"-0x0.0000000001p-127", -0.f}, {"-0x0.0000000001p-127", -0.0},
{"0x0.01p-142", 0.f}, {"0x0.01p-142", 0.0},
{"-0x0.01p-142", -0.f}, // Fraction causes additional underflow {"-0x0.01p-142", -0.0}, // Fraction causes additional underflow
{"0x1.0p-2147483520", 0}, // -(INT_MAX - 127) (smallest valid exponent) {"0x1.0p-2147483520", 0}, // -(INT_MAX - 127) (smallest valid exponent)
// Zero with non-zero exponent -> Zero // Zero with non-zero exponent -> Zero
{"0x0p+0", 0.f}, {"0x0p+0", 0.0},
{"0x0p+1", 0.f}, {"0x0p+1", 0.0},
{"0x0p-1", 0.f}, {"0x0p-1", 0.0},
{"0x0p+9999999999", 0.f}, {"0x0p+9999999999", 0.0},
{"0x0p-9999999999", 0.f}, {"0x0p-9999999999", 0.0},
// Same, but with very large positive exponents that would cause overflow // Same, but with very large positive exponents that would cause overflow
// if the mantissa were non-zero. // if the mantissa were non-zero.
{"0x0p+4000000000", 0.f}, // 4 billion: {"0x0p+4000000000", 0.0}, // 4 billion:
{"0x0p+40000000000", 0.f}, // 40 billion {"0x0p+40000000000", 0.0}, // 40 billion
{"-0x0p+40000000000", 0.f}, // As above 2, but negative mantissa {"-0x0p+40000000000", 0.0}, // As above 2, but negative mantissa
{"-0x0p+400000000000", 0.f}, {"-0x0p+400000000000", 0.0},
{"0x0.00p+4000000000", 0.f}, // As above 4, but with fractional part {"0x0.00p+4000000000", 0.0}, // As above 4, but with fractional part
{"0x0.00p+40000000000", 0.f}, {"0x0.00p+40000000000", 0.0},
{"-0x0.00p+40000000000", 0.f}, {"-0x0.00p+40000000000", 0.0},
{"-0x0.00p+400000000000", 0.f}, {"-0x0.00p+400000000000", 0.0},
{"0x0p-4000000000", 0.f}, // As above 8, but with negative exponents {"0x0p-4000000000", 0.0}, // As above 8, but with negative exponents
{"0x0p-40000000000", 0.f}, {"0x0p-40000000000", 0.0},
{"-0x0p-40000000000", 0.f}, {"-0x0p-40000000000", 0.0},
{"-0x0p-400000000000", 0.f}, {"-0x0p-400000000000", 0.0},
{"0x0.00p-4000000000", 0.f}, {"0x0.00p-4000000000", 0.0},
{"0x0.00p-40000000000", 0.f}, {"0x0.00p-40000000000", 0.0},
{"-0x0.00p-40000000000", 0.f}, {"-0x0.00p-40000000000", 0.0},
{"-0x0.00p-400000000000", 0.f}, {"-0x0.00p-400000000000", 0.0},
// Test parsing // Test parsing
{"0x0p0", 0.f}, {"0x0p0", 0.0},
{"0x0p-0", 0.f}, {"0x0p-0", 0.0},
{"0x0p+000", 0.f}, {"0x0p+000", 0.0},
{"0x00000000000000p+000000000000000", 0.f}, {"0x00000000000000p+000000000000000", 0.0},
{"0x00000000000000p-000000000000000", 0.f}, {"0x00000000000000p-000000000000000", 0.0},
{"0x00000000000001p+000000000000000", 1.f}, {"0x00000000000001p+000000000000000", 1.0},
{"0x00000000000001p-000000000000000", 1.f}, {"0x00000000000001p-000000000000000", 1.0},
{"0x0000000000000000000001.99999ap-000000000000000004", 0.1f}, {"0x0000000000000000000001.99999ap-000000000000000004", 0.10000000149011612},
{"0x2p+0", 2.f}, {"0x2p+0", 2.0},
{"0xFFp+0", 255.f}, {"0xFFp+0", 255.0},
{"0x0.8p+0", 0.5f}, {"0x0.8p+0", 0.5},
{"0x0.4p+0", 0.25f}, {"0x0.4p+0", 0.25},
{"0x0.4p+1", 2 * 0.25f}, {"0x0.4p+1", 2 * 0.25},
{"0x0.4p+2", 4 * 0.25f}, {"0x0.4p+2", 4 * 0.25},
{"0x123Ep+1", 9340.f}, {"0x123Ep+1", 9340.0},
{"-0x123Ep+1", -9340.f}, {"-0x123Ep+1", -9340.0},
{"0x1a2b3cP12", 7.024656e+09f}, {"0x1a2b3cP12", 7.024656384e+09},
{"-0x1a2b3cP12", -7.024656e+09f}, {"-0x1a2b3cP12", -7.024656384e+09},
// Examples without a binary exponent part. // Examples without a binary exponent part.
{"0x1.", 1.0f}, {"0x1.", 1.0},
{"0x.8", 0.5f}, {"0x.8", 0.5},
{"0x1.8", 1.5f}, {"0x1.8", 1.5},
{"-0x1.", -1.0f}, {"-0x1.", -1.0},
{"-0x.8", -0.5f}, {"-0x.8", -0.5},
{"-0x1.8", -1.5f}, {"-0x1.8", -1.5},
// Examples with a binary exponent and a 'f' suffix. // Examples with a binary exponent and a 'f' suffix.
{"0x1.p0f", 1.0f}, {"0x1.p0f", 1.0},
{"0x.8p2f", 2.0f}, {"0x.8p2f", 2.0},
{"0x1.8p-1f", 0.75f}, {"0x1.8p-1f", 0.75},
{"0x2p-2f", 0.5f}, // No binary point {"0x2p-2f", 0.5}, // No binary point
{"-0x1.p0f", -1.0f}, {"-0x1.p0f", -1.0},
{"-0x.8p2f", -2.0f}, {"-0x.8p2f", -2.0},
{"-0x1.8p-1f", -0.75f}, {"-0x1.8p-1f", -0.75},
{"-0x2p-2f", -0.5f}, // No binary point {"-0x2p-2f", -0.5}, // No binary point
}; };
} }
INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_HexFloat, INSTANTIATE_TEST_SUITE_P(ParserImplFloatLiteralTest_HexFloat,
@ -501,7 +536,10 @@ TEST_F(ParserImplTest, ConstLiteral_FloatHighest) {
EXPECT_FALSE(p->has_error()) << p->error(); EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(c.value, nullptr); ASSERT_NE(c.value, nullptr);
ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, std::numeric_limits<float>::max()); EXPECT_DOUBLE_EQ(c->As<ast::FloatLiteralExpression>()->value,
std::numeric_limits<float>::max());
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kNone);
EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 42u}})); EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 42u}}));
} }
@ -522,8 +560,10 @@ TEST_F(ParserImplTest, ConstLiteral_FloatLowest) {
EXPECT_FALSE(p->has_error()) << p->error(); EXPECT_FALSE(p->has_error()) << p->error();
ASSERT_NE(c.value, nullptr); ASSERT_NE(c.value, nullptr);
ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>()); ASSERT_TRUE(c->Is<ast::FloatLiteralExpression>());
EXPECT_FLOAT_EQ(c->As<ast::FloatLiteralExpression>()->value, EXPECT_DOUBLE_EQ(c->As<ast::FloatLiteralExpression>()->value,
std::numeric_limits<float>::lowest()); std::numeric_limits<float>::lowest());
EXPECT_EQ(c->As<ast::FloatLiteralExpression>()->suffix,
ast::FloatLiteralExpression::Suffix::kNone);
EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 43u}})); EXPECT_EQ(c->source.range, (Source::Range{{1u, 1u}, {1u, 43u}}));
} }

View File

@ -26,7 +26,9 @@ std::string_view Token::TypeToName(Type type) {
case Token::Type::kIdentifier: case Token::Type::kIdentifier:
return "identifier"; return "identifier";
case Token::Type::kFloatLiteral: case Token::Type::kFloatLiteral:
return "float literal"; return "abstract float literal";
case Token::Type::kFloatFLiteral:
return "'f'-suffixed float literal";
case Token::Type::kIntLiteral: case Token::Type::kIntLiteral:
return "abstract integer literal"; return "abstract integer literal";
case Token::Type::kIntILiteral: case Token::Type::kIntILiteral:
@ -278,8 +280,8 @@ Token::Token(Type type, const Source& source, const char* str)
Token::Token(Type type, const Source& source, int64_t val) Token::Token(Type type, const Source& source, int64_t val)
: type_(type), source_(source), value_(val) {} : type_(type), source_(source), value_(val) {}
Token::Token(const Source& source, float val) Token::Token(Type type, const Source& source, double val)
: type_(Type::kFloatLiteral), source_(source), value_(val) {} : type_(type), source_(source), value_(val) {}
Token::Token(Type type, const Source& source) : type_(type), source_(source) {} Token::Token(Type type, const Source& source) : type_(type), source_(source) {}
@ -304,7 +306,9 @@ bool Token::operator==(std::string_view ident) {
std::string Token::to_str() const { std::string Token::to_str() const {
switch (type_) { switch (type_) {
case Type::kFloatLiteral: case Type::kFloatLiteral:
return std::to_string(std::get<float>(value_)); return std::to_string(std::get<double>(value_));
case Type::kFloatFLiteral:
return std::to_string(std::get<double>(value_)) + "f";
case Type::kIntLiteral: case Type::kIntLiteral:
return std::to_string(std::get<int64_t>(value_)); return std::to_string(std::get<int64_t>(value_));
case Type::kIntILiteral: case Type::kIntILiteral:
@ -322,8 +326,8 @@ std::string Token::to_str() const {
} }
} }
float Token::to_f32() const { double Token::to_f64() const {
return std::get<float>(value_); return std::get<double>(value_);
} }
int64_t Token::to_i64() const { int64_t Token::to_i64() const {

View File

@ -38,8 +38,10 @@ class Token {
/// An identifier /// An identifier
kIdentifier, kIdentifier,
/// A float value /// A float literal with no suffix
kFloatLiteral, kFloatLiteral,
/// A float literal with an 'f' suffix
kFloatFLiteral,
/// An integer literal with no suffix /// An integer literal with no suffix
kIntLiteral, kIntLiteral,
/// An integer literal with an 'i' suffix /// An integer literal with an 'i' suffix
@ -304,10 +306,11 @@ class Token {
/// @param source the source of the token /// @param source the source of the token
/// @param val the source unsigned for the token /// @param val the source unsigned for the token
Token(Type type, const Source& source, int64_t val); Token(Type type, const Source& source, int64_t val);
/// Create a float Token /// Create a double Token
/// @param type the Token::Type of the token
/// @param source the source of the token /// @param source the source of the token
/// @param val the source float for the token /// @param val the source double for the token
Token(const Source& source, float val); Token(Type type, const Source& source, double val);
/// Move constructor /// Move constructor
Token(Token&&); Token(Token&&);
/// Copy constructor /// Copy constructor
@ -341,7 +344,7 @@ class Token {
bool IsLiteral() const { bool IsLiteral() const {
return type_ == Type::kIntLiteral || type_ == Type::kIntILiteral || return type_ == Type::kIntLiteral || type_ == Type::kIntILiteral ||
type_ == Type::kIntULiteral || type_ == Type::kFalse || type_ == Type::kTrue || type_ == Type::kIntULiteral || type_ == Type::kFalse || type_ == Type::kTrue ||
type_ == Type::kFloatLiteral; type_ == Type::kFloatLiteral || type_ == Type::kFloatFLiteral;
} }
/// @returns true if token is a 'matNxM' /// @returns true if token is a 'matNxM'
bool IsMatrix() const { bool IsMatrix() const {
@ -379,8 +382,8 @@ class Token {
std::string to_str() const; std::string to_str() const;
/// Returns the float value of the token. 0 is returned if the token does not /// Returns the float value of the token. 0 is returned if the token does not
/// contain a float value. /// contain a float value.
/// @return float /// @return double
float to_f32() const; double to_f64() const;
/// Returns the int64_t value of the token. 0 is returned if the token does /// Returns the int64_t value of the token. 0 is returned if the token does
/// not contain an integer value. /// not contain an integer value.
/// @return int64_t /// @return int64_t
@ -395,7 +398,7 @@ class Token {
/// The source where the token appeared /// The source where the token appeared
Source source_; Source source_;
/// The value represented by the token /// The value represented by the token
std::variant<int64_t, float, std::string, std::string_view> value_; std::variant<int64_t, double, std::string, std::string_view> value_;
}; };
#ifndef NDEBUG #ifndef NDEBUG

View File

@ -16,46 +16,53 @@
#include <limits> #include <limits>
#include "gtest/gtest.h" #include "gmock/gmock.h"
namespace tint::reader::wgsl { namespace tint::reader::wgsl {
namespace { namespace {
using ::testing::EndsWith;
using ::testing::Not;
using ::testing::StartsWith;
using TokenTest = testing::Test; using TokenTest = testing::Test;
TEST_F(TokenTest, ReturnsF32) { TEST_F(TokenTest, ReturnsF64) {
Token t1(Source{}, -2.345f); Token t1(Token::Type::kFloatFLiteral, Source{}, -2.345);
EXPECT_EQ(t1.to_f32(), -2.345f); EXPECT_EQ(t1.to_f64(), -2.345);
Token t2(Source{}, 2.345f); Token t2(Token::Type::kFloatFLiteral, Source{}, 2.345);
EXPECT_EQ(t2.to_f32(), 2.345f); EXPECT_EQ(t2.to_f64(), 2.345);
} }
TEST_F(TokenTest, ReturnsI32) { TEST_F(TokenTest, ReturnsI32) {
Token t1(Token::Type::kIntILiteral, Source{}, -2345); Token t1(Token::Type::kIntILiteral, Source{}, static_cast<int64_t>(-2345));
EXPECT_EQ(t1.to_i64(), -2345); EXPECT_EQ(t1.to_i64(), -2345);
Token t2(Token::Type::kIntILiteral, Source{}, 2345); Token t2(Token::Type::kIntILiteral, Source{}, static_cast<int64_t>(2345));
EXPECT_EQ(t2.to_i64(), 2345); EXPECT_EQ(t2.to_i64(), 2345);
} }
TEST_F(TokenTest, HandlesMaxI32) { TEST_F(TokenTest, HandlesMaxI32) {
Token t1(Token::Type::kIntILiteral, Source{}, std::numeric_limits<int32_t>::max()); Token t1(Token::Type::kIntILiteral, Source{},
static_cast<int64_t>(std::numeric_limits<int32_t>::max()));
EXPECT_EQ(t1.to_i64(), std::numeric_limits<int32_t>::max()); EXPECT_EQ(t1.to_i64(), std::numeric_limits<int32_t>::max());
} }
TEST_F(TokenTest, HandlesMinI32) { TEST_F(TokenTest, HandlesMinI32) {
Token t1(Token::Type::kIntILiteral, Source{}, std::numeric_limits<int32_t>::min()); Token t1(Token::Type::kIntILiteral, Source{},
static_cast<int64_t>(std::numeric_limits<int32_t>::min()));
EXPECT_EQ(t1.to_i64(), std::numeric_limits<int32_t>::min()); EXPECT_EQ(t1.to_i64(), std::numeric_limits<int32_t>::min());
} }
TEST_F(TokenTest, ReturnsU32) { TEST_F(TokenTest, ReturnsU32) {
Token t2(Token::Type::kIntULiteral, Source{}, 2345u); Token t2(Token::Type::kIntULiteral, Source{}, static_cast<int64_t>(2345u));
EXPECT_EQ(t2.to_i64(), 2345u); EXPECT_EQ(t2.to_i64(), 2345u);
} }
TEST_F(TokenTest, ReturnsMaxU32) { TEST_F(TokenTest, ReturnsMaxU32) {
Token t1(Token::Type::kIntULiteral, Source{}, std::numeric_limits<uint32_t>::max()); Token t1(Token::Type::kIntULiteral, Source{},
static_cast<int64_t>(std::numeric_limits<uint32_t>::max()));
EXPECT_EQ(t1.to_i64(), std::numeric_limits<uint32_t>::max()); EXPECT_EQ(t1.to_i64(), std::numeric_limits<uint32_t>::max());
} }
@ -71,5 +78,19 @@ TEST_F(TokenTest, Source) {
EXPECT_EQ(t.source().range.end.column, 3u); EXPECT_EQ(t.source().range.end.column, 3u);
} }
TEST_F(TokenTest, ToStr) {
double d = 123.0;
int64_t i = 123;
EXPECT_THAT(Token(Token::Type::kFloatLiteral, Source{}, d).to_str(), StartsWith("123"));
EXPECT_THAT(Token(Token::Type::kFloatLiteral, Source{}, d).to_str(), Not(EndsWith("f")));
EXPECT_THAT(Token(Token::Type::kFloatFLiteral, Source{}, d).to_str(), StartsWith("123"));
EXPECT_THAT(Token(Token::Type::kFloatFLiteral, Source{}, d).to_str(), EndsWith("f"));
EXPECT_EQ(Token(Token::Type::kIntLiteral, Source{}, i).to_str(), "123");
EXPECT_EQ(Token(Token::Type::kIntILiteral, Source{}, i).to_str(), "123i");
EXPECT_EQ(Token(Token::Type::kIntULiteral, Source{}, i).to_str(), "123u");
EXPECT_EQ(Token(Token::Type::kIdentifier, Source{}, "blah").to_str(), "blah");
EXPECT_EQ(Token(Token::Type::kError, Source{}, "blah").to_str(), "blah");
}
} // namespace } // namespace
} // namespace tint::reader::wgsl } // namespace tint::reader::wgsl

View File

@ -501,7 +501,7 @@ TEST_F(LocationAttributeTests, BadType_Input_Struct_RuntimeArray) {
// @stage(fragment) // @stage(fragment)
// fn main(param : Input) {} // fn main(param : Input) {}
auto* input = auto* input =
Structure("Input", {Member(Source{{13, 43}}, "a", ty.array<float>(), {Location(0)})}); Structure("Input", {Member(Source{{13, 43}}, "a", ty.array<f32>(), {Location(0)})});
auto* param = Param("param", ty.Of(input)); auto* param = Param("param", ty.Of(input));
Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)}); Func(Source{{12, 34}}, "main", {param}, ty.void_(), {}, {Stage(ast::PipelineStage::kFragment)});
@ -633,7 +633,7 @@ TEST_F(LocationAttributeTests, ReturnType_Struct_RuntimeArray) {
// fn main() -> Output { // fn main() -> Output {
// return Output(); // return Output();
// } // }
auto* output = Structure("Output", {Member(Source{{13, 43}}, "a", ty.array<float>(), auto* output = Structure("Output", {Member(Source{{13, 43}}, "a", ty.array<f32>(),
{Location(Source{{12, 34}}, 0)})}); {Location(Source{{12, 34}}, 0)})});
Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))}, Func(Source{{12, 34}}, "main", {}, ty.Of(output), {Return(Construct(ty.Of(output)))},
{Stage(ast::PipelineStage::kFragment)}); {Stage(ast::PipelineStage::kFragment)});

View File

@ -38,12 +38,12 @@ sem::Constant Resolver::EvaluateConstantValue(const ast::LiteralExpression* lite
literal, literal,
[&](const ast::IntLiteralExpression* lit) { [&](const ast::IntLiteralExpression* lit) {
if (lit->suffix == ast::IntLiteralExpression::Suffix::kU) { if (lit->suffix == ast::IntLiteralExpression::Suffix::kU) {
return sem::Constant{type, {u32(static_cast<uint32_t>(lit->value))}}; return sem::Constant{type, {u32(lit->value)}};
} }
return sem::Constant{type, {i32(static_cast<int32_t>(lit->value))}}; return sem::Constant{type, {i32(lit->value)}};
}, },
[&](const ast::FloatLiteralExpression* lit) { [&](const ast::FloatLiteralExpression* lit) {
return sem::Constant{type, {lit->value}}; return sem::Constant{type, {f32(lit->value)}};
}, },
[&](const ast::BoolLiteralExpression* lit) { [&](const ast::BoolLiteralExpression* lit) {
return sem::Constant{type, {lit->value}}; return sem::Constant{type, {lit->value}};
@ -71,7 +71,7 @@ sem::Constant Resolver::EvaluateConstantValue(const ast::CallExpression* call,
return sem::Constant(type, sem::Constant::Scalars(result_size, 0_u)); return sem::Constant(type, sem::Constant::Scalars(result_size, 0_u));
} }
if (elem_type->Is<sem::F32>()) { if (elem_type->Is<sem::F32>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, 0.f)); return sem::Constant(type, sem::Constant::Scalars(result_size, 0._f));
} }
if (elem_type->Is<sem::Bool>()) { if (elem_type->Is<sem::Bool>()) {
return sem::Constant(type, sem::Constant::Scalars(result_size, false)); return sem::Constant(type, sem::Constant::Scalars(result_size, false));

View File

@ -139,9 +139,9 @@ TEST_F(ResolverConstantsTest, Vec3_ZeroInit_f32) {
EXPECT_EQ(sem->ConstantValue().Type(), sem->Type()); EXPECT_EQ(sem->ConstantValue().Type(), sem->Type());
EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>()); EXPECT_TRUE(sem->ConstantValue().ElementType()->Is<sem::F32>());
ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u); ASSERT_EQ(sem->ConstantValue().Elements().size(), 3u);
EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 0u); EXPECT_EQ(sem->ConstantValue().Elements()[0].f32, 0.0f);
EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 0u); EXPECT_EQ(sem->ConstantValue().Elements()[1].f32, 0.0f);
EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 0u); EXPECT_EQ(sem->ConstantValue().Elements()[2].f32, 0.0f);
} }
TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) { TEST_F(ResolverConstantsTest, Vec3_ZeroInit_bool) {

View File

@ -127,7 +127,7 @@ TEST_F(ResolverVarLetValidationTest, LetOfPtrConstructedWithRef) {
// let b : ptr<function,f32> = a; // let b : ptr<function,f32> = a;
const auto priv = ast::StorageClass::kFunction; const auto priv = ast::StorageClass::kFunction;
auto* var_a = Var("a", ty.f32(), priv); auto* var_a = Var("a", ty.f32(), priv);
auto* var_b = Let(Source{{12, 34}}, "b", ty.pointer<float>(priv), Expr("a"), {}); auto* var_b = Let(Source{{12, 34}}, "b", ty.pointer<f32>(priv), Expr("a"), {});
WrapInFunction(var_a, var_b); WrapInFunction(var_a, var_b);
ASSERT_FALSE(r()->Resolve()); ASSERT_FALSE(r()->Resolve());

View File

@ -2177,7 +2177,7 @@ bool GeneratorImpl::EmitLiteral(std::ostream& out, const ast::LiteralExpression*
} else if (std::isnan(l->value)) { } else if (std::isnan(l->value)) {
out << "uintBitsToFloat(0x7fc00000u)"; out << "uintBitsToFloat(0x7fc00000u)";
} else { } else {
out << FloatToString(l->value) << "f"; out << FloatToString(static_cast<float>(l->value)) << "f";
} }
return true; return true;
}, },

View File

@ -3040,7 +3040,7 @@ bool GeneratorImpl::EmitLiteral(std::ostream& out, const ast::LiteralExpression*
} else if (std::isnan(fl->value)) { } else if (std::isnan(fl->value)) {
out << "asfloat(0x7fc00000u)"; out << "asfloat(0x7fc00000u)";
} else { } else {
out << FloatToString(fl->value) << "f"; out << FloatToString(static_cast<float>(fl->value)) << "f";
} }
return true; return true;
}, },

View File

@ -1517,7 +1517,7 @@ bool GeneratorImpl::EmitLiteral(std::ostream& out, const ast::LiteralExpression*
} else if (std::isnan(l->value)) { } else if (std::isnan(l->value)) {
out << "NAN"; out << "NAN";
} else { } else {
out << FloatToString(l->value) << "f"; out << FloatToString(static_cast<float>(l->value)) << "f";
} }
return true; return true;
}, },

View File

@ -746,7 +746,8 @@ bool Builder::GenerateGlobalVariable(const ast::Variable* var) {
init_id = Switch( init_id = Switch(
type, // type, //
[&](const sem::F32*) { [&](const sem::F32*) {
ast::FloatLiteralExpression l(ProgramID{}, Source{}, 0.0f); ast::FloatLiteralExpression l(ProgramID{}, Source{}, 0,
ast::FloatLiteralExpression::Suffix::kF);
return GenerateLiteralIfNeeded(var, &l); return GenerateLiteralIfNeeded(var, &l);
}, },
[&](const sem::U32*) { [&](const sem::U32*) {
@ -1621,8 +1622,13 @@ uint32_t Builder::GenerateLiteralIfNeeded(const ast::Variable* var,
} }
}, },
[&](const ast::FloatLiteralExpression* f) { [&](const ast::FloatLiteralExpression* f) {
constant.kind = ScalarConstant::Kind::kF32; switch (f->suffix) {
constant.value.f32 = f->value; case ast::FloatLiteralExpression::Suffix::kNone:
case ast::FloatLiteralExpression::Suffix::kF:
constant.kind = ScalarConstant::Kind::kF32;
constant.value.f32 = static_cast<float>(f->value);
return;
}
}, },
[&](Default) { error_ = "unknown literal type"; }); [&](Default) { error_ = "unknown literal type"; });
@ -2878,7 +2884,8 @@ bool Builder::GenerateTextureBuiltin(const sem::Call* call,
} }
spirv_params.emplace_back(gen_arg(Usage::kDepthRef)); spirv_params.emplace_back(gen_arg(Usage::kDepthRef));
ast::FloatLiteralExpression float_0(ProgramID(), Source{}, 0.0); ast::FloatLiteralExpression float_0(ProgramID(), Source{}, 0.0,
ast::FloatLiteralExpression::Suffix::kF);
image_operands.emplace_back(ImageOperand{ image_operands.emplace_back(ImageOperand{
SpvImageOperandsLodMask, Operand(GenerateLiteralIfNeeded(nullptr, &float_0))}); SpvImageOperandsLodMask, Operand(GenerateLiteralIfNeeded(nullptr, &float_0))});
break; break;

View File

@ -2104,8 +2104,8 @@ TEST_P(Builtin_Builtin_DataPacking_Test, Binary) {
auto param = GetParam(); auto param = GetParam();
bool pack4 = param.name == "pack4x8snorm" || param.name == "pack4x8unorm"; bool pack4 = param.name == "pack4x8snorm" || param.name == "pack4x8unorm";
auto* call = pack4 ? Call(param.name, vec4<float>(1.0f, 1.0f, 1.0f, 1.0f)) auto* call = pack4 ? Call(param.name, vec4<f32>(1.0f, 1.0f, 1.0f, 1.0f))
: Call(param.name, vec2<float>(1.0f, 1.0f)); : Call(param.name, vec2<f32>(1.0f, 1.0f));
auto* func = Func("a_func", {}, ty.void_(), {CallStmt(call)}); auto* func = Func("a_func", {}, ty.void_(), {CallStmt(call)});
spirv::Builder& b = Build(); spirv::Builder& b = Build();

View File

@ -133,7 +133,7 @@ TEST_F(BuilderTest, Literal_U32_Dedup) {
} }
TEST_F(BuilderTest, Literal_F32) { TEST_F(BuilderTest, Literal_F32) {
auto* i = create<ast::FloatLiteralExpression>(23.245f); auto* i = create<ast::FloatLiteralExpression>(23.245, ast::FloatLiteralExpression::Suffix::kF);
WrapInFunction(i); WrapInFunction(i);
spirv::Builder& b = Build(); spirv::Builder& b = Build();
@ -148,8 +148,8 @@ TEST_F(BuilderTest, Literal_F32) {
} }
TEST_F(BuilderTest, Literal_F32_Dedup) { TEST_F(BuilderTest, Literal_F32_Dedup) {
auto* i1 = create<ast::FloatLiteralExpression>(23.245f); auto* i1 = create<ast::FloatLiteralExpression>(23.245, ast::FloatLiteralExpression::Suffix::kF);
auto* i2 = create<ast::FloatLiteralExpression>(23.245f); auto* i2 = create<ast::FloatLiteralExpression>(23.245, ast::FloatLiteralExpression::Suffix::kF);
WrapInFunction(i1, i2); WrapInFunction(i1, i2);
spirv::Builder& b = Build(); spirv::Builder& b = Build();

View File

@ -262,7 +262,7 @@ bool GeneratorImpl::EmitLiteral(std::ostream& out, const ast::LiteralExpression*
return true; return true;
}, },
[&](const ast::FloatLiteralExpression* l) { // [&](const ast::FloatLiteralExpression* l) { //
out << FloatToBitPreservingString(l->value); out << FloatToBitPreservingString(static_cast<float>(l->value));
return true; return true;
}, },
[&](const ast::IntLiteralExpression* l) { // [&](const ast::IntLiteralExpression* l) { //

View File

@ -25,8 +25,8 @@
%out_var_SV_TARGET = OpVariable %_ptr_Private_v4float Private %5 %out_var_SV_TARGET = OpVariable %_ptr_Private_v4float Private %5
%void = OpTypeVoid %void = OpTypeVoid
%8 = OpTypeFunction %void %8 = OpTypeFunction %void
%float_0x1_1p_128 = OpConstant %float 0x1.1p+128 %float_0x1_9p_128 = OpConstant %float 0x1.9p+128
%13 = OpConstantComposite %v4float %float_0x1_1p_128 %float_0x1_1p_128 %float_0x1_1p_128 %float_0x1_1p_128 %13 = OpConstantComposite %v4float %float_0x1_9p_128 %float_0x1_9p_128 %float_0x1_9p_128 %float_0x1_9p_128
%main_out = OpTypeStruct %v4float %main_out = OpTypeStruct %v4float
%14 = OpTypeFunction %main_out %14 = OpTypeFunction %main_out
%main_1 = OpFunction %void None %8 %main_1 = OpFunction %void None %8

View File

@ -1,7 +1,7 @@
var<private> out_var_SV_TARGET : vec4<f32>; var<private> out_var_SV_TARGET : vec4<f32>;
fn main_1() { fn main_1() {
out_var_SV_TARGET = vec4<f32>(0x1.1p+128, 0x1.1p+128, 0x1.1p+128, 0x1.1p+128); out_var_SV_TARGET = vec4<f32>(0x1.9p+128, 0x1.9p+128, 0x1.9p+128, 0x1.9p+128);
return; return;
} }