Skip to content

Numbers in any compare by value, whatever types they were boxed with - #535

Merged
ASDAlexander77 merged 2 commits into
mainfrom
fix-any-strict-compare-kinds
Oct 11, 2026
Merged

ASDAlexander77 merged 2 commits into
mainfrom
fix-any-strict-compare-kinds

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Problem

=== and !== between two any values compared the boxes' bytes. When the two boxes had different sizes, e.g. a number and an s32, both answered false:

const g: any = 2.5; const h: any = 1;
g !== h   // false (should be true)
const e: any = 1.0; const f: any = 1;
e === f   // false (should be true)

DefaultLib's tests/math.ts and tests/shift_ops.ts check results with isEq(x: any, y: any) { if (x !== y) fail(); }, so a computed double compared with an integer literal could never fail. That is how isEq(Math.max(-1, -7), -1) passed while Math.max returned 2.2e-308 (docs/bugs/ntg_math_max_negative.ts, fixed separately in DefaultLib).

Also wrong, in the same code: loose == between two numbers with different tags (1.0 == 1 was false), and </> between numeric anys, which compared bytes.

Fix

AnyCompareOpLowering: when both tags are of the numeric kind, both values are read as f64 and compared with the operator's float predicate (oeq, une, olt, ...). The tag's first letter says float (f, number), unsigned (u) or signed, and the box's data size says the width. The existing number-unboxing helper only knew number, s64 and s32; it is now this shared member. For other kinds whose sizes differ, !== now answers true, as != already did.

Testing

  • New 00any_number_compare.ts (compile + JIT) covers mixed widths, s64/u8/f32/s16, ordering, NaN, and non-numeric kinds. It fails on the current release (assertion failed: 2.5 !== 1).
  • Full ctest: 3915/3915 pass.
  • DefaultLib's tests/math.ts run with this compiler now reports fail: 2.22507e-308 !== -1 (the real Math.max bug). With that line removed, the rest of math.ts passes, and so does shift_ops.ts, so nothing else was being hidden in those two files.

🤖 Generated with Claude Code

`===` and `!==` between two `any`s compared the boxes' bytes, and when the
sizes differed (a `number` and an `s32`) both answered false. So
`isEq(x: any, y: any) { if (x !== y) fail(); }`, the DefaultLib tests'
helper, could never fail on a computed double compared with an integer
literal, which is how `Math.max(-1, -7)` returning 2.2e-308 got past
DefaultLib's tests/math.ts. Loose `==` had no case for two numbers with
different tags either (`1.0 == 1` was false), and orderings compared bytes.

Two numeric `any`s now compare as f64 for every operator: the tag's first
letter says float, unsigned or signed, and the box's data size says the
width. Values of different kinds with different sizes are unequal under
`!==` too, not just under `!=`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit cb94839 into main Oct 11, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-any-strict-compare-kinds branch October 11, 2026 12:31
ASDAlexander77 added a commit that referenced this pull request Oct 12, 2026
…558)

Every repro in docs/bugs passes on main, or fails as TypeScript does:
the native_ts_graphics findings were fixed by #535-#544 and #549 (the
`let s = 0` one by design, with a warning since #544), testFallThrough,
generic_in_generic and array_of_ifaces_cast by #538, #542 and #543, and
bug14 and bug21 aren't valid TypeScript. Each fix has its regression
test in tslang/test/tester, and the findings document is in the history
and in those PRs, so the folder goes.

Its interop notes stay, in docs/c-cpp-header-import.md: a `string`
returned by a C function is its `char*`, not a copy; `number` is a
`double`; and a nullable `Ref<T>` field is spelled `Ref<T> | null`.

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