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
41 changes: 41 additions & 0 deletions tslang/lib/TypeScript/MLIRGenImpl.h
Original file line number Diff line number Diff line change
Expand Up @@ -10458,6 +10458,47 @@ class MLIRGenImpl
genericDeclExports << ss.str().str();
}

void addGenericInterfaceDeclarationToExport(GenericInterfaceInfo::TypePtr genericInterfaceInfo)
{
if (genericInterfaceInfo->interfaceType && isAddedToExport(genericInterfaceInfo->interfaceType))
{
// already added
return;
}

// same bounds-check as addGenericClassDeclarationToExport - a generic interface
// re-declared while re-importing another module's embedded declarations still
// carries its own `export` keyword verbatim, but at that point `sourceFile` is
// the ambient/outer file, not the "partial" buffer parsePartialStatements
// actually parsed this declaration from.
auto declEnd = static_cast<size_t>(genericInterfaceInfo->interfaceDeclaration->_end);
if (declEnd > genericInterfaceInfo->sourceFile->text.length())
{
return;
}

if (genericInterfaceInfo->interfaceType)
{
exportedTypes.insert(genericInterfaceInfo->interfaceType);
}

// like a generic class, a generic interface has no compiled body for any given
// instantiation: each importing module instantiates it locally, on demand,
// exactly like a same-file usage would. So the FULL original source - type
// parameters and member signatures intact, no @dllimport marker - must be
// re-exported verbatim for parsePartialStatements to recompile per instantiation
// in the importer.
auto declText = convertWideToUTF8(getTextOfNodeFromSourceText(
genericInterfaceInfo->sourceFile->text, genericInterfaceInfo->interfaceDeclaration.as<Node>(), true));

SmallVector<char> out;
llvm::raw_svector_ostream ss(out);
MLIRDeclarationPrinter dp(ss);
dp.printGenericClass(genericInterfaceInfo->elementNamespace, declText);

genericDeclExports << ss.str().str();
}

void addGenericTypeAliasDeclarationToExport(TypeAliasDeclaration typeAliasDeclarationAST, NamespaceInfo::TypePtr elementNamespace)
{
// same bounds-check as addGenericClassDeclarationToExport - a generic type alias
Expand Down
23 changes: 23 additions & 0 deletions tslang/lib/TypeScript/MLIRGenInterfaces.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,17 @@ namespace mlirgen
auto fullNamePtr = getFullNamespaceName(namePtr);
if (fullNameGenericInterfacesMap.count(fullNamePtr))
{
// already registered - but the registration itself typically happens during
// Stages::Discovering (before addGenericInterfaceDeclarationToExport's
// isAddedToExport gate, which only actually emits once stage ==
// Stages::SourceGeneration, will do anything) - retry the export step alone
// using the existing GenericInterfaceInfo rather than skipping it entirely.
// Mirrors registerGenericClass's identical gotcha-3 fix.
if (getExportModifier(interfaceDeclarationAST))
{
addGenericInterfaceDeclarationToExport(fullNameGenericInterfacesMap.lookup(fullNamePtr));
}

return mlir::success();
}

Expand All @@ -529,6 +540,18 @@ namespace mlirgen
getGenericInterfacesMap().insert({namePtr, newGenericInterfacePtr});
fullNameGenericInterfacesMap.insert(fullNamePtr, newGenericInterfacePtr);

// support dynamic loading: a generic interface is never instantiated in this
// module if nothing here uses it concretely, so mlirGen(InterfaceDeclaration)'s
// own addInterfaceDeclarationToExport call never runs for the bare template (it
// only fires for a SPECIALIZED instantiation) - the bare template needs to be
// exported here instead, the one place every generic interface declaration
// passes through regardless of whether it is ever instantiated locally. Mirrors
// registerGenericClass.
if (getExportModifier(interfaceDeclarationAST))
{
addGenericInterfaceDeclarationToExport(newGenericInterfacePtr);
}

return mlir::success();
}

