/// some C++ module.
bool isGlobalModule() const { return Kind == GlobalModuleFragment; }
+ bool isPrivateModule() const { return Kind == PrivateModuleFragment; }
+
private:
/// The submodules of this module, indexed by name.
std::vector<Module *> SubModules;
/// Get the primary module interface name from a partition.
StringRef getPrimaryModuleInterfaceName() const {
+ // Technically, global module fragment belongs to global module. And global
+ // module has no name: [module.unit]p6:
+ // The global module has no name, no module interface unit, and is not
+ // introduced by any module-declaration.
+ //
+ // <global> is the default name showed in module map.
+ if (isGlobalModule())
+ return "<global>";
+
if (isModulePartition()) {
auto pos = Name.find(':');
return StringRef(Name.data(), pos);
}
+
+ if (isPrivateModule())
+ return getTopLevelModuleName();
+
return Name;
}
/// Namespace definitions that we will export when they finish.
llvm::SmallPtrSet<const NamespaceDecl*, 8> DeferredExportedNamespaces;
- /// Get the module whose scope we are currently within.
+ /// Get the module unit whose scope we are currently within.
Module *getCurrentModule() const {
return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
}
VisibleModuleSet VisibleModules;
+ /// Cache for module units which is usable for current module.
+ llvm::DenseSet<const Module *> UsableModuleUnitsCache;
+
+ bool isUsableModule(const Module *M);
+
public:
/// Get the module owning an entity.
Module *getOwningModule(const Decl *Entity) {
return LookupModulesCache;
}
-/// Determine whether the module M is part of the current module from the
-/// perspective of a module-private visibility check.
-static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) {
+/// Determine if we could use all the declarations in the module.
+bool Sema::isUsableModule(const Module *M) {
+ assert(M && "We shouldn't check nullness for module here");
+ // Return quickly if we cached the result.
+ if (UsableModuleUnitsCache.count(M))
+ return true;
+
// If M is the global module fragment of a module that we've not yet finished
- // parsing, then it must be part of the current module.
- // If it's a partition, then it must be visible to an importer (since only
- // another partition or the named module can import it).
- return M->getTopLevelModuleName() == LangOpts.CurrentModule ||
- (M->Kind == Module::GlobalModuleFragment && !M->Parent) ||
- M->Kind == Module::ModulePartitionInterface ||
- M->Kind == Module::ModulePartitionImplementation;
+ // parsing, then M should be the global module fragment in current TU. So it
+ // should be usable.
+ // [module.global.frag]p1:
+ // The global module fragment can be used to provide declarations that are
+ // attached to the global module and usable within the module unit.
+ if ((M->isGlobalModule() && !M->Parent) ||
+ // If M is the private module fragment, it is usable only if it is within
+ // the current module unit. And it must be the current parsing module unit
+ // if it is within the current module unit according to the grammar of the
+ // private module fragment.
+ // NOTE: This is covered by the following condition. The intention of the
+ // check is to avoid string comparison as much as possible.
+ (M->isPrivateModule() && M == getCurrentModule()) ||
+ // The module unit which is in the same module with the current module
+ // unit is usable.
+ //
+ // FIXME: Here we judge if they are in the same module by comparing the
+ // string. Is there any better solution?
+ (M->getPrimaryModuleInterfaceName() ==
+ llvm::StringRef(getLangOpts().CurrentModule).split(':').first)) {
+ UsableModuleUnitsCache.insert(M);
+ return true;
+ }
+
+ return false;
}
bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) {
bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) {
for (const Module *Merged : Context.getModulesWithMergedDefinition(Def))
- if (isInCurrentModule(Merged, getLangOpts()))
+ if (isUsableModule(Merged))
return true;
return false;
}
// means it is part of the current module. For any other query, that means it
// is in our visible module set.
if (ModulePrivate) {
- if (isInCurrentModule(M, getLangOpts()))
+ if (isUsableModule(M))
return true;
else if (M->Kind == Module::ModuleKind::ModulePartitionImplementation &&
isModuleDirectlyImported(M))
--- /dev/null
+// RUN: rm -rf %t
+// RUN: mkdir -p %t
+// RUN: split-file %s %t
+// RUN: %clang_cc1 -std=c++20 %t/impl.cppm -emit-module-interface -o %t/M-impl.pcm
+// RUN: %clang_cc1 -std=c++20 %t/M.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/M.pcm
+// RUN: %clang_cc1 -std=c++20 %t/Use.cpp -fprebuilt-module-path=%t -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 %t/UseInPartA.cppm -fprebuilt-module-path=%t -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 %t/UseInAnotherModule.cppm -fprebuilt-module-path=%t -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 %t/Private.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/A.pcm
+// RUN: %clang_cc1 -std=c++20 %t/TryUseFromPrivate.cpp -fprebuilt-module-path=%t -verify -fsyntax-only
+// RUN: %clang_cc1 -std=c++20 %t/Global.cppm -emit-module-interface -fprebuilt-module-path=%t -o %t/C.pcm
+// RUN: %clang_cc1 -std=c++20 %t/UseGlobal.cpp -fprebuilt-module-path=%t -verify -fsyntax-only
+
+//--- impl.cppm
+module M:impl;
+class A {};
+
+//--- M.cppm
+export module M;
+import :impl;
+export A f();
+
+//--- Use.cpp
+import M;
+void test() {
+ A a; // expected-error {{unknown type name 'A'}}
+}
+
+//--- UseInPartA.cppm
+// expected-no-diagnostics
+export module M:partA;
+import :impl;
+void test() {
+ A a;
+}
+
+//--- UseInAnotherModule.cppm
+export module B;
+import M;
+void test() {
+ A a; // expected-error {{unknown type name 'A'}}
+}
+
+//--- Private.cppm
+export module A;
+module :private;
+
+class A {};
+void test() {
+ A a;
+}
+
+//--- TryUseFromPrivate.cpp
+
+import A;
+void test() {
+ A a; // expected-error {{unknown type name 'A'}}
+}
+
+//--- Global.cppm
+module;
+class A{};
+export module C;
+void test() {
+ A a;
+}
+
+//--- UseGlobal.cpp
+import C;
+void test() {
+ A a; // expected-error {{'A' must be declared before it is used}}
+ // expected-note@Global.cppm:2 {{declaration here is not visible}}
+}
int &c = n; // expected-error {{use of undeclared identifier}}
//--- std10-1-ex2-tu7.cpp
+// expected-no-diagnostics
module B:X3; // does not implicitly import B
-import :X2; // X2 is an implementation so exports nothing.
- // error: n not visible here.
-int &c = n; // expected-error {{use of undeclared identifier }}
+import :X2; // X2 is an implementation unit import B.
+// According to [module.import]p7:
+// Additionally, when a module-import-declaration in a module unit of some
+// module M imports another module unit U of M, it also imports all
+// translation units imported by non-exported module-import-declarations in
+// the module unit purview of U.
+//
+// So B is imported in B:X3 due to B:X2 imported B. So n is visible here.
+int &c = n;