Index: include/clang/Basic/DiagnosticCommonKinds.td =================================================================== --- include/clang/Basic/DiagnosticCommonKinds.td +++ include/clang/Basic/DiagnosticCommonKinds.td @@ -96,6 +96,8 @@ DefaultFatal; def err_module_prebuilt : Error< "error in loading module '%0' from prebuilt module path">, DefaultFatal; +def err_module_invalidated_by_child : Error< + "module '%0' is invalidated by a child thread">, DefaultFatal; def note_pragma_entered_here : Note<"#pragma entered here">; def note_decl_hiding_tag_type : Note< "%1 %0 is hidden by a non-type declaration of %0 here">; Index: include/clang/Serialization/ModuleManager.h =================================================================== --- include/clang/Serialization/ModuleManager.h +++ include/clang/Serialization/ModuleManager.h @@ -46,6 +46,9 @@ /// \brief All loaded modules, indexed by name. llvm::DenseMap Modules; + /// \brief Modules that are invalidated by the child threads. + llvm::DenseMap InvalidatedModules; + /// \brief FileManager that handles translating between filenames and /// FileEntry *. FileManager &FileMgr; @@ -280,6 +283,16 @@ time_t ExpectedModTime, const FileEntry *&File); + /// In the context of a parent thread spawn a child thread to build a module, + /// the child thread's module manager checks to see if any successfully loaded + /// module in the parent thread is invalidated here. It returns the name of + /// the module if such module exists. + std::string checkForInvalidateEntries(const ModuleMap *modMapInParent, + ModuleManager *parentManager); + + /// Pull invalidated entries from the child manager. + void mergeInvalidatedEntries(ModuleManager *childManager); + /// \brief View the graphviz representation of the module graph. void viewGraph(); }; Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -1060,6 +1060,21 @@ diag::remark_module_build_done) << Module->Name; + if (ImportingInstance.getModuleManager() && Instance.getModuleManager()) { + serialization::ModuleManager &ParentManager = + ImportingInstance.getModuleManager()->getModuleManager(); + serialization::ModuleManager &ChildManager = + Instance.getModuleManager()->getModuleManager(); + + // Copy the invalidated modules to the parent thread. + ParentManager.mergeInvalidatedEntries(&ChildManager); + std::string Invalid = ChildManager.checkForInvalidateEntries( + &ModMap, &ParentManager); + if (!Invalid.empty()) + Instance.getDiagnostics().Report(ImportLoc, + diag::err_module_invalidated_by_child) << Invalid; + } + // Delete the temporary module map file. // FIXME: Even though we're executing under crash protection, it would still // be nice to do this with RemoveFileOnSignal when we can. However, that Index: lib/Serialization/ModuleManager.cpp =================================================================== --- lib/Serialization/ModuleManager.cpp +++ lib/Serialization/ModuleManager.cpp @@ -182,6 +182,29 @@ return NewlyLoaded; } +// Check to see if the parent thread is holding on to an entry that the child +// thread invalidated. +std::string ModuleManager::checkForInvalidateEntries( + const ModuleMap *modMapInParent, ModuleManager *parentManager) { + for (auto E : InvalidatedModules) { + if (parentManager->lookup(E.first)) + return E.second; + + if (modMapInParent) { + if (Module *mod = modMapInParent->findModule(E.second)) { + if (mod->getASTFile() == E.first) + return E.second; + } + } + } + return ""; +} + +void ModuleManager::mergeInvalidatedEntries(ModuleManager *child) { + for (auto E : child->InvalidatedModules) + InvalidatedModules[E.first] = E.second; +} + void ModuleManager::removeModules( ModuleIterator first, ModuleIterator last, llvm::SmallPtrSetImpl &LoadedSuccessfully, @@ -228,8 +251,10 @@ // Files that didn't make it through ReadASTCore successfully will be // rebuilt (or there was an error). Invalidate them so that we can load the // new files that will be renamed over the old ones. - if (LoadedSuccessfully.count(*victim) == 0) + if (LoadedSuccessfully.count(*victim) == 0) { + InvalidatedModules[(*victim)->File] = (*victim)->ModuleName; FileMgr.invalidateCache((*victim)->File); + } delete *victim; } Index: test/Modules/Inputs/system-out-of-date/X.h =================================================================== --- test/Modules/Inputs/system-out-of-date/X.h +++ test/Modules/Inputs/system-out-of-date/X.h @@ -0,0 +1 @@ +#import Index: test/Modules/Inputs/system-out-of-date/Y.h =================================================================== --- test/Modules/Inputs/system-out-of-date/Y.h +++ test/Modules/Inputs/system-out-of-date/Y.h @@ -0,0 +1 @@ +//empty Index: test/Modules/Inputs/system-out-of-date/Z.h =================================================================== --- test/Modules/Inputs/system-out-of-date/Z.h +++ test/Modules/Inputs/system-out-of-date/Z.h @@ -0,0 +1 @@ +#import Index: test/Modules/Inputs/system-out-of-date/module.map =================================================================== --- test/Modules/Inputs/system-out-of-date/module.map +++ test/Modules/Inputs/system-out-of-date/module.map @@ -0,0 +1,12 @@ +module X [system] { + header "X.h" // imports Y + export * +} +module Y { + header "Y.h" + export * +} +module Z { + header "Z.h" // imports Y + export * +} Index: test/Modules/system-out-of-date-test.m =================================================================== --- test/Modules/system-out-of-date-test.m +++ test/Modules/system-out-of-date-test.m @@ -0,0 +1,14 @@ +// RUN: rm -rf %t +// RUN: echo '@import X;' | \ +// RUN: %clang_cc1 -fmodules -fimplicit-module-maps \ +// RUN: -fmodules-cache-path=%t -I %S/Inputs/system-out-of-date \ +// RUN: -fsyntax-only -x objective-c - + +// We have an version built with different diagnostic options. +// RUN: not %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -I %S/Inputs/system-out-of-date -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module %s -fsyntax-only 2>&1 | FileCheck %s + +@import X; + +#import +// CHECK: fatal error: module 'Y' is invalidated by its child +// CHECK: fatal error: could not build module 'Z'