diff --git a/clang/include/clang/Basic/Module.h b/clang/include/clang/Basic/Module.h --- a/clang/include/clang/Basic/Module.h +++ b/clang/include/clang/Basic/Module.h @@ -109,6 +109,9 @@ /// This is a C++20 module interface unit. ModuleInterfaceUnit, + /// This is a C++20 module implementation unit. + ModuleImplementationUnit, + /// This is a C++ 20 header unit. ModuleHeaderUnit, @@ -159,9 +162,16 @@ /// Does this Module scope describe part of the purview of a named C++ module? bool isModulePurview() const { - return Kind == ModuleInterfaceUnit || Kind == ModulePartitionInterface || - Kind == ModulePartitionImplementation || - Kind == PrivateModuleFragment; + switch (Kind) { + case ModuleInterfaceUnit: + case ModuleImplementationUnit: + case ModulePartitionInterface: + case ModulePartitionImplementation: + case PrivateModuleFragment: + return true; + default: + return false; + } } /// Does this Module scope describe a fragment of the global module within diff --git a/clang/include/clang/Lex/ModuleMap.h b/clang/include/clang/Lex/ModuleMap.h --- a/clang/include/clang/Lex/ModuleMap.h +++ b/clang/include/clang/Lex/ModuleMap.h @@ -561,6 +561,14 @@ Module *createModuleForInterfaceUnit(SourceLocation Loc, StringRef Name, Module *GlobalModule); + /// Create a new module for a C++ module implementation unit. + /// The interface module for this implementation (implicitly imported) must + /// exist and be loaded and present in the modules map. + /// + /// \returns The newly-created module. + Module *createModuleForImplementationUnit(SourceLocation Loc, StringRef Name, + Module *GlobalModule); + /// Create a header module from the specified list of headers. Module *createHeaderModule(StringRef Name, ArrayRef Headers); diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -1565,6 +1565,7 @@ return nullptr; case Module::ModuleInterfaceUnit: + case Module::ModuleImplementationUnit: case Module::ModulePartitionInterface: case Module::ModulePartitionImplementation: return M; diff --git a/clang/lib/Frontend/FrontendActions.cpp b/clang/lib/Frontend/FrontendActions.cpp --- a/clang/lib/Frontend/FrontendActions.cpp +++ b/clang/lib/Frontend/FrontendActions.cpp @@ -831,6 +831,8 @@ return "Module Map Module"; case Module::ModuleInterfaceUnit: return "Interface Unit"; + case Module::ModuleImplementationUnit: + return "Implementation Unit (should never be serialized)"; case Module::ModulePartitionInterface: return "Partition Interface"; case Module::ModulePartitionImplementation: diff --git a/clang/lib/Lex/ModuleMap.cpp b/clang/lib/Lex/ModuleMap.cpp --- a/clang/lib/Lex/ModuleMap.cpp +++ b/clang/lib/Lex/ModuleMap.cpp @@ -881,6 +881,34 @@ return Result; } +Module *ModuleMap::createModuleForImplementationUnit(SourceLocation Loc, + StringRef Name, + Module *GlobalModule) { + assert(LangOpts.CurrentModule == Name && "module name mismatch"); + // The interface for this implementation must exist and be loaded. + assert(Modules[Name] && Modules[Name]->Kind == Module::ModuleInterfaceUnit && + "creating implementation module without an interface"); + + auto *Result = new Module(Name, Loc, nullptr, /*IsFramework*/ false, + /*IsExplicit*/ false, NumCreatedModules++); + Result->Kind = Module::ModuleImplementationUnit; + SourceModule = Result; + + // Reparent the current global module fragment as a submodule of this module. + for (auto &Submodule : PendingSubmodules) { + Submodule->setParent(Result); + Submodule.release(); // now owned by parent + } + PendingSubmodules.clear(); + + // Mark the main source file as being within the newly-created module so that + // declarations and macros are properly visibility-restricted to it. + auto *MainFile = SourceMgr.getFileEntryForID(SourceMgr.getMainFileID()); + assert(MainFile && "no input file for module implementation"); + + return Result; +} + Module *ModuleMap::createHeaderModule(StringRef Name, ArrayRef Headers) { assert(LangOpts.CurrentModule == Name && "module name mismatch"); diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -1641,6 +1641,13 @@ if (NewM == OldM) return false; + // A module implementation unit has visibility of the decls in its implicitly + // imported interface. + if (getLangOpts().CPlusPlusModules && NewM && OldM && + NewM->Kind == Module::ModuleImplementationUnit && + OldM->Kind == Module::ModuleInterfaceUnit) + return NewM->Name == OldM->Name; + bool NewIsModuleInterface = NewM && NewM->isModulePurview(); bool OldIsModuleInterface = OldM && OldM->isModulePurview(); if (NewIsModuleInterface || OldIsModuleInterface) { diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -254,8 +254,8 @@ const_cast(getLangOpts()).CurrentModule = ModuleName; auto &Map = PP.getHeaderSearchInfo().getModuleMap(); - Module *Mod; - + Module *Mod; // The module we are creating. + Module *Interface = nullptr; // The interface fir an implementation. switch (MDK) { case ModuleDeclKind::Interface: case ModuleDeclKind::PartitionInterface: { @@ -288,15 +288,20 @@ // keyword nor a module-partition implicitly imports the primary // module interface unit of the module as if by a module-import- // declaration. - Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, - Module::AllVisible, - /*IsInclusionDirective=*/false); - if (!Mod) { + + // First find the interface we need to import. + Interface = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, + Module::AllVisible, + /*IsInclusionDirective=*/false); + if (!Interface) { Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; // Create an empty module interface unit for error recovery. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, GlobalModuleFragment); - } + } else + // We found the interface, now create the implementation module + Mod = Map.createModuleForImplementationUnit(ModuleLoc, ModuleName, + GlobalModuleFragment); } break; case ModuleDeclKind::PartitionImplementation: @@ -335,6 +340,19 @@ // statements, so imports are allowed. ImportState = ModuleImportState::ImportAllowed; + // We already potentially made an implicit import (in the case of a module + // implementation unit importing its interface). Make this module visible + // and return the import decl to be added to the current TU. + if (Interface) { + // Make the import decl for the interface. + ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc, + Interface, Path[0].second); + VisibleModules.setVisible(Interface, ModuleLoc); + + // If we made an implicit import of the module interface, then return the + // imported module decl. + return ConvertDeclToDeclGroup(Import); + } // FIXME: Create a ModuleDecl. return nullptr; } @@ -360,19 +378,17 @@ Diag(ModuleScopes.back().BeginLoc, diag::note_previous_definition); return nullptr; - case Module::ModuleInterfaceUnit: - break; - } - - if (!ModuleScopes.back().ModuleInterface) { + case Module::ModuleImplementationUnit: Diag(PrivateLoc, diag::err_private_module_fragment_not_module_interface); Diag(ModuleScopes.back().BeginLoc, diag::note_not_module_interface_add_export) << FixItHint::CreateInsertion(ModuleScopes.back().BeginLoc, "export "); return nullptr; + + case Module::ModuleInterfaceUnit: + break; } - // FIXME: Check this isn't a module interface partition. // FIXME: Check that this translation unit does not import any partitions; // such imports would violate [basic.link]/2's "shall be the only module unit" // restriction.