Fix parsing issues on 32-bit hosts

These were mostly bugs with code of the following form:
```
  if uint64Value < (... literal expression ...)
```
Swift's comparison operators allow their left- and right-hand sides to be of
different widths.  This in turn means that the literal expression above
typically gets typechecked by default as a plain `Int` or `UInt` expression.
In a number of cases, this led to truncation on platforms where `Int` is
not 64 bits.

In particular, this seems to fix tests on wasm32.
This commit is contained in:
Tim Kientzle
2025-12-12 07:16:59 -08:00
parent f82cf1ebfb
commit e75a8c03e7
3 changed files with 13 additions and 17 deletions

View File

@@ -450,7 +450,7 @@ fileprivate func hexFloat(
) -> ParseResult { ) -> ParseResult {
var i = start + 2 // Skip leading '0x' var i = start + 2 // Skip leading '0x'
let firstDigitOffset = i let firstDigitOffset = i
let limit = UInt64(1) << 60
var significand: UInt64 = 0 var significand: UInt64 = 0
// //
@@ -458,10 +458,10 @@ fileprivate func hexFloat(
// //
// Accumulate the most significant 64 bits... // Accumulate the most significant 64 bits...
while i < input.count && hexdigit(input[i]) < 16 && significand < (1 << 60) { while i < input.count && hexdigit(input[i]) < 16 && significand < limit {
significand &<<= 4 significand &<<= 4
significand += UInt64(hexdigit(input[i])) significand += UInt64(hexdigit(input[i]))
i += 1 i += 1
} }
// Initialize binary exponent to the number of bits we collected above // Initialize binary exponent to the number of bits we collected above
@@ -521,7 +521,7 @@ fileprivate func hexFloat(
} }
} }
// Pack more bits into the accumulator (up to 60) // Pack more bits into the accumulator (up to 60)
while i < input.count && hexdigit(input[i]) < 16 && significand < (1 << 60) { while i < input.count && hexdigit(input[i]) < 16 && significand < limit {
significand &<<= 4 significand &<<= 4
significand += UInt64(hexdigit(input[i])) significand += UInt64(hexdigit(input[i]))
i += 1 i += 1
@@ -631,12 +631,12 @@ fileprivate func hexFloat(
&& (targetSignificand & 1) == 1)) { && (targetSignificand & 1) == 1)) {
// Round up, test for overflow // Round up, test for overflow
targetSignificand += 1 targetSignificand += 1
if targetSignificand >= (1 << targetFormat.significandBits) { if targetSignificand >= (UInt64(1) << targetFormat.significandBits) {
// Normal overflowed, need to renormalize // Normal overflowed, need to renormalize
targetSignificand >>= 1 targetSignificand >>= 1
binaryExponent += 1 binaryExponent += 1
} else if (binaryExponent < targetFormat.minBinaryExponent } else if (binaryExponent < targetFormat.minBinaryExponent
&& targetSignificand >= (1 << (targetFormat.significandBits - 1))) { && targetSignificand >= (UInt64(1) << (targetFormat.significandBits - 1))) {
// Subnormal overflowed to normal // Subnormal overflowed to normal
binaryExponent += 1 binaryExponent += 1
} }
@@ -1769,7 +1769,7 @@ fileprivate func slowDecimalToBinary(
count: targetFormat.significandBits, count: targetFormat.significandBits,
remainderNonZero: false) remainderNonZero: false)
if significand >= (1 &<< targetFormat.significandBits) { if significand >= (UInt64(1) &<< targetFormat.significandBits) {
significand >>= 1 significand >>= 1
binaryExponent &+= 1 binaryExponent &+= 1
} }
@@ -1865,7 +1865,7 @@ fileprivate func slowDecimalToBinary(
range: quotientRange, range: quotientRange,
count: targetFormat.significandBits, count: targetFormat.significandBits,
remainderNonZero: remainderNonZero) remainderNonZero: remainderNonZero)
if significand >= (1 &<< targetFormat.significandBits) { if significand >= (UInt64(1) &<< targetFormat.significandBits) {
significand >>= 1 significand >>= 1
binaryExponent &+= 1 binaryExponent &+= 1
} }
@@ -1889,7 +1889,7 @@ fileprivate func slowDecimalToBinary(
// exponent. Then we've transitioned from a subnormal to // exponent. Then we've transitioned from a subnormal to
// a normal, so the extra overflow bit will naturally get // a normal, so the extra overflow bit will naturally get
// dropped, we just have to bump the exponent. // dropped, we just have to bump the exponent.
if significand >= (1 &<< (targetFormat.significandBits &- 1)) { if significand >= (UInt64(1) &<< (targetFormat.significandBits &- 1)) {
binaryExponent &+= 1 binaryExponent &+= 1
} }
targetSignificand = significand targetSignificand = significand

View File

@@ -7,9 +7,6 @@
// Float16 is only available in watchOS 7.0 or newer // Float16 is only available in watchOS 7.0 or newer
// UNSUPPORTED: OS=watchos // UNSUPPORTED: OS=watchos
// TODO: Figure out why this test breaks on wasm32
// UNSUPPORTED: CPU=wasm32
// Cannot test with old OS stdlib, because that used libc strtof // Cannot test with old OS stdlib, because that used libc strtof
// for parsing, which results in incorrect results. // for parsing, which results in incorrect results.
// UNSUPPORTED: use_os_stdlib // UNSUPPORTED: use_os_stdlib
@@ -105,7 +102,9 @@ tests.test("NaNs") {
expectRoundTrip(Float16.nan) expectRoundTrip(Float16.nan)
expectRoundTrip(-Float16.nan) expectRoundTrip(-Float16.nan)
expectRoundTrip(Float16(nan:73, signaling:false)) expectRoundTrip(Float16(nan:73, signaling:false))
#if !arch(wasm32)
expectRoundTrip(Float16(nan:73, signaling:true)) expectRoundTrip(Float16(nan:73, signaling:true))
#endif
expectParse("nan", Float16.nan) expectParse("nan", Float16.nan)
expectParse("NAN", Float16.nan) expectParse("NAN", Float16.nan)
expectParse("NaN", Float16.nan) expectParse("NaN", Float16.nan)

View File

@@ -5,9 +5,6 @@
// REQUIRES: executable_test // REQUIRES: executable_test
// TODO: Figure out why this test breaks on wasm32
// UNSUPPORTED: CPU=wasm32
// Needed to declare the ABI entry point // Needed to declare the ABI entry point
// REQUIRES: swift_feature_Extern // REQUIRES: swift_feature_Extern