Skip to content

A cast between two specializations of one generic is checked - #389

Merged
ASDAlexander77 merged 1 commit into
mainfrom
generic-specialization-cast-error
Sep 28, 2026
Merged

ASDAlexander77 merged 1 commit into
mainfrom
generic-specialization-cast-error

Conversation

@ASDAlexander77

Copy link
Copy Markdown
Owner
class Box<T> { constructor(public v: T) {} }
const a = new Box<number>(1);
const b: Box<string> = a;   // compiled without complaint
print(b.v);                 // read a number as a string: 0xC0000005

The cast became a plain ts.Cast between the two class types. Two specializations of one generic keep fields of different types in the same place, so reading through the wrong one crashed.

Fix (verifyCastCompatibility, MLIRGenCast.cpp): a cast between two specializations of the same generic class now needs one's type arguments to extend the other's. Otherwise it is an error:

error: type Box<!ts.number> is not assignable to type Box<!ts.string>

cast() is shared by assignments and explicit <T>x casts. Allowing either direction therefore keeps an upcast (Box<Dog> as Box<Animal>) and the explicit downcast back (<Box<Dog>>up) working, which matches TypeScript's rule for a type assertion.

Also fixed (extendsType, MLIRTypeHelper.h): the class branch compared only the first type argument and returned, so Pair<number, number> passed as a Pair<number, string>. Now every type argument has to extend.

Tests:

  • specialization-cast/box.ts and pair.ts: error tests matched on the message.
  • 00generic_specialization_cast.ts: an upcast, a downcast and a covariant Pair, registered for compile, jit and the corpus. It is also clean under -mm=rc --verify-ownership.

Results: the suite passes locally, 3019/3019, and the DefaultLib tests pass 156/156 in both jit and compile modes.

Not changed: casts between unrelated classes are still unchecked. class A { v: number } to class B { v: string } compiles and crashes the same way. Catching that needs a structural check, because TypeScript allows two unrelated classes of the same shape. That's left for its own change.

🤖 Generated with Claude Code

`const b: Box<string> = new Box<number>(1)` compiled to a plain ts.Cast
between the two class types, and reading `b.v` then read a number as a
string and crashed. A cast between two specializations of the same
generic class now needs one's type arguments to extend the other's -
Box<Dog> as Box<Animal>, or back down as an assertion - which is
TypeScript's rule for an assertion too; otherwise it is an error.

extendsType's class branch compared only the first type argument and
returned, so Pair<number, number> passed as Pair<number, string>. Every
type argument has to extend now.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ASDAlexander77
ASDAlexander77 merged commit 50a084d into main Sep 28, 2026
2 checks passed
@ASDAlexander77
ASDAlexander77 deleted the generic-specialization-cast-error branch September 28, 2026 13:08
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