Skip to content

Comparing a union's field with null or undefined no longer crashes - #376

Merged
ASDAlexander77 merged 1 commit into
mainfrom
fix-safe-cast-null-const
Sep 27, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
fix-safe-cast-null-const

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner

Comparing a field of a union-typed object with null or undefined crashed the compiler. The debug build asserts, and the release build fails with an access violation. Found in the BrowserLib sources attached to #231.

interface Circle { kind: "circle"; r: number | null; }
interface Square { kind: "square"; r: number | null; }
function hasR(s: Circle | Square) { return s.r !== null; }   // crashed

Cause

checkSafeCastPropertyAccessLogic narrows discriminated unions such as s.kind === "circle". It checks that the compared value is a constant with isConstValue, then assumes that value is a ConstantOp. But isConstValue is also true for NullOp and UndefOp, so constantOp was null and getValueAttr() dereferenced it.

Fix

  • The narrowing now runs only for a real ConstantOp. null and undefined are never discriminant literals, so skipping them loses nothing.
  • In the same loop, a field that is missing from one member of the union no longer indexes the tuple with -1 or dereferences a null FieldInfo.

Tests

  • New 00safe_cast_null_field.ts (compile, jit, and the rc/none corpus). It compares an interface union's field with null and with undefined, and checks that s.kind === "circle" still narrows. The old build fails it with 0xC0000005.
  • Full release suite: 2968/2968 passed.

Not in this PR

These are the other #231 findings, left out on purpose:

  • Narrowing in an else branch leaks past the if (a separate crash): next PR.
  • Reading a property through a union of object-literal types (type S = {kind:"a"} | {kind:"b"}) fails verification with 'ts.PropertyRef' op operand #0 must be …, even with no null involved. It fails the same way before this change.
  • A property of an un-narrowed N | null (h.next !== null) now gets that same verification error instead of crashing. TypeScript in strict mode rejects it too ("possibly null").

🤖 Generated with Claude Code

The discriminated-union narrowing in checkSafeCastPropertyAccessLogic treated
every const value as a ConstantOp. isConstValue is also true for null and
undefined, so `s.r !== null` on a union-typed `s` asserted in debug builds and
dereferenced a null op in release. null and undefined cannot be discriminant
literals, so the narrowing now skips them. A field missing from one member of
the union no longer indexes the tuple with -1 or dereferences a null FieldInfo.

Found in the BrowserLib sources attached to #231.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit b86fa56 into main Sep 27, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the fix-safe-cast-null-const branch September 27, 2026 21:44
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