From b8fbb0c445c0fce08a1db6841890a86d82474595 Mon Sep 17 00:00:00 2001 From: Swastik Tripathi Date: Thu, 12 Nov 2026 12:45:31 +0530 Subject: [PATCH] fix: wrap small integers before casting them to bool --- NOTICE | 1 + src/compiler.ts | 4 +- tests/compiler/bool.debug.wat | 137 +++++++++++++++++++++++++++++++++- tests/compiler/bool.ts | 24 ++++++ tests/compiler/cast.debug.wat | 6 ++ 5 files changed, 168 insertions(+), 4 deletions(-) diff --git a/NOTICE b/NOTICE index da2cb563d4..e91ace5852 100644 --- a/NOTICE +++ b/NOTICE @@ -63,6 +63,7 @@ under the licensing terms detailed in LICENSE: * Mopsgamer <79159094+Mopsgamer@users.noreply.github.com> * EDM115 * Weixie Cui +* Swastik Tripathi Portions of this software are derived from third-party works licensed under the following terms: diff --git a/src/compiler.ts b/src/compiler.ts index f99bd5a049..37135a94aa 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -3802,8 +3802,8 @@ export class Compiler extends DiagnosticEmitter { } else { // small i32 to ... if (fromType.isShortIntegerValue) { - // small i32 to larger i32 - if (fromType.size < toType.size) { + // small i32 to larger i32, or to bool, which tests all 32 bits + if (fromType.size < toType.size || toType.isBooleanValue) { expr = this.ensureSmallIntegerWrap(expr, fromType); // must clear garbage bits } // same size diff --git a/tests/compiler/bool.debug.wat b/tests/compiler/bool.debug.wat index 35346823bc..2e6d814c48 100644 --- a/tests/compiler/bool.debug.wat +++ b/tests/compiler/bool.debug.wat @@ -1,6 +1,7 @@ (module - (type $0 (func)) - (type $1 (func (param i32 i32 i32 i32))) + (type $0 (func (param i32) (result i32))) + (type $1 (func)) + (type $2 (func (param i32 i32 i32 i32))) (import "env" "abort" (func $~lib/builtins/abort (param i32 i32 i32 i32))) (global $bool/i (mut i32) (i32.const 2)) (global $bool/I (mut i64) (i64.const 2)) @@ -50,6 +51,48 @@ (elem $0 (i32.const 1)) (export "memory" (memory $0)) (start $~start) + (func $bool/u8PlusOne (param $a i32) (result i32) + (local $c i32) + local.get $a + i32.const 1 + i32.add + local.set $c + local.get $c + i32.const 255 + i32.and + return + ) + (func $bool/i8PlusOne (param $a i32) (result i32) + (local $c i32) + local.get $a + i32.const 1 + i32.add + local.set $c + local.get $c + i32.extend8_s + return + ) + (func $bool/u16PlusOne (param $a i32) (result i32) + (local $c i32) + local.get $a + i32.const 1 + i32.add + local.set $c + local.get $c + i32.const 65535 + i32.and + return + ) + (func $bool/i16PlusOne (param $a i32) (result i32) + (local $c i32) + local.get $a + i32.const 1 + i32.add + local.set $c + local.get $c + i32.extend16_s + return + ) (func $start:bool global.get $bool/i i32.const 0 @@ -759,6 +802,96 @@ call $~lib/builtins/abort unreachable end + i32.const 255 + call $bool/u8PlusOne + i32.const 0 + i32.ne + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 80 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 0 + call $bool/u8PlusOne + i32.const 0 + i32.ne + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 81 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const -1 + call $bool/i8PlusOne + i32.const 0 + i32.ne + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 86 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 127 + call $bool/i8PlusOne + i32.const 0 + i32.ne + i32.const 1 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 87 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const 65535 + call $bool/u16PlusOne + i32.const 0 + i32.ne + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 92 + i32.const 1 + call $~lib/builtins/abort + unreachable + end + i32.const -1 + call $bool/i16PlusOne + i32.const 0 + i32.ne + i32.const 0 + i32.eq + i32.eqz + if + i32.const 0 + i32.const 32 + i32.const 97 + i32.const 1 + call $~lib/builtins/abort + unreachable + end ) (func $~start call $start:bool diff --git a/tests/compiler/bool.ts b/tests/compiler/bool.ts index 626f58076c..7623516be7 100644 --- a/tests/compiler/bool.ts +++ b/tests/compiler/bool.ts @@ -71,3 +71,27 @@ assert(F13 == false); var uu = 2; assert(uu == true); + +// small integers are wrapped before testing them, so a value that overflowed to 0 is false +function u8PlusOne(a: u8): bool { + let c: u8 = a + 1; + return c; +} +assert(u8PlusOne(255) == false); +assert(u8PlusOne(0) == true); +function i8PlusOne(a: i8): bool { + let c: i8 = a + 1; + return c as bool; +} +assert(i8PlusOne(-1) == false); +assert(i8PlusOne(127) == true); +function u16PlusOne(a: u16): bool { + let c: u16 = a + 1; + return bool(c); +} +assert(u16PlusOne(65535) == false); +function i16PlusOne(a: i16): bool { + let c: i16 = a + 1; + return c; +} +assert(i16PlusOne(-1) == false); diff --git a/tests/compiler/cast.debug.wat b/tests/compiler/cast.debug.wat index 3a7ae38bce..1c9b7b2106 100644 --- a/tests/compiler/cast.debug.wat +++ b/tests/compiler/cast.debug.wat @@ -79,6 +79,7 @@ (func $"cast/test" (param $x i32) (result i32) (local $y i32) local.get $x + i32.extend8_s local.set $y local.get $y return @@ -156,6 +157,8 @@ (func $"cast/test" (param $x i32) (result i32) (local $y i32) local.get $x + i32.const 255 + i32.and local.set $y local.get $y return @@ -225,6 +228,7 @@ (func $"cast/test" (param $x i32) (result i32) (local $y i32) local.get $x + i32.extend16_s local.set $y local.get $y return @@ -298,6 +302,8 @@ (func $"cast/test" (param $x i32) (result i32) (local $y i32) local.get $x + i32.const 65535 + i32.and local.set $y local.get $y return