diff options
author | Chuanqi Xu <yedeng.yd@linux.alibaba.com> | 2024-03-06 15:34:37 +0800 |
---|---|---|
committer | Chuanqi Xu <yedeng.yd@linux.alibaba.com> | 2024-03-06 15:46:55 +0800 |
commit | d3df2a834cf6febb44c699d109b9e7f622194837 (patch) | |
tree | e3608172efc23d01d0b77bf91b9c6e01a52d3573 /clang/lib/Sema/SemaModule.cpp | |
parent | d3e79e4cc33b89c61a8763a130f60a443eed4775 (diff) | |
download | llvm-d3df2a834cf6febb44c699d109b9e7f622194837.zip llvm-d3df2a834cf6febb44c699d109b9e7f622194837.tar.gz llvm-d3df2a834cf6febb44c699d109b9e7f622194837.tar.bz2 |
[C++20] [Modules] Handle transitive import in the module properly
Close https://github.com/llvm/llvm-project/issues/84002
Per [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.
However, we only tried to implement it during the implicit import of
primary module interface for module implementation unit.
Also we didn't implement the last sentence from [module.import]p7
completely:
> These rules can in turn lead to the importation of yet more
> translation units.
This patch tries to care the both issues.
Diffstat (limited to 'clang/lib/Sema/SemaModule.cpp')
-rw-r--r-- | clang/lib/Sema/SemaModule.cpp | 94 |
1 files changed, 91 insertions, 3 deletions
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index ed7f626..f08c1cb3 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -73,6 +73,90 @@ static std::string stringFromPath(ModuleIdPath Path) { return Name; } +/// Helper function for makeTransitiveImportsVisible to decide whether +/// the \param Imported module unit is in the same module with the \param +/// CurrentModule. +/// \param FoundPrimaryModuleInterface is a helper parameter to record the +/// primary module interface unit corresponding to the module \param +/// CurrentModule. Since currently it is expensive to decide whether two module +/// units come from the same module by comparing the module name. +static bool +isImportingModuleUnitFromSameModule(Module *Imported, Module *CurrentModule, + Module *&FoundPrimaryModuleInterface) { + if (!Imported->isNamedModule()) + return false; + + // The a partition unit we're importing must be in the same module of the + // current module. + if (Imported->isModulePartition()) + return true; + + // If we found the primary module interface during the search process, we can + // return quickly to avoid expensive string comparison. + if (FoundPrimaryModuleInterface) + return Imported == FoundPrimaryModuleInterface; + + if (!CurrentModule) + return false; + + // Then the imported module must be a primary module interface unit. It + // is only allowed to import the primary module interface unit from the same + // module in the implementation unit and the implementation partition unit. + + // Since we'll handle implementation unit above. We can only care + // about the implementation partition unit here. + if (!CurrentModule->isModulePartitionImplementation()) + return false; + + if (Imported->getPrimaryModuleInterfaceName() == + CurrentModule->getPrimaryModuleInterfaceName()) { + assert(!FoundPrimaryModuleInterface || + FoundPrimaryModuleInterface == Imported); + FoundPrimaryModuleInterface = Imported; + return true; + } + + return false; +} + +/// [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. These rules can in turn lead to the +/// importation of yet more translation units. +static void +makeTransitiveImportsVisible(VisibleModuleSet &VisibleModules, Module *Imported, + Module *CurrentModule, SourceLocation ImportLoc, + bool IsImportingPrimaryModuleInterface = false) { + assert(Imported->isNamedModule() && + "'makeTransitiveImportsVisible()' is intended for standard C++ named " + "modules only."); + + llvm::SmallVector<Module *, 4> Worklist; + Worklist.push_back(Imported); + + Module *FoundPrimaryModuleInterface = + IsImportingPrimaryModuleInterface ? Imported : nullptr; + + while (!Worklist.empty()) { + Module *Importing = Worklist.pop_back_val(); + + if (VisibleModules.isVisible(Importing)) + continue; + + // FIXME: The ImportLoc here is not meaningful. It may be problematic if we + // use the sourcelocation loaded from the visible modules. + VisibleModules.setVisible(Importing, ImportLoc); + + if (isImportingModuleUnitFromSameModule(Importing, CurrentModule, + FoundPrimaryModuleInterface)) + for (Module *TransImported : Importing->Imports) + if (!VisibleModules.isVisible(TransImported)) + Worklist.push_back(TransImported); + } +} + Sema::DeclGroupPtrTy Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) { // We start in the global module; @@ -396,8 +480,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, // and return the import decl to be added to the current TU. if (Interface) { - VisibleModules.setVisible(Interface, ModuleLoc); - VisibleModules.makeTransitiveImportsVisible(Interface, ModuleLoc); + makeTransitiveImportsVisible(VisibleModules, Interface, Mod, ModuleLoc, + /*IsImportingPrimaryModuleInterface=*/true); // Make the import decl for the interface in the impl module. ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc, @@ -554,7 +638,11 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, if (Mod->isHeaderUnit()) Diag(ImportLoc, diag::warn_experimental_header_unit); - VisibleModules.setVisible(Mod, ImportLoc); + if (Mod->isNamedModule()) + makeTransitiveImportsVisible(VisibleModules, Mod, getCurrentModule(), + ImportLoc); + else + VisibleModules.setVisible(Mod, ImportLoc); checkModuleImportContext(*this, Mod, ImportLoc, CurContext); |