Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -332,6 +332,11 @@ /// data member. mutable std::string CUIDHash; + /// A cache mapping modules to the hash value of their primary module name. + /// + /// This is lazily created. This is intentionally not serialized. + mutable llvm::DenseMap PrimaryModuleHash; + /// Representation of a "canonical" template template parameter that /// is used in canonical template names. class CanonicalTemplateTemplateParm : public llvm::FoldingSetNode { @@ -1066,7 +1071,7 @@ /// Get the additional modules in which the definition \p Def has /// been merged. - ArrayRef getModulesWithMergedDefinition(const NamedDecl *Def); + ArrayRef getModulesWithMergedDefinition(const NamedDecl *Def) const; /// Add a declaration to the list of declarations that are initialized /// for a module. This will typically be a global variable (with internal @@ -2691,6 +2696,14 @@ bool isSameDefaultTemplateArgument(const NamedDecl *X, const NamedDecl *Y) const; + /// Determine whether two module units lives in the same module. + /// Note that the term `Module` in implementation has different meanings + /// with the term in C++ spec. In C++ spec, a module could consist of + /// multiple module units and a module unit could consist of some of module + /// fragment. But `Module` in the implementation could be a clang map module, + /// header unit, a module unit or a module fragment. + bool isInSameModule(const Module *X, const Module *Y) const; + /// Retrieve the "canonical" template argument. /// /// The canonical template argument is the simplest template argument Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2271,10 +2271,7 @@ VisibleModuleSet VisibleModules; - /// Cache for module units which is usable for current module. - llvm::DenseSet UsableModuleUnitsCache; - - bool isUsableModule(const Module *M); + bool isUsableModule(const Module *M) const; bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind); @@ -2350,7 +2347,7 @@ const NamedDecl *D, llvm::SmallVectorImpl *Modules = nullptr); bool hasVisibleMergedDefinition(NamedDecl *Def); - bool hasMergedDefinitionInCurrentModule(NamedDecl *Def); + bool hasMergedDefinitionInCurrentModule(NamedDecl *Def) const; /// Determine if \p D and \p Suggested have a structurally compatible /// layout as described in C11 6.2.7/1. Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -1129,7 +1129,7 @@ } ArrayRef -ASTContext::getModulesWithMergedDefinition(const NamedDecl *Def) { +ASTContext::getModulesWithMergedDefinition(const NamedDecl *Def) const { auto MergedIt = MergedDefModules.find(cast(Def->getCanonicalDecl())); if (MergedIt == MergedDefModules.end()) @@ -6212,6 +6212,31 @@ llvm_unreachable("bad template name!"); } +bool ASTContext::isInSameModule(const Module *X, const Module *Y) const { + // If either one is not module unit, they must not be in the same module. + if (!X || !Y) + return false; + + if (X == Y) + return true; + + /// A header module won't be in the same module with a different address. + if (X->isHeaderLikeModule() || Y->isHeaderLikeModule()) + return false; + + auto getHashValueForPrimaryModule = [this](const Module *M) { + auto Iter = PrimaryModuleHash.find(M); + if (Iter == PrimaryModuleHash.end()) { + auto HashValue = llvm::hash_value(M->getPrimaryModuleInterfaceName()); + PrimaryModuleHash.insert({M, HashValue}); + return HashValue; + } + return Iter->second; + }; + + return getHashValueForPrimaryModule(X) == getHashValueForPrimaryModule(Y); +} + bool ASTContext::hasSameTemplateName(const TemplateName &X, const TemplateName &Y) const { return getCanonicalTemplateName(X).getAsVoidPointer() == Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -1644,8 +1644,7 @@ // Partitions are part of the module, but a partition could import another // module, so verify that the PMIs agree. if (NewM && OldM && (NewM->isModulePartition() || OldM->isModulePartition())) - return NewM->getPrimaryModuleInterfaceName() == - OldM->getPrimaryModuleInterfaceName(); + return getASTContext().isInSameModule(NewM, OldM); bool NewIsModuleInterface = NewM && NewM->isModulePurview(); bool OldIsModuleInterface = OldM && OldM->isModulePurview(); Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -1568,38 +1568,27 @@ } /// Determine if we could use all the declarations in the module. -bool Sema::isUsableModule(const Module *M) { +bool Sema::isUsableModule(const Module *M) const { 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 the current translation unit. 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 == GlobalModuleFragment || - // If M is the module we're parsing, it should be usable. This covers the - // private module fragment. The private module fragment 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 == 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; + // + // If M is the module we're parsing, it should be usable. This covers the + // private module fragment. The private module fragment 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 + // expensive comparison as much as possible. + // + // The module unit which is in the same module with the current module + // unit is usable. + return isModuleUnitOfCurrentTU(M) || + getASTContext().isInSameModule(M, getCurrentModule()); } bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) { @@ -1609,7 +1598,7 @@ return false; } -bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) { +bool Sema::hasMergedDefinitionInCurrentModule(NamedDecl *Def) const { for (const Module *Merged : Context.getModulesWithMergedDefinition(Def)) if (isUsableModule(Merged)) return true;