[modules] PR22534: Load files specified by -fmodule-file= eagerly. In particular, this avoids the need to re-parse module map files when using such a module.

llvm-svn: 244416
This commit is contained in:
Richard Smith 2015-08-09 08:48:41 +00:00
parent 38c1e6d355
commit 0f99d6a441
12 changed files with 191 additions and 124 deletions

View File

@ -194,9 +194,8 @@ def err_unable_to_make_temp : Error<
"unable to make temporary file: %0">;
// Modules
def err_module_file_conflict : Error<"module '%0' found in both '%1' and '%2'">;
def err_module_format_unhandled : Error<
"no handler registered for module format '%0'">;
"no handler registered for module format '%0'">, DefaultFatal;
// TransformActions
// TODO: Use a custom category name to distinguish rewriter errors.

View File

@ -194,16 +194,6 @@ def remark_module_build_done : Remark<"finished building module '%0'">,
def err_conflicting_module_names : Error<
"conflicting module names specified: '-fmodule-name=%0' and "
"'-fmodule-implementation-of %1'">;
def err_conflicting_module_files : Error<
"module '%0' is defined in both '%1' and '%2'">;
def err_module_file_not_found : Error<
"module file '%0' not found">, DefaultFatal;
def err_module_file_invalid : Error<
"file '%0' is not a valid precompiled module file">, DefaultFatal;
def note_module_file_imported_by : Note<
"imported by %select{|module '%2' in }1'%0'">;
def err_module_file_not_module : Error<
"AST file '%0' was not built as a module">, DefaultFatal;
def err_missing_vfs_overlay_file : Error<
"virtual filesystem overlay file '%0' not found">, DefaultFatal;

View File

@ -53,6 +53,20 @@ def err_pch_different_branch : Error<
def err_pch_with_compiler_errors : Error<
"PCH file contains compiler errors">;
def err_module_file_conflict : Error<
"module '%0' is defined in both '%1' and '%2'">, DefaultFatal;
def err_module_file_not_found : Error<
"%select{PCH|module|AST}0 file '%1' not found%select{|: %3}2">, DefaultFatal;
def err_module_file_out_of_date : Error<
"%select{PCH|module|AST}0 file '%1' is out of date and "
"needs to be rebuilt%select{|: %3}2">, DefaultFatal;
def err_module_file_invalid : Error<
"file '%1' is not a valid precompiled %select{PCH|module|AST}0 file">, DefaultFatal;
def note_module_file_imported_by : Note<
"imported by %select{|module '%2' in }1'%0'">;
def err_module_file_not_module : Error<
"AST file '%0' was not built as a module">, DefaultFatal;
def err_imported_module_not_found : Error<
"module '%0' in AST file '%1' (imported by AST file '%2') "
"is not defined in any loaded module map file; "
@ -82,9 +96,6 @@ def err_pch_pp_detailed_record : Error<
"'-detailed-preprocessing-record' but %select{precompiled header was not "
"built with it|it is not present on the command line}0">;
def err_not_a_pch_file : Error<
"'%0' does not appear to be a precompiled header file">, DefaultFatal;
def err_module_odr_violation_missing_decl : Error<
"%q0 from module '%1' is not present in definition of %q2"
"%select{ in module '%4'| provided earlier}3">, NoSFINAE;

View File

