From 49cc1ccb00df06cc915e0ec5014d04082506f752 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Thu, 18 Aug 2016 21:59:42 +0000 Subject: [PATCH] C++ Modules TS: Add parsing support for module import declaration. llvm-svn: 279163 --- clang/include/clang/Basic/DiagnosticParseKinds.td | 2 ++ clang/include/clang/Parse/Parser.h | 3 +- clang/lib/Lex/Preprocessor.cpp | 12 ++++--- clang/lib/Parse/ParseDecl.cpp | 22 +++++++----- clang/lib/Parse/Parser.cpp | 43 +++++++++++++++++------ clang/lib/Sema/SemaDecl.cpp | 5 ++- clang/test/Parser/cxx-modules-import.cpp | 22 ++++++++++++ 7 files changed, 81 insertions(+), 28 deletions(-) create mode 100644 clang/test/Parser/cxx-modules-import.cpp diff --git a/clang/include/clang/Basic/DiagnosticParseKinds.td b/clang/include/clang/Basic/DiagnosticParseKinds.td index fb3db57..bbef0bd 100644 --- a/clang/include/clang/Basic/DiagnosticParseKinds.td +++ b/clang/include/clang/Basic/DiagnosticParseKinds.td @@ -1023,6 +1023,8 @@ 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">; +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_missing_before_module_end : Error<"expected %0 at end of module">; diff --git a/clang/include/clang/Parse/Parser.h b/clang/include/clang/Parse/Parser.h index 2ba9f71..9948dbb 100644 --- a/clang/include/clang/Parse/Parser.h +++ b/clang/include/clang/Parse/Parser.h @@ -2116,7 +2116,8 @@ private: // Forbid C++11 attributes that appear on certain syntactic // locations which standard permits but we don't supported yet, // for example, attributes appertain to decl specifiers. - void ProhibitCXX11Attributes(ParsedAttributesWithRange &attrs); + void ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs, + unsigned DiagID); /// \brief Skip C++11 attributes and return the end location of the last one. /// \returns SourceLocation() if there are no attributes. diff --git a/clang/lib/Lex/Preprocessor.cpp b/clang/lib/Lex/Preprocessor.cpp index 7303937..096a610 100644 --- a/clang/lib/Lex/Preprocessor.cpp +++ b/clang/lib/Lex/Preprocessor.cpp @@ -712,9 +712,12 @@ bool Preprocessor::HandleIdentifier(Token &Identifier) { // Note that we do not treat 'import' as a contextual // keyword when we're in a caching lexer, because caching lexers only get // used in contexts where import declarations are disallowed. - if (LastTokenWasAt && II.isModulesImport() && !InMacroArgs && - !DisableMacroExpansion && - (getLangOpts().Modules || getLangOpts().DebuggerSupport) && + // + // Likewise if this is the C++ Modules TS import keyword. + if (((LastTokenWasAt && II.isModulesImport()) || + Identifier.is(tok::kw_import)) && + !InMacroArgs && !DisableMacroExpansion && + (getLangOpts().Modules || getLangOpts().DebuggerSupport) && CurLexerKind != CLK_CachingLexer) { ModuleImportLoc = Identifier.getLocation(); ModuleImportPath.clear(); @@ -782,7 +785,8 @@ void Preprocessor::LexAfterModuleImport(Token &Result) { } // If we're expecting a '.' or a ';', and we got a '.', then wait until we - // see the next identifier. + // see the next identifier. (We can also see a '[[' that begins an + // attribute-specifier-seq here under the C++ Modules TS.) if (!ModuleImportExpectsIdentifier && Result.getKind() == tok::period) { ModuleImportExpectsIdentifier = true; CurLexerKind = CLK_LexAfterModuleImport; diff --git a/clang/lib/Parse/ParseDecl.cpp b/clang/lib/Parse/ParseDecl.cpp index 91a3eff..0459cb7 100644 --- a/clang/lib/Parse/ParseDecl.cpp +++ b/clang/lib/Parse/ParseDecl.cpp @@ -1407,15 +1407,19 @@ void Parser::DiagnoseProhibitedAttributes(ParsedAttributesWithRange &attrs) { << attrs.Range; } -void Parser::ProhibitCXX11Attributes(ParsedAttributesWithRange &attrs) { - AttributeList *AttrList = attrs.getList(); - while (AttrList) { - if (AttrList->isCXX11Attribute()) { - Diag(AttrList->getLoc(), diag::err_attribute_not_type_attr) - << AttrList->getName(); - AttrList->setInvalid(); +void Parser::ProhibitCXX11Attributes(ParsedAttributesWithRange &Attrs, + unsigned DiagID) { + for (AttributeList *Attr = Attrs.getList(); Attr; Attr = Attr->getNext()) { + if (!Attr->isCXX11Attribute()) + continue; + if (Attr->getKind() == AttributeList::UnknownAttribute) + Diag(Attr->getLoc(), diag::warn_unknown_attribute_ignored) + << Attr->getName(); + else { + Diag(Attr->getLoc(), DiagID) + << Attr->getName(); + Attr->setInvalid(); } - AttrList = AttrList->getNext(); } } @@ -2717,7 +2721,7 @@ void Parser::ParseDeclarationSpecifiers(DeclSpec &DS, // Reject C++11 attributes that appertain to decl specifiers as // we don't support any C++11 attributes that appertain to decl // specifiers. This also conforms to what g++ 4.8 is doing. - ProhibitCXX11Attributes(attrs); + ProhibitCXX11Attributes(attrs, diag::err_attribute_not_type_attr); DS.takeAttributesFrom(attrs); } diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp index f442bd7..9f941f4 100644 --- a/clang/lib/Parse/Parser.cpp +++ b/clang/lib/Parse/Parser.cpp @@ -553,6 +553,10 @@ bool Parser::ParseTopLevelDecl(DeclGroupPtrTy &Result) { HandlePragmaUnused(); return false; + case tok::kw_import: + Result = ParseModuleImport(SourceLocation()); + return false; + case tok::annot_module_include: Actions.ActOnModuleInclude(Tok.getLocation(), reinterpret_cast( @@ -1996,15 +2000,29 @@ void Parser::ParseMicrosoftIfExistsExternalDeclaration() { Braces.consumeClose(); } +/// 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 '.')* ';' +/// [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 '.' Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { - assert(Tok.isObjCAtKeyword(tok::objc_import) && + assert((AtLoc.isInvalid() ? Tok.is(tok::kw_import) + : Tok.isObjCAtKeyword(tok::objc_import)) && "Improper start to module import"); SourceLocation ImportLoc = ConsumeToken(); + SourceLocation StartLoc = AtLoc.isInvalid() ? ImportLoc : AtLoc; SmallVector, 2> Path; // Parse the module path. - do { + while (true) { if (!Tok.is(tok::identifier)) { if (Tok.is(tok::code_completion)) { Actions.CodeCompleteModuleImport(ImportLoc, Path); @@ -2020,14 +2038,17 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { // Record this part of the module path. Path.push_back(std::make_pair(Tok.getIdentifierInfo(), Tok.getLocation())); ConsumeToken(); - - if (Tok.is(tok::period)) { - ConsumeToken(); - continue; - } - - break; - } while (true); + + if (Tok.isNot(tok::period)) + break; + + ConsumeToken(); + } + + ParsedAttributesWithRange Attrs(AttrFactory); + MaybeParseCXX11Attributes(Attrs); + // We don't support any module import attributes yet. + ProhibitCXX11Attributes(Attrs, diag::err_attribute_not_import_attr); if (PP.hadModuleLoaderFatalFailure()) { // With a fatal failure in the module loader, we abort parsing. @@ -2035,7 +2056,7 @@ Parser::DeclGroupPtrTy Parser::ParseModuleImport(SourceLocation AtLoc) { return nullptr; } - DeclResult Import = Actions.ActOnModuleImport(AtLoc, ImportLoc, Path); + DeclResult Import = Actions.ActOnModuleImport(StartLoc, ImportLoc, Path); ExpectAndConsumeSemi(diag::err_module_expected_semi); if (Import.isInvalid()) return nullptr; diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp index 42f8dea..5dab15a 100644 --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -15178,7 +15178,7 @@ void Sema::diagnoseMisplacedModuleImport(Module *M, SourceLocation ImportLoc) { return checkModuleImportContext(*this, M, ImportLoc, CurContext); } -DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, +DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc, SourceLocation ImportLoc, ModuleIdPath Path) { Module *Mod = @@ -15213,8 +15213,7 @@ DeclResult Sema::ActOnModuleImport(SourceLocation AtLoc, } TranslationUnitDecl *TU = getASTContext().getTranslationUnitDecl(); - ImportDecl *Import = ImportDecl::Create(Context, TU, - AtLoc.isValid()? AtLoc : ImportLoc, + ImportDecl *Import = ImportDecl::Create(Context, TU, StartLoc, Mod, IdentifierLocs); if (!ModuleScopes.empty()) Context.addModuleInitializer(ModuleScopes.back().Module, Import); diff --git a/clang/test/Parser/cxx-modules-import.cpp b/clang/test/Parser/cxx-modules-import.cpp new file mode 100644 index 0000000..bf99e7b --- /dev/null +++ b/clang/test/Parser/cxx-modules-import.cpp @@ -0,0 +1,22 @@ +// 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 + +int use_1 = a; // expected-error {{undeclared}} + +import x; + +int use_2 = b; // ok + +import x [[]]; +import x [[foo]]; // expected-warning {{unknown attribute 'foo' ignored}} +import x [[noreturn]]; // expected-error {{'noreturn' attribute cannot be applied to a module import}} +import x [[blarg::noreturn]]; // expected-warning {{unknown attribute 'noreturn' ignored}} + +import x.y; +import x.; // expected-error {{expected a module name after module import}} +import .x; // expected-error {{expected a module name after module import}} + +import blarg; // expected-error {{module 'blarg' not found}} -- 2.7.4