diff --git a/tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h b/tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h index b80b93e06..284669664 100644 --- a/tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h +++ b/tslang/include/TypeScript/MLIRLogic/MLIRGenStore.h @@ -1270,6 +1270,9 @@ struct NamespaceInfo bool isFunctionNamespace; + // `import * as M`: M has nothing in it, its names are the module's, which are at the top level + bool isModuleImport = false; + NamespaceInfo::TypePtr parentNamespace; }; diff --git a/tslang/lib/TypeScript/MLIRGenAccessCall.cpp b/tslang/lib/TypeScript/MLIRGenAccessCall.cpp index 1c4f80286..66474778f 100644 --- a/tslang/lib/TypeScript/MLIRGenAccessCall.cpp +++ b/tslang/lib/TypeScript/MLIRGenAccessCall.cpp @@ -341,16 +341,13 @@ namespace mlirgen assert(namespaceInfo); MLIRNamespaceGuard ng(currentNamespace); - currentNamespace = namespaceInfo; - - // `ns.value` names the namespace's member: look there before the local scope, - // which resolved it to a local variable `value` of the calling function. - if (auto member = resolveIdentifierInNamespace(location, name, genContext)) - { - return member; - } - - return V(mlirGen(location, name, genContext)); + // `ns.value` names the namespace's member and nothing else: a local variable + // `value` of the calling function, or a `value` of an outer namespace, is not + // `ns.value`. A name the namespace hasn't got is the error below. The names of + // `import * as M` are the module's, declared at the top level (see + // mlirGenImportBindings). + currentNamespace = namespaceInfo->isModuleImport ? rootNamespace : namespaceInfo; + return resolveIdentifierInNamespace(location, name, genContext); }) .Case([&](auto classStorageType) { LLVM_DEBUG(llvm::dbgs() << "\n\t...field: \t" << cl.getName();); diff --git a/tslang/lib/TypeScript/MLIRGenModule.cpp b/tslang/lib/TypeScript/MLIRGenModule.cpp index 5a56fecdb..fcc348140 100644 --- a/tslang/lib/TypeScript/MLIRGenModule.cpp +++ b/tslang/lib/TypeScript/MLIRGenModule.cpp @@ -1678,6 +1678,7 @@ namespace mlirgen auto namespaceName = MLIRHelper::getName(namedBindings.as()->name, stringAllocator); MLIRNamespaceGuard nsGuard(currentNamespace); registerNamespace(namespaceName); + currentNamespace->isModuleImport = true; return mlir::success(); } diff --git a/tslang/test/tester/CMakeLists.txt b/tslang/test/tester/CMakeLists.txt index fc8ccd084..5feb2e703 100644 --- a/tslang/test/tester/CMakeLists.txt +++ b/tslang/test/tester/CMakeLists.txt @@ -2947,6 +2947,20 @@ set_tests_properties(test-compile-diagnostic-location-implicit-constructor PROPERTIES PASS_REGULAR_EXPRESSION "implicit_constructor\\.ts:9:1: error: Class constructor is not accessable" FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed|implicit_constructor\\.ts:1:") +# `ns.name` is the namespace's member or an error: a local or an outer namespace's `name` isn't it. +foreach(namespace_member_case missing_member outer_member) + add_test(NAME test-compile-namespace-member-${namespace_member_case} + COMMAND $ --emit=obj --no-default-lib -mm=none + "${PROJECT_SOURCE_DIR}/test/tester/namespace-member/${namespace_member_case}.ts" + -o "${CMAKE_CURRENT_BINARY_DIR}/namespace-member-${namespace_member_case}.obj") +endforeach() +set_tests_properties(test-compile-namespace-member-missing_member + PROPERTIES PASS_REGULAR_EXPRESSION "missing_member\\.ts:12:11: error: Can't resolve property 'missing' of type ns" + FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed") +set_tests_properties(test-compile-namespace-member-outer_member + PROPERTIES PASS_REGULAR_EXPRESSION "outer_member\\.ts:15:11: error: Can't resolve property 'outer' of type ns" + FAIL_REGULAR_EXPRESSION "Stack dump|Assertion failed") + # A call to the collector's API (GC_gcollect, or the weak-link functions DefaultLib's WeakRef uses) # is an error under every model but gc, which alone links the collector - it used to reach the # linker as an unresolved symbol. Under gc the same file compiles. diff --git a/tslang/test/tester/import-bindings/import_namespace.ts b/tslang/test/tester/import-bindings/import_namespace.ts index ab855929b..5fed225d6 100644 --- a/tslang/test/tester/import-bindings/import_namespace.ts +++ b/tslang/test/tester/import-bindings/import_namespace.ts @@ -20,4 +20,12 @@ const b = new M.Box(6); assert(b.v == 6); assert(M.identity(7) == 7); +// M.ANSWER is the module's, whatever a local ANSWER is +function shadowed() { + const ANSWER = 1; + assert(M.ANSWER == 42); + assert(ANSWER == 1); +} +shadowed(); + print("done."); diff --git a/tslang/test/tester/namespace-member/missing_member.ts b/tslang/test/tester/namespace-member/missing_member.ts new file mode 100644 index 000000000..d723dc996 --- /dev/null +++ b/tslang/test/tester/namespace-member/missing_member.ts @@ -0,0 +1,13 @@ +// `ns.missing` is an error when the namespace has no `missing`, even with a local `missing` in +// scope: it used to fall back to the general lookup, which found the local. + +namespace ns { + export function value(): int { + return 42; + } +} + +function main() { + const missing = 5; + print(ns.missing); +} diff --git a/tslang/test/tester/namespace-member/outer_member.ts b/tslang/test/tester/namespace-member/outer_member.ts new file mode 100644 index 000000000..1937e3796 --- /dev/null +++ b/tslang/test/tester/namespace-member/outer_member.ts @@ -0,0 +1,16 @@ +// `ns.outer()` is an error when only the root namespace has an `outer`: the general lookup the +// access fell back to also searched the outer namespaces. + +function outer(): int { + return 1; +} + +namespace ns { + export function value(): int { + return 42; + } +} + +function main() { + print(ns.outer()); +} diff --git a/tslang/test/tester/tests/00ns_member_shadowed.ts b/tslang/test/tester/tests/00ns_member_shadowed.ts index e8cbd4de2..67af65750 100644 --- a/tslang/test/tester/tests/00ns_member_shadowed.ts +++ b/tslang/test/tester/tests/00ns_member_shadowed.ts @@ -18,7 +18,26 @@ namespace ns { } } +// a member declared after its use, and one in a second block of the same namespace +function early(): int { + return ns2.later() + ns2.second(); +} + +namespace ns2 { + export function later(): int { + return 1; + } +} + +namespace ns2 { + export function second(): int { + return 2; + } +} + function main() { + assert(early() == 3); + const value = ns.value(); assert(value == 42); assert(ns.value() == 42);