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
19 changes: 18 additions & 1 deletion tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,23 @@ struct InterfaceInfo
{
}

// own methods plus every inherited method from `extends`, in the same
// extends-first-then-own order as getTupleTypeFields/getVirtualTable -
// needed wherever code must patch/visit every method slot a cast target
// could own, not just this interface's own directly-declared ones.
void getAllMethods(llvm::SmallVector<InterfaceMethodInfo *> &allMethods)
{
for (auto &extent : extends)
{
std::get<1>(extent)->getAllMethods(allMethods);
}

for (auto &method : methods)
{
allMethods.push_back(&method);
}
}

mlir::LogicalResult getTupleTypeFields(llvm::SmallVector<mlir_ts::FieldInfo> &tupleFields, mlir::MLIRContext *context)
{
for (auto &extent : extends)
Expand Down Expand Up @@ -335,7 +352,7 @@ struct InterfaceInfo
{
for (auto &extent : extends)
{
if (mlir::failed(std::get<1>(extent)->getVirtualTable(vtable, resolveField, resolveMethod)))
if (mlir::failed(std::get<1>(extent)->getVirtualTable(vtable, resolveField, resolveMethod, methodsAsFields)))
{
return mlir::failure();
}
Expand Down
14 changes: 11 additions & 3 deletions tslang/lib/TypeScript/MLIRGenInterfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,14 @@ namespace mlirgen
auto globalVTableRefValue = resolveFullNameIdentifier(location, fullObjectInterfaceVTableFieldName, true, genContext);

// we need to update methods references in VTable with functions from object;
if (newInterfacePtr->methods.size() > 0) {
// includes methods inherited via `extends`, not just this interface's own -
// an inherited method's vtable slot needs patching (or at least visiting) the
// same as an own one; only checking newInterfacePtr->methods here left every
// inherited method's slot holding its initial offset-placeholder value
// (never a real function pointer), crashing on the first call through it.
llvm::SmallVector<InterfaceMethodInfo *> allMethods;
newInterfacePtr->getAllMethods(allMethods);
if (allMethods.size() > 0) {

mlir_ts::TupleType storeType;
if (auto objectStoreType = dyn_cast<mlir_ts::ObjectStorageType>(objectType.getStorageType()))
Expand All @@ -220,8 +227,9 @@ namespace mlirgen
// local funcOp to name) still need their function pointer read out of the
// actual object `in` at cast time.
llvm::SmallVector<InterfaceMethodInfo *> methodsNeedingPatch;
for (auto& method : newInterfacePtr->methods)
for (auto* methodPtr : allMethods)
{
auto& method = *methodPtr;
auto fieldId = builder.getStringAttr(method.name);
auto index = mth.getFieldIndexByFieldName(storeType, fieldId);
if (index == -1)
Expand All @@ -232,7 +240,7 @@ namespace mlirgen
auto fieldInfo = mth.getFieldInfoByIndex(storeType, index);
if (lookupObjectLiteralMethodSymbol(fieldInfo.type, fieldId).empty())
{
methodsNeedingPatch.push_back(&method);
methodsNeedingPatch.push_back(methodPtr);
}
}

Expand Down
4 changes: 4 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ add_test(NAME test-compile-00-interface-captures COMMAND test-runner "${PROJECT_
add_test(NAME test-compile-00-object-annotated-method COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-compile-00-object-annotated-method-params COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-compile-00-object-annotated-method-interleaved COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_interleaved.ts")
add_test(NAME test-compile-00-object-annotated-method-extends-interface COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_extends_interface.ts")
add_test(NAME test-compile-00-interface-object-array COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_object_array.ts")
add_test(NAME test-compile-00-interface-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_generic.ts")
add_test(NAME test-compile-00-interface-new COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_new.ts")
Expand Down Expand Up @@ -647,6 +648,7 @@ add_test(NAME test-jit-00-interface-captures COMMAND test-runner -jit "${PROJECT
add_test(NAME test-jit-00-object-annotated-method COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method.ts")
add_test(NAME test-jit-00-object-annotated-method-params COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_params.ts")
add_test(NAME test-jit-00-object-annotated-method-interleaved COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_interleaved.ts")
add_test(NAME test-jit-00-object-annotated-method-extends-interface COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00object_annotated_method_extends_interface.ts")
add_test(NAME test-jit-00-interface-object-array COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_object_array.ts")
add_test(NAME test-jit-00-interface-generic COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_generic.ts")
add_test(NAME test-jit-00-interface-new COMMAND test-runner -jit "${PROJECT_SOURCE_DIR}/test/tester/tests/00interface_new.ts")
Expand Down Expand Up @@ -861,6 +863,7 @@ add_test(NAME test-compile-shared-export-import-object-literal-structural-typed
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-params COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_params.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_params.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-multi-method COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_multi_method.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_multi_method.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-interleaved COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_interleaved.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_interleaved.ts")
add_test(NAME test-compile-shared-export-import-object-literal-structural-typed-extends-interface COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_extends_interface.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_extends_interface.ts")
add_test(NAME test-compile-shared-export-import-vars COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars.ts")
add_test(NAME test-compile-shared-export-import-vars-2 COMMAND test-runner -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars2.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars2.ts")
add_test(NAME test-compile-shared-export-import-enum COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_enum.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_enum.ts")
Expand All @@ -882,6 +885,7 @@ add_test(NAME test-jit-shared-export-import-object-literal-structural-typed COMM
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-params COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_params.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_params.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-multi-method COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_multi_method.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_multi_method.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-interleaved COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_interleaved.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_interleaved.ts")
add_test(NAME test-jit-shared-export-import-object-literal-structural-typed-extends-interface COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_object_literal_structural_typed_extends_interface.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_object_literal_structural_typed_extends_interface.ts")
add_test(NAME test-jit-shared-export-import-vars COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars.ts")
add_test(NAME test-jit-shared-export-import-vars-2 COMMAND test-runner -jit -shared -gctors-as-method "${PROJECT_SOURCE_DIR}/test/tester/tests/import_vars2.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_vars2.ts")
add_test(NAME test-jit-shared-export-import-enum COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_enum.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_enum.ts")
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Casting a structurally-typed object literal to an interface that EXTENDS
// another interface used to fail outright: interface B { base; addBase() }
// interface Accumulator extends B { total; add(); addTwice(); scaled() }.
//
// Two separate bugs, both in the interface vtable machinery:
// 1. InterfaceInfo::getVirtualTable's recursion into `extends` forgot to
// propagate the `methodsAsFields` flag to the recursive call, so an
// inherited (non-conditional) method always hit a stub resolver that
// unconditionally returns failure - the CAST STATEMENT ITSELF failed to
// compile ("error: failed statement", no specific diagnostic).
// 2. Once (1) was fixed, the interface cast's runtime vtable-patch loop
// (mlirGenCreateInterfaceVTableForObject) only walked the interface's
// OWN methods, never inherited ones - an inherited method's vtable slot
// was left holding its unpatched offset-placeholder value forever,
// crashing with an access violation on the first call through it.

function main() {
interface Base {
base: number;
addBase(n: number): void;
}

interface Accumulator extends Base {
total: number;
add(n: number): void;
addTwice(n: number): void;
scaled(factor: number): number;
}

let raw = {
base: 100.0,
addBase(n: number) { this.base = this.base + n; },
total: 0.0,
add(n: number) { this.total = this.total + n; },
addTwice(n: number) { this.add(n); this.add(n); },
scaled(factor: number) { return this.total * factor; },
};

let acc: Accumulator = <Accumulator>raw;

acc.addBase(5);
assert(acc.base == 105);
print(acc.base);

acc.add(3);
assert(acc.total == 3);
print(acc.total);

acc.addTwice(2);
assert(acc.total == 7);
print(acc.total);

const result = acc.scaled(2);
assert(result == 14);
print(result);

print("done.");
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
namespace A {

export interface Base {
base: number;
addBase(n: number): void;
}

// extends export_object_literal_structural_typed_interleaved.ts's
// coverage: that test's interface is flat (no `extends`). Here
// Accumulator inherits base/addBase from Base - see
// 00object_annotated_method_extends_interface.ts for the same-module
// version of this coverage and the two bugs this used to hit (interface
// vtable construction and patching both need to walk inherited members,
// not just an interface's own directly-declared ones).
export interface Accumulator extends Base {
total: number;
add(n: number): void;
addTwice(n: number): void;
scaled(factor: number): number;
}

export var acc: {
base: number;
addBase(n: number): void;
total: number;
add(n: number): void;
addTwice(n: number): void;
scaled(factor: number): number;
} = {
base: 100.0,
addBase(n: number) { this.base = this.base + n; },
total: 0.0,
add(n: number) { this.total = this.total + n; },
addTwice(n: number) { this.add(n); this.add(n); },
scaled(factor: number) { return this.total * factor; },
};
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import './export_object_literal_structural_typed_extends_interface'

var acc: A.Accumulator = <A.Accumulator>A.acc;

acc.addBase(5);
print(acc.base);
assert(acc.base == 105);

acc.add(3);
print(acc.total);
assert(acc.total == 3);

acc.addTwice(2);
print(acc.total);
assert(acc.total == 7);

print(acc.scaled(2));
assert(acc.scaled(2) == 14);

print("done.");
Loading