[modules] Frontend support for building a header module from a list of

headaer files.

llvm-svn: 342304
This commit is contained in:
Richard Smith 2018-09-15 01:21:15 +00:00
parent 66f3dc031d
commit d6509cf21d
20 changed files with 248 additions and 37 deletions

View File

@ -187,6 +187,8 @@ def err_module_build_requires_fmodules : Error<
"module compilation requires '-fmodules'">;
def err_module_interface_requires_modules_ts : Error<
"module interface compilation requires '-fmodules-ts'">;
def err_header_module_requires_modules : Error<
"header module compilation requires '-fmodules' or '-fmodules-ts'">;
def warn_module_config_mismatch : Warning<
"module file %0 cannot be loaded due to a configuration mismatch with the current "
"compilation">, InGroup<DiagGroup<"module-file-config-mismatch">>, DefaultError;
@ -224,6 +226,10 @@ def remark_module_build_done : Remark<"finished building module '%0'">,
def err_modules_embed_file_not_found :
Error<"file '%0' specified by '-fmodules-embed-file=' not found">,
DefaultFatal;
def err_module_header_file_not_found :
Error<"module header file '%0' not found">, DefaultFatal;
def err_module_header_file_invalid :
Error<"unexpected module header file input '%0'">, DefaultFatal;
def err_test_module_file_extension_version : Error<
"test module file extension '%0' has different version (%1.%2) than expected "

View File

