diff --git a/tslang/include/TypeScript/MLIRLogic/MLIRGenContext.h b/tslang/include/TypeScript/MLIRLogic/MLIRGenContext.h index d9472c00f..eac41c614 100644 --- a/tslang/include/TypeScript/MLIRLogic/MLIRGenContext.h +++ b/tslang/include/TypeScript/MLIRLogic/MLIRGenContext.h @@ -159,6 +159,10 @@ struct GenContext bool insertIntoParentScope = false; mlir::Operation *currentOperation = nullptr; mlir_ts::FuncOp funcOp; + // The function a root's own statements are generated in when they are held back from the + // module level (generateGlobalEntryCode): its variables are still the module's, and an + // exported one is exported (#448). A function nested in them has a funcOp of its own. + mlir_ts::FuncOp rootStatementsFuncOp; FunctionPrototypeDOM::TypePtr funcProto; llvm::StringMap *capturedVars = nullptr; llvm::SmallVector *usingVars = nullptr; diff --git a/tslang/lib/TypeScript/MLIRGenImpl.h b/tslang/lib/TypeScript/MLIRGenImpl.h index 13c50b3a8..f9741ccbe 100644 --- a/tslang/lib/TypeScript/MLIRGenImpl.h +++ b/tslang/lib/TypeScript/MLIRGenImpl.h @@ -2533,7 +2533,10 @@ class MLIRGenImpl { // there is no initializer, var declration can be undefined //type = getUnionType(type, getUndefinedType()); - if (!isa(type) && !hasModifier(parent, SyntaxKind::DeclareKeyword)) + // a variable a library declares to its importers (`@dllimport let k: s32;` in its + // __decls) is defined in the library, so it has no initializer here (#448) + if (!isa(type) && !hasModifier(parent, SyntaxKind::DeclareKeyword) && + !MLIRHelper::hasDecorator(parent, DLL_IMPORT)) { emitWarning(loc(item), "'let' does not have initializer, use undefined union type ' | undefined'."); } diff --git a/tslang/lib/TypeScript/MLIRGenModule.cpp b/tslang/lib/TypeScript/MLIRGenModule.cpp index 9657b913e..5abc23ed5 100644 --- a/tslang/lib/TypeScript/MLIRGenModule.cpp +++ b/tslang/lib/TypeScript/MLIRGenModule.cpp @@ -793,6 +793,10 @@ namespace mlirgen return mlir::success(); } + // the root's own statements: its variables are still the module's to export + GenContext rootGenContext(genContext); + rootGenContext.rootStatementsFuncOp = genContext.funcOp; + for (auto &statement : statements) { auto isVariableStatement = statement == SyntaxKind::VariableStatement; @@ -808,11 +812,11 @@ namespace mlirgen [](auto decl) { return decl->initializer == SyntaxKind::ArrowFunction; }); if (!hasArrowDeclaration) { - variableStatement->declarationList->flags &= ~NodeFlags::Const; + variableStatement->declarationList->flags &= ~NodeFlags::Const; } } - if (failed(mlirGen(statement, genContext))) + if (failed(mlirGen(statement, rootGenContext))) { emitError(loc(statement), "failed statement"); return mlir::failure(); diff --git a/tslang/lib/TypeScript/MLIRGenVariables.cpp b/tslang/lib/TypeScript/MLIRGenVariables.cpp index 8f9422146..c5ef94733 100644 --- a/tslang/lib/TypeScript/MLIRGenVariables.cpp +++ b/tslang/lib/TypeScript/MLIRGenVariables.cpp @@ -1001,9 +1001,13 @@ namespace mlirgen // every function's locals too, and declared them to importers as `@dllimport` globals // in a `.f_` namespace, which no library defines. auto itemVarClass = varClass; + // A root's statements held back into its entry function are still the module's + // (rootStatementsFuncOp): `export const k = 5` beside top-level code was not exported, + // and the importer of the library could not resolve k (#448). + auto atModuleLevel = !genContext.funcOp || genContext.funcOp == genContext.rootStatementsFuncOp; if (variableDeclarationListAST->parent && !exportByDecorator) { - itemVarClass.isExport = !genContext.funcOp && getExportModifier( + itemVarClass.isExport = atModuleLevel && getExportModifier( variableDeclarationListAST->parent, MLIRHelper::getName(item->name, stringAllocator)); } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index 3273d9527..4c589daf7 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -3048,6 +3048,14 @@ tslang_add_import_tests(import-source-new-imported-class "${import_source_dir}" # instruction", and a module linked beside the program got a `main` of its own (#447). tslang_add_test(NAME test-jit-import-source-toplevel COMMAND test-runner -jit "${import_source_dir}/import_toplevel.ts") tslang_add_import_tests(import-source-toplevel "${import_source_dir}" import_toplevel toplevel_module) +# An exported const of a module with top-level statements, imported: the importer could not resolve +# it under -shared ("can't resolve name: k"), and it was a `let` with no initializer otherwise (#448). +tslang_add_test(NAME test-jit-import-source-export-const COMMAND test-runner -jit "${import_source_dir}/import_export_const.ts") +tslang_add_import_tests(import-source-export-const "${import_source_dir}" import_export_const export_const_module) +# the library declares k to its importer as `@dllimport let k: s32;`, defined in the library: no +# "'let' does not have initializer" warning for it +set_tests_properties(test-compile-shared-import-source-export-const test-jit-shared-import-source-export-const + PROPERTIES FAIL_REGULAR_EXPRESSION "does not have initializer|Stack dump|Assertion failed") # The JIT cache (--emit=jit, on by default): each .ts module imported is an object of its own, # loaded beside the program's - imported through two others and initialized first; and a program diff --git a/tslang/test/tester/import-source/export_const_module.ts b/tslang/test/tester/import-source/export_const_module.ts new file mode 100644 index 000000000..df9f9fdf2 --- /dev/null +++ b/tslang/test/tester/import-source/export_const_module.ts @@ -0,0 +1,4 @@ +// A module with an exported const and a top-level statement, imported (#448). +export const k = 5; + +print("module init"); diff --git a/tslang/test/tester/import-source/import_export_const.ts b/tslang/test/tester/import-source/import_export_const.ts new file mode 100644 index 000000000..2cfb7a859 --- /dev/null +++ b/tslang/test/tester/import-source/import_export_const.ts @@ -0,0 +1,7 @@ +// The importer of a module with an exported const and a top-level statement (#448). +import "./export_const_module"; + +function main() { + assert(k == 5, "the module's exported const"); + print("done."); +}