From f06298d2a9585d09bcf7e3a04e0d05d57d4974ab Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Sun, 11 Oct 2026 21:09:26 +0100 Subject: [PATCH] import: a module's file for the target, ..ts, before .ts An import without an extension resolves to .win32.ts on Windows, .linux.ts on Linux and Android (..ts elsewhere) when that file exists, else to .ts. A module with code per target - a C++ ABI's vtables and struct layouts, for @cppvtable classes - is then picked as the default library picks lib.win32.ts or lib.linux.ts, while the importer names one module. Tests: import_os_suffix.ts with export_os_suffix.win32.ts / .linux.ts, compiled and run on the host; and the import's declarations checked for each target triple (--emit=mlir), on any host. Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenImpl.h | 42 ++++++++++++++++--- tslang/test/tester/CMakeLists.txt | 21 ++++++++++ .../tester/tests/export_os_suffix.linux.ts | 13 ++++++ .../tester/tests/export_os_suffix.win32.ts | 13 ++++++ tslang/test/tester/tests/import_os_suffix.ts | 12 ++++++ 5 files changed, 96 insertions(+), 5 deletions(-) create mode 100644 tslang/test/tester/tests/export_os_suffix.linux.ts create mode 100644 tslang/test/tester/tests/export_os_suffix.win32.ts create mode 100644 tslang/test/tester/tests/import_os_suffix.ts diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 63aac409e..1c01ddf16 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -60,6 +60,7 @@ #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/Path.h" +#include "llvm/Support/FileSystem.h" #include "llvm/Support/Error.h" #include "llvm/Support/raw_ostream.h" //#include "llvm/IR/DebugInfoMetadata.h" @@ -13899,6 +13900,11 @@ class MLIRGenImpl } // The file an `import` of a local source module names: next to the main file, `.ts` added. + // Without an extension, the module's file for the target comes first: `import "./m"` is m.win32.ts + // on Windows, m.linux.ts on Linux and Android, m..ts elsewhere, when it + // exists, else m.ts. A module with code per target (the vtables and layouts of a C++ ABI, say) + // is then picked as the default library picks lib.win32.ts or lib.linux.ts, while the importer + // names one module. std::string includeFilePath(StringRef fileName) { SmallString<256> fileNameStr(fileName); @@ -13910,11 +13916,6 @@ class MLIRGenImpl fileNameStr.append(subStr); } - if (sys::path::extension(fileName) == "") - { - fileNameStr += ".ts"; - } - SmallString<256> fullPath; if (!sys::path::has_root_path(fileNameStr)) { @@ -13924,9 +13925,40 @@ class MLIRGenImpl } sys::path::append(fullPath, fileNameStr); + + if (sys::path::extension(fileName) == "") + { + SmallString<256> targetPath(fullPath); + targetPath += "."; + targetPath += targetOSFileSuffix(); + targetPath += ".ts"; + if (sys::fs::exists(targetPath)) + { + return targetPath.str().str(); + } + + fullPath += ".ts"; + } + return fullPath.str().str(); } + // The OS part of a module's file name for the target (includeFilePath): win32, linux (Android + // too, as the default library builds lib.linux.ts for it), or the target triple's OS name. + StringRef targetOSFileSuffix() + { + llvm::Triple triple(compileOptions.moduleTargetTriple); + switch (triple.getOS()) + { + case llvm::Triple::Win32: + return "win32"; + case llvm::Triple::Linux: + return "linux"; + default: + return llvm::Triple::getOSTypeName(triple.getOS()); + } + } + std::pair> loadIncludeFile(mlir::Location location, StringRef fullPath) { std::string ignored; diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index e8302c4b6..11694dce5 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -1340,6 +1340,27 @@ tslang_add_test(NAME test-compile-export-import-owned-returns COMMAND test-runne tslang_add_test(NAME test-compile-export-import-const-locals COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_const_locals.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_const_locals.ts") tslang_add_test(NAME test-compile-export-import-const-functions COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_const_functions.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_const_functions.ts") tslang_add_test(NAME test-compile-export-import-generators COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_generators.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_generators.ts") +# `import './export_os_suffix'` takes the target's file, export_os_suffix.win32.ts or +# export_os_suffix.linux.ts: the host's is compiled and linked beside it, and the import's +# declarations for each target are those of its file (the OS function that file declares). +if (WIN32) + tslang_add_test(NAME test-compile-export-import-os-suffix COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_os_suffix.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_os_suffix.win32.ts") +elseif (CMAKE_SYSTEM_NAME STREQUAL "Linux") + tslang_add_test(NAME test-compile-export-import-os-suffix COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_os_suffix.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_os_suffix.linux.ts") +endif() +foreach(os_suffix "win32|x86_64-pc-windows-msvc|GetCurrentProcessId|getpid" "linux|x86_64-unknown-linux-gnu|getpid|GetCurrentProcessId") + string(REPLACE "|" ";" os_suffix "${os_suffix}") + list(GET os_suffix 0 os_name) + list(GET os_suffix 1 os_triple) + list(GET os_suffix 2 os_function) + list(GET os_suffix 3 os_other_function) + add_test(NAME test-compile-import-os-suffix-${os_name} + COMMAND $ --emit=mlir --no-default-lib -mtriple=${os_triple} + "${PROJECT_SOURCE_DIR}/test/tester/tests/import_os_suffix.ts") + set_tests_properties(test-compile-import-os-suffix-${os_name} + PROPERTIES PASS_REGULAR_EXPRESSION "ts\\.Func @${os_function} " + FAIL_REGULAR_EXPRESSION "ts\\.Func @${os_other_function} |error:") +endforeach() tslang_add_test(NAME test-compile-rc-export-import-owned-returns COMMAND test-runner -mm=rc "${PROJECT_SOURCE_DIR}/test/tester/tests/import_owned_returns.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_owned_returns.ts") tslang_add_test(NAME test-compile-none-export-import-owned-returns COMMAND test-runner -mm=none "${PROJECT_SOURCE_DIR}/test/tester/tests/import_owned_returns.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_owned_returns.ts") tslang_add_test(NAME test-compile-export-import-class-extends COMMAND test-runner "${PROJECT_SOURCE_DIR}/test/tester/tests/import_class_extends.ts" "${PROJECT_SOURCE_DIR}/test/tester/tests/export_class_extends.ts") diff --git a/tslang/test/tester/tests/export_os_suffix.linux.ts b/tslang/test/tester/tests/export_os_suffix.linux.ts new file mode 100644 index 000000000..3288a7819 --- /dev/null +++ b/tslang/test/tester/tests/export_os_suffix.linux.ts @@ -0,0 +1,13 @@ +// The Linux file of the module `import './export_os_suffix'` names (import_os_suffix.ts): an +// import without an extension takes .win32.ts on Windows, .linux.ts on Linux. + +declare function getpid(): s32; + +namespace O { + + export const osFile = "linux"; + + export function processId(): u32 { + return getpid(); + } +} diff --git a/tslang/test/tester/tests/export_os_suffix.win32.ts b/tslang/test/tester/tests/export_os_suffix.win32.ts new file mode 100644 index 000000000..6da3ecbbf --- /dev/null +++ b/tslang/test/tester/tests/export_os_suffix.win32.ts @@ -0,0 +1,13 @@ +// The Windows file of the module `import './export_os_suffix'` names (import_os_suffix.ts): an +// import without an extension takes .win32.ts on Windows, .linux.ts on Linux. + +declare function GetCurrentProcessId(): u32; + +namespace O { + + export const osFile = "win32"; + + export function processId(): u32 { + return GetCurrentProcessId(); + } +} diff --git a/tslang/test/tester/tests/import_os_suffix.ts b/tslang/test/tester/tests/import_os_suffix.ts new file mode 100644 index 000000000..d6e2f65f3 --- /dev/null +++ b/tslang/test/tester/tests/import_os_suffix.ts @@ -0,0 +1,12 @@ +import './export_os_suffix' + +// There is no export_os_suffix.ts: the import takes the target's file, export_os_suffix.win32.ts or +// export_os_suffix.linux.ts, the one compiled and linked beside this (the other one's functions +// would be missing at link time). + +function main() { + assert(O.osFile == "win32" || O.osFile == "linux", "the module's file for the target"); + assert(O.processId() != 0, "and its functions"); + + print("done."); +}