@ -30,7 +30,7 @@ namespace clang {
enum {
DIAG_SIZE_COMMON = 300,
DIAG_SIZE_DRIVER = 200,
DIAG_SIZE_FRONTEND = 100,
DIAG_SIZE_FRONTEND = 150,
DIAG_SIZE_SERIALIZATION = 120,
DIAG_SIZE_LEX = 400,
DIAG_SIZE_PARSE = 500,

View File

@ -9090,6 +9090,8 @@ def err_invalid_type_for_program_scope_var : Error<
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">;
def err_module_interface_implementation_mismatch : Error<
"missing 'export' specifier in module declaration while "
"building module interface">;

View File

@ -73,8 +73,11 @@ public:
/// Compiling a module from a module map.
CMK_ModuleMap,
/// Compiling a module from a list of header files.
CMK_HeaderModule,
/// Compiling a C++ modules TS module interface unit.
CMK_ModuleInterface
CMK_ModuleInterface,
};
enum PragmaMSPointersToMembersKind {

View File

@ -543,6 +543,8 @@ def emit_module : Flag<["-"], "emit-module">,
HelpText<"Generate pre-compiled module file from a module map">;
def emit_module_interface : Flag<["-"], "emit-module-interface">,
HelpText<"Generate pre-compiled module file from a C++ module interface">;
def emit_header_module : Flag<["-"], "emit-header-module">,
HelpText<"Generate pre-compiled module file from a set of header files">;
def emit_pth : Flag<["-"], "emit-pth">,
HelpText<"Generate pre-tokenized header file">;
def emit_pch : Flag<["-"], "emit-pch">,

View File

@ -48,6 +48,12 @@ protected:
/// @name Implementation Action Interface
/// @{
/// Prepare to execute the action on the given CompilerInstance.
///
/// This is called before executing the action on any inputs, and can modify
/// the configuration as needed (including adjusting the input list).
virtual bool PrepareToExecuteAction(CompilerInstance &CI) { return true; }
/// Create the AST consumer object for this action, if supported.
///
/// This routine is called as part of BeginSourceFile(), which will
@ -130,11 +136,18 @@ public:
return CurrentInput;
}
const StringRef getCurrentFile() const {
StringRef getCurrentFile() const {
assert(!CurrentInput.isEmpty() && "No current file!");
return CurrentInput.getFile();
}
StringRef getCurrentFileOrBufferName() const {
assert(!CurrentInput.isEmpty() && "No current file!");
return CurrentInput.isFile()
? CurrentInput.getFile()
: CurrentInput.getBuffer()->getBufferIdentifier();
}
InputKind getCurrentFileKind() const {
assert(!CurrentInput.isEmpty() && "No current file!");
return CurrentInput.getKind();
@ -190,6 +203,11 @@ public:
/// @name Public Action Interface
/// @{
/// Prepare the action to execute on the given compiler instance.
bool PrepareToExecute(CompilerInstance &CI) {
return PrepareToExecuteAction(CI);
}
/// Prepare the action for processing the input file \p Input.
///
/// This is run after the options and frontend have been initialized,

View File

@ -142,6 +142,19 @@ private:
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
};
class GenerateHeaderModuleAction : public GenerateModuleAction {
/// The synthesized module input buffer for the current compilation.
std::unique_ptr<llvm::MemoryBuffer> Buffer;
std::vector<std::string> ModuleHeaders;
private:
bool PrepareToExecuteAction(CompilerInstance &CI) override;
bool BeginSourceFileAction(CompilerInstance &CI) override;
std::unique_ptr<raw_pwrite_stream>
CreateOutputFile(CompilerInstance &CI, StringRef InFile) override;
};
class SyntaxOnlyAction : public ASTFrontendAction {
protected:
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,

View File

@ -82,6 +82,9 @@ enum ActionKind {
/// Generate pre-compiled module from a C++ module interface file.
GenerateModuleInterface,
/// Generate pre-compiled module from a set of header files.
GenerateHeaderModule,
/// Generate pre-compiled header.
GeneratePCH,

View File

@ -92,9 +92,9 @@ class ModuleMap {
/// named LangOpts::CurrentModule, if we've loaded it).
Module *SourceModule = nullptr;
/// The global module for the current TU, if we still own it. (Ownership is
/// transferred if/when we create an enclosing module.
std::unique_ptr<Module> PendingGlobalModule;
/// Submodules of the current module that have not yet been attached to it.
/// (Ownership is transferred if/when we create an enclosing module.)
llvm::SmallVector<std::unique_ptr<Module>, 8> PendingSubmodules;
/// The top-level modules that are known.
llvm::StringMap<Module *> Modules;
@ -519,8 +519,7 @@ public:
bool IsFramework,
bool IsExplicit);
/// Create a 'global module' for a C++ Modules TS module interface
/// unit.
/// Create a 'global module' for a C++ Modules TS module interface unit.
///
/// We model the global module as a submodule of the module interface unit.
/// Unfortunately, we can't create the module interface unit's Module until
@ -537,6 +536,9 @@ public:
Module *createModuleForInterfaceUnit(SourceLocation Loc, StringRef Name,
Module *GlobalModule);
/// Create a header module from the specified list of headers.
Module *createHeaderModule(StringRef Name, ArrayRef<Module::Header> Headers);
/// Infer the contents of a framework module map from the given
/// framework directory.
Module *inferFrameworkModule(const DirectoryEntry *FrameworkDir,

View File

@ -911,6 +911,9 @@ bool CompilerInstance::ExecuteAction(FrontendAction &Act) {
// taking it as an input instead of hard-coding llvm::errs.
raw_ostream &OS = llvm::errs();
if (!Act.PrepareToExecute(*this))
return false;
// Create the target instance.
setTarget(TargetInfo::CreateTargetInfo(getDiagnostics(),
getInvocation().TargetOpts));
@ -1615,22 +1618,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
Module::NameVisibilityKind Visibility,
bool IsInclusionDirective) {
// Determine what file we're searching from.
// FIXME: Should we be deciding whether this is a submodule (here and
// below) based on -fmodules-ts or should we pass a flag and make the
// caller decide?
std::string ModuleName;
if (getLangOpts().ModulesTS) {
// FIXME: Same code as Sema::ActOnModuleDecl() so there is probably a
// better place/way to do this.
for (auto &Piece : Path) {
if (!ModuleName.empty())
ModuleName += ".";
ModuleName += Piece.first->getName();
}
}
else
ModuleName = Path[0].first->getName();
StringRef ModuleName = Path[0].first->getName();
SourceLocation ModuleNameLoc = Path[0].second;
// If we've already handled this import, just return the cached result.
@ -1859,7 +1847,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
// Verify that the rest of the module path actually corresponds to
// a submodule.
bool MapPrivateSubModToTopLevel = false;
if (!getLangOpts().ModulesTS && Path.size() > 1) {
if (Path.size() > 1) {
for (unsigned I = 1, N = Path.size(); I != N; ++I) {
StringRef Name = Path[I].first->getName();
clang::Module *Sub = Module->findSubmodule(Name);

View File

@ -1454,6 +1454,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
Opts.ProgramAction = frontend::GenerateModule; break;
case OPT_emit_module_interface:
Opts.ProgramAction = frontend::GenerateModuleInterface; break;
case OPT_emit_header_module:
Opts.ProgramAction = frontend::GenerateHeaderModule; break;
case OPT_emit_pch:
Opts.ProgramAction = frontend::GeneratePCH; break;
case OPT_emit_pth:
@ -2830,6 +2832,7 @@ static bool isStrictlyPreprocessorAction(frontend::ActionKind Action) {
case frontend::FixIt:
case frontend::GenerateModule:
case frontend::GenerateModuleInterface:
case frontend::GenerateHeaderModule:
case frontend::GeneratePCH:
case frontend::GeneratePTH:
case frontend::ParseSyntaxOnly:

View File

@ -523,7 +523,6 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
setCurrentInput(Input);
setCompilerInstance(&CI);
StringRef InputFile = Input.getFile();
bool HasBegunSourceFile = false;
bool ReplayASTFile = Input.getKind().getFormat() == InputKind::Precompiled &&
usesPreprocessorOnly();
@ -541,6 +540,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
&Diags->getDiagnosticOptions()));
ASTDiags->setClient(Diags->getClient(), /*OwnsClient*/false);
// FIXME: What if the input is a memory buffer?
StringRef InputFile = Input.getFile();
std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromASTFile(
InputFile, CI.getPCHContainerReader(), ASTUnit::LoadPreprocessorOnly,
ASTDiags, CI.getFileSystemOpts(), CI.getCodeGenOpts().DebugTypeExtRefs);
@ -604,6 +606,9 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(&CI.getDiagnostics());
// FIXME: What if the input is a memory buffer?
StringRef InputFile = Input.getFile();
std::unique_ptr<ASTUnit> AST = ASTUnit::LoadFromASTFile(
InputFile, CI.getPCHContainerReader(), ASTUnit::LoadEverything, Diags,
CI.getFileSystemOpts(), CI.getCodeGenOpts().DebugTypeExtRefs);
@ -791,7 +796,7 @@ bool FrontendAction::BeginSourceFile(CompilerInstance &CI,
// For preprocessed files, check if the first line specifies the original
// source file name with a linemarker.
std::string PresumedInputFile = InputFile;
std::string PresumedInputFile = getCurrentFileOrBufferName();
if (Input.isPreprocessed())
ReadOriginalFileName(CI, PresumedInputFile);

View File

@ -242,6 +242,76 @@ GenerateModuleInterfaceAction::CreateOutputFile(CompilerInstance &CI,
return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
}
bool GenerateHeaderModuleAction::PrepareToExecuteAction(
CompilerInstance &CI) {
if (!CI.getLangOpts().Modules && !CI.getLangOpts().ModulesTS) {
CI.getDiagnostics().Report(diag::err_header_module_requires_modules);
return false;
}
auto &Inputs = CI.getFrontendOpts().Inputs;
if (Inputs.empty())
return GenerateModuleAction::BeginInvocation(CI);
auto Kind = Inputs[0].getKind();
// Convert the header file inputs into a single module input buffer.
SmallString<256> HeaderContents;
ModuleHeaders.reserve(Inputs.size());
for (const FrontendInputFile &FIF : Inputs) {
// FIXME: We should support re-compiling from an AST file.
if (FIF.getKind().getFormat() != InputKind::Source || !FIF.isFile()) {
CI.getDiagnostics().Report(diag::err_module_header_file_not_found)
<< (FIF.isFile() ? FIF.getFile()
: FIF.getBuffer()->getBufferIdentifier());
return true;
}
HeaderContents += "#include \"";
HeaderContents += FIF.getFile();
HeaderContents += "\"\n";
ModuleHeaders.push_back(FIF.getFile());
}
Buffer = llvm::MemoryBuffer::getMemBufferCopy(
HeaderContents, Module::getModuleInputBufferName());
// Set that buffer up as our "real" input.
Inputs.clear();
Inputs.push_back(FrontendInputFile(Buffer.get(), Kind, /*IsSystem*/false));
return GenerateModuleAction::PrepareToExecuteAction(CI);
}
bool GenerateHeaderModuleAction::BeginSourceFileAction(
CompilerInstance &CI) {
CI.getLangOpts().setCompilingModule(LangOptions::CMK_HeaderModule);
// Synthesize a Module object for the given headers.
auto &HS = CI.getPreprocessor().getHeaderSearchInfo();
SmallVector<Module::Header, 16> Headers;
for (StringRef Name : ModuleHeaders) {
const DirectoryLookup *CurDir = nullptr;
const FileEntry *FE = HS.LookupFile(
Name, SourceLocation(), /*Angled*/ false, nullptr, CurDir,
None, nullptr, nullptr, nullptr, nullptr, nullptr);
if (!FE) {
CI.getDiagnostics().Report(diag::err_module_header_file_not_found)
<< Name;
continue;
}
Headers.push_back({Name, FE});
}
HS.getModuleMap().createHeaderModule(CI.getLangOpts().CurrentModule, Headers);
return GenerateModuleAction::BeginSourceFileAction(CI);
}
std::unique_ptr<raw_pwrite_stream>
GenerateHeaderModuleAction::CreateOutputFile(CompilerInstance &CI,
StringRef InFile) {
return CI.createDefaultOutputFile(/*Binary=*/true, InFile, "pcm");
}
SyntaxOnlyAction::~SyntaxOnlyAction() {
}

View File

@ -61,6 +61,8 @@ CreateFrontendBaseAction(CompilerInstance &CI) {
return llvm::make_unique<GenerateModuleFromModuleMapAction>();
case GenerateModuleInterface:
return llvm::make_unique<GenerateModuleInterfaceAction>();
case GenerateHeaderModule:
return llvm::make_unique<GenerateHeaderModuleAction>();
case GeneratePCH: return llvm::make_unique<GeneratePCHAction>();
case GeneratePTH: return llvm::make_unique<GeneratePTHAction>();
case InitOnly: return llvm::make_unique<InitOnlyAction>();

View File

@ -806,12 +806,11 @@ std::pair<Module *, bool> ModuleMap::findOrCreateModule(StringRef Name,
}
Module *ModuleMap::createGlobalModuleForInterfaceUnit(SourceLocation Loc) {
assert(!PendingGlobalModule && "created multiple global modules");
PendingGlobalModule.reset(
PendingSubmodules.emplace_back(
new Module("<global>", Loc, nullptr, /*IsFramework*/ false,
/*IsExplicit*/ true, NumCreatedModules++));
PendingGlobalModule->Kind = Module::GlobalModuleFragment;
return PendingGlobalModule.get();
PendingSubmodules.back()->Kind = Module::GlobalModuleFragment;
return PendingSubmodules.back().get();
}
Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
@ -827,10 +826,11 @@ Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
Modules[Name] = SourceModule = Result;
// Reparent the current global module fragment as a submodule of this module.
assert(GlobalModule == PendingGlobalModule.get() &&
"unexpected global module");
GlobalModule->setParent(Result);
PendingGlobalModule.release(); // now owned by parent
for (auto &Submodule : PendingSubmodules) {
Submodule->setParent(Result);
Submodule.release(); // now owned by parent
}
PendingSubmodules.clear();
// Mark the main source file as being within the newly-created module so that
// declarations and macros are properly visibility-restricted to it.
@ -841,6 +841,29 @@ Module *ModuleMap::createModuleForInterfaceUnit(SourceLocation Loc,
return Result;
}
Module *ModuleMap::createHeaderModule(StringRef Name,
ArrayRef<Module::Header> Headers) {
assert(LangOpts.CurrentModule == Name && "module name mismatch");
assert(!Modules[Name] && "redefining existing module");
auto *Result =
new Module(Name, SourceLocation(), nullptr, /*IsFramework*/ false,
/*IsExplicit*/ false, NumCreatedModules++);
Result->Kind = Module::ModuleInterfaceUnit;
Modules[Name] = SourceModule = Result;
for (const Module::Header &H : Headers) {
auto *M = new Module(H.NameAsWritten, SourceLocation(), Result,
/*IsFramework*/ false,
/*IsExplicit*/ true, NumCreatedModules++);
// Header modules are implicitly 'export *'.
M->Exports.push_back(Module::ExportDecl(nullptr, true));
addHeader(M, H, NormalHeader);
}
return Result;
}
/// For a framework module, infer the framework against which we
/// should link.
static void inferFrameworkLink(Module *Mod, const DirectoryEntry *FrameworkDir,

View File

@ -977,7 +977,8 @@ void Sema::ActOnEndOfTranslationUnit() {
// module declaration by now.
if (getLangOpts().getCompilingModule() ==
LangOptions::CMK_ModuleInterface &&
ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit) {
(ModuleScopes.empty() ||
ModuleScopes.back().Module->Kind != Module::ModuleInterfaceUnit)) {
// FIXME: Make a better guess as to where to put the module declaration.
Diag(getSourceManager().getLocForStartOfFile(
getSourceManager().getMainFileID()),

View File

@ -16832,6 +16832,10 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
case LangOptions::CMK_ModuleMap:
Diag(ModuleLoc, diag::err_module_decl_in_module_map_module);
return nullptr;
case LangOptions::CMK_HeaderModule:
Diag(ModuleLoc, diag::err_module_decl_in_header_module);
return nullptr;
}
assert(ModuleScopes.size() == 1 && "expected to be at global module scope");
@ -16900,7 +16904,8 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
case ModuleDeclKind::Implementation:
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc(
PP.getIdentifierInfo(ModuleName), Path[0].second);
Mod = getModuleLoader().loadModule(ModuleLoc, Path, Module::AllVisible,
Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc},
Module::AllVisible,
/*IsIncludeDirective=*/false);
if (!Mod) {
Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName;
@ -16930,6 +16935,19 @@ Sema::DeclGroupPtrTy Sema::ActOnModuleDecl(SourceLocation StartLoc,
DeclResult Sema::ActOnModuleImport(SourceLocation StartLoc,
SourceLocation ImportLoc,
ModuleIdPath Path) {
// Flatten the module path for a Modules TS module name.
std::pair<IdentifierInfo *, SourceLocation> ModuleNameLoc;
if (getLangOpts().ModulesTS) {
std::string ModuleName;
for (auto &Piece : Path) {
if (!ModuleName.empty())
ModuleName += ".";
ModuleName += Piece.first->getName();
}
ModuleNameLoc = {PP.getIdentifierInfo(ModuleName), Path[0].second};
Path = ModuleIdPath(ModuleNameLoc);
}
Module *Mod =
getModuleLoader().loadModule(ImportLoc, Path, Module::AllVisible,
/*IsIncludeDirective=*/false);

View File

@ -0,0 +1,4 @@
#ifndef A_H
#define A_H
void a();
#endif

View File

@ -0,0 +1,5 @@
#ifndef B_H
#define B_H
#include "a.h"
void b();
#endif

View File

@ -0,0 +1,43 @@
// RUN: %clang_cc1 -fmodules-ts -fmodule-name=ab -x c++-header %S/Inputs/no-module-map/a.h %S/Inputs/no-module-map/b.h -emit-header-module -o %t.pcm
// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify
// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify -DA
// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify -DB
// RUN: %clang_cc1 -fmodules-ts -fmodule-file=%t.pcm %s -I%S/Inputs/no-module-map -verify -DA -DB
#ifdef B
// expected-no-diagnostics
#endif
#ifdef A
#include "a.h"
#endif
#ifdef B
#include "b.h"
#endif
#if defined(A) || defined(B)
#ifndef A_H
#error A_H should be defined
#endif
#else
#ifdef A_H
#error A_H should not be defined
#endif
// expected-error@+3 {{must be imported from}}
// expected-note@* {{previous declaration}}
#endif
void use_a() { a(); }
#if defined(B)
#ifndef B_H
#error B_H should be defined
#endif
#else
#ifdef B_H
#error B_H should not be defined
#endif
// expected-error@+3 {{must be imported from}}
// expected-note@* {{previous declaration}}
#endif
void use_b() { b(); }