Repository navigation
A null class, string or array widened to a union becomes its null member - #546
Merged
Merged
Conversation
Cast into a tagged union, a value got its static type as the tag. A class, string, array or object value holding null was therefore stored as that type holding a null pointer: a null `Handle | null` widened to `boolean | Handle | null` was tagged "class", so `=== null` missed it, `typeof` said "class", and a truth test read its vtable through the null pointer. Where the union has a null member of its own, such a value now takes the null tag when its pointer is null. 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.
Problem
A class, string or array value holding null, widened into a union that has its own
nullmember, kept its type's tag:This is the gap #540 left open (null in a union with two or more classes), and it isn't limited to truth tests. In #540 it was worked around only for a union with one class.
Cause
CastLogicHelperbuilds a tagged union from a non-union value withTypeOfOp(in): the tag comes from the value's static type. TheHandle | nullsource goes there too, as its base typeHandle. A null pointer therefore became theclassmember with a null pointer, while=== null,typeofand the generated union cast all look for thenulltag.Fix
When the source is a single pointer that can be null (class, string, array, object, opaque) and the target union's storage has a
nullmember, the tag is chosen at run time:select(pointer == null, typeof null, typeof value).This is the conversion half of the
<nullable type> | nullrule. A follow-up will stop giving a null member to unions whose members can all hold null themselves (A | B | null); the declared type keepsnullso strict-null checks still decide whether null may be assigned.Testing
00null_into_tagged_union.ts(compile + JIT + rc/none corpus): a nullAintoboolean | A | B | null(=== null,!== null,typeof, truth test,??), a null string intostring | number | null, and a null array intonumber[] | boolean | null, each alongside a non-null value. On current main it crashes with0x80000003.🤖 Generated with Claude Code