Repository navigation
Throw a u32 as an unsigned int; convert u32 to number as unsigned - #355
Merged
Merged
Conversation
A thrown u32 went out as a signed int (`.H` on Windows, `_ZTIi` on Linux), and every reader took its top bit for a sign: `throw <u32> 0xFFFFFFFF` caught by `catch (e: number)` or `catch (e)` gave -1. On Windows the int's widening and boxing thunks were also shared by name, so a module throwing both an s32 and a u32 used whichever was built first. A u32 now has RTTI of its own: `.I` records with their own `.PEAX` box and `.N` widening thunks on Windows, `_ZTIj` on Linux, listed by `catch (e: number)`, bound by loadCaughtNumber (uitofp) and boxed as a u32 by an untyped catch. The thunk alone did not fix it: every u32-to-number cast lowered to sitofp, because castLLVMTypesLogic only sees the signless LLVM type. An unsigned integer now converts to a float with uitofp. That exposed two more signedness gaps that had cancelled it out in 02numbers.ts: - number to a narrow integer was fptosi straight to i32, poison above 0x7FFFFFFF, so `0x80000001 << 1` was garbage once the literal arrived as 2147483649. It now goes through i64 and truncates, which wraps modulo 2^32 as ToInt32/ToUint32 do. - `>>>` produced a signless i32 that widened as signed; its result is now a u32, so `-1 >>> 0` is 4294967295. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
throw <u32>0xFFFFFFFFcaught bycatch (e: number)orcatch (e)gave -1. Two layers were wrong.int(.Hon Windows,_ZTIion Linux). On Windows the int's.Nwidening and.PEAXboxing thunks were also shared by name, so a module that throws both an s32 and a u32 used whichever thunk was built first. A u32 now has RTTI of its own:.Irecords with their own_CT??_R0PEAX@I84.box/.eh.copy.box.Iand_CT??_R0N@88.fromI/.eh.copy.num.I._ZTIj, listed bycatch (e: number), bound byloadCaughtNumberwithuitofp, and boxed as a u32 by an untyped catch.numberconversion lowered tositofp, becausecastLLVMTypesLogiconly sees the signless LLVM type. The new thunk used that cast, so the RTTI fix alone changed nothing.castTypeScriptTypesnow converts an unsigned integer to a float withuitofp.Correcting the cast exposed two more signedness bugs. In
02numbers.tsthey had cancelled the first one out:fptosistraight to i32, which is poison above 0x7FFFFFFF.0x80000001 << 1gave garbage once the literal arrived as 2147483649. It now converts through i64 and truncates, which wraps modulo 2^32 the wayToInt32/ToUint32do.>>>produced a signless i32 that widened as signed. Its result is now a u32, so-1 >>> 0is 4294967295.Both of these, and the u32 → number cast, change behaviour outside exceptions as well.
Verification
00throw_u32.ts(compile, jit, and the rc/none corpus). One module throws both s32 and u32. It checks typed, untyped andnumbercatches, and that an s32 passes a u32 catch. Values are checked by sign: an== 4294967295check passed on the broken build. Onmainit fails at the first assert.02numbers.ts.🤖 Generated with Claude Code