From 853784a389790bc0ca1ef6351271023150259666 Mon Sep 17 00:00:00 2001 From: ASDAlexander77 Date: Mon, 28 Sep 2026 12:01:02 +0100 Subject: [PATCH] --export=all exports the module's variables, not a function's locals An --export filter such as `all` matched every variable statement, function bodies included, so a library's __decls declared each local of each function, method and accessor as a `@dllimport` global in a `.f_` namespace - symbols no library defines. `export` cannot be written inside a function; the filters now apply only where there is no function (the module and its namespaces). Co-Authored-By: Claude Opus 5.5 --- tslang/lib/TypeScript/MLIRGenVariables.cpp | 7 ++++-- tslang/test/tester/CMakeLists.txt | 8 +++++++ tslang/test/tester/export-all/locals.ts | 27 ++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 tslang/test/tester/export-all/locals.ts diff --git a/tslang/lib/TypeScript/MLIRGenVariables.cpp b/tslang/lib/TypeScript/MLIRGenVariables.cpp index c6b9da6b2..8a780bcf0 100644 --- a/tslang/lib/TypeScript/MLIRGenVariables.cpp +++ b/tslang/lib/TypeScript/MLIRGenVariables.cpp @@ -954,11 +954,14 @@ namespace mlirgen // we need it for support "undefined type" in 'let' without initialization item->parent = variableDeclarationListAST; - // --export filters by name, and one statement can declare several names + // --export filters by name, and one statement can declare several names. Only a + // variable of the module or a namespace can be exported: a filter such as `all` matched + // every function's locals too, and declared them to importers as `@dllimport` globals + // in a `.f_` namespace, which no library defines. auto itemVarClass = varClass; if (variableDeclarationListAST->parent && !exportByDecorator) { - itemVarClass.isExport = getExportModifier( + itemVarClass.isExport = !genContext.funcOp && getExportModifier( variableDeclarationListAST->parent, MLIRHelper::getName(item->name, stringAllocator)); } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 4a31c31d7..22d6f2d72 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2555,6 +2555,14 @@ if (WIN32) FAIL_REGULAR_EXPRESSION "Stack dump|unlinked") endif() +# --export=all exports the module's variables, not the locals of its functions. +add_test(NAME test-compile-export-all-no-locals + COMMAND $ --emit=mlir --no-default-lib --export=all + "${PROJECT_SOURCE_DIR}/test/tester/export-all/locals.ts") +set_tests_properties(test-compile-export-all-no-locals + PROPERTIES PASS_REGULAR_EXPRESSION "let moduleLevel" + FAIL_REGULAR_EXPRESSION "namespace [.]f_|Stack dump|error:") + # The shared-component tier under the other two models. A shared library records the model # it was built under, so both halves of a pair are built with the same flag - which is what # these run. The file pairs are the default model's, verbatim. diff --git a/tslang/test/tester/export-all/locals.ts b/tslang/test/tester/export-all/locals.ts new file mode 100644 index 000000000..351b0cab3 --- /dev/null +++ b/tslang/test/tester/export-all/locals.ts @@ -0,0 +1,27 @@ +// --export=all exports the module's variables, not a function's: those were declared to +// importers as `@dllimport` globals in a `.f_` namespace, which no library defines. +export class Node { + items: number[] = []; + + get total(): number { + let t = 0; + for (const c of this.items) { + t += c; + } + + return t; + } + + sum(): number { + let u = 0; + for (let i = 0; i < this.items.length; i++) u += this.items[i]; + return u; + } +} + +export function f() { + let w = 1; + return w; +} + +let moduleLevel = 5;