diff options
-rw-r--r-- | clang/include/clang/Basic/DiagnosticParseKinds.td | 6 | ||||
-rw-r--r-- | clang/include/clang/Basic/DiagnosticSemaKinds.td | 5 | ||||
-rw-r--r-- | clang/include/clang/Parse/Parser.h | 9 | ||||
-rw-r--r-- | clang/include/clang/Sema/Sema.h | 11 | ||||
-rw-r--r-- | clang/lib/Parse/ParseAST.cpp | 2 | ||||
-rw-r--r-- | clang/lib/Parse/Parser.cpp | 128 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDecl.cpp | 55 | ||||
-rw-r--r-- | clang/test/Parser/cxx-modules-import.cpp | 37 |
8 files changed, 217 insertions, 36 deletions
diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index bbef0bd1d3c..34280b902e3 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1022,7 +1022,11 @@ def warn_pragma_unroll_cuda_value_in_parens : Warning< let CategoryName = "Modules Issue" in { def err_module_expected_ident : Error< - "expected a module name after module import">; + "expected a module name after module%select{| import}0">; +def err_unexpected_module_kind : Error< + "unexpected module kind %0; expected 'implementation' or 'partition'">; +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< diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index d64ca537be8..bf6e6ecab9a 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -8462,6 +8462,11 @@ def note_related_result_type_explicit : Note< } let CategoryName = "Modules Issue" in { +def err_module_interface_implementation_mismatch : Error< + "%select{'module'|'module partition'|'module implementation'}0 declaration " + "found while %select{not |not |}0building module interface">; +def err_current_module_name_mismatch : Error< + "module name '%0' specified on command line does not match name of module">; def err_module_private_specialization : Error< "%select{template|partial|member}0 specialization cannot be " "declared __module_private__">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 9948dbb810d..b44a2096d65 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -278,6 +278,9 @@ public: /// void Initialize(); + /// Parse the first top-level declaration in a translation unit. + bool ParseFirstTopLevelDecl(DeclGroupPtrTy &Result); + /// ParseTopLevelDecl - Parse one top-level declaration. Returns true if /// the EOF was encountered. bool ParseTopLevelDecl(DeclGroupPtrTy &Result); @@ -2656,6 +2659,7 @@ private: //===--------------------------------------------------------------------===// // Modules + DeclGroupPtrTy ParseModuleDecl(); DeclGroupPtrTy ParseModuleImport(SourceLocation AtLoc); bool parseMisplacedModuleImport(); bool tryParseMisplacedModuleImport() { @@ -2666,6 +2670,11 @@ private: return false; } + bool ParseModuleName( + SourceLocation UseLoc, + SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path, + bool IsImport); + //===--------------------------------------------------------------------===// // C++11/G++: Type Traits [Type-Traits.html in the GCC manual] ExprResult ParseTypeTrait(); diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d56c8c91135..668399bf846 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -1859,6 +1859,17 @@ public: AttributeList *AttrList, SourceLocation SemiLoc); + enum class ModuleDeclKind { + Module, ///< 'module X;' + Partition, ///< 'module partition X;' + Implementation, ///< 'module implementation X;' + }; + + /// The parser has processed a module-declaration that begins the definition + /// of a module interface or implementation. + DeclGroupPtrTy ActOnModuleDecl(SourceLocation ModuleLoc, ModuleDeclKind MDK, + ModuleIdPath Path); + /// \brief The parser has processed a module import declaration. /// /// \param AtLoc The location of the '@' symbol, if any. diff --git a/clang/lib/Parse/ParseAST.cpp b/clang/lib/Parse/ParseAST.cpp index 1fb57a08c43..bab3dbefb0d 100644 --- a/clang/lib/Parse/ParseAST.cpp +++ b/clang/lib/Parse/ParseAST.cpp @@ -147,7 +147,7 @@ void clang::ParseAST(Sema &S, bool PrintStats, bool SkipFunctionBodies) { if (External) External->StartTranslationUnit(Consumer); - if (P.ParseTopLevelDecl(ADecl)) { + if (P.ParseFirstTopLevelDecl(ADecl)) { if (!External && !S.getLangOpts().CPlusPlus) P.Diag(diag::ext_empty_translation_unit); } else { diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index 9f941f40da1..a391363ff38 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -537,6 +537,20 @@ void Parser::LateTemplateParserCleanupCallback(void *P) { DestroyTemplateIdAnnotationsRAIIObj CleanupRAII(((Parser *)P)->TemplateIds); } +bool Parser::ParseFirstTopLevelDecl(DeclGroupPtrTy &Result) { + // C++ Modules TS: module-declaration must be the first declaration in the + // file. (There can be no preceding preprocessor directives, but we expect + // the lexer to check that.) + if (Tok.is(tok::kw_module)) { + Result = ParseModuleDecl(); + return false; + } + // FIXME: If we're parsing a module interface and we don't have a module + // declaration here, diagnose. + + return ParseTopLevelDecl(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) { @@ -2000,18 +2014,58 @@ 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. +/// +/// module-declaration: [Modules TS + P0273R0] +/// 'module' module-kind[opt] module-name attribute-specifier-seq[opt] ';' +/// module-kind: +/// 'implementation' +/// 'partition' +/// +/// Note that the module-kind values are context-sensitive keywords. +Parser::DeclGroupPtrTy Parser::ParseModuleDecl() { + assert(Tok.is(tok::kw_module) && getLangOpts().ModulesTS && + "should not be parsing a module declaration"); + SourceLocation ModuleLoc = ConsumeToken(); + + // Check for a module-kind. + Sema::ModuleDeclKind MDK = Sema::ModuleDeclKind::Module; + if (Tok.is(tok::identifier) && NextToken().is(tok::identifier)) { + if (Tok.getIdentifierInfo()->isStr("implementation")) + MDK = Sema::ModuleDeclKind::Implementation; + else if (Tok.getIdentifierInfo()->isStr("partition")) + MDK = Sema::ModuleDeclKind::Partition; + else { + Diag(Tok, diag::err_unexpected_module_kind) << Tok.getIdentifierInfo(); + SkipUntil(tok::semi); + return nullptr; + } + ConsumeToken(); + } + + SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; + if (ParseModuleName(ModuleLoc, Path, /*IsImport*/false)) + return nullptr; + + ParsedAttributesWithRange Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + // We don't support any module attributes yet. + ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_module_attr); + + ExpectAndConsumeSemi(diag::err_module_expected_semi); + + return Actions.ActOnModuleDecl(ModuleLoc, MDK, Path); +} + /// Parse a module import declaration. This is essentially the same for /// Objective-C and the C++ Modules TS, except for the leading '@' (in ObjC) /// and the trailing optional attributes (in C++). /// /// [ObjC] @import declaration: -/// '@' 'import' (identifier '.')* ';' +/// '@' 'import' module-name ';' /// [ModTS] module-import-declaration: -/// 'module' module-name attribute-specifier-seq[opt] ';' -/// module-name: -/// module-name-qualifier[opt] identifier -/// module-name-qualifier: -/// module-name-qualifier[opt] identifier '.' +/// 'import' module-name attribute-specifier-seq[opt] ';' Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { assert((AtLoc.isInvalid() ? Tok.is(tok::kw_import) : Tok.isObjCAtKeyword(tok::objc_import)) && @@ -2020,30 +2074,8 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { SourceLocation StartLoc = AtLoc.isInvalid() ? ImportLoc : AtLoc; SmallVector<std::pair<IdentifierInfo *, SourceLocation>, 2> Path; - - // Parse the module path. - while (true) { - if (!Tok.is(tok::identifier)) { - if (Tok.is(tok::code_completion)) { - Actions.CodeCompleteModuleImport(ImportLoc, Path); - cutOffParsing(); - return nullptr; - } - - Diag(Tok, diag::err_module_expected_ident); - SkipUntil(tok::semi); - return nullptr; - } - - // Record this part of the module path. - Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation())); - ConsumeToken(); - - if (Tok.isNot(tok::period)) - break; - - ConsumeToken(); - } + if (ParseModuleName(ImportLoc, Path, /*IsImport*/true)) + return nullptr; ParsedAttributesWithRange Attrs(AttrFactory); MaybeParseCXX11Attributes(Attrs); @@ -2064,6 +2096,42 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { return Actions.ConvertDeclToDeclGroup(Import.get()); } +/// Parse a C++ Modules TS / Objective-C module name (both forms use the same +/// grammar). +/// +/// module-name: +/// module-name-qualifier[opt] identifier +/// module-name-qualifier: +/// module-name-qualifier[opt] identifier '.' +bool Parser::ParseModuleName( + SourceLocation UseLoc, + SmallVectorImpl<std::pair<IdentifierInfo *, SourceLocation>> &Path, + bool IsImport) { + // Parse the module path. + while (true) { + if (!Tok.is(tok::identifier)) { + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteModuleImport(UseLoc, Path); + cutOffParsing(); + return true; + } + + Diag(Tok, diag::err_module_expected_ident) << IsImport; + SkipUntil(tok::semi); + return true; + } + + // Record this part of the module path. + Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation())); + ConsumeToken(); + + if (Tok.isNot(tok::period)) + return false; + + ConsumeToken(); + } +} + /// \brief Try recover parser when module annotation appears where it must not /// be found. /// \returns false if the recover was successful and parsing may be continued, or diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 5dab15a8d66..ca4de55fb51 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15178,6 +15178,56 @@ void Sema::diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc) { return checkModuleImportContext(*this, M, ImportLoc, CurContext); } +Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation ModuleLoc, + ModuleDeclKind MDK, + ModuleIdPath Path) { + // We should see 'module implementation' if and only if we are not compiling + // a module interface. + if (getLangOpts().CompilingModule == + (MDK == ModuleDeclKind::Implementation)) { + Diag(ModuleLoc, diag::err_module_interface_implementation_mismatch) + << (unsigned)MDK; + return nullptr; + } + + // FIXME: Create a ModuleDecl and return it. + // FIXME: Teach the lexer to handle this declaration too. + + switch (MDK) { + case ModuleDeclKind::Module: + // FIXME: Check we're not in a submodule. + // FIXME: Set CurrentModule and create a corresponding Module object. + return nullptr; + + case ModuleDeclKind::Partition: + // FIXME: Check we are in a submodule of the named module. + return nullptr; + + case ModuleDeclKind::Implementation: + DeclResult Import = ActOnModuleImport(ModuleLoc, ModuleLoc, Path); + if (Import.isInvalid()) + return nullptr; + ImportDecl *ID = cast<ImportDecl>(Import.get()); + + // The current module is whatever we just loaded. + // + // FIXME: We should probably do this from the lexer rather than waiting + // until now, in case we look ahead across something where the current + // module matters (eg a #include). + auto Name = ID->getImportedModule()->getTopLevelModuleName(); + if (!getLangOpts().CurrentModule.empty() && + getLangOpts().CurrentModule != Name) { + Diag(Path.front().second, diag::err_current_module_name_mismatch) + << SourceRange(Path.front().second, Path.back().second) + << getLangOpts().CurrentModule; + } + const_cast<LangOptions&>(getLangOpts()).CurrentModule = Name; + return ConvertDeclToDeclGroup(ID); + } + + llvm_unreachable("unexpected module decl kind"); +} + DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ImportLoc, ModuleIdPath Path) { @@ -15194,7 +15244,10 @@ 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. - if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule) + // Import-from-implementation is valid in the Modules TS. FIXME: Should we + // warn on a redundant import of the current module? + if (Mod->getTopLevelModuleName() == getLangOpts().CurrentModule && + (getLangOpts().CompilingModule || !getLangOpts().ModulesTS)) Diag(ImportLoc, getLangOpts().CompilingModule ? diag::err_module_self_import : diag::err_module_import_in_implementation) diff --git a/clang/test/Parser/cxx-modules-import.cpp b/clang/test/Parser/cxx-modules-import.cpp index bf99e7bb730..cd4174eabd0 100644 --- a/clang/test/Parser/cxx-modules-import.cpp +++ b/clang/test/Parser/cxx-modules-import.cpp @@ -1,10 +1,41 @@ // RUN: rm -rf %t // RUN: mkdir -p %t // RUN: echo 'int a, b;' > %t/x.h -// RUN: echo 'module x { header "x.h" module y {} }' > %t/map -// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s +// RUN: echo 'module x { header "x.h" module y {} } module z {}' > %t/map +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME=z +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=2 -DMODULE_KIND=implementation -DMODULE_NAME=x +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=3 -DMODULE_KIND= -DMODULE_NAME=z +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=4 -DMODULE_KIND=partition -DMODULE_NAME=z +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=5 -DMODULE_KIND=elderberry -DMODULE_NAME=z +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=1 -DMODULE_KIND=implementation -DMODULE_NAME='z [[]]' +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=6 -DMODULE_KIND=implementation -DMODULE_NAME='z [[fancy]]' +// RUN: %clang_cc1 -std=c++1z -fmodules-ts -I%S/Inputs -fmodules-cache-path=%t -fmodule-map-file=%t/map -verify %s \ +// RUN: -DTEST=7 -DMODULE_KIND=implementation -DMODULE_NAME='z [[maybe_unused]]' -int use_1 = a; // expected-error {{undeclared}} +module MODULE_KIND MODULE_NAME; +#if TEST == 3 +// expected-error@-2 {{'module' declaration found while not building module interface}} +#elif TEST == 4 +// expected-error@-4 {{'module partition' declaration found while not building module interface}} +#elif TEST == 5 +// expected-error@-6 {{unexpected module kind 'elderberry'}} +#elif TEST == 6 +// expected-warning@-8 {{unknown attribute 'fancy' ignored}} +#elif TEST == 7 +// expected-error-re@-10 {{'maybe_unused' attribute cannot be applied to a module{{$}}}} +#endif + +int use_1 = a; +#if TEST != 2 +// expected-error@-2 {{undeclared}} +#endif import x; |