From 33eb2fffd975207bf64cb479d6eb9493d5f4110d Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Sun, 14 Apr 2019 08:06:59 +0000 Subject: [PATCH] [c++20] Parsing support for module-declarations, import-declarations, and the global and private module fragment. For now, the private module fragment introducer is ignored, but use of the global module fragment introducer should be properly enforced. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@358353 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Basic/DiagnosticParseKinds.td | 11 +- include/clang/Basic/DiagnosticSemaKinds.td | 12 +- include/clang/Lex/Preprocessor.h | 1 + include/clang/Parse/Parser.h | 10 +- include/clang/Sema/Sema.h | 35 ++- lib/Lex/PPDirectives.cpp | 4 + lib/Lex/Preprocessor.cpp | 1 + lib/Parse/Parser.cpp | 202 +++++++++++++++--- lib/Sema/Sema.cpp | 32 ++- lib/Sema/SemaDecl.cpp | 121 +++++++++-- test/CXX/basic/basic.link/p1.cpp | 37 ++++ test/CXX/basic/basic.link/p3.cpp | 53 +++++ test/CXX/module/module.unit/p3.cpp | 4 + .../basic/basic.link/module-declaration.cpp | 34 ++- 14 files changed, 460 insertions(+), 97 deletions(-) create mode 100644 test/CXX/basic/basic.link/p1.cpp create mode 100644 test/CXX/basic/basic.link/p3.cpp create mode 100644 test/CXX/module/module.unit/p3.cpp diff --git a/include/clang/Basic/DiagnosticParseKinds.td b/include/clang/Basic/DiagnosticParseKinds.td index 84c24836f7..3003d0a8d3 100644 --- a/include/clang/Basic/DiagnosticParseKinds.td +++ b/include/clang/Basic/DiagnosticParseKinds.td @@ -1223,15 +1223,22 @@ def err_unexpected_module_decl : Error< "module declaration can only appear at the top level">; def err_module_expected_ident : Error< "expected a module name after '%select{module|import}0'">; -def err_module_implementation_partition : Error< - "module partition must be declared 'export'">; def err_attribute_not_module_attr : Error< "%0 attribute cannot be applied to a module">; def err_attribute_not_import_attr : Error< "%0 attribute cannot be applied to a module import">; def err_module_expected_semi : Error< "expected ';' after module name">; +def err_global_module_introducer_not_at_start : Error< + "'module;' introducing a global module fragment can appear only " + "at the start of the translation unit">; +def err_module_fragment_exported : Error< + "%select{global|private}0 module fragment cannot be exported">; +def err_private_module_fragment_expected_semi : Error< + "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">; def err_export_empty : Error<"export declaration cannot be empty">; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 6b824da360..18792c10b7 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -9199,7 +9199,7 @@ let CategoryName = "Modules Issue" in { def err_module_decl_in_module_map_module : Error< "'module' declaration found while building module from module map">; def err_module_decl_in_header_module : Error< - "'module' declaration found while building header module">; + "'module' declaration found while building header unit">; def err_module_interface_implementation_mismatch : Error< "missing 'export' specifier in module declaration while " "building module interface">; @@ -9217,6 +9217,9 @@ def err_module_redeclaration : Error< def note_prev_module_declaration : Note<"previous module declaration is here">; def err_module_declaration_missing : Error< "missing 'export module' declaration in module interface unit">; +def err_module_declaration_missing_after_global_module_introducer : Error< + "missing 'module' declaration at end of global module fragment " + "introduced here">; def err_module_private_specialization : Error< "%select{template|partial|member}0 specialization cannot be " "declared __module_private__">; @@ -9254,7 +9257,12 @@ def err_module_self_import : Error< def err_module_import_in_implementation : Error< "@import of module '%0' in implementation of '%1'; use #import">; -// C++ Modules TS +// C++ Modules +def err_module_decl_not_at_start : Error< + "module declaration must occur at the start of the translation unit">; +def note_global_module_introducer_missing : Note< + "add 'module;' to the start of the file to introduce a " + "global module fragment">; def err_export_within_export : Error< "export declaration appears within another export declaration">; def err_export_not_in_module_interface : Error< diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index db06df1ab8..293fbafdab 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -2172,6 +2172,7 @@ private: None, ModuleBegin, ModuleImport, + SkippedModuleImport, } Kind; Module *ModuleForHeader = nullptr; diff --git a/include/clang/Parse/Parser.h b/include/clang/Parse/Parser.h index c77011d759..e831e226d4 100644 --- a/include/clang/Parse/Parser.h +++ b/include/clang/Parse/Parser.h @@ -150,11 +150,15 @@ class Parser : public CodeCompletionHandler { IdentifierInfo *Ident_language, *Ident_defined_in, *Ident_generated_declaration; - /// C++0x contextual keywords. + /// C++11 contextual keywords. mutable IdentifierInfo *Ident_final; mutable IdentifierInfo *Ident_GNU_final; mutable IdentifierInfo *Ident_override; + // C++2a contextual keywords. + mutable IdentifierInfo *Ident_import; + mutable IdentifierInfo *Ident_module; + // C++ type trait keywords that can be reverted to identifiers and still be // used as type traits. llvm::SmallDenseMap RevertibleTypeTraits; @@ -424,7 +428,7 @@ public: /// ParseTopLevelDecl - Parse one top-level declaration. Returns true if /// the EOF was encountered. - bool ParseTopLevelDecl(DeclGroupPtrTy &Result); + bool ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl = false); bool ParseTopLevelDecl() { DeclGroupPtrTy Result; return ParseTopLevelDecl(Result); @@ -2996,7 +3000,7 @@ private: //===--------------------------------------------------------------------===// // Modules - DeclGroupPtrTy ParseModuleDecl(); + DeclGroupPtrTy ParseModuleDecl(bool IsFirstDecl); Decl *ParseModuleImport(SourceLocation AtLoc); bool parseMisplacedModuleImport(); bool tryParseMisplacedModuleImport() { diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 41f331ca78..b09a4eb93d 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1624,8 +1624,10 @@ private: TypeDiagnoser *Diagnoser); struct ModuleScope { + SourceLocation BeginLoc; clang::Module *Module = nullptr; bool ModuleInterface = false; + bool ImplicitGlobalModuleFragment = false; VisibleModuleSet OuterVisibleModules; }; /// The modules we're currently parsing. @@ -2155,24 +2157,43 @@ public: enum class ModuleDeclKind { Interface, ///< 'export module X;' Implementation, ///< 'module X;' - Partition, ///< 'module partition X;' }; /// The parser has processed a module-declaration that begins the definition /// of a module interface or implementation. DeclGroupPtrTy ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, ModuleDeclKind MDK, - ModuleIdPath Path); + ModuleIdPath Path, bool IsFirstDecl); + + /// The parser has processed a global-module-fragment declaration that begins + /// the definition of the global module fragment of the current module unit. + /// \param ModuleLoc The location of the 'module' keyword. + DeclGroupPtrTy ActOnGlobalModuleFragmentDecl(SourceLocation ModuleLoc); + + /// The parser has processed a private-module-fragment declaration that begins + /// the definition of the private module fragment of the current module unit. + /// \param ModuleLoc The location of the 'module' keyword. + /// \param PrivateLoc The location of the 'private' keyword. + DeclGroupPtrTy ActOnPrivateModuleFragmentDecl(SourceLocation ModuleLoc, + SourceLocation PrivateLoc) { + // FIXME + return DeclGroupPtrTy(); + } /// The parser has processed a module import declaration. /// - /// \param AtLoc The location of the '@' symbol, if any. - /// + /// \param StartLoc The location of the first token in the declaration. This + /// 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. - DeclResult ActOnModuleImport(SourceLocation AtLoc, SourceLocation ImportLoc, - ModuleIdPath Path); + DeclResult ActOnModuleImport(SourceLocation StartLoc, + SourceLocation ExportLoc, + SourceLocation ImportLoc, ModuleIdPath Path); + DeclResult ActOnModuleImport(SourceLocation StartLoc, + SourceLocation ExportLoc, + SourceLocation ImportLoc, Module *M, + ModuleIdPath Path = {}); /// The parser has processed a module import translated from a /// #include or similar preprocessing directive. diff --git a/lib/Lex/PPDirectives.cpp b/lib/Lex/PPDirectives.cpp index c4adec83fb..b3778c1d6f 100644 --- a/lib/Lex/PPDirectives.cpp +++ b/lib/Lex/PPDirectives.cpp @@ -1648,6 +1648,7 @@ void Preprocessor::HandleIncludeDirective(SourceLocation HashLoc, EndLoc, LookupFrom, LookupFromFile); switch (Action.Kind) { case ImportAction::None: + case ImportAction::SkippedModuleImport: break; case ImportAction::ModuleBegin: EnterAnnotationToken(SourceRange(HashLoc, EndLoc), @@ -2034,6 +2035,8 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( switch (Action) { case Skip: // If we don't need to enter the file, stop now. + if (Module *M = SuggestedModule.getModule()) + return {ImportAction::SkippedModuleImport, M}; return {ImportAction::None}; case IncludeLimitReached: @@ -2117,6 +2120,7 @@ Preprocessor::ImportAction Preprocessor::HandleHeaderIncludeOrImport( return {ImportAction::ModuleBegin, M}; } + assert(!IsImportDecl && "failed to diagnose missing module for import decl"); return {ImportAction::None}; } diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 48d4a62ab7..ebd4ae07ba 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -1168,6 +1168,7 @@ bool Preprocessor::LexAfterModuleImport(Token &Result) { LLVM_FALLTHROUGH; case ImportAction::ModuleImport: + case ImportAction::SkippedModuleImport: // We chose to import (or textually enter) the file. Convert the // header-name token into a header unit annotation token. Suffix[0].setKind(tok::annot_header_unit); diff --git a/lib/Parse/Parser.cpp b/lib/Parse/Parser.cpp index b477e1f8bb..d1920a9ae7 100644 --- a/lib/Parse/Parser.cpp +++ b/lib/Parse/Parser.cpp @@ -460,6 +460,8 @@ void Parser::Initialize() { Ident_sealed = nullptr; Ident_override = nullptr; Ident_GNU_final = nullptr; + Ident_import = nullptr; + Ident_module = nullptr; Ident_super = &PP.getIdentifierTable().get("super"); @@ -512,6 +514,11 @@ void Parser::Initialize() { PP.SetPoisonReason(Ident_AbnormalTermination,diag::err_seh___finally_block); } + if (getLangOpts().CPlusPlusModules) { + Ident_import = PP.getIdentifierInfo("import"); + Ident_module = PP.getIdentifierInfo("module"); + } + Actions.Initialize(); // Prime the lexer look-ahead. @@ -525,6 +532,16 @@ void Parser::LateTemplateParserCleanupCallback(void *P) { DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(((Parser *)P)->TemplateIds); } +/// Parse the first top-level declaration in a translation unit. +/// +/// translation-unit: +/// [C] external-declaration +/// [C] translation-unit external-declaration +/// [C++] top-level-declaration-seq[opt] +/// [C++20] global-module-fragment[opt] module-declaration +/// top-level-declaration-seq[opt] private-module-fragment[opt] +/// +/// Note that in C, it is an error if there is no first declaration. bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) { Actions.ActOnStartOfTranslationUnit(); @@ -532,7 +549,7 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) { // declaration. C++ doesn't have this restriction. We also don't want to // complain if we have a precompiled header, although technically if the PCH // is empty we should still emit the (pedantic) diagnostic. - bool NoTopLevelDecls = ParseTopLevelDecl(Result); + bool NoTopLevelDecls = ParseTopLevelDecl(Result, true); if (NoTopLevelDecls && !Actions.getASTContext().getExternalSource() && !getLangOpts().CPlusPlus) Diag(diag::ext_empty_translation_unit); @@ -542,7 +559,11 @@ bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) { /// ParseTopLevelDecl - Parse one top-level declaration, return whatever the /// action tells us to. This returns true if the EOF was encountered. -bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { +/// +/// top-level-declaration: +/// declaration +/// [C++20] module-import-declaration +bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result, bool IsFirstDecl) { DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(TemplateIds); // Skip over the EOF token, flagging end of previous input for incremental @@ -557,13 +578,46 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { return false; case tok::kw_export: - if (NextToken().isNot(tok::kw_module)) + switch (NextToken().getKind()) { + case tok::kw_module: + goto module_decl; + + // Note: no need to handle kw_import here. We only form kw_import under + // the Modules TS, and in that case 'export import' is parsed as an + // export-declaration containing an import-declaration. + + // Recognize context-sensitive C++20 'export module' and 'export import' + // declarations. + case tok::identifier: { + IdentifierInfo *II = NextToken().getIdentifierInfo(); + if ((II == Ident_module || II == Ident_import) && + GetLookAheadToken(2).isNot(tok::coloncolon)) { + if (II == Ident_module) + goto module_decl; + else + goto import_decl; + } break; - LLVM_FALLTHROUGH; + } + + default: + break; + } + break; + case tok::kw_module: - Result = ParseModuleDecl(); + module_decl: + Result = ParseModuleDecl(IsFirstDecl); return false; + // tok::kw_import is handled by ParseExternalDeclaration. (Under the Modules + // TS, an import can occur within an export block.) + import_decl: { + Decl *ImportDecl = ParseModuleImport(SourceLocation()); + Result = Actions.ConvertDeclToDeclGroup(ImportDecl); + return false; + } + case tok::annot_module_include: Actions.ActOnModuleInclude(Tok.getLocation(), reinterpret_cast( @@ -595,6 +649,21 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { //else don't tell Sema that we ended parsing: more input might come. return true; + case tok::identifier: + // C++2a [basic.link]p3: + // A token sequence beginning with 'export[opt] module' or + // 'export[opt] import' and not immediately followed by '::' + // is never interpreted as the declaration of a top-level-declaration. + if ((Tok.getIdentifierInfo() == Ident_module || + Tok.getIdentifierInfo() == Ident_import) && + NextToken().isNot(tok::coloncolon)) { + if (Tok.getIdentifierInfo() == Ident_module) + goto module_decl; + else + goto import_decl; + } + break; + default: break; } @@ -2074,38 +2143,83 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() { Braces.consumeClose(); } -/// Parse a C++ Modules TS module declaration, which appears at the beginning -/// of a module interface, module partition, or module implementation file. +/// Parse a declaration beginning with the 'module' keyword or C++20 +/// context-sensitive keyword (optionally preceded by 'export'). /// -/// module-declaration: [Modules TS + P0273R0 + P0629R0] -/// 'export'[opt] 'module' 'partition'[opt] -/// module-name attribute-specifier-seq[opt] ';' +/// module-declaration: [Modules TS + P0629R0] +/// 'export'[opt] 'module' module-name attribute-specifier-seq[opt] ';' /// -/// Note that 'partition' is a context-sensitive keyword. -Parser::DeclGroupPtrTy Parser::ParseModuleDecl() { +/// global-module-fragment: [C++2a] +/// 'module' ';' top-level-declaration-seq[opt] +/// module-declaration: [C++2a] +/// 'export'[opt] 'module' module-name module-partition[opt] +/// attribute-specifier-seq[opt] ';' +/// private-module-fragment: [C++2a] +/// 'module' ':' 'private' ';' top-level-declaration-seq[opt] +Parser::DeclGroupPtrTy Parser::ParseModuleDecl(bool IsFirstDecl) { SourceLocation StartLoc = Tok.getLocation(); Sema::ModuleDeclKind MDK = TryConsumeToken(tok::kw_export) ? Sema::ModuleDeclKind::Interface : Sema::ModuleDeclKind::Implementation; - assert(Tok.is(tok::kw_module) && "not a module declaration"); + assert( + (Tok.is(tok::kw_module) || + (Tok.is(tok::identifier) && Tok.getIdentifierInfo() == Ident_module)) && + "not a module declaration"); SourceLocation ModuleLoc = ConsumeToken(); - if (Tok.is(tok::identifier) && NextToken().is(tok::identifier) && - Tok.getIdentifierInfo()->isStr("partition")) { - // If 'partition' is present, this must be a module interface unit. - if (MDK != Sema::ModuleDeclKind::Interface) - Diag(Tok.getLocation(), diag::err_module_implementation_partition) - << FixItHint::CreateInsertion(ModuleLoc, "export "); - MDK = Sema::ModuleDeclKind::Partition; + // Attributes appear after the module name, not before. + if (Tok.is(tok::l_square)) + CheckProhibitedCXX11Attribute(); + + // Parse a global-module-fragment, if present. + if (getLangOpts().CPlusPlusModules && Tok.is(tok::semi)) { + SourceLocation SemiLoc = ConsumeToken(); + if (!IsFirstDecl) { + Diag(StartLoc, diag::err_global_module_introducer_not_at_start) + << SourceRange(StartLoc, SemiLoc); + return nullptr; + } + if (MDK == Sema::ModuleDeclKind::Interface) { + Diag(StartLoc, diag::err_module_fragment_exported) + << /*global*/0 << FixItHint::CreateRemoval(StartLoc); + } + return Actions.ActOnGlobalModuleFragmentDecl(ModuleLoc); + } + + // Parse a private-module-fragment, if present. + if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon) && + NextToken().is(tok::kw_private)) { + if (MDK == Sema::ModuleDeclKind::Interface) { + Diag(StartLoc, diag::err_module_fragment_exported) + << /*private*/1 << FixItHint::CreateRemoval(StartLoc); + } ConsumeToken(); + SourceLocation PrivateLoc = ConsumeToken(); + if (Tok.is(tok::l_square)) + CheckProhibitedCXX11Attribute(); + ExpectAndConsumeSemi(diag::err_private_module_fragment_expected_semi); + return Actions.ActOnPrivateModuleFragmentDecl(ModuleLoc, PrivateLoc); } SmallVector, 2> Path; if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false)) return nullptr; + // Parse the optional module-partition. + if (Tok.is(tok::colon)) { + SourceLocation ColonLoc = ConsumeToken(); + SmallVector, 2> Partition; + 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. ParsedAttributesWithRange Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); @@ -2113,7 +2227,7 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() { ExpectAndConsumeSemi(diag::err_module_expected_semi); - return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path); + return Actions.ActOnModuleDecl(StartLoc, ModuleLoc, MDK, Path, IsFirstDecl); } /// Parse a module import declaration. This is essentially the same for @@ -2124,17 +2238,50 @@ Parser::DeclGroupPtrTy Parser::ParseModuleDecl() { /// '@' 'import' module-name ';' /// [ModTS] module-import-declaration: /// 'import' module-name attribute-specifier-seq[opt] ';' +/// [C++2a] module-import-declaration: +/// 'export'[opt] 'import' module-name +/// attribute-specifier-seq[opt] ';' +/// 'export'[opt] 'import' module-partition +/// attribute-specifier-seq[opt] ';' +/// 'export'[opt] 'import' header-name +/// attribute-specifier-seq[opt] ';' Decl *Parser::ParseModuleImport(SourceLocation AtLoc) { - assert((AtLoc.isInvalid() ? Tok.is(tok::kw_import) + SourceLocation StartLoc = AtLoc.isInvalid() ? Tok.getLocation() : AtLoc; + + SourceLocation ExportLoc; + TryConsumeToken(tok::kw_export, ExportLoc); + + assert((AtLoc.isInvalid() ? Tok.isOneOf(tok::kw_import, tok::identifier) : Tok.isObjCAtKeyword(tok::objc_import)) && "Improper start to module import"); bool IsObjCAtImport = Tok.isObjCAtKeyword(tok::objc_import); SourceLocation ImportLoc = ConsumeToken(); - SourceLocation StartLoc = AtLoc.isInvalid() ? ImportLoc : AtLoc; SmallVector, 2> Path; - if (ParseModuleName(ImportLoc, Path, /*IsImport*/true)) + 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 + // been diagnosed. + ConsumeToken(); + } else if (Tok.is(tok::annot_header_unit)) { + // This is a header import that the preprocessor mapped to a module import. + HeaderUnit = reinterpret_cast(Tok.getAnnotationValue()); + ConsumeAnnotationToken(); + } else if (getLangOpts().CPlusPlusModules && Tok.is(tok::colon)) { + SourceLocation ColonLoc = ConsumeToken(); + if (ParseModuleName(ImportLoc, Path, /*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)) + return nullptr; + } ParsedAttributesWithRange Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); @@ -2147,7 +2294,12 @@ Decl *Parser::ParseModuleImport(SourceLocation AtLoc) { return nullptr; } - DeclResult Import = Actions.ActOnModuleImport(StartLoc, ImportLoc, Path); + DeclResult Import; + if (HeaderUnit) + Import = + Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, HeaderUnit); + else if (!Path.empty()) + Import = Actions.ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Path); ExpectAndConsumeSemi(diag::err_module_expected_semi); if (Import.isInvalid()) return nullptr; diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index bc4fdfbe58..befe6ac763 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -849,24 +849,11 @@ void Sema::ActOnStartOfTranslationUnit() { if (getLangOpts().ModulesTS && (getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface || getLangOpts().getCompilingModule() == LangOptions::CMK_None)) { + // We start in an implied global module fragment. SourceLocation StartOfTU = SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()); - - // We start in the global module; all those declarations are implicitly - // module-private (though they do not have module linkage). - auto &Map = PP.getHeaderSearchInfo().getModuleMap(); - auto *GlobalModule = Map.createGlobalModuleForInterfaceUnit(StartOfTU); - assert(GlobalModule && "module creation should not fail"); - - // Enter the scope of the global module. - ModuleScopes.push_back({}); - ModuleScopes.back().Module = GlobalModule; - VisibleModules.setVisible(GlobalModule, StartOfTU); - - // All declarations created from now on are owned by the global module. - auto *TU = Context.getTranslationUnitDecl(); - TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible); - TU->setLocalOwningModule(GlobalModule); + ActOnGlobalModuleFragmentDecl(StartOfTU); + ModuleScopes.back().ImplicitGlobalModuleFragment = true; } } @@ -997,13 +984,24 @@ void Sema::ActOnEndOfTranslationUnit() { checkUndefinedButUsed(*this); } + // A global-module-fragment is only permitted within a module unit. + bool DiagnosedMissingModuleDeclaration = false; + if (!ModuleScopes.empty() && + ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment && + !ModuleScopes.back().ImplicitGlobalModuleFragment) { + Diag(ModuleScopes.back().BeginLoc, + diag::err_module_declaration_missing_after_global_module_introducer); + DiagnosedMissingModuleDeclaration = true; + } + if (TUKind == TU_Module) { // If we are building a module interface unit, we need to have seen the // module declaration by now. if (getLangOpts().getCompilingModule() == LangOptions::CMK_ModuleInterface && (ModuleScopes.empty() || - ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit)) { + ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) && + !DiagnosedMissingModuleDeclaration) { // FIXME: Make a better guess as to where to put the module declaration. Diag(getSourceManager().getLocForStartOfFile( getSourceManager().getMainFileID()), diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp index fa29e8c904..cb1a5b684a 100644 --- a/lib/Sema/SemaDecl.cpp +++ b/lib/Sema/SemaDecl.cpp @@ -17016,12 +17016,44 @@ static void checkModuleImportContext(Sema &S, Module *M, } } -Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, - SourceLocation ModuleLoc, - ModuleDeclKind MDK, - ModuleIdPath Path) { - assert(getLangOpts().ModulesTS && - "should only have module decl in modules TS"); +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). + auto &Map = PP.getHeaderSearchInfo().getModuleMap(); + auto *GlobalModule = Map.createGlobalModuleForInterfaceUnit(ModuleLoc); + assert(GlobalModule && "module creation should not fail"); + + // Enter the scope of the global module. + ModuleScopes.push_back({}); + ModuleScopes.back().BeginLoc = ModuleLoc; + ModuleScopes.back().Module = GlobalModule; + VisibleModules.setVisible(GlobalModule, ModuleLoc); + + // All declarations created from now on are owned by the global module. + auto *TU = Context.getTranslationUnitDecl(); + TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::Visible); + TU->setLocalOwningModule(GlobalModule); + + // FIXME: Consider creating an explicit representation of this declaration. + return nullptr; +} + +Sema::DeclGroupPtrTy +Sema::ActOnModuleDecl(SourceLocation StartLoc, SourceLocation ModuleLoc, + ModuleDeclKind MDK, ModuleIdPath Path, bool IsFirstDecl) { + assert((getLangOpts().ModulesTS || getLangOpts().CPlusPlusModules) && + "should only have module decl in Modules TS or C++20"); // 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 @@ -17051,19 +17083,40 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, return nullptr; } - assert(ModuleScopes.size() == 1 && "expected to be at global module scope"); + assert(ModuleScopes.size() <= 1 && "expected to be at global module scope"); // FIXME: Most of this work should be done by the preprocessor rather than // here, in order to support macro import. // Only one module-declaration is permitted per source file. - if (ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) { + if (!ModuleScopes.empty() && + ModuleScopes.back().Module->Kind == Module::ModuleInterfaceUnit) { Diag(ModuleLoc, diag::err_module_redeclaration); Diag(VisibleModules.getImportLoc(ModuleScopes.back().Module), diag::note_prev_module_declaration); return nullptr; } + // Find the global module fragment we're adopting into this module, if any. + Module *GlobalModuleFragment = nullptr; + if (!ModuleScopes.empty() && + ModuleScopes.back().Module->Kind == Module::GlobalModuleFragment) + GlobalModuleFragment = ModuleScopes.back().Module; + + // In C++20, the module-declaration must be the first declaration if there + // is no global module fragment. + if (getLangOpts().CPlusPlusModules && !IsFirstDecl && !GlobalModuleFragment) { + Diag(ModuleLoc, diag::err_module_decl_not_at_start); + SourceLocation BeginLoc = + ModuleScopes.empty() + ? SourceMgr.getLocForStartOfFile(SourceMgr.getMainFileID()) + : ModuleScopes.back().BeginLoc; + if (BeginLoc.isValid()) { + Diag(BeginLoc, diag::note_global_module_introducer_missing) + << FixItHint::CreateInsertion(BeginLoc, "module;\n"); + } + } + // 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. @@ -17105,15 +17158,11 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, // Create a Module for the module that we're defining. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, - ModuleScopes.front().Module); + GlobalModuleFragment); assert(Mod && "module creation should not fail"); break; } - case ModuleDeclKind::Partition: - // FIXME: Check we are in a submodule of the named module. - return nullptr; - case ModuleDeclKind::Implementation: std::pair ModuleNameLoc( PP.getIdentifierInfo(ModuleName), Path[0].second); @@ -17124,12 +17173,19 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; // Create an empty module interface unit for error recovery. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, - ModuleScopes.front().Module); + GlobalModuleFragment); } break; } - // Switch from the global module to the named module. + if (!GlobalModuleFragment) { + ModuleScopes.push_back({}); + if (getLangOpts().ModulesLocalVisibility) + ModuleScopes.back().OuterVisibleModules = std::move(VisibleModules); + } + + // 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; VisibleModules.setVisible(Mod, ModuleLoc); @@ -17146,6 +17202,7 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc, } DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, + SourceLocation ExportLoc, SourceLocation ImportLoc, ModuleIdPath Path) { // Flatten the module path for a Modules TS module name. @@ -17167,6 +17224,13 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, if (!Mod) return true; + return ActOnModuleImport(StartLoc, ExportLoc, ImportLoc, Mod, Path); +} + +DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, + SourceLocation ExportLoc, + SourceLocation ImportLoc, + Module *Mod, ModuleIdPath Path) { VisibleModules.setVisible(Mod, ImportLoc); checkModuleImportContext(*this, Mod, ImportLoc, CurContext); @@ -17176,12 +17240,15 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, // 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 && - (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) + (getLangOpts().isCompilingModule() || !getLangOpts().ModulesTS)) { Diag(ImportLoc, getLangOpts().isCompilingModule() ? diag::err_module_self_import : diag::err_module_import_in_implementation) << Mod->getFullModuleName() << getLangOpts().CurrentModule; + } SmallVector IdentifierLocs; Module *ModCheck = Mod; @@ -17195,16 +17262,30 @@ DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, 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) { + IdentifierLocs.push_back(SourceLocation()); + } + } + ImportDecl *Import = ImportDecl::Create(Context, CurContext, StartLoc, Mod, IdentifierLocs); + CurContext->addDecl(Import); + + // Sequence initialization of the imported module before that of the current + // module, if any. if (!ModuleScopes.empty()) Context.addModuleInitializer(ModuleScopes.back().Module, Import); - CurContext->addDecl(Import); // Re-export the module if needed. - if (Import->isExported() && - !ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) - getCurrentModule()->Exports.emplace_back(Mod, false); + if (!ModuleScopes.empty() && ModuleScopes.back().ModuleInterface) { + if (ExportLoc.isValid() || Import->isExported()) + getCurrentModule()->Exports.emplace_back(Mod, false); + } else if (ExportLoc.isValid()) { + Diag(ExportLoc, diag::err_export_not_in_module_interface); + } return Import; } diff --git a/test/CXX/basic/basic.link/p1.cpp b/test/CXX/basic/basic.link/p1.cpp new file mode 100644 index 0000000000..b94e57017f --- /dev/null +++ b/test/CXX/basic/basic.link/p1.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s +// RUN: %clang_cc1 -std=c++2a -verify -DNO_GLOBAL_FRAG %s +// RUN: %clang_cc1 -std=c++2a -verify -DNO_MODULE_DECL %s +// RUN: %clang_cc1 -std=c++2a -verify -DEXPORT_FRAGS %s + +#ifdef NO_GLOBAL_FRAG +// expected-error@#mod-decl {{module declaration must occur at the start of the translation unit}} +// expected-note@1 {{add 'module;' to the start of the file to introduce a global module fragment}} +#else +#ifdef EXPORT_FRAGS +export // expected-error {{global module fragment cannot be exported}} +#endif +module; // #glob-frag +#endif + +extern int a; // #a1 + +#ifdef NO_MODULE_DECL +// expected-error@#glob-frag {{missing 'module' declaration at end of global module fragment introduced here}} +#else +export module Foo; // #mod-decl + +// expected-error@#a2 {{declaration of 'a' in module Foo follows declaration in the global module}} +// expected-note@#a1 {{previous decl}} +#endif + +int a; // #a2 +extern int b; + +module; // expected-error {{'module;' introducing a global module fragment can appear only at the start of the translation unit}} + +#ifdef EXPORT_FRAGS +export // expected-error {{private module fragment cannot be exported}} +#endif +module :private; + +int b; // ok diff --git a/test/CXX/basic/basic.link/p3.cpp b/test/CXX/basic/basic.link/p3.cpp new file mode 100644 index 0000000000..23f39d11b6 --- /dev/null +++ b/test/CXX/basic/basic.link/p3.cpp @@ -0,0 +1,53 @@ +// RUN: %clang_cc1 -std=c++2a -verify %s +// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=1 +// RUN: %clang_cc1 -std=c++2a -verify %s -DIMPORT_ERROR=2 + +module; + +#if IMPORT_ERROR != 2 +struct import { struct inner {}; }; +#endif +struct module { struct inner {}; }; + +constexpr int n = 123; + +export module m; // #1 + +// Import errors are fatal, so we test them in isolation. +#if IMPORT_ERROR == 1 +import x = {}; // expected-error {{module 'x' not found}} + +#elif IMPORT_ERROR == 2 +struct X; +template struct import; +template<> struct import { + static X y; +}; + +// This is not valid because the 'import ' is a pp-import, even though it +// grammatically can't possibly be an import declaration. +struct X {} import::y; // expected-error {{'n' file not found}} + +#else +module y = {}; // expected-error {{multiple module declarations}} expected-error 2{{}} +// expected-note@#1 {{previous module declaration}} + +::import x = {}; +::module y = {}; + +import::inner xi = {}; +module::inner yi = {}; + +namespace N { + module a; + import b; +} + +extern "C++" module cxxm; +extern "C++" import cxxi; + +template module module_var_template; + +// This is a variable named 'import' that shadows the type 'import' above. +struct X {} import; +#endif diff --git a/test/CXX/module/module.unit/p3.cpp b/test/CXX/module/module.unit/p3.cpp new file mode 100644 index 0000000000..2e08f4a9a8 --- /dev/null +++ b/test/CXX/module/module.unit/p3.cpp @@ -0,0 +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}} diff --git a/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp b/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp index feb0afdda0..7615536b81 100644 --- a/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp +++ b/test/CXX/modules-ts/basic/basic.link/module-declaration.cpp @@ -10,47 +10,39 @@ // // Module implementation for unknown and known module. (The former is ill-formed.) // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=1 -DEXPORT= -DPARTITION= -DMODULE_NAME=z +// RUN: -DTEST=1 -DEXPORT= -DMODULE_NAME=z // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=2 -DEXPORT= -DPARTITION= -DMODULE_NAME=x +// RUN: -DTEST=2 -DEXPORT= -DMODULE_NAME=x // // Module interface for unknown and known module. (The latter is ill-formed due to // redefinition.) // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=3 -DEXPORT=export -DPARTITION= -DMODULE_NAME=z +// RUN: -DTEST=3 -DEXPORT=export -DMODULE_NAME=z // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=4 -DEXPORT=export -DPARTITION= -DMODULE_NAME=x -// -// Defining a module partition. -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=5 -DEXPORT=export -DPARTITION=partition -DMODULE_NAME=z -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=6 -DEXPORT= -DPARTITION=partition -DMODULE_NAME=z +// RUN: -DTEST=4 -DEXPORT=export -DMODULE_NAME=x // // Miscellaneous syntax. // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=7 -DEXPORT= -DPARTITION=elderberry -DMODULE_NAME=z +// RUN: -DTEST=7 -DEXPORT=export -DMODULE_NAME='z elderberry' // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=8 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[]]' +// RUN: -DTEST=8 -DEXPORT=export -DMODULE_NAME='z [[]]' // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=9 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[fancy]]' +// RUN: -DTEST=9 -DEXPORT=export -DMODULE_NAME='z [[fancy]]' // RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%t -fmodule-file=%t/x.y.pcm -verify %s \ -// RUN: -DTEST=10 -DEXPORT=export -DPARTITION= -DMODULE_NAME='z [[maybe_unused]]' +// RUN: -DTEST=10 -DEXPORT=export -DMODULE_NAME='z [[maybe_unused]]' -EXPORT module PARTITION MODULE_NAME; +EXPORT module MODULE_NAME; #if TEST == 4 // expected-error@-2 {{redefinition of module 'x'}} // expected-note-re@module-declaration.cpp:* {{loaded from '{{.*[/\\]}}x.pcm'}} -#elif TEST == 6 -// expected-error@-5 {{module partition must be declared 'export'}} #elif TEST == 7 -// expected-error@-7 {{expected ';'}} expected-error@-7 {{requires a type specifier}} expected-error@-7 {{definition of module 'elderberry' is not available}} +// expected-error@-5 {{expected ';'}} expected-error@-5 {{requires a type specifier}} #elif TEST == 9 -// expected-warning@-9 {{unknown attribute 'fancy' ignored}} +// expected-warning@-7 {{unknown attribute 'fancy' ignored}} #elif TEST == 10 -// expected-error-re@-11 {{'maybe_unused' attribute cannot be applied to a module{{$}}}} +// expected-error-re@-9 {{'maybe_unused' attribute cannot be applied to a module{{$}}}} #elif TEST == 1 -// expected-error@-13 {{definition of module 'z' is not available}} +// expected-error@-11 {{definition of module 'z' is not available}} #else // expected-no-diagnostics #endif -- 2.40.0