Skip to content

Commit 45bfe04

Browse files
authored
fix: Coerce NaN to false when casting to bool (#1437)
1 parent a6e5da6 commit 45bfe04

8 files changed

+963
-246
lines changed

Diff for: src/compiler.ts

+30-24
Original file line numberDiff line numberDiff line change
@@ -3682,7 +3682,7 @@ export class Compiler extends DiagnosticEmitter {
36823682
// f32 to int
36833683
if (fromType.kind == TypeKind.F32) {
36843684
if (toType.isBooleanValue) {
3685-
expr = module.binary(BinaryOp.NeF32, expr, module.f32(0));
3685+
expr = this.makeIsTrueish(expr, Type.f32, reportNode);
36863686
wrap = false;
36873687
} else if (toType.isSignedIntegerValue) {
36883688
if (toType.isLongIntegerValue) {
@@ -3701,7 +3701,7 @@ export class Compiler extends DiagnosticEmitter {
37013701
// f64 to int
37023702
} else {
37033703
if (toType.isBooleanValue) {
3704-
expr = module.binary(BinaryOp.NeF64, expr, module.f64(0));
3704+
expr = this.makeIsTrueish(expr, Type.f64, reportNode);
37053705
wrap = false;
37063706
} else if (toType.isSignedIntegerValue) {
37073707
if (toType.isLongIntegerValue) {
@@ -10790,32 +10790,38 @@ export class Compiler extends DiagnosticEmitter {
1079010790
: expr;
1079110791
}
1079210792
case TypeKind.F32: {
10793-
// (x != 0.0) & (x == x)
10794-
let flow = this.currentFlow;
10795-
let temp = flow.getTempLocal(Type.f32);
10796-
let ret = module.binary(BinaryOp.AndI32,
10797-
module.binary(BinaryOp.NeF32, module.local_tee(temp.index, expr), module.f32(0)),
10798-
module.binary(BinaryOp.EqF32,
10799-
module.local_get(temp.index, NativeType.F32),
10800-
module.local_get(temp.index, NativeType.F32)
10801-
)
10793+
// 0 < abs(bitCast(x)) <= bitCast(Infinity) or
10794+
// (reinterpret<u32>(x) & 0x7FFFFFFF) - 1 <= 0x7F800000 - 1
10795+
//
10796+
// and finally:
10797+
// (reinterpret<u32>(x) << 1) - (1 << 1) <= ((0x7F800000 - 1) << 1)
10798+
return module.binary(BinaryOp.LeU32,
10799+
module.binary(BinaryOp.SubI32,
10800+
module.binary(BinaryOp.ShlI32,
10801+
module.unary(UnaryOp.ReinterpretF32, expr),
10802+
module.i32(1)
10803+
),
10804+
module.i32(2) // 1 << 1
10805+
),
10806+
module.i32(0xFEFFFFFE) // (0x7F800000 - 1) << 1
1080210807
);
10803-
flow.freeTempLocal(temp);
10804-
return ret;
1080510808
}
1080610809
case TypeKind.F64: {
10807-
// (x != 0.0) & (x == x)
10808-
let flow = this.currentFlow;
10809-
let temp = flow.getTempLocal(Type.f64);
10810-
let ret = module.binary(BinaryOp.AndI32,
10811-
module.binary(BinaryOp.NeF64, module.local_tee(temp.index, expr), module.f64(0)),
10812-
module.binary(BinaryOp.EqF64,
10813-
module.local_get(temp.index, NativeType.F64),
10814-
module.local_get(temp.index, NativeType.F64)
10815-
)
10810+
// 0 < abs(bitCast(x)) <= bitCast(Infinity) or
10811+
// (reinterpret<u64>(x) & 0x7FFFFFFFFFFFFFFF) - 1 <= 0x7FF0000000000000 - 1
10812+
//
10813+
// and finally:
10814+
// (reinterpret<u64>(x) << 1) - (1 << 1) <= ((0x7FF0000000000000 - 1) << 1)
10815+
return module.binary(BinaryOp.LeU64,
10816+
module.binary(BinaryOp.SubI64,
10817+
module.binary(BinaryOp.ShlI64,
10818+
module.unary(UnaryOp.ReinterpretF64, expr),
10819+
module.i64(1)
10820+
),
10821+
module.i64(2) // 1 << 1
10822+
),
10823+
module.i64(0xFFFFFFFE, 0xFFDFFFFF) // (0x7FF0000000000000 - 1) << 1
1081610824
);
10817-
flow.freeTempLocal(temp);
10818-
return ret;
1081910825
}
1082010826
case TypeKind.EXTERNREF: {
1082110827
// TODO: non-null object might still be considered falseish

Diff for: tests/compiler/bool.ts

+59
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,68 @@ var u = <u32>2;
66
assert(<bool>u == true);
77
var U = <u64>2;
88
assert(<bool>U == true);
9+
910
var f = <f32>2;
1011
assert(<bool>f == true);
12+
var f0 = <f32>+0.0;
13+
assert(<bool>f0 == false);
14+
var f1 = <f32>-0.0;
15+
assert(<bool>f1 == false);
16+
var f2 = <f32>+NaN;
17+
assert(<bool>f2 == false);
18+
var f3 = <f32>-NaN;
19+
assert(<bool>f3 == false);
20+
var f4 = +f32.MAX_VALUE;
21+
assert(<bool>f4 == true);
22+
var f5 = -f32.MAX_VALUE;
23+
assert(<bool>f5 == true);
24+
var f6 = <f32>+Infinity;
25+
assert(<bool>f6 == true);
26+
var f7 = <f32>-Infinity;
27+
assert(<bool>f7 == true);
28+
var f8 = +f32.MIN_VALUE;
29+
assert(<bool>f8 == true);
30+
var f9 = -f32.MIN_VALUE;
31+
assert(<bool>f9 == true);
32+
var f10 = reinterpret<f32>(1);
33+
assert(<bool>f10 == true);
34+
var f11 = reinterpret<f32>(0x7F800000 - 1);
35+
assert(<bool>f11 == true);
36+
var f12 = reinterpret<f32>(0x7F800000 + 1);
37+
assert(<bool>f12 == false);
38+
var f13 = reinterpret<f32>(0xFF800000 + 1);
39+
assert(<bool>f13 == false);
40+
1141
var F = <f64>2;
1242
assert(<bool>F == true);
43+
var F0 = <f64>+0.0;
44+
assert(<bool>F0 == false);
45+
var F1 = <f64>-0.0;
46+
assert(<bool>F1 == false);
47+
var F2 = <f64>+NaN;
48+
assert(<bool>F2 == false);
49+
var F3 = <f64>-NaN;
50+
assert(<bool>F3 == false);
51+
var F4 = +f64.MAX_VALUE;
52+
assert(<bool>F4 == true);
53+
var F5 = -f64.MAX_VALUE;
54+
assert(<bool>F5 == true);
55+
var F6 = +Infinity;
56+
assert(<bool>F6 == true);
57+
var F7 = -Infinity;
58+
assert(<bool>F7 == true);
59+
var F8 = +f64.MIN_VALUE;
60+
assert(<bool>F8 == true);
61+
var F9 = -f64.MIN_VALUE;
62+
assert(<bool>F9 == true);
63+
var F10 = reinterpret<f64>(1);
64+
assert(<bool>F10 == true);
65+
var F11 = reinterpret<f64>(0x7FF0000000000000 - 1);
66+
assert(<bool>F11 == true);
67+
var F12 = reinterpret<f64>(0x7FF0000000000000 + 1);
68+
assert(<bool>F12 == false);
69+
var F13 = reinterpret<f64>(0xFFF0000000000000 + 1);
70+
assert(<bool>F13 == false);
71+
1372
var uu = <u8>2;
1473
assert(<bool>uu == true);

0 commit comments

Comments
 (0)