diff --git a/tslang/lib/TypeScript/OwnershipFacts.h b/tslang/lib/TypeScript/OwnershipFacts.h index abb342acf..992003b45 100644 --- a/tslang/lib/TypeScript/OwnershipFacts.h +++ b/tslang/lib/TypeScript/OwnershipFacts.h @@ -215,7 +215,9 @@ inline bool holdsAsIs(mlir::Type wide, mlir::Type narrow, bool onlyNullable = fa // An op whose result is the very block its operand holds: a class widened to a union or an // optional or narrowed back, a union made from its payload or read back from it, an object seen -// through an interface (`{vtable, this}`, released through `this`). rc's retain or release of +// through an interface (`{vtable, this}`, released through `this`), and an interface seen as +// another one or inside a union (`this` is the same; a vtable laid out for the other interface is +// interned, nobody's block). rc's retain or release of // either is one of the block, so the pass looks through them. A cast that converts (a number // printed into a string) or boxes (into `any`) makes a new block and is not one. inline bool isView(mlir::Operation *op) @@ -233,7 +235,7 @@ inline bool isView(mlir::Operation *op) } auto keeps = [](mlir::Type type) { - return mlir::isa(type); + return mlir::isa(type); }; // a bound function and a hybrid one are the same `{func, this, tag}`: a closure passed on as // a function value is still its box diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 70a2e5d53..d8aa2aa7f 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -3774,6 +3774,14 @@ endforeach() # a null class object given to an interface (also in TSLANG_CORPUS: rc, none) tslang_add_test(NAME test-jit-own-00-class-to-interface-null COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_to_interface_null.ts") tslang_add_test(NAME test-compile-own-00-class-to-interface-null COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_to_interface_null.ts") +# an interface value seen as another interface, or as one inside a union, is the block it shows: +# own moves and borrows it as it does the object (also in TSLANG_CORPUS: rc, none) +foreach(iface_cast_test 00interface_cast_unrelated 00interface_union_cast_unrelated 00nullable_interface_to_interface + 00null_interface_to_interface) + string(REPLACE "_" "-" iface_cast_name "${iface_cast_test}") + tslang_add_test(NAME test-jit-own-${iface_cast_name} COMMAND test-runner -jit -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/${iface_cast_test}.ts") + tslang_add_test(NAME test-compile-own-${iface_cast_name} COMMAND test-runner -mm=own "${PROJECT_SOURCE_DIR}/test/tester/tests/${iface_cast_test}.ts") +endforeach() # A `let` given a new value after its value moved owns the new one (#528); the same program under # rc and gc (not none: nothing is freed there, and the loops would hold about 800 MB). diff --git a/tslang/test/tester/tests/00interface_cast_unrelated.ts b/tslang/test/tester/tests/00interface_cast_unrelated.ts index 096d483e9..12db6b245 100644 --- a/tslang/test/tester/tests/00interface_cast_unrelated.ts +++ b/tslang/test/tester/tests/00interface_cast_unrelated.ts @@ -77,7 +77,7 @@ function main() { assert(d2.dist == 3, "a field of a call's result"); assert(distOf(h) == 3, "a field of an argument"); assert((h).dist == 3, "a field through "); - const ds: HasDist[] = [h]; + const ds: HasDist[] = [mkHit()]; assert(ds[0].dist == 3, "a field of an element"); // two members in another order @@ -122,8 +122,9 @@ function main() { // casts made again and again share one vtable per content and keep reading right let sum: number = 0; for (let i = 0; i < 1000; i++) { - const each: HasDist = i % 2 == 0 ? h : hc; - sum += each.dist; + const even: HasDist = h; + const odd: HasDist = hc; + sum += i % 2 == 0 ? even.dist : odd.dist; } assert(sum == 500 * 3 + 500 * 30, "casts in a loop"); diff --git a/tslang/test/tester/tests/00null_interface_to_interface.ts b/tslang/test/tester/tests/00null_interface_to_interface.ts index e9950d9b7..92a16971d 100644 --- a/tslang/test/tester/tests/00null_interface_to_interface.ts +++ b/tslang/test/tester/tests/00null_interface_to_interface.ts @@ -1,8 +1,7 @@ // @strict-null false // A null interface cast to an interface it does not extend, whose members are elsewhere (#666 builds // that interface's vtable out of the value's own one) is null still, and its methods throw: the null -// vtable's slots, stubs that throw, are what the new vtable is made of. Not under -mm=own, which -// does not take an interface value of one interface as another yet. +// vtable's slots, stubs that throw, are what the new vtable is made of. interface Hit { thing: number; dist: number; diff --git a/tslang/test/tester/tests/00nullable_interface_to_interface.ts b/tslang/test/tester/tests/00nullable_interface_to_interface.ts index 92ae0b04b..bcdbb4698 100644 --- a/tslang/test/tester/tests/00nullable_interface_to_interface.ts +++ b/tslang/test/tester/tests/00nullable_interface_to_interface.ts @@ -1,8 +1,7 @@ // An interface-or-null given to another interface-or-null whose interface it does not extend is the // interface-to-interface cast (#666), which builds the target's vtable out of the value's own one. A // plain copy kept the source's vtable, and a field read through the target read a method slot as the -// field's offset. A null value is null still, and its methods throw. Not under -mm=own, which does not -// take this program's interface values returned from calls yet. +// field's offset. A null value is null still, and its methods throw. interface Shape { id: number; area(): number;