Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,7 @@ tslang_add_test(NAME test-compile-00-nullable-class-to-interface COMMAND test-ru
tslang_add_test(NAME test-compile-00-class-to-interface-null COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_to_interface_null.ts")
tslang_add_test(NAME test-compile-00-null-interface-to-interface COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00null_interface_to_interface.ts")
tslang_add_test(NAME test-compile-00-interface-type-tag-in-vtable COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_type_tag_in_vtable.ts")
tslang_add_test(NAME test-compile-00-nullable-interface-to-interface COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00nullable_interface_to_interface.ts")
tslang_add_test(NAME test-compile-00-argument-place-aliased COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00argument_place_aliased.ts")
tslang_add_test(NAME test-compile-00-temporary-read-in-finally COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00temporary_read_in_finally.ts")
tslang_add_test(NAME test-compile-00-unowned-local-store COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00unowned_local_store.ts")
Expand Down Expand Up @@ -1015,6 +1016,7 @@ tslang_add_test(NAME test-jit-00-nullable-class-to-interface COMMAND test-runner
tslang_add_test(NAME test-jit-00-class-to-interface-null COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00class_to_interface_null.ts")
tslang_add_test(NAME test-jit-00-null-interface-to-interface COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00null_interface_to_interface.ts")
tslang_add_test(NAME test-jit-00-interface-type-tag-in-vtable COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_type_tag_in_vtable.ts")
tslang_add_test(NAME test-jit-00-nullable-interface-to-interface COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00nullable_interface_to_interface.ts")
tslang_add_test(NAME test-jit-00-argument-place-aliased COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00argument_place_aliased.ts")
tslang_add_test(NAME test-jit-00-temporary-read-in-finally COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00temporary_read_in_finally.ts")
tslang_add_test(NAME test-jit-00-unowned-local-store COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00unowned_local_store.ts")
Expand Down Expand Up @@ -2002,6 +2004,7 @@ set(TSLANG_CORPUS
00class_to_interface_null.ts
00null_interface_to_interface.ts
00interface_type_tag_in_vtable.ts
00nullable_interface_to_interface.ts
00argument_place_aliased.ts
00temporary_read_in_finally.ts
00unowned_local_store.ts
Expand Down
84 changes: 84 additions & 0 deletions tslang/test/tester/tests/00nullable_interface_to_interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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.
interface Shape {
id: number;
area(): number;
}

interface HasId {
id: number;
}

interface Sized {
area(): number;
id: number;
}

class Box {
constructor(public w: number, public h: number) {}
}

class Rect implements Shape {
constructor(public id: number, public box: Box) {}

area(): number {
return this.box.w * this.box.h;
}
}

class Square extends Rect {
constructor(id: number, side: number, public label: Box) {
super(id, new Box(side, side));
}
}

@noinline function mkSquare(id: number, side: number): Shape {
return new Square(id, side, new Box(id, id));
}

@noinline function mkShape(flag: boolean): Shape | null {
return flag ? mkSquare(5, 2) : null;
}

@noinline function mkNull(): HasId | null {
return null;
}

// what a method of the interface does: -2 when it throws
function call(s: Sized | null): number {
try {
return s.area();
} catch (e) {
return -2;
}
}

function main() {
// a field first, and no method: a method called through a wrong vtable runs whatever code it lands on
const b: Shape | null = mkSquare(7, 3);
let n: HasId | null = mkNull();
n = b;
assert(n.id == 7, "a field read through the other interface");

// members in another order: the field and the method each moved
const s: Sized | null = b;
assert(s.id == 7, "a field that moved");
assert(call(s) == 9, "a method that moved");

// a declaration, and a value that came back from a call
const m: HasId | null = mkShape(true);
assert(m.id == 5, "a returned value seen through the other interface");

// null stays null, and its methods throw
const nullShape: Shape | null = mkShape(false);
const nullId: HasId | null = nullShape;
assert(nullId === null, "null seen through the other interface");
const nullSized: Sized | null = nullShape;
assert(nullSized === null, "null seen through an interface with members in another order");
assert(call(nullSized) == -2, "its method throws");

print("done.");
}
Loading