diff options
author | Dimitry Andric <dim@FreeBSD.org> | 2023-07-26 19:03:47 +0000 |
---|---|---|
committer | Dimitry Andric <dim@FreeBSD.org> | 2023-07-26 19:04:23 +0000 |
commit | 7fa27ce4a07f19b07799a767fc29416f3b625afb (patch) | |
tree | 27825c83636c4de341eb09a74f49f5d38a15d165 /clang/lib/Sema/SemaModule.cpp | |
parent | e3b557809604d036af6e00c60f012c2025b59a5e (diff) |
Diffstat (limited to 'clang/lib/Sema/SemaModule.cpp')
-rw-r--r-- | clang/lib/Sema/SemaModule.cpp | 323 |
1 files changed, 140 insertions, 183 deletions
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index f52c0247f01c..cd38cd4cf69d 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -15,6 +15,7 @@ #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaInternal.h" +#include "llvm/ADT/StringExtras.h" #include <optional> using namespace clang; @@ -74,20 +75,9 @@ static std::string stringFromPath(ModuleIdPath Path) { Sema::DeclGroupPtrTy Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) { - if (!ModuleScopes.empty() && - ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment) { - // Under -std=c++2a -fmodules-ts, we can find an explicit 'module;' after - // already implicitly entering the global module fragment. That's OK. - assert(getLangOpts().CPlusPlusModules && getLangOpts().ModulesTS && - "unexpectedly encountered multiple global module fragment decls"); - ModuleScopes.back().BeginLoc = ModuleLoc; - return nullptr; - } - - // We start in the global module; all those declarations are implicitly - // module-private (though they do not have module linkage). + // We start in the global module; Module *GlobalModule = - PushGlobalModuleFragment(ModuleLoc, /*IsImplicit=*/false); + PushGlobalModuleFragment(ModuleLoc); // All declarations created from now on are owned by the global module. auto *TU = Context.getTranslationUnitDecl(); @@ -135,7 +125,6 @@ void Sema::HandleStartOfHeaderUnit() { ModuleScopes.back().BeginLoc = StartOfTU; ModuleScopes.back().Module = Mod; ModuleScopes.back().ModuleInterface = true; - ModuleScopes.back().IsPartition = false; VisibleModules.setVisible(Mod, StartOfTU); // From now on, we have an owning module for all declarations we see. @@ -168,19 +157,24 @@ static bool DiagReservedModuleName(Sema &S, const IdentifierInfo *II, if (Reason == Reserved && S.getSourceManager().isInSystemHeader(Loc)) Reason = Valid; - if (Reason != Valid) { - S.Diag(Loc, diag::err_invalid_module_name) << II << (int)Reason; - return true; + switch (Reason) { + case Valid: + return false; + case Invalid: + return S.Diag(Loc, diag::err_invalid_module_name) << II; + case Reserved: + S.Diag(Loc, diag::warn_reserved_module_name) << II; + return false; } - return false; + llvm_unreachable("fell off a fully covered switch"); } Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, ModuleIdPath Path, ModuleIdPath Partition, ModuleImportState &ImportState) { - assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) && - "should only have module decl in Modules TS or C++20"); + assert(getLangOpts().CPlusPlusModules && + "should only have module decl in standard C++ modules"); bool IsFirstDecl = ImportState == ModuleImportState::FirstDecl; bool SeenGMF = ImportState == ModuleImportState::GlobalFragment; @@ -244,8 +238,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, return nullptr; } - assert((!getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS || - SeenGMF == (bool)this->GlobalModuleFragment) && + assert((!getLangOpts().CPlusPlusModules || + SeenGMF == (bool)this->TheGlobalModuleFragment) && "mismatched global module state"); // In C++20, the module-declaration must be the first declaration if there @@ -262,7 +256,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, } } - // C++2b [module.unit]p1: ... The identifiers module and import shall not + // C++23 [module.unit]p1: ... The identifiers module and import shall not // appear as identifiers in a module-name or module-partition. All // module-names either beginning with an identifier consisting of std // followed by zero or more digits or containing a reserved identifier @@ -275,11 +269,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, if (!getSourceManager().isInSystemHeader(Path[0].second) && (FirstComponentName == "std" || (FirstComponentName.startswith("std") && - llvm::all_of(FirstComponentName.drop_front(3), &llvm::isDigit)))) { - Diag(Path[0].second, diag::err_invalid_module_name) - << Path[0].first << /*reserved*/ 1; - return nullptr; - } + llvm::all_of(FirstComponentName.drop_front(3), &llvm::isDigit)))) + Diag(Path[0].second, diag::warn_reserved_module_name) << Path[0].first; // Then test all of the components in the path to see if any of them are // using another kind of reserved or invalid identifier. @@ -310,8 +301,8 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName; auto &Map = PP.getHeaderSearchInfo().getModuleMap(); - Module *Mod; - + Module *Mod; // The module we are creating. + Module *Interface = nullptr; // The interface for an implementation. switch (MDK) { case ModuleDeclKind::Interface: case ModuleDeclKind::PartitionInterface: { @@ -337,19 +328,29 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, } case ModuleDeclKind::Implementation: { - std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc( - PP.getIdentifierInfo(ModuleName), Path[0].second); // C++20 A module-declaration that contains neither an export- // 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) { + std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc( + PP.getIdentifierInfo(ModuleName), Path[0].second); + + // The module loader will assume we're trying to import the module that + // we're building if `LangOpts.CurrentModule` equals to 'ModuleName'. + // Change the value for `LangOpts.CurrentModule` temporarily to make the + // module loader work properly. + const_cast<LangOptions &>(getLangOpts()).CurrentModule = ""; + Interface = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, + Module::AllVisible, + /*IsInclusionDirective=*/false); + const_cast<LangOptions&>(getLangOpts()).CurrentModule = ModuleName; + + if (!Interface) { Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; // Create an empty module interface unit for error recovery. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName); + } else { + Mod = Map.createModuleForImplementationUnit(ModuleLoc, ModuleName); } } break; @@ -361,7 +362,7 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, break; } - if (!this->GlobalModuleFragment) { + if (!this->TheGlobalModuleFragment) { ModuleScopes.push_back({}); if (getLangOpts().ModulesLocalVisibility) ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules); @@ -374,7 +375,6 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleScopes.back().BeginLoc = StartLoc; ModuleScopes.back().Module = Mod; ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation; - ModuleScopes.back().IsPartition = IsPartition; VisibleModules.setVisible(Mod, ModuleLoc); // From now on, we have an owning module for all declarations we see. @@ -390,17 +390,32 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, // statements, so imports are allowed. ImportState = ModuleImportState::ImportAllowed; - // For an implementation, We already made an implicit import (its interface). - // Make and return the import decl to be added to the current TU. - if (MDK == ModuleDeclKind::Implementation) { - // Make the import decl for the interface. - ImportDecl *Import = - ImportDecl::Create(Context, CurContext, ModuleLoc, Mod, Path[0].second); - // and return it to be added. + getASTContext().setCurrentNamedModule(Mod); + + // 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) { + + VisibleModules.setVisible(Interface, ModuleLoc); + VisibleModules.makeTransitiveImportsVisible(Interface, ModuleLoc); + + // Make the import decl for the interface in the impl module. + ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc, + Interface, Path[0].second); + CurContext->addDecl(Import); + + // Sequence initialization of the imported module before that of the current + // module, if any. + Context.addModuleInitializer(ModuleScopes.back().Module, Import); + Mod->Imports.insert(Interface); // As if we imported it. + // Also save this as a shortcut to checking for decls in the interface + ThePrimaryInterface = Interface; + // 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; } @@ -410,10 +425,11 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc, // C++20 [basic.link]/2: // A private-module-fragment shall appear only in a primary module // interface unit. - switch (ModuleScopes.empty() ? Module::GlobalModuleFragment + switch (ModuleScopes.empty() ? Module::ExplicitGlobalModuleFragment : ModuleScopes.back().Module->Kind) { case Module::ModuleMapModule: - case Module::GlobalModuleFragment: + case Module::ExplicitGlobalModuleFragment: + case Module::ImplicitGlobalModuleFragment: case Module::ModulePartitionImplementation: case Module::ModulePartitionInterface: case Module::ModuleHeaderUnit: @@ -425,19 +441,17 @@ Sema::ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc, 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. @@ -473,9 +487,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, ModuleIdPath Path, bool IsPartition) { - - bool Cxx20Mode = getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS; - assert((!IsPartition || Cxx20Mode) && "partition seen in non-C++20 code?"); + assert((!IsPartition || getLangOpts().CPlusPlusModules) && + "partition seen in non-C++20 code?"); // For a C++20 module name, flatten into a single identifier with the source // location of the first component. @@ -493,7 +506,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, ModuleName += stringFromPath(Path); ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second}; Path = ModuleIdPath(ModuleNameLoc); - } else if (Cxx20Mode) { + } else if (getLangOpts().CPlusPlusModules) { ModuleName = stringFromPath(Path); ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second}; Path = ModuleIdPath(ModuleNameLoc); @@ -533,6 +546,9 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, Module *Mod, ModuleIdPath Path) { + if (Mod->isHeaderUnit()) + Diag(ImportLoc, diag::warn_experimental_header_unit); + VisibleModules.setVisible(Mod, ImportLoc); checkModuleImportContext(*this, Mod, ImportLoc, CurContext); @@ -541,8 +557,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // of the same top-level module. Until we do, make it an error rather than // silently ignoring the import. // FIXME: Should we warn on a redundant import of the current module? - if (Mod->isForBuilding(getLangOpts()) && - (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) { + if (Mod->isForBuilding(getLangOpts())) { Diag(ImportLoc, getLangOpts().isCompilingModule() ? diag::err_module_self_import : diag::err_module_import_in_implementation) @@ -602,16 +617,9 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // [module.interface]p1: // An export-declaration shall inhabit a namespace scope and appear in the // purview of a module interface unit. - Diag(ExportLoc, diag::err_export_not_in_module_interface) - << (!ModuleScopes.empty() && - !ModuleScopes.back().ImplicitGlobalModuleFragment); + Diag(ExportLoc, diag::err_export_not_in_module_interface); } - // In some cases we need to know if an entity was present in a directly- - // imported module (as opposed to a transitive import). This avoids - // searching both Imports and Exports. - DirectModuleImports.insert(Mod); - return Import; } @@ -630,11 +638,9 @@ void Sema::BuildModuleInclude(SourceLocation DirectiveLoc, Module *Mod) { TUKind == TU_Module && getSourceManager().isWrittenInMainFile(DirectiveLoc); - bool ShouldAddImport = !IsInModuleIncludes; - - // If this module import was due to an inclusion directive, create an - // implicit import declaration to capture it in the AST. - if (ShouldAddImport) { + // If we are really importing a module (not just checking layering) due to an + // #include in the main file, synthesize an ImportDecl. + if (getLangOpts().Modules && !IsInModuleIncludes) { TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); ImportDecl *ImportD = ImportDecl::CreateImplicit(getASTContext(), TU, DirectiveLoc, Mod, @@ -810,76 +816,22 @@ Decl *Sema::ActOnStartExportDecl(Scope *S, SourceLocation ExportLoc, return D; } -static bool checkExportedDeclContext(Sema &S, DeclContext *DC, - SourceLocation BlockStart); - -namespace { -enum class UnnamedDeclKind { - Empty, - StaticAssert, - Asm, - UsingDirective, - Namespace, - Context -}; -} - -static std::optional<UnnamedDeclKind> getUnnamedDeclKind(Decl *D) { - if (isa<EmptyDecl>(D)) - return UnnamedDeclKind::Empty; - if (isa<StaticAssertDecl>(D)) - return UnnamedDeclKind::StaticAssert; - if (isa<FileScopeAsmDecl>(D)) - return UnnamedDeclKind::Asm; - if (isa<UsingDirectiveDecl>(D)) - return UnnamedDeclKind::UsingDirective; - // Everything else either introduces one or more names or is ill-formed. - return std::nullopt; -} - -unsigned getUnnamedDeclDiag(UnnamedDeclKind UDK, bool InBlock) { - switch (UDK) { - case UnnamedDeclKind::Empty: - case UnnamedDeclKind::StaticAssert: - // Allow empty-declarations and static_asserts in an export block as an - // extension. - return InBlock ? diag::ext_export_no_name_block : diag::err_export_no_name; +static bool checkExportedDecl(Sema &, Decl *, SourceLocation); - case UnnamedDeclKind::UsingDirective: - // Allow exporting using-directives as an extension. - return diag::ext_export_using_directive; - - case UnnamedDeclKind::Namespace: - // Anonymous namespace with no content. - return diag::introduces_no_names; - - case UnnamedDeclKind::Context: - // Allow exporting DeclContexts that transitively contain no declarations - // as an extension. - return diag::ext_export_no_names; - - case UnnamedDeclKind::Asm: - return diag::err_export_no_name; - } - llvm_unreachable("unknown kind"); -} - -static void diagExportedUnnamedDecl(Sema &S, UnnamedDeclKind UDK, Decl *D, - SourceLocation BlockStart) { - S.Diag(D->getLocation(), getUnnamedDeclDiag(UDK, BlockStart.isValid())) - << (unsigned)UDK; - if (BlockStart.isValid()) - S.Diag(BlockStart, diag::note_export); +/// Check that it's valid to export all the declarations in \p DC. +static bool checkExportedDeclContext(Sema &S, DeclContext *DC, + SourceLocation BlockStart) { + bool AllUnnamed = true; + for (auto *D : DC->decls()) + AllUnnamed &= checkExportedDecl(S, D, BlockStart); + return AllUnnamed; } /// Check that it's valid to export \p D. static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) { - // C++2a [module.interface]p3: - // An exported declaration shall declare at least one name - if (auto UDK = getUnnamedDeclKind(D)) - diagExportedUnnamedDecl(S, *UDK, D, BlockStart); - // [...] shall not declare a name with internal linkage. + // C++20 [module.interface]p3: + // [...] it shall not declare a name with internal linkage. bool HasName = false; if (auto *ND = dyn_cast<NamedDecl>(D)) { // Don't diagnose anonymous union objects; we'll diagnose their members @@ -889,6 +841,7 @@ static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) { S.Diag(ND->getLocation(), diag::err_export_internal) << ND; if (BlockStart.isValid()) S.Diag(BlockStart, diag::note_export); + return false; } } @@ -904,31 +857,29 @@ static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) { S.Diag(Target->getLocation(), diag::note_using_decl_target); if (BlockStart.isValid()) S.Diag(BlockStart, diag::note_export); + return false; } } // Recurse into namespace-scope DeclContexts. (Only namespace-scope - // declarations are exported.). + // declarations are exported). if (auto *DC = dyn_cast<DeclContext>(D)) { - if (isa<NamespaceDecl>(D) && DC->decls().empty()) { - if (!HasName) - // We don't allow an empty anonymous namespace (we don't allow decls - // in them either, but that's handled in the recursion). - diagExportedUnnamedDecl(S, UnnamedDeclKind::Namespace, D, BlockStart); - // We allow an empty named namespace decl. - } else if (DC->getRedeclContext()->isFileContext() && !isa<EnumDecl>(D)) - return checkExportedDeclContext(S, DC, BlockStart); - } - return false; -} - -/// Check that it's valid to export all the declarations in \p DC. -static bool checkExportedDeclContext(Sema &S, DeclContext *DC, - SourceLocation BlockStart) { - bool AllUnnamed = true; - for (auto *D : DC->decls()) - AllUnnamed &= checkExportedDecl(S, D, BlockStart); - return AllUnnamed; + if (!isa<NamespaceDecl>(D)) + return true; + + if (auto *ND = dyn_cast<NamedDecl>(D)) { + if (!ND->getDeclName()) { + S.Diag(ND->getLocation(), diag::err_export_anon_ns_internal); + if (BlockStart.isValid()) + S.Diag(BlockStart, diag::note_export); + return false; + } else if (!DC->decls().empty() && + DC->getRedeclContext()->isFileContext()) { + return checkExportedDeclContext(S, DC, BlockStart); + } + } + } + return true; } /// Complete the definition of an export declaration. @@ -943,12 +894,7 @@ Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) { SourceLocation BlockStart = ED->hasBraces() ? ED->getBeginLoc() : SourceLocation(); for (auto *Child : ED->decls()) { - if (checkExportedDecl(*this, Child, BlockStart)) { - // If a top-level child is a linkage-spec declaration, it might contain - // no declarations (transitively), in which case it's ill-formed. - diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child, - BlockStart); - } + checkExportedDecl(*this, Child, BlockStart); if (auto *FD = dyn_cast<FunctionDecl>(Child)) { // [dcl.inline]/7 // If an inline function or variable that is attached to a named module @@ -966,44 +912,55 @@ Decl *Sema::ActOnFinishExportDecl(Scope *S, Decl *D, SourceLocation RBraceLoc) { return D; } -Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc, - bool IsImplicit) { +Module *Sema::PushGlobalModuleFragment(SourceLocation BeginLoc) { // We shouldn't create new global module fragment if there is already // one. - if (!GlobalModuleFragment) { + if (!TheGlobalModuleFragment) { ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap(); - GlobalModuleFragment = Map.createGlobalModuleFragmentForModuleUnit( + TheGlobalModuleFragment = Map.createGlobalModuleFragmentForModuleUnit( BeginLoc, getCurrentModule()); } - assert(GlobalModuleFragment && "module creation should not fail"); + assert(TheGlobalModuleFragment && "module creation should not fail"); // Enter the scope of the global module. - ModuleScopes.push_back({BeginLoc, GlobalModuleFragment, + ModuleScopes.push_back({BeginLoc, TheGlobalModuleFragment, /*ModuleInterface=*/false, - /*IsPartition=*/false, - /*ImplicitGlobalModuleFragment=*/IsImplicit, /*OuterVisibleModules=*/{}}); - VisibleModules.setVisible(GlobalModuleFragment, BeginLoc); + VisibleModules.setVisible(TheGlobalModuleFragment, BeginLoc); - return GlobalModuleFragment; + return TheGlobalModuleFragment; } void Sema::PopGlobalModuleFragment() { - assert(!ModuleScopes.empty() && getCurrentModule()->isGlobalModule() && + assert(!ModuleScopes.empty() && + getCurrentModule()->isExplicitGlobalModule() && "left the wrong module scope, which is not global module fragment"); ModuleScopes.pop_back(); } -bool Sema::isModuleUnitOfCurrentTU(const Module *M) const { - assert(M); - - Module *CurrentModuleUnit = getCurrentModule(); +Module *Sema::PushImplicitGlobalModuleFragment(SourceLocation BeginLoc, + bool IsExported) { + Module **M = IsExported ? &TheExportedImplicitGlobalModuleFragment + : &TheImplicitGlobalModuleFragment; + if (!*M) { + ModuleMap &Map = PP.getHeaderSearchInfo().getModuleMap(); + *M = Map.createImplicitGlobalModuleFragmentForModuleUnit( + BeginLoc, IsExported, getCurrentModule()); + } + assert(*M && "module creation should not fail"); - // If we are not in a module currently, M must not be the module unit of - // current TU. - if (!CurrentModuleUnit) - return false; + // Enter the scope of the global module. + ModuleScopes.push_back({BeginLoc, *M, + /*ModuleInterface=*/false, + /*OuterVisibleModules=*/{}}); + VisibleModules.setVisible(*M, BeginLoc); + return *M; +} - return M->isSubModuleOf(CurrentModuleUnit->getTopLevelModule()); +void Sema::PopImplicitGlobalModuleFragment() { + assert(!ModuleScopes.empty() && + getCurrentModule()->isImplicitGlobalModule() && + "left the wrong module scope, which is not global module fragment"); + ModuleScopes.pop_back(); } |