diff options
author | Iain Sandoe <iain@sandoe.co.uk> | 2021-02-11 00:15:56 +0000 |
---|---|---|
committer | Iain Sandoe <iain@sandoe.co.uk> | 2022-02-20 10:13:57 +0000 |
commit | 8a3f9a584ad43369cf6a034dc875ebfca76d9033 (patch) | |
tree | 01260c9ab4df791f80241d6791cd9348090a9437 /clang/lib/Sema/SemaModule.cpp | |
parent | 2a46450849de6904fc64f9a65303b20ca7fc9dbd (diff) | |
download | llvm-8a3f9a584ad43369cf6a034dc875ebfca76d9033.zip llvm-8a3f9a584ad43369cf6a034dc875ebfca76d9033.tar.gz llvm-8a3f9a584ad43369cf6a034dc875ebfca76d9033.tar.bz2 |
[C++20][Modules][1/8] Track valid import state.
In C++20 modules imports must be together and at the start of the module.
Rather than growing more ad-hoc flags to test state, this keeps track of the
phase of of a valid module TU (first decl, global module frag, module,
private module frag). If the phasing is broken (with some diagnostic) the
pattern does not conform to a valid C++20 module, and we set the state
accordingly.
We can thus issue diagnostics when imports appear in the wrong places and
decouple the C++20 modules state from other module variants (modules-ts and
clang modules). Additionally, we attempt to diagnose wrong imports before
trying to find the module where possible (the latter will generally emit an
unhelpful diagnostic about the module not being available).
Although this generally simplifies the handling of C++20 module import
diagnostics, the motivation was that, in particular, it allows detecting
invalid imports like:
import module A;
int some_decl();
import module B;
where being in a module purview is insufficient to identify them.
Differential Revision: https://reviews.llvm.org/D118893
Diffstat (limited to 'clang/lib/Sema/SemaModule.cpp')
-rw-r--r-- | clang/lib/Sema/SemaModule.cpp | 46 |
1 files changed, 34 insertions, 12 deletions
diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 85e5864..9bed3cb 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -80,12 +80,20 @@ Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) { return nullptr; } -Sema::DeclGroupPtrTy -Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, - ModuleDeclKind MDK, ModuleIdPath Path, bool IsFirstDecl) { +Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, + SourceLocation ModuleLoc, + ModuleDeclKind MDK, + ModuleIdPath Path, + ModuleImportState &ImportState) { assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) && "should only have module decl in Modules TS or C++20"); + bool IsFirstDecl = ImportState == ModuleImportState::FirstDecl; + bool SeenGMF = ImportState == ModuleImportState::GlobalFragment; + // If any of the steps here fail, we count that as invalidating C++20 + // module state; + ImportState = ModuleImportState::NotACXX20Module; + // A module implementation unit requires that we are not compiling a module // of any kind. A module interface unit requires that we are not compiling a // module map. @@ -134,9 +142,13 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment) GlobalModuleFragment = ModuleScopes.back().Module; + assert((!getLangOpts().CPlusPlusModules || + SeenGMF == (bool)GlobalModuleFragment) && + "mismatched global module state"); + // In C++20, the module-declaration must be the first declaration if there // is no global module fragment. - if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !GlobalModuleFragment) { + if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !SeenGMF) { Diag(ModuleLoc, diag::err_module_decl_not_at_start); SourceLocation BeginLoc = ModuleScopes.empty() @@ -231,6 +243,10 @@ Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); TU->setLocalOwningModule(Mod); + // We are in the module purview, but before any other (non import) + // statements, so imports are allowed. + ImportState = ModuleImportState::ImportAllowed; + // FIXME: Create a ModuleDecl. return nullptr; } @@ -301,10 +317,10 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, ModuleIdPath Path) { - // Flatten the module path for a Modules TS module name. + // Flatten the module path for a C++20 or Modules TS module name. std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc; - if (getLangOpts().ModulesTS) { - std::string ModuleName; + std::string ModuleName; + if (getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS) { for (auto &Piece : Path) { if (!ModuleName.empty()) ModuleName += "."; @@ -314,6 +330,14 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, Path = ModuleIdPath(ModuleNameLoc); } + // Diagnose self-import before attempting a load. + if (getLangOpts().CPlusPlusModules && isCurrentModulePurview() && + getCurrentModule()->Name == ModuleName) { + Diag(ImportLoc, diag::err_module_self_import) + << ModuleName << getLangOpts().CurrentModule; + return true; + } + Module *Mod = getModuleLoader().loadModule(ImportLoc, Path, Module::AllVisible, /*IsInclusionDirective=*/false); @@ -342,11 +366,9 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // FIXME: we should support importing a submodule within a different submodule // of the same top-level module. Until we do, make it an error rather than // silently ignoring the import. - // Import-from-implementation is valid in the Modules TS. FIXME: Should we - // warn on a redundant import of the current module? - // FIXME: Import of a module from an implementation partition of the same - // module is permitted. - if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule && + // FIXME: Should we warn on a redundant import of the current module? + if (!getLangOpts().CPlusPlusModules && + Mod->getTopLevelModuleName() == getLangOpts().CurrentModule && (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) { Diag(ImportLoc, getLangOpts().isCompilingModule() ? diag::err_module_self_import |