Skip to content

Shifts and bitwise operators keep a 64-bit integer operand's width - #556

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-u64-shift-width
Oct 12, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-u64-shift-width

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

<<, >>, >>>, &, | and ^ cast both operands to i32 (the JavaScript Int32 coercion of number) whatever their type, so:

let a: u64 = 1;
print(a << 40);   // 256, expected 1099511627776
  • A left operand that is a 64-bit integer now keeps its type, and the right operand is cast to it. Narrower operands and number keep the Int32 rule (1 << 33 is still 2).
  • >> of an unsigned operand shifted the sign bit in: the lowering picked the shift by the lowered, signless type, and constant folding always used ashr. Both now look at the operand's own type.

Test: 00int64_shift_bitwise.ts (compile + JIT, and the rc/none corpus). Full suite: 4018/4018 passed (Release).

Found while porting native_ts_graphics samples (issue 3 of the native_ts_graphics findings).

🤖 Generated with Claude Code

`<<`, `>>`, `>>>`, `&`, `|` and `^` cast both operands to i32, the
JavaScript Int32 coercion of `number`, whatever their type. So
`u64 << 40` shifted by 40 mod 32 = 8 and gave 256, and `&`/`|`/`^`
lost the upper half of a u64. A left operand that is a 64-bit integer
now keeps its type, and the right one is cast to it; narrower operands
keep the Int32 rule.

Also, `>>` of an unsigned operand shifted the sign bit in: the lowering
picked the shift by the lowered (signless) operand type, and constant
folding always used an arithmetic shift. Both look at the operand's
own type now.

Found while porting native_ts_graphics samples (TSLANG_ISSUES.md 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 5b53f2c into main Oct 12, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-u64-shift-width branch October 12, 2026 16:05
ASDAlexander77 added a commit that referenced this pull request Oct 13, 2026
`%` cast both operands to number (f64), so a u64 remainder was wrong once
the value passed 2^53. When both operands are integers and one is 64-bit,
`%` is now done in that type; narrower integers keep the number path,
which is exact for them. The lowering took the signed remainder for the
signless lowered operand, so an unsigned one now takes `remui`, as `>>`
does since #556.

`/` still gives a number, as in JavaScript. A 64-bit `x % 0` traps like
integer division in C instead of giving NaN.

Found reading the bytes of a u64 word (TSLANG_ISSUES.md 16).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant