Skip to content

Commit

Permalink
fix: Coerce NaN to false when casting to bool (#1437)
Browse files Browse the repository at this point in the history
  • Loading branch information
MaxGraey authored Aug 20, 2020
1 parent a6e5da6 commit 45bfe04
Show file tree
Hide file tree
Showing 8 changed files with 963 additions and 246 deletions.
54 changes: 30 additions & 24 deletions src/compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3682,7 +3682,7 @@ export class Compiler extends DiagnosticEmitter {
// f32 to int
if (fromType.kind == TypeKind.F32) {
if (toType.isBooleanValue) {
expr = module.binary(BinaryOp.NeF32, expr, module.f32(0));
expr = this.makeIsTrueish(expr, Type.f32, reportNode);
wrap = false;
} else if (toType.isSignedIntegerValue) {
if (toType.isLongIntegerValue) {
Expand All @@ -3701,7 +3701,7 @@ export class Compiler extends DiagnosticEmitter {
// f64 to int
} else {
if (toType.isBooleanValue) {
expr = module.binary(BinaryOp.NeF64, expr, module.f64(0));
expr = this.makeIsTrueish(expr, Type.f64, reportNode);
wrap = false;
} else if (toType.isSignedIntegerValue) {
if (toType.isLongIntegerValue) {
Expand Down Expand Up @@ -10790,32 +10790,38 @@ export class Compiler extends DiagnosticEmitter {
: expr;
}
case TypeKind.F32: {
// (x != 0.0) & (x == x)
let flow = this.currentFlow;
let temp = flow.getTempLocal(Type.f32);
let ret = module.binary(BinaryOp.AndI32,
module.binary(BinaryOp.NeF32, module.local_tee(temp.index, expr), module.f32(0)),
module.binary(BinaryOp.EqF32,
module.local_get(temp.index, NativeType.F32),
module.local_get(temp.index, NativeType.F32)
)
// 0 < abs(bitCast(x)) <= bitCast(Infinity) or
// (reinterpret<u32>(x) & 0x7FFFFFFF) - 1 <= 0x7F800000 - 1
//
// and finally:
// (reinterpret<u32>(x) << 1) - (1 << 1) <= ((0x7F800000 - 1) << 1)
return module.binary(BinaryOp.LeU32,
module.binary(BinaryOp.SubI32,
module.binary(BinaryOp.ShlI32,
module.unary(UnaryOp.ReinterpretF32, expr),
module.i32(1)
),
module.i32(2) // 1 << 1
),
module.i32(0xFEFFFFFE) // (0x7F800000 - 1) << 1
);
flow.freeTempLocal(temp);
return ret;
}
case TypeKind.F64: {
// (x != 0.0) & (x == x)
let flow = this.currentFlow;
let temp = flow.getTempLocal(Type.f64);
let ret = module.binary(BinaryOp.AndI32,
module.binary(BinaryOp.NeF64, module.local_tee(temp.index, expr), module.f64(0)),
module.binary(BinaryOp.EqF64,
module.local_get(temp.index, NativeType.F64),
module.local_get(temp.index, NativeType.F64)
)
// 0 < abs(bitCast(x)) <= bitCast(Infinity) or
// (reinterpret<u64>(x) & 0x7FFFFFFFFFFFFFFF) - 1 <= 0x7FF0000000000000 - 1
//
// and finally:
// (reinterpret<u64>(x) << 1) - (1 << 1) <= ((0x7FF0000000000000 - 1) << 1)
return module.binary(BinaryOp.LeU64,
module.binary(BinaryOp.SubI64,
module.binary(BinaryOp.ShlI64,
module.unary(UnaryOp.ReinterpretF64, expr),
module.i64(1)
),
module.i64(2) // 1 << 1
),
module.i64(0xFFFFFFFE, 0xFFDFFFFF) // (0x7FF0000000000000 - 1) << 1
);
flow.freeTempLocal(temp);
return ret;
}
case TypeKind.EXTERNREF: {
// TODO: non-null object might still be considered falseish
Expand Down
59 changes: 59 additions & 0 deletions tests/compiler/bool.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,68 @@ var u = <u32>2;
assert(<bool>u == true);
var U = <u64>2;
assert(<bool>U == true);

var f = <f32>2;
assert(<bool>f == true);
var f0 = <f32>+0.0;
assert(<bool>f0 == false);
var f1 = <f32>-0.0;
assert(<bool>f1 == false);
var f2 = <f32>+NaN;
assert(<bool>f2 == false);
var f3 = <f32>-NaN;
assert(<bool>f3 == false);
var f4 = +f32.MAX_VALUE;
assert(<bool>f4 == true);
var f5 = -f32.MAX_VALUE;
assert(<bool>f5 == true);
var f6 = <f32>+Infinity;
assert(<bool>f6 == true);
var f7 = <f32>-Infinity;
assert(<bool>f7 == true);
var f8 = +f32.MIN_VALUE;
assert(<bool>f8 == true);
var f9 = -f32.MIN_VALUE;
assert(<bool>f9 == true);
var f10 = reinterpret<f32>(1);
assert(<bool>f10 == true);
var f11 = reinterpret<f32>(0x7F800000 - 1);
assert(<bool>f11 == true);
var f12 = reinterpret<f32>(0x7F800000 + 1);
assert(<bool>f12 == false);
var f13 = reinterpret<f32>(0xFF800000 + 1);
assert(<bool>f13 == false);

var F = <f64>2;
assert(<bool>F == true);
var F0 = <f64>+0.0;
assert(<bool>F0 == false);
var F1 = <f64>-0.0;
assert(<bool>F1 == false);
var F2 = <f64>+NaN;
assert(<bool>F2 == false);
var F3 = <f64>-NaN;
assert(<bool>F3 == false);
var F4 = +f64.MAX_VALUE;
assert(<bool>F4 == true);
var F5 = -f64.MAX_VALUE;
assert(<bool>F5 == true);
var F6 = +Infinity;
assert(<bool>F6 == true);
var F7 = -Infinity;
assert(<bool>F7 == true);
var F8 = +f64.MIN_VALUE;
assert(<bool>F8 == true);
var F9 = -f64.MIN_VALUE;
assert(<bool>F9 == true);
var F10 = reinterpret<f64>(1);
assert(<bool>F10 == true);
var F11 = reinterpret<f64>(0x7FF0000000000000 - 1);
assert(<bool>F11 == true);
var F12 = reinterpret<f64>(0x7FF0000000000000 + 1);
assert(<bool>F12 == false);
var F13 = reinterpret<f64>(0xFFF0000000000000 + 1);
assert(<bool>F13 == false);

var uu = <u8>2;
assert(<bool>uu == true);
Loading

0 comments on commit 45bfe04

Please sign in to comment.