Expand Down
3 changes: 3 additions & 0 deletions tslang/test/tester/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,7 @@ add_test(NAME test-compile-export-import-class-static COMMAND test-runner "${PRO
add_test(NAME test-compile-export-import-class-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_generic.ts")
add_test(NAME test-compile-export-import-function-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_function_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_function_generic.ts")
add_test(NAME test-compile-export-import-type-alias-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_type_alias_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_type_alias_generic.ts")
add_test(NAME test-compile-export-import-interface-generic COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_generic.ts")
add_test(NAME test-compile-export-import-class-accessor COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_accessor.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_accessor.ts")
add_test(NAME test-compile-export-import-class-indexer COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_indexer.ts")
add_test(NAME test-compile-export-import-interface-indexer COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_indexer.ts")
Expand Down Expand Up @@ -959,6 +960,7 @@ add_test(NAME test-compile-shared-export-import-class-structural-interface COMMA
add_test(NAME test-compile-shared-export-import-class-generic COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_generic.ts")
add_test(NAME test-compile-shared-export-import-function-generic COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_function_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_function_generic.ts")
add_test(NAME test-compile-shared-export-import-type-alias-generic COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_type_alias_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_type_alias_generic.ts")
add_test(NAME test-compile-shared-export-import-interface-generic COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_generic.ts")
# FIXED (2026-07-22): the -shared declaration-reconstruction path never printed real
# get/set syntax for a class's accessors, only the underlying get_x/set_x funcOps as
# ordinary methods - so property-style access ("Class member 'celsius' can't be found")
Expand Down Expand Up @@ -1007,6 +1009,7 @@ add_test(NAME test-jit-shared-export-import-class-structural-interface COMMAND t
add_test(NAME test-jit-shared-export-import-class-generic COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_generic.ts")
add_test(NAME test-jit-shared-export-import-function-generic COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_function_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_function_generic.ts")
add_test(NAME test-jit-shared-export-import-type-alias-generic COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_type_alias_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_type_alias_generic.ts")
add_test(NAME test-jit-shared-export-import-interface-generic COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_interface_generic.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_interface_generic.ts")
# FIXED: see the matching test-compile-shared-export-import-class-accessor comment above (2026-07-22).
add_test(NAME test-jit-shared-export-import-class-accessor COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_accessor.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_accessor.ts")
add_test(NAME test-jit-shared-export-import-class-indexer COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_indexer.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_indexer.ts")
Expand Down
20 changes: 20 additions & 0 deletions tslang/test/tester/tests/export_interface_generic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace M {

// Cross-module counterpart to 00interface_generic.ts. GenericInterfaceInfo
// (MLIRGenStore.h) is structurally analogous to GenericClassInfo/
// GenericFunctionInfo, both of whose cross-module export gaps needed
// dedicated fixes (PR #280, PR #285) - flagged as a plausible, unverified
// instance of the same "never routed into declExports" gap in
// decls-cross-module-declaration-mechanism / generic-function-cross-module-export-fix.

export interface Box<T> {
value: T;
get(): T;
}

export interface Pair<A, B> {
first: A;
second: B;
describe(): string;
}
}
38 changes: 38 additions & 0 deletions tslang/test/tester/tests/import_interface_generic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import './export_interface_generic'

class NumberBox implements M.Box<number> {
value: number;

constructor(v: number) {
this.value = v;
}

get(): number {
return this.value;
}
}

class NumberStringPair implements M.Pair<number, string> {
first: number;
second: string;

constructor(a: number, b: string) {
this.first = a;
this.second = b;
}

describe(): string {
return `${this.second}-${this.first}`;
}
}

function main() {
const b: M.Box<number> = new NumberBox(42);
assert(b.get() == 42);
assert(b.value == 42);

const p: M.Pair<number, string> = new NumberStringPair(1, "one");
assert(p.describe() == "one-1");

print("done.");
}
Loading