Skip to content

1 !== true is true, and a length merged with -1 keeps -1 - #425

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-strict-neq-and-index-merge
Oct 1, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-strict-neq-and-index-merge

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Fixes the two bugs left open by #424.

1 !== true and "a" !== 1 were false

=== / !== between a boolean, a number and a string folds to a constant in adjustTypesForBinaryOp, but the operator still runs on the replacement operands. They were (result, true), so !== computed true !== true. Both operators now get (false, true): false === true is false, false !== true is true.

The fold also missed operands typed number (isIntOrIndexOrFloat() does not match !ts.number), so const x: number = 1; x === true coerced and was true. isNumeric now includes NumberType.

A length merged with -1 returned 4294967295

function f(p: string, c: boolean) { if (c) return p.length; return -1; }   // f("abc", false) == 4294967295

canWideTypeWithoutDataLoss treats index as a signless integer, so si32 -> index counts as lossless and the merge picked index, which holds an unsigned value. The new mergeIndexWithSignedInt merges index (or a signless integer) with a signed integer into a signed integer of at least 64 bits. It runs in:

  • mergeType: return types, array literals ([p.length, -1])
  • findBaseType: conditionals (c ? p.length : -1), ??, generic type arguments inferred from several arguments

canCastFromTo itself is unchanged, so overload resolution and argument passing are not affected.

Decisions to check

  • The merged type is si64, not TypeScript's number. That matches the existing u32 + s32 -> si64 rule in mergeIntTypes.
  • The rule covers signless integers, not just index: const p = "abc"; c ? p.length : -1 folds the length to a signless i32 constant, which wrapped the same way. Side effect: a function returning a signless i32 binding (e.g. Date.getDate()) on one path and -1 on another now infers si64 instead of i32.
  • On x86, index(32) + si32 also becomes si64.

Not in this PR

let x = p.length; x = -1; still prints 4294967295: the variable's type is fixed at its declaration, no merge happens.

Tests

  • New: 00strict_equals_mismatched_types.ts (literals, consts, number/boolean/string-typed values; loose == still coerces) and 00length_with_negative_merge.ts (both return orders, conditionals, array literals, constant-string length), each compile + jit.
  • Release suite: 3335/3335 passed (one test disabled, as before).
  • DefaultLib (main b67a213) rebuilt release/gc with this compiler: 158/158 compile, 158/158 jit.
  • Not run: Debug, other memory models, x86.

🤖 Generated with Claude Code

@ASDAlexander77
ASDAlexander77 merged commit 19d57cf into main Oct 1, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-strict-neq-and-index-merge branch October 1, 2026 16:05
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