Skip to content

instanceof is always false for a class value held in a union or an optional #450

Description

@ASDAlexander77

x instanceof C is false whenever x is typed as a union or an optional that holds a class, even when it holds a C, and even after narrowing. Every memory model (gc, rc, none), JIT and AOT, with and without --opt.

Repro

class C { constructor(public x: number) {} }
class D { y = 1; }
function main() {
    let a = new C(1);
    print("C:", a instanceof C);
    let b: C | D = new C(2);
    print("C | D:", b instanceof C);
    let e: C | null = new C(3);
    print("C | null:", e instanceof C);
    if (e) print("C | null, narrowed:", e instanceof C);
    let f: C | number = new C(4);
    print("C | number:", f instanceof C);
}

tslang --emit=jit --no-default-lib -mm=gc --shared-libs=TypeScriptRuntime.dll io.ts prints:

C: true
C | D: false
C | null: false
C | null, narrowed: false
C | number: false

Expected true on every line.

Cause

mlirGenInstanceOfLogic (tslang/lib/TypeScript/MLIRGenImpl.h) dispatches on the left operand's type: a class calls .instanceOf with the RTTI, any checks typeof and goes through the vtable, and opaque and interface values have paths of their own. A union or an optional matches none of them and falls through to the "default logic", which emits a compile-time constant leftType == rightType. That is false for union<C, null> against C. The MLIR shows "ts.Constant"() <{value = false}> where the test should be. Narrowing with if (e) doesn't change the type the expression is generated with, so the narrowed case folds the same way.

A fix would handle a union or optional on the left at run time: for an optional, test HasValue and run the class path on the value; for a union, run it on each class member's payload, selected by the union's type tag. Non-class members (null, number) give false.

Found while writing -mm=own phase 6 tests (#449); own_declared_constant.ts tests its union value with typeof instead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions