iains created this revision. iains updated this revision to Diff 405588. iains added a comment. iains updated this revision to Diff 405590. iains updated this revision to Diff 408760. iains updated this revision to Diff 408855. iains added reviewers: rsmith, urnathan, ChuanqiXu. iains published this revision for review. Herald added a project: clang. Herald added a subscriber: cfe-commits.
rebased onto the import state machine. iains added a comment. reapply rebase iains added a comment. Rebased onto other modules work. Also had to bump DIAG_SIZE_PARSE, since the existing allocation was full. iains added a comment. update after fixing (most) lint suggestions. The change to DiagnosticIDs.h is not going to be applicable - it would need the whole file to be rewritten and that change is incidental to this patch series. iains added a comment. this is the third patch in an 8 patch series to implement basic module partition support. The change to clang/include/clang/Basic/DiagnosticIDs.h is required for this to build (at present) but is otherwise incidental. This implements the parsing and recognition of module partition CMIs and removes the FIXMEs in the parser. Module partitions are recognised in the base computation of visibility, however additional amendments to visibility follow in subsequent patches. Add initial testcases for partition handling, derived from the examples in Section 10 of the C++20 standard, which identifies what should be accepted and/or rejected. Repository: rG LLVM Github Monorepo https://reviews.llvm.org/D118586 Files: clang/include/clang/AST/Decl.h clang/include/clang/Basic/DiagnosticIDs.h clang/include/clang/Basic/DiagnosticParseKinds.td clang/include/clang/Sema/Sema.h clang/lib/Parse/Parser.cpp clang/lib/Sema/SemaDecl.cpp clang/lib/Sema/SemaLookup.cpp clang/lib/Sema/SemaModule.cpp clang/test/CXX/module/module.unit/p3.cpp clang/test/CXX/module/module.unit/p8.cpp clang/test/Modules/cxx20-import-diagnostics-a.cpp clang/test/Modules/cxx20-partition-diagnostics-a.cpp
Index: clang/test/Modules/cxx20-partition-diagnostics-a.cpp =================================================================== --- /dev/null +++ clang/test/Modules/cxx20-partition-diagnostics-a.cpp @@ -0,0 +1,18 @@ +// Module Partition diagnostics + +// RUN: %clang_cc1 -std=c++20 -S -D TU=1 -x c++ %s -o /dev/null -verify + +// RUN: %clang_cc1 -std=c++20 -S -D TU=2 -x c++ %s -o /dev/null -verify + +#if TU == 1 + +import :B; // expected-error {{module partition imports must be within a module purview}} + +#elif TU == 2 +module; // expected-error {{missing 'module' declaration at end of global module fragment introduced here}} + +import :Part; // expected-error {{module partition imports cannot be in the global module fragment}} + +#else +#error "no TU set" +#endif Index: clang/test/Modules/cxx20-import-diagnostics-a.cpp =================================================================== --- clang/test/Modules/cxx20-import-diagnostics-a.cpp +++ clang/test/Modules/cxx20-import-diagnostics-a.cpp @@ -51,6 +51,8 @@ #elif TU == 2 +module; + export module AOK1; import B; Index: clang/test/CXX/module/module.unit/p8.cpp =================================================================== --- clang/test/CXX/module/module.unit/p8.cpp +++ clang/test/CXX/module/module.unit/p8.cpp @@ -12,7 +12,7 @@ #elif MODE == 1 // expected-no-diagnostics -module foo; +module foo; // Implementation, implicitly imports foo. #define IMPORTED #elif MODE == 2 @@ -21,15 +21,15 @@ #define IMPORTED #elif MODE == 3 -export module bar; +export module bar; // A different module #elif MODE == 4 -module foo:bar; // expected-error {{not yet supported}} -#define IMPORTED // FIXME +module foo:bar; // Partition implementation +//#define IMPORTED (we don't import foo here) #elif MODE == 5 -export module foo:bar; // expected-error {{not yet supported}} expected-error {{redefinition}} expected-note@* {{loaded from}} -#define IMPORTED // FIXME +export module foo:bar; // Partition interface +//#define IMPORTED (we don't import foo here) #endif Index: clang/test/CXX/module/module.unit/p3.cpp =================================================================== --- clang/test/CXX/module/module.unit/p3.cpp +++ clang/test/CXX/module/module.unit/p3.cpp @@ -1,4 +1,4 @@ // RUN: %clang_cc1 -std=c++2a -verify %s -export module foo:bar; // expected-error {{sorry, module partitions are not yet supported}} -import :baz; // expected-error {{sorry, module partitions are not yet supported}} +export module foo:bar; +import :baz; // expected-error {{module 'foo:baz' not found}} Index: clang/lib/Sema/SemaModule.cpp =================================================================== --- clang/lib/Sema/SemaModule.cpp +++ clang/lib/Sema/SemaModule.cpp @@ -54,6 +54,19 @@ } } +static std::string stringFromPath(ModuleIdPath Path) { + std::string Name; + if (Path.empty()) + return Name; + + for (auto &Piece : Path) { + if (!Name.empty()) + Name += "."; + Name += Piece.first->getName(); + } + return Name; +} + Sema::DeclGroupPtrTy Sema::ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc) { if (!ModuleScopes.empty() && @@ -82,8 +95,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, - ModuleDeclKind MDK, ModuleIdPath Path, - ModuleImportState &ImportState) { + ModuleDeclKind MDK, ModuleIdPath NamePath, + ModuleIdPath Partition, ModuleImportState &ImportState) { assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) && "should only have module decl in Modules TS or C++20"); @@ -158,19 +171,20 @@ // Flatten the dots in a module name. Unlike Clang's hierarchical module map // modules, the dots here are just another character that can appear in a // module name. - std::string ModuleName; - for (auto &Piece : Path) { - if (!ModuleName.empty()) - ModuleName += "."; - ModuleName += Piece.first->getName(); + std::string ModuleName = stringFromPath(NamePath); + bool IsPartition = !Partition.empty(); + if (IsPartition) { + ModuleName += ":"; + ModuleName += stringFromPath(Partition); } - // If a module name was explicitly specified on the command line, it must be // correct. if (!getLangOpts().CurrentModule.empty() && getLangOpts().CurrentModule != ModuleName) { - Diag(Path.front().second, diag::err_current_module_name_mismatch) - << SourceRange(Path.front().second, Path.back().second) + Diag(NamePath.front().second, diag::err_current_module_name_mismatch) + << SourceRange(NamePath.front().second, IsPartition + ? Partition.back().second + : NamePath.back().second) << getLangOpts().CurrentModule; return nullptr; } @@ -184,7 +198,7 @@ // We can't have parsed or imported a definition of this module or parsed a // module map defining it already. if (auto *M = Map.findModule(ModuleName)) { - Diag(Path[0].second, diag::err_module_redefinition) << ModuleName; + Diag(NamePath[0].second, diag::err_module_redefinition) << ModuleName; if (M->DefinitionLoc.isValid()) Diag(M->DefinitionLoc, diag::note_prev_module_definition); else if (Optional<FileEntryRef> FE = M->getASTFile()) @@ -197,21 +211,35 @@ // Create a Module for the module that we're defining. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, GlobalModuleFragment); + if (IsPartition) + Mod->Kind = Module::ModulePartitionInterface; assert(Mod && "module creation should not fail"); break; } case ModuleDeclKind::Implementation: std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc( - PP.getIdentifierInfo(ModuleName), Path[0].second); - Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, - Module::AllVisible, - /*IsInclusionDirective=*/false); - if (!Mod) { - Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; - // Create an empty module interface unit for error recovery. + PP.getIdentifierInfo(ModuleName), NamePath[0].second); + if (IsPartition) { + // Create an interface, but note that it is an implementation + // unit. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, GlobalModuleFragment); + Mod->Kind = Module::ModulePartitionImplementation; + } else { + // 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) { + Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; + // Create an empty module interface unit for error recovery. + Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, + GlobalModuleFragment); + } } break; } @@ -228,7 +256,9 @@ // Switch from the global module fragment (if any) to the named module. ModuleScopes.back().BeginLoc = StartLoc; ModuleScopes.back().Module = Mod; - ModuleScopes.back().ModuleInterface = MDK != ModuleDeclKind::Implementation; + ModuleScopes.back().ModuleInterface = + (MDK != ModuleDeclKind::Implementation || IsPartition); + ModuleScopes.back().IsPartition = IsPartition; VisibleModules.setVisible(Mod, ModuleLoc); // From now on, we have an owning module for all declarations we see. @@ -313,18 +343,42 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, - ModuleIdPath Path) { - // Flatten the module path for a C++20 or Modules TS module name. + ModuleIdPath NamePath, + ModuleIdPath Partition) { + + bool IsPartition = !Partition.empty(); + bool Cxx20Mode = getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS; + assert((!IsPartition || Cxx20Mode) && "partition seen in non-C++20 code?"); + assert((!IsPartition || NamePath.empty()) && + "trying to import a partition with its named module specified?"); + + // For a C++20/Modules TS module name, flatten into a single identifier + // with the source location of the first component. std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc; + std::string ModuleName; - if (getLangOpts().CPlusPlusModules || getLangOpts().ModulesTS) { - for (auto &Piece : Path) { - if (!ModuleName.empty()) - ModuleName += "."; - ModuleName += Piece.first->getName(); + if (IsPartition) { + // We already checked that we are in a module purview in the parser. + assert(!ModuleScopes.empty() && "in a module purview, but no module?"); + Module *NamedMod = ModuleScopes.back().Module; + if (ModuleScopes.back().IsPartition) { + // We're importing a partition into a partition, find the name of the + // owning named module. + size_t P = NamedMod->Name.find_first_of(":"); + ModuleName = NamedMod->Name.substr(0, P + 1); + } else { + // We're importing a partition into the named module itself (either the + // interface or an implementation TU). + ModuleName = NamedMod->Name; + ModuleName += ":"; } - ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second}; - Path = ModuleIdPath(ModuleNameLoc); + ModuleName += stringFromPath(Partition); + ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Partition[0].second}; + Partition = ModuleIdPath(ModuleNameLoc); + } else if (Cxx20Mode) { + ModuleName = stringFromPath(NamePath); + ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), NamePath[0].second}; + NamePath = ModuleIdPath(ModuleNameLoc); } // Diagnose self-import before attempting a load. @@ -335,13 +389,14 @@ return true; } - Module *Mod = - getModuleLoader().loadModule(ImportLoc, Path, Module::AllVisible, - /*IsInclusionDirective=*/false); + Module *Mod = getModuleLoader().loadModule( + ImportLoc, IsPartition ? Partition : NamePath, Module::AllVisible, + /*IsInclusionDirective=*/false); if (!Mod) return true; - return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path); + return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, + IsPartition ? Partition : NamePath); } /// Determine whether \p D is lexically within an export-declaration. @@ -354,8 +409,8 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, - SourceLocation ImportLoc, - Module *Mod, ModuleIdPath Path) { + SourceLocation ImportLoc, Module *Mod, + ModuleIdPath NamePath) { VisibleModules.setVisible(Mod, ImportLoc); checkModuleImportContext(*this, Mod, ImportLoc, CurContext); @@ -374,22 +429,26 @@ } SmallVector<SourceLocation, 2> IdentifierLocs; - Module *ModCheck = Mod; - for (unsigned I = 0, N = Path.size(); I != N; ++I) { - // If we've run out of module parents, just drop the remaining identifiers. - // We need the length to be consistent. - if (!ModCheck) - break; - ModCheck = ModCheck->Parent; - - IdentifierLocs.push_back(Path[I].second); - } - // If this was a header import, pad out with dummy locations. - // FIXME: Pass in and use the location of the header-name token in this case. - if (Path.empty()) { - for (; ModCheck; ModCheck = ModCheck->Parent) { + if (NamePath.empty()) { + // If this was a header import, pad out with dummy locations. + // FIXME: Pass in and use the location of the header-name token in this + // case. + for (Module *ModCheck = Mod; ModCheck; ModCheck = ModCheck->Parent) IdentifierLocs.push_back(SourceLocation()); + } else if (getLangOpts().CPlusPlusModules && !Mod->Parent) { + // A single identifier for the whole name. + IdentifierLocs.push_back(NamePath[0].second); + } else { + Module *ModCheck = Mod; + for (unsigned I = 0, N = NamePath.size(); I != N; ++I) { + // If we've run out of module parents, just drop the remaining + // identifiers. We need the length to be consistent. + if (!ModCheck) + break; + ModCheck = ModCheck->Parent; + + IdentifierLocs.push_back(NamePath[I].second); } } @@ -415,6 +474,10 @@ // 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) << 0; + } else if (getLangOpts().isCompilingModule()) { + Module *ThisModule = PP.getHeaderSearchInfo().lookupModule( + getLangOpts().CurrentModule, ExportLoc, false, false); + assert(ThisModule && "was expecting a module if building one"); } return Import; @@ -452,6 +515,12 @@ getModuleLoader().makeModuleVisible(Mod, Module::AllVisible, DirectiveLoc); VisibleModules.setVisible(Mod, DirectiveLoc); + + if (getLangOpts().isCompilingModule()) { + Module *ThisModule = PP.getHeaderSearchInfo().lookupModule( + getLangOpts().CurrentModule, DirectiveLoc, false, false); + assert(ThisModule && "was expecting a module if building one"); + } } void Sema::ActOnModuleBegin(SourceLocation DirectiveLoc, Module *Mod) { @@ -752,8 +821,9 @@ // Enter the scope of the global module. ModuleScopes.push_back({BeginLoc, GlobalModuleFragment, /*ModuleInterface=*/false, + /*IsPartition=*/false, /*ImplicitGlobalModuleFragment=*/IsImplicit, - /*VisibleModuleSet*/ {}}); + /*OuterVisibleModules=*/{}}); VisibleModules.setVisible(GlobalModuleFragment, BeginLoc); return GlobalModuleFragment; Index: clang/lib/Sema/SemaLookup.cpp =================================================================== --- clang/lib/Sema/SemaLookup.cpp +++ clang/lib/Sema/SemaLookup.cpp @@ -1561,8 +1561,11 @@ static bool isInCurrentModule(const Module *M, const LangOptions &LangOpts) { // If M is the global module fragment of a module that we've not yet finished // parsing, then it must be part of the current module. + // If it's a partition, then it must be visible to an importer (since only + // another partition or the named module can import it). return M->getTopLevelModuleName() == LangOpts.CurrentModule || - (M->Kind == Module::GlobalModuleFragment && !M->Parent); + (M->Kind == Module::GlobalModuleFragment && !M->Parent) || + M->isPartition(); } bool Sema::hasVisibleMergedDefinition(NamedDecl *Def) { Index: clang/lib/Sema/SemaDecl.cpp =================================================================== --- clang/lib/Sema/SemaDecl.cpp +++ clang/lib/Sema/SemaDecl.cpp @@ -1608,6 +1608,14 @@ if (OldM && OldM->Kind == Module::PrivateModuleFragment) OldM = OldM->Parent; + // If we have a decl in a module partition it is part of the containing + // module (which is the only thing that can be importing it). + if (NewM && OldM && + (OldM->Kind == Module::ModulePartitionInterface || + OldM->Kind == Module::ModulePartitionImplementation)) { + return false; + } + if (NewM == OldM) return false; Index: clang/lib/Parse/Parser.cpp =================================================================== --- clang/lib/Parse/Parser.cpp +++ clang/lib/Parse/Parser.cpp @@ -2365,20 +2365,19 @@ } SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; - if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false)) + if (ParseModuleName(ModuleLoc, Path, /*IsImport*/ false)) return nullptr; // Parse the optional module-partition. + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition; if (Tok.is(tok::colon)) { SourceLocation ColonLoc = ConsumeToken(); - SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition; - if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/false)) + if (!getLangOpts().CPlusPlusModules) + Diag(ColonLoc, diag::err_unsupported_module_partition) + << SourceRange(ColonLoc, Partition.back().second); + // Recover by ignoring the partition name. + else if (ParseModuleName(ModuleLoc, Partition, /*IsImport*/ false)) return nullptr; - - // FIXME: Support module partition declarations. - Diag(ColonLoc, diag::err_unsupported_module_partition) - << SourceRange(ColonLoc, Partition.back().second); - // Recover by parsing as a non-partition. } // We don't support any module attributes yet; just parse them and diagnose. @@ -2388,18 +2387,19 @@ ExpectAndConsumeSemi(diag::err_module_expected_semi); - return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, ImportState); + return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, Partition, + ImportState); } /// Parse a module import declaration. This is essentially the same for -/// Objective-C and the C++ Modules TS, except for the leading '@' (in ObjC) +/// Objective-C and the C++20/Modules TS, except for the leading '@' (in ObjC) /// and the trailing optional attributes (in C++). /// /// [ObjC] @import declaration: /// '@' 'import' module-name ';' /// [ModTS] module-import-declaration: /// 'import' module-name attribute-specifier-seq[opt] ';' -/// [C++2a] module-import-declaration: +/// [C++20] module-import-declaration: /// 'export'[opt] 'import' module-name /// attribute-specifier-seq[opt] ';' /// 'export'[opt] 'import' module-partition @@ -2419,9 +2419,10 @@ bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import); SourceLocation ImportLoc = ConsumeToken(); - SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; + // For C++20 modules, we can have "name" or ":Partition name" as valid input. + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> NamePath; + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Partition; Module *HeaderUnit = nullptr; - if (Tok.is(tok::header_name)) { // This is a header import that the preprocessor decided we should skip // because it was malformed in some way. Parse and ignore it; it's already @@ -2431,17 +2432,16 @@ // This is a header import that the preprocessor mapped to a module import. HeaderUnit = reinterpret_cast<Module *>(Tok.getAnnotationValue()); ConsumeAnnotationToken(); - } else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) { + } else if (Tok.is(tok::colon)) { SourceLocation ColonLoc = ConsumeToken(); - if (ParseModuleName(ImportLoc, Path, /*IsImport*/true)) + if (!getLangOpts().CPlusPlusModules) + Diag(ColonLoc, diag::err_unsupported_module_partition) + << SourceRange(ColonLoc, Partition.back().second); + // Recover by leaving partition empty. + else if (ParseModuleName(ColonLoc, Partition, /*IsImport*/ true)) return nullptr; - - // FIXME: Support module partition import. - Diag(ColonLoc, diag::err_unsupported_module_partition) - << SourceRange(ColonLoc, Path.back().second); - return nullptr; } else { - if (ParseModuleName(ImportLoc, Path, /*IsImport*/true)) + if (ParseModuleName(ImportLoc, NamePath, /*IsImport*/ true)) return nullptr; } @@ -2458,21 +2458,25 @@ // Diagnose mis-imports. bool SeenError = true; + bool HasPart = !Partition.empty(); switch (ImportState) { case Sema::ModuleImportState::ImportAllowed: SeenError = false; break; case Sema::ModuleImportState::FirstDecl: case Sema::ModuleImportState::NotACXX20Module: - // These cases will be an error when partitions are implemented. - SeenError = false; + // We can only import a partition within a module purview. + if (HasPart) + Diag(ImportLoc, diag::err_partition_import_outside_module); + else + SeenError = false; break; case Sema::ModuleImportState::GlobalFragment: // We can only have pre-processor directives in the global module // fragment. We can, however have a header unit import here. if(!HeaderUnit) // We do not have partition support yet. - Diag(ImportLoc, diag::err_import_in_global_fragment) << 0; + Diag(ImportLoc, diag::err_import_in_global_fragment) << HasPart; else SeenError = false; break; @@ -2483,8 +2487,7 @@ SeenError = false; break; case Sema::ModuleImportState::PrivateFragment: - // We do not have partition support yet. - Diag(ImportLoc, diag::err_import_in_private_fragment) << 0; + Diag(ImportLoc, diag::err_import_in_private_fragment) << HasPart; break; } if (SeenError) { @@ -2496,8 +2499,9 @@ if (HeaderUnit) Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit); - else if (!Path.empty()) - Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path); + else if (!NamePath.empty() || !Partition.empty()) + Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, NamePath, + Partition); ExpectAndConsumeSemi(diag::err_module_expected_semi); if (Import.isInvalid()) return nullptr; Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -2213,6 +2213,7 @@ SourceLocation BeginLoc; clang::Module *Module = nullptr; bool ModuleInterface = false; + bool IsPartition = false; bool ImplicitGlobalModuleFragment = false; VisibleModuleSet OuterVisibleModules; }; @@ -2964,7 +2965,7 @@ /// of a module interface or implementation. DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, - ModuleIdPath Path, + ModuleIdPath Path, ModuleIdPath Partition, ModuleImportState &ImportState); /// The parser has processed a global-module-fragment declaration that begins @@ -2985,14 +2986,16 @@ /// could be the location of an '@', 'export', or 'import'. /// \param ExportLoc The location of the 'export' keyword, if any. /// \param ImportLoc The location of the 'import' keyword. - /// \param Path The module access path. + /// \param NamePath The module toplevel name as an access path. + /// \param Partition The module partition name as an access path. DeclResult ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, - SourceLocation ImportLoc, ModuleIdPath Path); + SourceLocation ImportLoc, ModuleIdPath NamePath, + ModuleIdPath Partition = {}); DeclResult ActOnModuleImport(SourceLocation StartLoc, SourceLocation ExportLoc, SourceLocation ImportLoc, Module *M, - ModuleIdPath Path = {}); + ModuleIdPath NamePath = {}); /// The parser has processed a module import translated from a /// #include or similar preprocessing directive. Index: clang/include/clang/Basic/DiagnosticParseKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticParseKinds.td +++ clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1538,9 +1538,11 @@ "expected ';' after private module fragment declaration">; def err_missing_before_module_end : Error<"expected %0 at end of module">; def err_unsupported_module_partition : Error< - "sorry, module partitions are not yet supported">; + "module partitions are only supported for C++20 onwards">; def err_import_not_allowed_here : Error< "imports must be contiguous and immediately follow the module declaration">; +def err_partition_import_outside_module : Error< + "module partition imports must be within a module purview">; def err_import_in_global_fragment : Error< "module%select{| partition}0 imports cannot be in the global module fragment">; def err_import_in_private_fragment : Error< Index: clang/include/clang/Basic/DiagnosticIDs.h =================================================================== --- clang/include/clang/Basic/DiagnosticIDs.h +++ clang/include/clang/Basic/DiagnosticIDs.h @@ -34,7 +34,7 @@ DIAG_SIZE_FRONTEND = 150, DIAG_SIZE_SERIALIZATION = 120, DIAG_SIZE_LEX = 400, - DIAG_SIZE_PARSE = 600, + DIAG_SIZE_PARSE = 610, DIAG_SIZE_AST = 250, DIAG_SIZE_COMMENT = 100, DIAG_SIZE_CROSSTU = 100, Index: clang/include/clang/AST/Decl.h =================================================================== --- clang/include/clang/AST/Decl.h +++ clang/include/clang/AST/Decl.h @@ -4449,6 +4449,16 @@ /// @import std.vector; /// \endcode /// +/// A C++20 module import declaration imports the named module or partition. +/// Periods are permitted in C++20 module names, but have no semantic meaning. +/// For example: +/// \code +/// import NamedModule; +/// import :SomePartition; // Must be a partition of the current module. +/// import Names.Like.this; // Allowed. +/// import :and.Also.Partition.names; +/// \endcode +/// /// Import declarations can also be implicitly generated from /// \#include/\#import directives. class ImportDecl final : public Decl,
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits