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.

llvm-svn: 145665
This commit is contained in:
Douglas Gregor 2011-12-02 01:47:07 +00:00
parent 4e3a11ba57
commit 2b82c2a59e
8 changed files with 274 additions and 7 deletions

View File

@ -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 "

View File

@ -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 <string>
#include <utility>
namespace llvm {
class raw_ostream;
@ -28,6 +30,10 @@ namespace clang {
class FileEntry;
/// \brief Describes the name of a module.
typedef llvm::SmallVector<std::pair<std::string, SourceLocation>, 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<Module *, 1, bool> ExportDecl;
/// \brief The set of export declarations.
llvm::SmallVector<ExportDecl, 2> 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<UnresolvedExportDecl, 2> UnresolvedExports;
/// \brief Construct a top-level module.
explicit Module(StringRef Name, SourceLocation DefinitionLoc,
bool IsFramework)

View File

@ -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.
///

View File

@ -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);

View File

@ -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<DiagnosticIDs> DiagIDs(new DiagnosticIDs);
Diags = llvm::IntrusiveRefCntPtr<DiagnosticsEngine>(
@ -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<Module *>::iterator Sub = Context->SubModules.find(Name);
if (Sub != Context->SubModules.end())
return Sub->getValue();
return 0;
}
std::pair<Module *, bool>
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<MMToken::TokenKind>(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);

View File

@ -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

View File

@ -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<Module *, 2> 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<Module *>::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;

View File

@ -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" }