From e67c64f6f85861aff9439e4725d2b8a939a8c75e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 15:17:52 +0000 Subject: [PATCH 1/3] An import pointing to a .ts file includes it with its bodies mlirGenInclude generated an imported .ts module as declarations only, so a program run on its own (tslang main.ts, JIT) found no bodies for what it imported: 'Symbols not found: [ S..new ]' for new S() of an imported class. An import pointing to a .ts file now includes it with its bodies, like a referenced file; one pointing to a library (lib.so / .dll) still takes them from the library. A .d.ts stays declarations only. Known: test-runner still compiles each imported module into an object of its own and links it beside the importer, so the multi-module export-import, reference-path, shared-class-members and import-bindings tests now fail with 'multiple definition' link errors until the runner stops doing that. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VPAHhg8c9NEdoYNw52AtTg --- tslang/lib/TypeScript/MLIRGenModule.cpp | 5 ++++- tslang/test/tester/CMakeLists.txt | 6 ++++++ .../test/tester/import-source/imported_class_module.ts | 6 ++++++ tslang/test/tester/import-source/new_imported_class.ts | 9 +++++++++ 4 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tslang/test/tester/import-source/imported_class_module.ts create mode 100644 tslang/test/tester/import-source/new_imported_class.ts diff --git a/tslang/lib/TypeScript/MLIRGenModule.cpp b/tslang/lib/TypeScript/MLIRGenModule.cpp index b1b1d4125..4acf11dd8 100644 --- a/tslang/lib/TypeScript/MLIRGenModule.cpp +++ b/tslang/lib/TypeScript/MLIRGenModule.cpp @@ -1086,8 +1086,11 @@ namespace mlirgen filesInProgress.insert(canonicalPath); auto inProgress = llvm::make_scope_exit([&]() { filesInProgress.erase(canonicalPath); }); + // An import that points to a .ts file includes it with its bodies, as a referenced file + // is; only an import that points to a library (mlirGenImportSharedLib) takes the bodies + // from the library. A .d.ts is read as declarations either way. MLIRValueGuard vg(declarationMode); - declarationMode = true; + declarationMode = false; // What the imported module exports is its own to export: it is compiled separately, with // its declarations in its own __decls. Added to this module's too, a library holding both diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index fb1c35991..17d2df625 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2598,6 +2598,12 @@ add_test(NAME test-compile-reference-path-import-cycle-quiet "${reference_path_dir}/cycle_class_node.ts" -o "${CMAKE_CURRENT_BINARY_DIR}/cycle_class_node.obj") set_tests_properties(test-compile-reference-path-import-cycle-quiet PROPERTIES FAIL_REGULAR_EXPRESSION "error:") +# An import pointing to a .ts file includes it with its bodies: a program run on its own found no +# `S..new` for the imported class, which was only declared. +set(import_source_dir "${PROJECT_SOURCE_DIR}/test/tester/import-source") +tslang_add_test(NAME test-jit-import-source-new-imported-class COMMAND test-runner -jit "${import_source_dir}/new_imported_class.ts") +tslang_add_test(NAME test-compile-import-source-new-imported-class COMMAND test-runner "${import_source_dir}/new_imported_class.ts") + # `import { a as b }` and `import * as M` - each used to be "can't resolve name". set(import_bindings_dir "${PROJECT_SOURCE_DIR}/test/tester/import-bindings") tslang_add_import_tests(import-bindings-renamed "${import_bindings_dir}" import_renamed names_module) diff --git a/tslang/test/tester/import-source/imported_class_module.ts b/tslang/test/tester/import-source/imported_class_module.ts new file mode 100644 index 000000000..631029a5c --- /dev/null +++ b/tslang/test/tester/import-source/imported_class_module.ts @@ -0,0 +1,6 @@ +export class S +{ + toString() { + return "Hi"; + } +} diff --git a/tslang/test/tester/import-source/new_imported_class.ts b/tslang/test/tester/import-source/new_imported_class.ts new file mode 100644 index 000000000..2feead230 --- /dev/null +++ b/tslang/test/tester/import-source/new_imported_class.ts @@ -0,0 +1,9 @@ +// `import './m'` pointing to a .ts file includes it with its bodies: run on its own, the program +// used to fail with "Symbols not found: [ S..new ]" - the imported class was only declared. +import './imported_class_module' + +const s = new S(); +assert(s.toString() == "Hi"); +print("Hello World", s); + +print("done."); From d6e08826077a48ec254af9479bd23a84256a056f Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 15:19:20 +0000 Subject: [PATCH 2/3] test-runner -jit -shared: the imported library is loaded by the import, not --shared-libs A library an `import` points to is loaded by the program at run time; passing it with --shared-libs as well is not how it is meant to be used (and matches the Windows runner, which already did not). The 56 test-jit-shared tests pass, including the reference-path diamond/cycle and shared-class-members ones. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VPAHhg8c9NEdoYNw52AtTg --- tslang/test/tester/test-runner.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tslang/test/tester/test-runner.cpp b/tslang/test/tester/test-runner.cpp index 70be76a15..73344f274 100644 --- a/tslang/test/tester/test-runner.cpp +++ b/tslang/test/tester/test-runner.cpp @@ -686,7 +686,8 @@ void createSharedMultiBatchFile(std::string tempOutputFileNameNoExt, std::vector if (jitRun) { // one extra "../" because we run from the per-test working directory - batFile << "$TSLANGEXEPATH/tslang --emit=jit " << tslang_opt << " --shared-libs=../../../lib/libTypeScriptRuntime.so --shared-libs=./lib" << shared_filenameNoExt << ".so " << *files.begin() << " 1> ../$FILENAME.txt 2> ../$FILENAME.err" + // the library is not passed with --shared-libs: `import './'` loads it at run time + batFile << "$TSLANGEXEPATH/tslang --emit=jit " << tslang_opt << " --shared-libs=../../../lib/libTypeScriptRuntime.so " << *files.begin() << " 1> ../$FILENAME.txt 2> ../$FILENAME.err" << std::endl; } else From 5fb648cb918c78164267f3a3c638913bc107c65e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 29 Sep 2026 15:38:43 +0000 Subject: [PATCH 3/3] Compiled, an import pointing to a .ts file is a declaration; under the JIT it is included Compiled, each .ts module is an object of its own and the objects are linked into one program, so an import pointing to a .ts file only declares what the module has. Under the JIT there is no other object: the file is included with its bodies. The import-source test now also runs compiled, with the module's object linked beside the program. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01VPAHhg8c9NEdoYNw52AtTg --- tslang/lib/TypeScript/MLIRGenModule.cpp | 10 ++++++---- tslang/test/tester/CMakeLists.txt | 7 ++++--- tslang/test/tester/import-source/new_imported_class.ts | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/tslang/lib/TypeScript/MLIRGenModule.cpp b/tslang/lib/TypeScript/MLIRGenModule.cpp index 4acf11dd8..433fec27f 100644 --- a/tslang/lib/TypeScript/MLIRGenModule.cpp +++ b/tslang/lib/TypeScript/MLIRGenModule.cpp @@ -1086,11 +1086,13 @@ namespace mlirgen filesInProgress.insert(canonicalPath); auto inProgress = llvm::make_scope_exit([&]() { filesInProgress.erase(canonicalPath); }); - // An import that points to a .ts file includes it with its bodies, as a referenced file - // is; only an import that points to a library (mlirGenImportSharedLib) takes the bodies - // from the library. A .d.ts is read as declarations either way. + // An import that points to a .ts file: compiled, it is a declaration - the module is + // compiled into an object of its own, and the objects are linked into one program. Under + // the JIT there is no other object, so it is included with its bodies, as a referenced + // file is. (An import that points to a library is mlirGenImportSharedLib.) A .d.ts is + // read as declarations either way. MLIRValueGuard vg(declarationMode); - declarationMode = false; + declarationMode = !compileOptions.isJit; // What the imported module exports is its own to export: it is compiled separately, with // its declarations in its own __decls. Added to this module's too, a library holding both diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 17d2df625..8e2da2d1d 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2598,11 +2598,12 @@ add_test(NAME test-compile-reference-path-import-cycle-quiet "${reference_path_dir}/cycle_class_node.ts" -o "${CMAKE_CURRENT_BINARY_DIR}/cycle_class_node.obj") set_tests_properties(test-compile-reference-path-import-cycle-quiet PROPERTIES FAIL_REGULAR_EXPRESSION "error:") -# An import pointing to a .ts file includes it with its bodies: a program run on its own found no -# `S..new` for the imported class, which was only declared. +# Under the JIT an import pointing to a .ts file includes it with its bodies: a program run on its +# own found no `S..new` for the imported class, which was only declared. Compiled, the import is a +# declaration and the module's own object is linked beside it. set(import_source_dir "${PROJECT_SOURCE_DIR}/test/tester/import-source") tslang_add_test(NAME test-jit-import-source-new-imported-class COMMAND test-runner -jit "${import_source_dir}/new_imported_class.ts") -tslang_add_test(NAME test-compile-import-source-new-imported-class COMMAND test-runner "${import_source_dir}/new_imported_class.ts") +tslang_add_import_tests(import-source-new-imported-class "${import_source_dir}" new_imported_class imported_class_module) # `import { a as b }` and `import * as M` - each used to be "can't resolve name". set(import_bindings_dir "${PROJECT_SOURCE_DIR}/test/tester/import-bindings") diff --git a/tslang/test/tester/import-source/new_imported_class.ts b/tslang/test/tester/import-source/new_imported_class.ts index 2feead230..02f0091c0 100644 --- a/tslang/test/tester/import-source/new_imported_class.ts +++ b/tslang/test/tester/import-source/new_imported_class.ts @@ -1,5 +1,5 @@ -// `import './m'` pointing to a .ts file includes it with its bodies: run on its own, the program -// used to fail with "Symbols not found: [ S..new ]" - the imported class was only declared. +// Under the JIT an import pointing to a .ts file includes it with its bodies: run on its own, the +// program used to fail with "Symbols not found: [ S..new ]" - the imported class was only declared. import './imported_class_module' const s = new S();