Repository navigation
J | null given to I | null, or to I, is the cast of J to I - #669
Merged
Merged
Conversation
An interface J that does not extend I gets a vtable laid out for I when it is cast to I (#666), but `J | null` given to `I | null` was a plain cast of the union: the value kept J's vtable, and I read it by its own slot numbers (`d.dist` read `thing`, `b.g()` called `f`). The same union given to `I` (as `I | undefined` is, through its optional) was a plain cast too, and for `C | null`, a class, lowering could not make the class pointer an interface. Both unions are stored as their one member, null a null pointer in it, so these are the cast of that member: castToNullableUnionType takes an interface member as it takes a class one (#665), and castToInterfaceSpecialCases casts the member of such a union. Null stays null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ASDAlexander77
added a commit
that referenced
this pull request
Oct 23, 2026
#669 made `J | null` given to `I | null` (and to `I`) the cast of J to I, the change this branch made to castToNullableUnionType and more. Main's MLIRGenCast.cpp is taken as it is; this branch now adds only the test 00nullable_interface_to_interface.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 23, 2026
ASDAlexander77
added a commit
that referenced
this pull request
Oct 23, 2026
…s the block it shows (#671) own_facts::isView took a cast between class, union and optional types as the same block, but not one from or to an interface. So `const b: B = a` (A to B), `const d: HasDist = mkHit()`, `return h` into `Hit | null`, and even a cast to a base interface (B extends A, A of B) were each a second reference own could not prove a move or a borrow of. Every interface-to-interface cast (#666, #669, #670) was rejected under own. The cast keeps `this`; the vtable laid out for the other interface is interned, nobody's block. So it is a view like the others, and own moves and borrows through it as it does the object: a read after `[a]` moved it is still an error. 00interface_cast_unrelated, 00interface_union_cast_unrelated, 00nullable_interface_to_interface and 00null_interface_to_interface now run under own too. 00interface_cast_unrelated's element and loop cases no longer move or merge `h` (own's rules, not this cast's). 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.
Extends #666 to unions. #666 gives an interface
Jcast to an interfaceIit does not extend a vtable laid out forI.J | nullgiven toI | nulldidn't get that remap, because it was a plain cast of the union. The value keptJ's vtable, andIread it by its own slot numbers. This happened under every model, gc included:The same union given to
Ialone, asI | undefinedis through its optional, was a plain cast as well. With a class member (C | null→I | undefined), lowering couldn't turn the class pointer into an interface. It warnedinvalid cast from '!llvm.ptr' to '!llvm.struct<...>'.Fix
Both unions are stored as their one member, with null as a null pointer in it. So each is the cast of that member (
MLIRGenCast.cpp):castToNullableUnionTypenow accepts an interface member as well as a class member (A null class object, null or undefined given to an interface is a null interface whose methods throw #665).J | null→I | nulltherefore castsJtoI, and An interface cast to one it does not extend gets a vtable laid out for it #666 remaps the vtable.castToInterfaceSpecialCaseshandles such a union by casting its member. A nullJbecomes a nullI, with the throwing stub from A null class object, null or undefined given to an interface is a null interface whose methods throw #665.Tests
tests/00interface_union_cast_unrelated.tscovers:Hit | null→HasDist | null, for a value and for null, as a local and as an argument;A | null→B | null, a method;I | undefinedfrom an interface and from a class;<I>ofJ | null;It runs as a compile test and a JIT test under gc, and in the rc/none corpus.
Tooth: with the compiler change reverted, gc and rc fail with
assertion failed: a field through J | null, along with the invalid-cast warning.Not covered
pick(return h) with or without this change, and An interface cast to one it does not extend gets a vtable laid out for it #666's interface-to-interface cast has the same limitation. So the test doesn't run under own.{ vtable, this }. Whichever lands second needs a rebase. The remap doesn't depend on that layout, because it goes through the An interface cast to one it does not extend gets a vtable laid out for it #666 lowering.Verification
MALLOC_PERTURB_=165 MALLOC_CHECK_=3.🤖 Generated with Claude Code