From 3c7999ff9f44acdb706eb30768fadc94d78906ee Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Thu, 23 Jul 2026 09:14:25 +0100 Subject: [PATCH] Fix cross-module generic type alias export and add coverage Last remaining item from decls-cross-module-declaration-mechanism.md's unverified list (after generic classes/PR #280, generic functions/PR #285, generic interfaces/PR #286): genericTypeAliasMap's registration path in mlirGen(TypeAliasDeclaration) never called any export function for the generic branch - only the non-generic else-branch calls addTypeDeclarationToExport. Confirmed with a cross-module repro: a generic type alias failed cross-module resolution under -shared. Unlike the other three generic declaration kinds, there is no dedicated GenericTypeAliasInfo struct tracking sourceFile/ elementNamespace for a generic type alias - genericTypeAliasMap's value is just {typeParams, typeNode}, since resolution (getTypeByTypeReference / resolveGenericTypeInNamespace) is pure compile-time type substitution on the stored TypeNode, never re-invoking mlirGen on the whole declaration the way a class/function/ interface instantiation does. So instead of adding a new Info struct, addGenericTypeAliasDeclarationToExport takes the AST node and namespace directly and is called inline from the one registration site, gated on `stage == Stages::SourceGeneration` (a given top-level TypeAliasDeclaration is visited exactly once per stage, so this is sufficient dedup - no "already registered" early-return branch exists here to retry from, unlike the class/function/interface fixes). Tested with both a single- and two-type-param alias (Box, Pair) to check for the classes/functions' export-bleed-on- instantiation bug; neither triggered it, matching generic interfaces' outcome - a type alias has no compiled body or runtime entity at all, so there's nothing for an instantiation to wrongly dllexport in the first place. All 3 tiers passed on the first attempt, no follow-up fix needed - the simplest of the four generic-declaration-kind fixes. New export_type_alias_generic.ts/import_type_alias_generic.ts cross-module test pair (compile, -shared compile, -jit -shared tiers). 802/802 green. This closes out the generic cross-module export arc. Co-Authored-By: Claude Sonnet 5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 50 +++++++++++++++++++ tslang/test/tester/CMakeLists.txt | 3 ++ .../tester/tests/export_type_alias_generic.ts | 18 +++++++ .../tester/tests/import_type_alias_generic.ts | 12 +++++ 4 files changed, 83 insertions(+) create mode 100644 tslang/test/tester/tests/export_type_alias_generic.ts create mode 100644 tslang/test/tester/tests/import_type_alias_generic.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index e3b7cdf29..7e0162845 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -8311,6 +8311,26 @@ class MLIRGenImpl } getGenericTypeAliasMap().insert({namePtr, {typeParameters, typeAliasDeclarationAST->type}}); + + // support dynamic loading: like a generic class/function/interface, a + // generic type alias has no compiled body or runtime entity at all - + // resolution is pure compile-time type substitution + // (getTypeByTypeReference/resolveGenericTypeInNamespace call + // getType(typeNode, ...) directly on the stored TypeNode, never + // re-invoking mlirGen on the whole declaration), unlike + // GenericClassInfo/GenericFunctionInfo/GenericInterfaceInfo there is no + // dedicated Info struct tracking sourceFile/elementNamespace for this + // declaration kind - print inline here instead of adding one, since this + // is the only place needing it and there is no "already registered" + // early-return branch to retry from (unlike registerGenericClass/ + // registerGenericFunctionLike/registerGenericInterface): + // isAddedToExport's own stage gate is unnecessary too since a given + // top-level TypeAliasDeclaration node is visited exactly once per stage, + // so a direct stage check is sufficient dedup. + if (hasExportModifier && stage == Stages::SourceGeneration) + { + addGenericTypeAliasDeclarationToExport(typeAliasDeclarationAST, currentNamespace); + } } else { @@ -10367,6 +10387,36 @@ class MLIRGenImpl genericDeclExports << ss.str().str(); } + void addGenericTypeAliasDeclarationToExport(TypeAliasDeclaration typeAliasDeclarationAST, NamespaceInfo::TypePtr elementNamespace) + { + // same bounds-check as addGenericClassDeclarationToExport - a generic type alias + // 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(typeAliasDeclarationAST->_end); + if (declEnd > sourceFile->text.length()) + { + return; + } + + // like a generic class, a generic type alias has no compiled body for any given + // instantiation: resolution is pure compile-time type substitution, done fresh in + // whichever module references it. So the FULL original source - type parameters + // and the aliased type expression intact, no @dllimport marker - must be + // re-exported verbatim for parsePartialStatements to recompile per reference in + // the importer. + auto declText = convertWideToUTF8(getTextOfNodeFromSourceText( + sourceFile->text, typeAliasDeclarationAST.as(), true)); + + SmallVector out; + llvm::raw_svector_ostream ss(out); + MLIRDeclarationPrinter dp(ss); + dp.printGenericClass(elementNamespace, declText); + + genericDeclExports << ss.str().str(); + } + auto getNamespaceName() -> StringRef { return currentNamespace->name; diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 9ebc986af..425e8746e 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -884,6 +884,7 @@ add_test(NAME test-compile-export-import-class-static COMMAND test-runner "${PRO # declaration's own re-export attempt hit a stale source-file/AST-position mismatch). # See class-generic-declaration-export-fix memory for the full account. 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-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-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") @@ -955,6 +956,7 @@ add_test(NAME test-compile-shared-export-import-class-structural-interface COMMA # add_test(NAME test-compile-shared-export-import-class-implements-interface-abstract COMMAND test-runner -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_implements_interface_abstract.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_implements_interface_abstract.ts") # FIXED: see the matching test-compile-export-import-class-generic comment above (2026-07-22). 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-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") # 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") @@ -1001,6 +1003,7 @@ add_test(NAME test-jit-shared-export-import-class-structural-interface COMMAND t # add_test(NAME test-jit-shared-export-import-class-implements-interface-abstract COMMAND test-runner -jit -shared "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_implements_interface_abstract.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_implements_interface_abstract.ts") # FIXED: see the matching test-compile-export-import-class-generic comment above (2026-07-22). 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-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") # 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") diff --git a/tslang/test/tester/tests/export_type_alias_generic.ts b/tslang/test/tester/tests/export_type_alias_generic.ts new file mode 100644 index 000000000..e30c72f8e --- /dev/null +++ b/tslang/test/tester/tests/export_type_alias_generic.ts @@ -0,0 +1,18 @@ +namespace M { + + // Cross-module counterpart to 00type_aliases_in_generics.ts. The + // genericTypeAliasMap entry (MLIRGenStore.h) has no dedicated Info struct + // (unlike GenericClassInfo/GenericFunctionInfo/GenericInterfaceInfo) - it + // was the last unverified item flagged in + // decls-cross-module-declaration-mechanism / generic-interface-cross-module-export-fix + // as a plausible instance of the same "never routed into declExports" gap. + + export type Box = { + value: T; + }; + + export type Pair = { + first: A; + second: B; + }; +} diff --git a/tslang/test/tester/tests/import_type_alias_generic.ts b/tslang/test/tester/tests/import_type_alias_generic.ts new file mode 100644 index 000000000..f31cb8aac --- /dev/null +++ b/tslang/test/tester/tests/import_type_alias_generic.ts @@ -0,0 +1,12 @@ +import './export_type_alias_generic' + +function main() { + const b: M.Box = { value: 42 }; + assert(b.value == 42); + + const p: M.Pair = { first: 1, second: "one" }; + assert(p.first == 1); + assert(p.second == "one"); + + print("done."); +}