From: Douglas Gregor Date: Fri, 2 Dec 2011 01:47:07 +0000 (+0000) Subject: Implementing parsing and resolution of module export declarations X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=90db26000aefe9335370013eec64c85232d80227;p=clang Implementing parsing and resolution of module export declarations within module maps, which will (eventually) be used to re-export a module from another module. There are still some pieces missing, however. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@145665 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticLexKinds.td b/include/clang/Basic/DiagnosticLexKinds.td index ccfa3216c1..70951a6531 100644 --- a/include/clang/Basic/DiagnosticLexKinds.td +++ b/include/clang/Basic/DiagnosticLexKinds.td @@ -399,6 +399,12 @@ def err_mmap_umbrella_header_submodule : Error< "submodule '%0' can not have an umbrella header">; def err_mmap_umbrella_clash : Error< "umbrella header for module '%0' already covers this directory">; +def err_mmap_export_module_id : Error< + "expected an exported module name or '*'">; +def err_mmap_missing_module_unqualified : Error< + "no module named '%0' visible from '%1'">; +def err_mmap_missing_module_qualified : Error< + "no module named '%0' in '%1'">; def warn_auto_module_import : Warning< "treating #%select{include|import|include_next|__include_macros}0 as an " diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h index 4eaa1be291..43edcfacec 100644 --- a/include/clang/Basic/Module.h +++ b/include/clang/Basic/Module.h @@ -15,10 +15,12 @@ #define LLVM_CLANG_BASIC_MODULE_H #include "clang/Basic/SourceLocation.h" +#include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include +#include namespace llvm { class raw_ostream; @@ -28,6 +30,10 @@ namespace clang { class FileEntry; +/// \brief Describes the name of a module. +typedef llvm::SmallVector, 2> + ModuleId; + /// \brief Describes a module or submodule. class Module { public: @@ -73,6 +79,33 @@ public: ///\ brief The visibility of names within this particular module. NameVisibilityKind NameVisibility; + /// \brief Describes an exported module. + /// + /// The pointer is the module being re-exported, while the bit will be true + /// to indicate that this is a wildcard export. + typedef llvm::PointerIntPair ExportDecl; + + /// \brief The set of export declarations. + llvm::SmallVector Exports; + + /// \brief Describes an exported module that has not yet been resolved + /// (perhaps because the module it refers to has not yet been loaded). + struct UnresolvedExportDecl { + /// \brief The location of the 'export' keyword in the module map file. + SourceLocation ExportLoc; + + /// \brief The name of the module. + ModuleId Id; + + /// \brief Whether this export declaration ends in a wildcard, indicating + /// that all of its submodules should be exported (rather than the named + /// module itself). + bool Wildcard; + }; + + /// \brief The set of export declarations that have yet to be resolved. + llvm::SmallVector UnresolvedExports; + /// \brief Construct a top-level module. explicit Module(StringRef Name, SourceLocation DefinitionLoc, bool IsFramework) diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h index b93e65a4a5..40488b29fc 100644 --- a/include/clang/Lex/ModuleMap.h +++ b/include/clang/Lex/ModuleMap.h @@ -57,6 +57,22 @@ class ModuleMap { friend class ModuleMapParser; + /// \brief Resolve the given export declaration into an actual export + /// declaration. + /// + /// \param Mod The module in which we're resolving the export declaration. + /// + /// \param Unresolved The export declaration to resolve. + /// + /// \param Complain Whether this routine should complain about unresolvable + /// exports. + /// + /// \returns The resolved export declaration, which will have a NULL pointer + /// if the export could not be resolved. + Module::ExportDecl + resolveExport(Module *Mod, const Module::UnresolvedExportDecl &Unresolved, + bool Complain); + public: /// \brief Construct a new module map. /// @@ -86,6 +102,28 @@ public: /// /// \returns The named module, if known; otherwise, returns null. Module *findModule(StringRef Name); + + /// \brief Retrieve a module with the given name using lexical name lookup, + /// starting at the given context. + /// + /// \param The name of the module to look up. + /// + /// \param Context The module context, from which we will perform lexical + /// name lookup. + /// + /// \returns The named module, if known; otherwise, returns null. + Module *lookupModuleUnqualified(StringRef Name, Module *Context); + + /// \brief Retrieve a module with the given name within the given context, + /// using direct (qualified) name lookup. + /// + /// \param The name of the module to look up. + /// + /// \param Context The module for which we will look for a submodule. If + /// null, we will look for a top-level module. + /// + /// \returns The named submodule, if known; otherwose, returns null. + Module *lookupModuleQualified(StringRef Name, Module *Context); /// \brief Find a new module or submodule, or create it if it does not already /// exist. @@ -118,7 +156,17 @@ public: /// \returns The file entry for the module map file containing the given /// module, or NULL if the module definition was inferred. const FileEntry *getContainingModuleMapFile(Module *Module); - + + /// \brief Resolve all of the unresolved exports in the given module. + /// + /// \param Mod The module whose exports should be resolved. + /// + /// \param Complain Whether to emit diagnostics for failures. + /// + /// \returns true if any errors were encountered while resolving exports, + /// false otherwise. + bool resolveExports(Module *Mod, bool Complain); + /// \brief Parse the given module map file, and record any modules we /// encounter. /// diff --git a/include/clang/Lex/Preprocessor.h b/include/clang/Lex/Preprocessor.h index 0800ef3e45..8e7ab5edbf 100644 --- a/include/clang/Lex/Preprocessor.h +++ b/include/clang/Lex/Preprocessor.h @@ -993,6 +993,9 @@ public: unsigned getCounterValue() const { return CounterValue; } void setCounterValue(unsigned V) { CounterValue = V; } + /// \brief Retrieves the module that we're currently building, if any. + Module *getCurrentModule(); + /// AllocateMacroInfo - Allocate a new MacroInfo object with the provide /// SourceLocation. MacroInfo *AllocateMacroInfo(SourceLocation L); diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp index 547cdc2636..84f7bec919 100644 --- a/lib/Lex/ModuleMap.cpp +++ b/lib/Lex/ModuleMap.cpp @@ -27,6 +27,41 @@ #include "llvm/ADT/StringSwitch.h" using namespace clang; +Module::ExportDecl +ModuleMap::resolveExport(Module *Mod, + const Module::UnresolvedExportDecl &Unresolved, + bool Complain) { + // Find the starting module. + Module *Context = lookupModuleUnqualified(Unresolved.Id[0].first, Mod); + if (!Context) { + if (Complain) + Diags->Report(Unresolved.Id[0].second, + diag::err_mmap_missing_module_unqualified) + << Unresolved.Id[0].first << Mod->getFullModuleName(); + + return Module::ExportDecl(); + } + + // Dig into the module path. + for (unsigned I = 1, N = Unresolved.Id.size(); I != N; ++I) { + Module *Sub = lookupModuleQualified(Unresolved.Id[I].first, + Context); + if (!Sub) { + if (Complain) + Diags->Report(Unresolved.Id[I].second, + diag::err_mmap_missing_module_qualified) + << Unresolved.Id[I].first << Context->getFullModuleName() + << SourceRange(Unresolved.Id[0].second, Unresolved.Id[I-1].second); + + return Module::ExportDecl(); + } + + Context = Sub; + } + + return Module::ExportDecl(Context, Unresolved.Wildcard); +} + ModuleMap::ModuleMap(FileManager &FileMgr, const DiagnosticConsumer &DC) { llvm::IntrusiveRefCntPtr DiagIDs(new DiagnosticIDs); Diags = llvm::IntrusiveRefCntPtr( @@ -97,6 +132,26 @@ Module *ModuleMap::findModule(StringRef Name) { return 0; } +Module *ModuleMap::lookupModuleUnqualified(StringRef Name, Module *Context) { + for(; Context; Context = Context->Parent) { + if (Module *Sub = lookupModuleQualified(Name, Context)) + return Sub; + } + + return findModule(Name); +} + +Module *ModuleMap::lookupModuleQualified(StringRef Name, Module *Context) { + if (!Context) + return findModule(Name); + + llvm::StringMap::iterator Sub = Context->SubModules.find(Name); + if (Sub != Context->SubModules.end()) + return Sub->getValue(); + + return 0; +} + std::pair ModuleMap::findOrCreateModule(StringRef Name, Module *Parent, bool IsFramework, bool IsExplicit) { @@ -169,6 +224,20 @@ void ModuleMap::dump() { } } +bool ModuleMap::resolveExports(Module *Mod, bool Complain) { + bool HadError = false; + for (unsigned I = 0, N = Mod->UnresolvedExports.size(); I != N; ++I) { + Module::ExportDecl Export = resolveExport(Mod, Mod->UnresolvedExports[I], + Complain); + if (Export.getPointer()) + Mod->Exports.push_back(Export); + else + HadError = true; + } + Mod->UnresolvedExports.clear(); + return HadError; +} + //----------------------------------------------------------------------------// // Module map file parser //----------------------------------------------------------------------------// @@ -181,9 +250,12 @@ namespace clang { HeaderKeyword, Identifier, ExplicitKeyword, + ExportKeyword, FrameworkKeyword, ModuleKeyword, + Period, UmbrellaKeyword, + Star, StringLiteral, LBrace, RBrace @@ -247,6 +319,7 @@ namespace clang { void parseModuleDecl(); void parseUmbrellaDecl(); void parseHeaderDecl(); + void parseExportDecl(); public: explicit ModuleMapParser(Lexer &L, SourceManager &SourceMgr, @@ -283,6 +356,7 @@ retry: Tok.Kind = llvm::StringSwitch(Tok.getString()) .Case("header", MMToken::HeaderKeyword) .Case("explicit", MMToken::ExplicitKeyword) + .Case("export", MMToken::ExportKeyword) .Case("framework", MMToken::FrameworkKeyword) .Case("module", MMToken::ModuleKeyword) .Case("umbrella", MMToken::UmbrellaKeyword) @@ -297,10 +371,18 @@ retry: Tok.Kind = MMToken::LBrace; break; + case tok::period: + Tok.Kind = MMToken::Period; + break; + case tok::r_brace: Tok.Kind = MMToken::RBrace; break; + case tok::star: + Tok.Kind = MMToken::Star; + break; + case tok::string_literal: { // Parse the string literal. LangOptions LangOpts; @@ -373,6 +455,7 @@ void ModuleMapParser::skipUntil(MMToken::TokenKind K) { /// umbrella-declaration /// header-declaration /// 'explicit'[opt] module-declaration +/// export-declaration void ModuleMapParser::parseModuleDecl() { assert(Tok.is(MMToken::ExplicitKeyword) || Tok.is(MMToken::ModuleKeyword) || Tok.is(MMToken::FrameworkKeyword)); @@ -457,6 +540,10 @@ void ModuleMapParser::parseModuleDecl() { parseModuleDecl(); break; + case MMToken::ExportKeyword: + parseExportDecl(); + break; + case MMToken::HeaderKeyword: parseHeaderDecl(); break; @@ -464,7 +551,7 @@ void ModuleMapParser::parseModuleDecl() { case MMToken::UmbrellaKeyword: parseUmbrellaDecl(); break; - + default: Diags.Report(Tok.getLocation(), diag::err_mmap_expected_member); consumeToken(); @@ -625,6 +712,52 @@ void ModuleMapParser::parseHeaderDecl() { } } +/// \brief Parse a module export declaration. +/// +/// export-declaration: +/// 'export' wildcard-module-id +/// +/// wildcard-module-id: +/// identifier +/// '*' +/// identifier '.' wildcard-module-id +void ModuleMapParser::parseExportDecl() { + assert(Tok.is(MMToken::ExportKeyword)); + SourceLocation ExportLoc = consumeToken(); + + // Parse the module-id with an optional wildcard at the end. + ModuleId ParsedModuleId; + bool Wildcard = false; + do { + if (Tok.is(MMToken::Identifier)) { + ParsedModuleId.push_back(std::make_pair(Tok.getString(), + Tok.getLocation())); + consumeToken(); + + if (Tok.is(MMToken::Period)) { + consumeToken(); + continue; + } + + break; + } + + if(Tok.is(MMToken::Star)) { + Wildcard = true; + break; + } + + Diags.Report(Tok.getLocation(), diag::err_mmap_export_module_id); + HadError = true; + return; + } while (true); + + Module::UnresolvedExportDecl Unresolved = { + ExportLoc, ParsedModuleId, Wildcard + }; + ActiveModule->UnresolvedExports.push_back(Unresolved); +} + /// \brief Parse a module map file. /// /// module-map-file: @@ -641,10 +774,13 @@ bool ModuleMapParser::parseModuleMapFile() { break; case MMToken::ExplicitKeyword: + case MMToken::ExportKeyword: case MMToken::HeaderKeyword: case MMToken::Identifier: case MMToken::LBrace: + case MMToken::Period: case MMToken::RBrace: + case MMToken::Star: case MMToken::StringLiteral: case MMToken::UmbrellaKeyword: Diags.Report(Tok.getLocation(), diag::err_mmap_expected_module); diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 180e1e2c25..6cf34e226a 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -369,7 +369,12 @@ void Preprocessor::CreateString(const char *Buf, unsigned Len, Token &Tok, Tok.setLiteralData(DestPtr); } - +Module *Preprocessor::getCurrentModule() { + if (getLangOptions().CurrentModule.empty()) + return 0; + + return getHeaderSearchInfo().getModule(getLangOptions().CurrentModule); +} //===----------------------------------------------------------------------===// // Preprocessor Initialization Methods diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp index fe393dd2fe..2b7be7eaca 100644 --- a/lib/Sema/Sema.cpp +++ b/lib/Sema/Sema.cpp @@ -33,11 +33,11 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" +#include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/PartialDiagnostic.h" #include "clang/Basic/TargetInfo.h" - using namespace clang; using namespace sema; @@ -484,6 +484,32 @@ void Sema::ActOnEndOfTranslationUnit() { } if (TUKind == TU_Module) { + // If we are building a module, resolve all of the exported declarations + // now. + if (Module *CurrentModule = PP.getCurrentModule()) { + ModuleMap &ModMap = PP.getHeaderSearchInfo().getModuleMap(); + + llvm::SmallVector Stack; + Stack.push_back(CurrentModule); + while (!Stack.empty()) { + Module *Mod = Stack.back(); + Stack.pop_back(); + + // Resolve the exported declarations. + // FIXME: Actually complain, once we figure out how to teach the + // diagnostic client to deal with complains in the module map at this + // point. + ModMap.resolveExports(Mod, /*Complain=*/false); + + // Queue the submodules, so their exports will also be resolved. + for (llvm::StringMap::iterator Sub = Mod->SubModules.begin(), + SubEnd = Mod->SubModules.end(); + Sub != SubEnd; ++Sub) { + Stack.push_back(Sub->getValue()); + } + } + } + // Modules don't need any of the checking below. TUScope = 0; return; diff --git a/test/Modules/Inputs/module.map b/test/Modules/Inputs/module.map index b14ab24a8a..d18b9f7505 100644 --- a/test/Modules/Inputs/module.map +++ b/test/Modules/Inputs/module.map @@ -1,7 +1,17 @@ module diamond_top { header "diamond_top.h" } -module diamond_left { header "diamond_left.h" } -module diamond_right { header "diamond_right.h" } -module diamond_bottom { header "diamond_bottom.h" } +module diamond_left { + header "diamond_left.h" + export diamond_top +} +module diamond_right { + header "diamond_right.h" + export diamond_top +} +module diamond_bottom { + header "diamond_bottom.h" + export diamond_left + export diamond_right +} module irgen { header "irgen.h" } module lookup_left_objc { header "lookup_left.h" } module lookup_right_objc { header "lookup_right.h" }