@ -58,7 +58,7 @@ public:
const std::string &OutputFileName,
raw_pwrite_stream *OS,
std::shared_ptr<PCHBuffer> Buffer)
: Diags(diags), HeaderSearchOpts(HSO), PreprocessorOpts(PPO),
: Diags(diags), Ctx(nullptr), HeaderSearchOpts(HSO), PreprocessorOpts(PPO),
TargetOpts(TO), LangOpts(LO), OS(OS), Buffer(Buffer) {
// The debug info output isn't affected by CodeModel and
// ThreadModel, but the backend expects them to be nonempty.
@ -71,6 +71,11 @@ public:
virtual ~PCHContainerGenerator() {}
void Initialize(ASTContext &Context) override {
if (Ctx) {
assert(Ctx == &Context);
return;
}
Ctx = &Context;
VMContext.reset(new llvm::LLVMContext());
M.reset(new llvm::Module(MainFileName, *VMContext));

View File

@ -1261,8 +1261,10 @@ void CompilerInstance::createModuleManager() {
getASTContext().setExternalSource(ModuleManager);
if (hasSema())
ModuleManager->InitializeSema(getSema());
if (hasASTConsumer())
if (hasASTConsumer()) {
getASTConsumer().Initialize(getASTContext());
ModuleManager->StartTranslationUnit(&getASTConsumer());
}
if (TheDependencyFileGenerator)
TheDependencyFileGenerator->AttachToASTReader(*ModuleManager);
@ -1284,86 +1286,44 @@ bool CompilerInstance::loadModuleFile(StringRef FileName) {
// the files we were handed.
struct ReadModuleNames : ASTReaderListener {
CompilerInstance &CI;
std::vector<StringRef> ModuleFileStack;
std::vector<StringRef> ModuleNameStack;
bool Failed;
bool TopFileIsModule;
llvm::SmallVector<IdentifierInfo*, 8> LoadedModules;
ReadModuleNames(CompilerInstance &CI)
: CI(CI), Failed(false), TopFileIsModule(false) {}
bool needsImportVisitation() const override { return true; }
void visitImport(StringRef FileName) override {
if (!CI.ExplicitlyLoadedModuleFiles.insert(FileName).second) {
if (ModuleFileStack.size() == 0)
TopFileIsModule = true;
return;
}
ModuleFileStack.push_back(FileName);
ModuleNameStack.push_back(StringRef());
if (ASTReader::readASTFileControlBlock(FileName, CI.getFileManager(),
CI.getPCHContainerReader(),
*this)) {
CI.getDiagnostics().Report(
SourceLocation(), CI.getFileManager().getBufferForFile(FileName)
? diag::err_module_file_invalid
: diag::err_module_file_not_found)
<< FileName;
for (int I = ModuleFileStack.size() - 2; I >= 0; --I)
CI.getDiagnostics().Report(SourceLocation(),
diag::note_module_file_imported_by)
<< ModuleFileStack[I]
<< !ModuleNameStack[I].empty() << ModuleNameStack[I];
Failed = true;
}
ModuleNameStack.pop_back();
ModuleFileStack.pop_back();
}
ReadModuleNames(CompilerInstance &CI) : CI(CI) {}
void ReadModuleName(StringRef ModuleName) override {
if (ModuleFileStack.size() == 1)
TopFileIsModule = true;
ModuleNameStack.back() = ModuleName;
auto &ModuleFile = CI.ModuleFileOverrides[ModuleName];
if (!ModuleFile.empty() &&
CI.getFileManager().getFile(ModuleFile) !=
CI.getFileManager().getFile(ModuleFileStack.back()))
CI.getDiagnostics().Report(SourceLocation(),
diag::err_conflicting_module_files)
<< ModuleName << ModuleFile << ModuleFileStack.back();
ModuleFile = ModuleFileStack.back();
LoadedModules.push_back(
CI.getPreprocessor().getIdentifierInfo(ModuleName));
}
} RMN(*this);
void registerAll() {
for (auto *II : LoadedModules) {
CI.KnownModules[II] = CI.getPreprocessor()
.getHeaderSearchInfo()
.getModuleMap()
.findModule(II->getName());
}
LoadedModules.clear();
}
};
// If we don't already have an ASTReader, create one now.
if (!ModuleManager)
createModuleManager();
// Tell the module manager about this module file.
if (getModuleManager()->getModuleManager().addKnownModuleFile(FileName)) {
getDiagnostics().Report(SourceLocation(), diag::err_module_file_not_found)
<< FileName;
return false;
}
auto Listener = llvm::make_unique<ReadModuleNames>(*this);
auto &ListenerRef = *Listener;
ASTReader::ListenerScope ReadModuleNamesListener(*ModuleManager,
std::move(Listener));
// Build our mapping of module names to module files from this file
// and its imports.
RMN.visitImport(FileName);
if (RMN.Failed)
// Try to load the module file.
if (ModuleManager->ReadAST(FileName, serialization::MK_ExplicitModule,
SourceLocation(), ASTReader::ARR_None)
!= ASTReader::Success)
return false;
// If we never found a module name for the top file, then it's not a module,
// it's a PCH or preamble or something.
if (!RMN.TopFileIsModule) {
getDiagnostics().Report(SourceLocation(), diag::err_module_file_not_module)
<< FileName;
return false;
}
// We successfully loaded the module file; remember the set of provided
// modules so that we don't try to load implicit modules for them.
ListenerRef.registerAll();
return true;
}
@ -1412,6 +1372,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
return ModuleLoadResult();
}
// FIXME: Rmove ModuleFileOverrides
auto Override = ModuleFileOverrides.find(ModuleName);
bool Explicit = Override != ModuleFileOverrides.end();
@ -1507,7 +1468,7 @@ CompilerInstance::loadModule(SourceLocation ImportLoc,
case ASTReader::ConfigurationMismatch:
case ASTReader::HadErrors:
ModuleLoader::HadFatalFailure = true;
// FIXME: The ASTReader will already have complained, but can we showhorn
// FIXME: The ASTReader will already have complained, but can we shoehorn
// that diagnostic information into a more useful form?
KnownModules[Path[0].first] = nullptr;
return ModuleLoadResult();

View File

@ -2029,6 +2029,21 @@ void ASTReader::ResolveImportedPath(std::string &Filename, StringRef Prefix) {
Filename.assign(Buffer.begin(), Buffer.end());
}
static bool isDiagnosedResult(ASTReader::ASTReadResult ARR, unsigned Caps) {
switch (ARR) {
case ASTReader::Failure: return true;
case ASTReader::Missing: return !(Caps & ASTReader::ARR_Missing);
case ASTReader::OutOfDate: return !(Caps & ASTReader::ARR_OutOfDate);
case ASTReader::VersionMismatch: return !(Caps & ASTReader::ARR_VersionMismatch);
case ASTReader::ConfigurationMismatch:
return !(Caps & ASTReader::ARR_ConfigurationMismatch);
case ASTReader::HadErrors: return true;
case ASTReader::Success: return false;
}
llvm_unreachable("unknown ASTReadResult");
}
ASTReader::ASTReadResult
ASTReader::ReadControlBlock(ModuleFile &F,
SmallVectorImpl<ImportedModule> &Loaded,
@ -2064,8 +2079,9 @@ ASTReader::ReadControlBlock(ModuleFile &F,
PP.getHeaderSearchInfo().getHeaderSearchOpts();
// All user input files reside at the index range [0, NumUserInputs), and
// system input files reside at [NumUserInputs, NumInputs).
if (!DisableValidation) {
// system input files reside at [NumUserInputs, NumInputs). For explicitly
// loaded module files, ignore missing inputs.
if (!DisableValidation && F.Kind != MK_ExplicitModule) {
bool Complain = (ClientLoadCapabilities & ARR_OutOfDate) == 0;
// If we are reading a module, we will create a verification timestamp,
@ -2181,10 +2197,23 @@ ASTReader::ReadControlBlock(ModuleFile &F,
ASTFileSignature StoredSignature = Record[Idx++];
auto ImportedFile = ReadPath(F, Record, Idx);
// If our client can't cope with us being out of date, we can't cope with
// our dependency being missing.
unsigned Capabilities = ClientLoadCapabilities;
if ((ClientLoadCapabilities & ARR_OutOfDate) == 0)
Capabilities &= ~ARR_Missing;
// Load the AST file.
switch(ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F, Loaded,
StoredSize, StoredModTime, StoredSignature,
ClientLoadCapabilities)) {
auto Result = ReadASTCore(ImportedFile, ImportedKind, ImportLoc, &F,
Loaded, StoredSize, StoredModTime,
StoredSignature, Capabilities);
// If we diagnosed a problem, produce a backtrace.
if (isDiagnosedResult(Result, Capabilities))
Diag(diag::note_module_file_imported_by)
<< F.FileName << !F.ModuleName.empty() << F.ModuleName;
switch (Result) {
case Failure: return Failure;
// If we have to ignore the dependency, we'll have to ignore this too.
case Missing:
@ -3152,11 +3181,18 @@ ASTReader::ReadModuleMapFileBlock(RecordData &Record, ModuleFile &F,
const FileEntry *ModMap = M ? Map.getModuleMapFileForUniquing(M) : nullptr;
if (!ModMap) {
assert(ImportedBy && "top-level import should be verified");
if ((ClientLoadCapabilities & ARR_Missing) == 0)
Diag(diag::err_imported_module_not_found) << F.ModuleName << F.FileName
<< ImportedBy->FileName
<< F.ModuleMapPath;
return Missing;
if ((ClientLoadCapabilities & ARR_OutOfDate) == 0) {
if (auto *ASTFE = M ? M->getASTFile() : nullptr)
// This module was defined by an imported (explicit) module.
Diag(diag::err_module_file_conflict) << F.ModuleName << F.FileName
<< ASTFE->getName();
else
// This module was built with a different module map.
Diag(diag::err_imported_module_not_found)
<< F.ModuleName << F.FileName << ImportedBy->FileName
<< F.ModuleMapPath;
}
return OutOfDate;
}
assert(M->Name == F.ModuleName && "found module with different name");
@ -3557,6 +3593,20 @@ static bool startsWithASTFileMagic(BitstreamCursor &Stream) {
Stream.Read(8) == 'H';
}
static unsigned moduleKindForDiagnostic(ModuleKind Kind) {
switch (Kind) {
case MK_PCH:
return 0; // PCH
case MK_ImplicitModule:
case MK_ExplicitModule:
return 1; // module
case MK_MainFile:
case MK_Preamble:
return 2; // main source file
}
llvm_unreachable("unknown module kind");
}
ASTReader::ASTReadResult
ASTReader::ReadASTCore(StringRef FileName,
ModuleKind Type,
@ -3589,11 +3639,9 @@ ASTReader::ReadASTCore(StringRef FileName,
return Missing;
// Otherwise, return an error.
{
std::string Msg = "Unable to load module \"" + FileName.str() + "\": "
+ ErrorStr;
Error(Msg);
}
Diag(diag::err_module_file_not_found) << moduleKindForDiagnostic(Type)
<< FileName << ErrorStr.empty()
<< ErrorStr;
return Failure;
case ModuleManager::OutOfDate:
@ -3603,11 +3651,9 @@ ASTReader::ReadASTCore(StringRef FileName,
return OutOfDate;
// Otherwise, return an error.
{
std::string Msg = "Unable to load module \"" + FileName.str() + "\": "
+ ErrorStr;
Error(Msg);
}
Diag(diag::err_module_file_out_of_date) << moduleKindForDiagnostic(Type)
<< FileName << ErrorStr.empty()
<< ErrorStr;
return Failure;
}
@ -3628,7 +3674,8 @@ ASTReader::ReadASTCore(StringRef FileName,
// Sniff for the signature.
if (!startsWithASTFileMagic(Stream)) {
Diag(diag::err_not_a_pch_file) << FileName;
Diag(diag::err_module_file_invalid) << moduleKindForDiagnostic(Type)
<< FileName;
return Failure;
}
@ -3661,6 +3708,18 @@ ASTReader::ReadASTCore(StringRef FileName,
HaveReadControlBlock = true;
switch (ReadControlBlock(F, Loaded, ImportedBy, ClientLoadCapabilities)) {
case Success:
// Check that we didn't try to load a non-module AST file as a module.
//
// FIXME: Should we also perform the converse check? Loading a module as
// a PCH file sort of works, but it's a bit wonky.
if ((Type == MK_ImplicitModule || Type == MK_ExplicitModule) &&
F.ModuleName.empty()) {
auto Result = (Type == MK_ImplicitModule) ? OutOfDate : Failure;
if (Result != OutOfDate ||
(ClientLoadCapabilities & ARR_OutOfDate) == 0)
Diag(diag::err_module_file_not_module) << FileName;
return Result;
}
break;
case Failure: return Failure;
@ -3690,8 +3749,6 @@ ASTReader::ReadASTCore(StringRef FileName,
break;
}
}
return Success;
}
void ASTReader::InitializeContext() {

View File

@ -9,6 +9,6 @@
// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fdisable-module-hash -fmodules-cache-path=%t/modules-cache -I %S/Inputs/Headers -verify-pch %t/use_LibA.pch 2>&1 | FileCheck -check-prefix=VERIFY %s
// RUN: not c-index-test -test-load-source all -x c -fmodules -fimplicit-module-maps -Xclang -fdisable-module-hash -fmodules-cache-path=%t/modules-cache -I %S/Inputs/Headers -include-pch %t/use_LibA.pch %s 2>&1 | FileCheck -check-prefix=INDEX %s
// VERIFY: fatal error: malformed or corrupted AST file: 'Unable to load module
// VERIFY: fatal error: module file '{{.*}}LibA.pcm' not found
// INDEX: {{^}}Failure: AST deserialization error occurred{{$}}

View File

@ -1,10 +1,10 @@
// RUN: cd %S
// RUN: rm -rf %t
//
// RUN: %clang_cc1 -I. -x c++ -fmodule-name=test -fmodules -emit-module -fno-validate-pch -fmodules-strict-decluse %s -dependency-file - -MT implicit.pcm -o %t/implicit.pcm -fmodules-cache-path=%t -fmodule-map-file-home-is-cwd | FileCheck %s
// RUN: %clang_cc1 -I. -x c++ -fmodule-name=test -fmodules -emit-module -fno-validate-pch -fmodules-strict-decluse %s -dependency-file - -MT implicit.pcm -o %t/implicit.pcm -fmodules-cache-path=%t -fmodule-map-file-home-is-cwd | FileCheck %s --check-prefix=IMPLICIT
//
// RUN: %clang_cc1 -I. -x c++ -fmodule-name=test-base -fmodules -emit-module -fno-validate-pch -fmodules-strict-decluse Inputs/dependency-gen-base.modulemap -o %t/base.pcm -fmodule-map-file-home-is-cwd
// RUN: %clang_cc1 -I. -x c++ -fmodule-name=test -fmodules -emit-module -fno-validate-pch -fmodules-strict-decluse -fmodule-file=%t/base.pcm %s -dependency-file - -MT explicit.pcm -o %t/explicit.pcm -fmodules-cache-path=%t -fmodule-map-file-home-is-cwd | FileCheck %s
// RUN: %clang_cc1 -I. -x c++ -fmodule-name=test -fmodules -emit-module -fno-validate-pch -fmodules-strict-decluse -fmodule-file=%t/base.pcm %s -dependency-file - -MT explicit.pcm -o %t/explicit.pcm -fmodules-cache-path=%t -fmodule-map-file-home-is-cwd | FileCheck %s --check-prefix=EXPLICIT
module "test" {
export *
header "Inputs/dependency-gen.h"
@ -14,10 +14,30 @@ module "test" {
extern module "test-base2" "Inputs/dependency-gen-base2.modulemap"
extern module "test-base" "Inputs/dependency-gen-base.modulemap"
// CHECK-DAG: {{[/\\]}}dependency-gen.modulemap
// CHECK-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-base.modulemap
// CHECK-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-base2.modulemap
// For implicit use of a module via the module cache, the input files
// referenced by the .pcm are also dependencies of this build.
//
// IMPLICIT-DAG: {{[/\\]}}dependency-gen.modulemap
// IMPLICIT-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-base.modulemap
// IMPLICIT-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-base2.modulemap
// IMPLICIT-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen.h
// IMPLICIT-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-included.h
// IMPLICIT-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-included2.h
// CHECK-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen.h
// CHECK-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-included.h
// CHECK-DAG: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen-included2.h
// For an explicit use of a module via -fmodule-file=, the other module maps
// and included headers are not dependencies of this target (they are instead
// dependencies of the explicitly-specified .pcm input).
//
// FIXME: We should avoid loading the other referenced module maps (we already
// have a parsed copy of their contents from the .pcm file) and thus not list
// them here.
//
// FIXME: We should list a dependency on the explicitly specified .pcm files
// (whether or not -module-file-deps is specified on the command line).
//
// EXPLICIT-FIXME-NOT: dependency-gen-
// EXPLICIT-FIXME: {{.*}}/base.pcm
//
// EXPLICIT: {{^}}explicit.pcm:
// EXPLICIT: {{.*}}/dependency-gen.modulemap
// EXPLICIT: {{ |\.[/\\]}}Inputs{{[/\\]}}dependency-gen.h

View File

@ -0,0 +1,21 @@
// RUN: rm -rf %t
// RUN: mkdir %t
// RUN: echo 'extern int a;' > %t/a.h
// RUN: echo 'extern int b;' > %t/b.h
// RUN: echo 'module a { header "a.h" header "b.h" }' > %t/modulemap
// We lazily check that the files referenced by an explicitly-specified .pcm
// file exist. Test this by removing files and ensuring that the compilation
// still succeeds.
//
// RUN: %clang_cc1 -fmodules -I %t -emit-module -fmodule-name=a -x c++ %t/modulemap -o %t/a.pcm
// RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s
// RUN: rm %t/modulemap
// RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s
// RUN: rm %t/b.h
// RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s
// RUN: rm %t/a.h
// RUN: %clang_cc1 -fmodules -I %t -fmodule-file=%t/a.pcm %s -verify
#include "a.h" // expected-error {{file not found}}
int x = b;

View File

@ -179,6 +179,7 @@
// CHECK-NO-FILE-INDIRECT: error: module file '{{.*}}a.pcm' not found
// CHECK-NO-FILE-INDIRECT-NEXT: note: imported by module 'b' in '{{.*}}b.pcm'
// CHECK-NO-FILE-INDIRECT-NEXT: note: imported by module 'c' in '{{.*}}c.pcm'
// CHECK-NO-FILE-INDIRECT-NOT: note:
// -------------------------------
// Check that we don't get upset if B's timestamp is newer than C's.
@ -198,4 +199,6 @@
// RUN: -fmodule-file=%t/c.pcm \
// RUN: %s -DHAVE_A -DHAVE_B -DHAVE_C 2>&1 | FileCheck --check-prefix=CHECK-MISMATCHED-B %s
//
// CHECK-MISMATCHED-B: fatal error: malformed or corrupted AST file: {{.*}}b.pcm": module file out of date
// CHECK-MISMATCHED-B: fatal error: module file '{{.*}}b.pcm' is out of date and needs to be rebuilt
// CHECK-MISMATCHED-B-NEXT: note: imported by module 'c'
// CHECK-MISMATCHED-B-NOT: note:

View File

@ -8,13 +8,13 @@
#ifdef IMPLICIT
// expected-error@+1{{does not appear to be}}
// expected-error@+1{{Module.pcm' is not a valid precompiled module file}}
#import <Module/Module.h>
#pragma clang __debug crash;
#else
// expected-error@+1{{does not appear to be}}
// expected-error@+1{{Module.pcm' is not a valid precompiled module file}}
@import Module;
#pragma clang __debug crash;

View File

@ -25,6 +25,6 @@
// CHECK-BUILD: Inputs/relative-dep-gen-1.h
// CHECK-BUILD: Inputs/relative-dep-gen-2.h
// CHECK-USE: use.o:
// CHECK-USE: Inputs/relative-dep-gen{{(-cwd)?}}.modulemap
// CHECK-USE: relative-dep-gen.cpp
// CHECK-USE: Inputs/relative-dep-gen-1.h
// CHECK-USE-DAG: Inputs/relative-dep-gen{{(-cwd)?}}.modulemap
// CHECK-USE-DAG: relative-dep-gen.cpp
// CHECK-USE-DAG: Inputs/relative-dep-gen-1.h