When loading a module fails because it is out of date, rebuild that
authorDouglas Gregor <dgregor@apple.com>
Wed, 7 Nov 2012 17:46:15 +0000 (17:46 +0000)
committerDouglas Gregor <dgregor@apple.com>
Wed, 7 Nov 2012 17:46:15 +0000 (17:46 +0000)
module in place. <rdar://problem/10138913>

llvm-svn: 167539

clang/include/clang/Serialization/ModuleManager.h
clang/lib/Frontend/CompilerInstance.cpp
clang/lib/Serialization/ASTReader.cpp
clang/lib/Serialization/ModuleManager.cpp
clang/test/Modules/Inputs/Modified/A.h [new file with mode: 0644]
clang/test/Modules/Inputs/Modified/B.h [new file with mode: 0644]
clang/test/Modules/Inputs/Modified/module.map [new file with mode: 0644]
clang/test/Modules/modify-module.m [new file with mode: 0644]

index 2233eaf..6dcaa21 100644 (file)
@@ -105,7 +105,10 @@ public:
   std::pair<ModuleFile *, bool> 
   addModule(StringRef FileName, ModuleKind Type, ModuleFile *ImportedBy,
             unsigned Generation, std::string &ErrorStr);
-  
+
+  /// \brief Remove the given set of modules.
+  void removeModules(ModuleIterator first, ModuleIterator last);
+
   /// \brief Add an in-memory buffer the list of known buffers
   void addInMemoryBuffer(StringRef FileName, llvm::MemoryBuffer *Buffer);
   
index b858322..576ca94 100644 (file)
@@ -762,7 +762,7 @@ static void compileModule(CompilerInstance &ImportingInstance,
     // Someone else is responsible for building the module. Wait for them to
     // finish.
     Locked.waitForUnlock();
-    break;
+    return;
   }
 
   ModuleMap &ModMap 
@@ -975,13 +975,36 @@ Module *CompilerInstance::loadModule(SourceLocation ImportLoc,
     }
 
     // Try to load the module we found.
+    unsigned ARRFlags = ASTReader::ARR_None;
+    if (Module)
+      ARRFlags |= ASTReader::ARR_OutOfDate;
     switch (ModuleManager->ReadAST(ModuleFile->getName(),
                                    serialization::MK_Module,
-                                   ASTReader::ARR_None)) {
+                                   ARRFlags)) {
     case ASTReader::Success:
       break;
 
-    case ASTReader::OutOfDate:
+    case ASTReader::OutOfDate: {
+      // The module file is out-of-date. Rebuild it.
+      getFileManager().invalidateCache(ModuleFile);
+      bool Existed;
+      llvm::sys::fs::remove(ModuleFileName, Existed);
+      compileModule(*this, Module, ModuleFileName);
+
+      // Try loading the module again.
+      ModuleFile = FileMgr->getFile(ModuleFileName);
+      if (!ModuleFile ||
+          ModuleManager->ReadAST(ModuleFileName,
+                                 serialization::MK_Module,
+                                 ASTReader::ARR_None) != ASTReader::Success) {
+        KnownModules[Path[0].first] = 0;
+        return 0;
+      }
+
+      // Okay, we've rebuilt and now loaded the module.
+      break;
+    }
+
     case ASTReader::VersionMismatch:
     case ASTReader::ConfigurationMismatch:
     case ASTReader::HadErrors:
index fad0fe1..0f3e553 100644 (file)
@@ -2675,16 +2675,21 @@ ASTReader::ASTReadResult ASTReader::ReadAST(const std::string &FileName,
   // Bump the generation number.
   unsigned PreviousGeneration = CurrentGeneration++;
 
-  // Load the core of the AST files.
+  unsigned NumModules = ModuleMgr.size();
   llvm::SmallVector<ModuleFile *, 4> Loaded;
-  switch(ReadASTCore(FileName, Type, /*ImportedBy=*/0, Loaded,
-                     ClientLoadCapabilities)) {
-  case Failure: return Failure;
-  case OutOfDate: return OutOfDate;
-  case VersionMismatch: return VersionMismatch;
-  case ConfigurationMismatch: return ConfigurationMismatch;
-  case HadErrors: return HadErrors;
-  case Success: break;
+  switch(ASTReadResult ReadResult = ReadASTCore(FileName, Type,
+                                                /*ImportedBy=*/0, Loaded,
+                                                ClientLoadCapabilities)) {
+  case Failure:
+  case OutOfDate:
+  case VersionMismatch:
+  case ConfigurationMismatch:
+  case HadErrors:
+    ModuleMgr.removeModules(ModuleMgr.begin() + NumModules, ModuleMgr.end());
+    return ReadResult;
+
+  case Success:
+    break;
   }
 
   // Here comes stuff that we only do once the entire chain is loaded.
index c46e9f0..efe4421 100644 (file)
@@ -88,6 +88,45 @@ ModuleManager::addModule(StringRef FileName, ModuleKind Type,
   return std::make_pair(ModuleEntry, NewModule);
 }
 
+namespace {
+  /// \brief Predicate that checks whether a module file occurs within
+  /// the given set.
+  class IsInModuleFileSet : public std::unary_function<ModuleFile *, bool> {
+    llvm::SmallPtrSet<ModuleFile *, 4> &Removed;
+
+  public:
+    IsInModuleFileSet(llvm::SmallPtrSet<ModuleFile *, 4> &Removed)
+    : Removed(Removed) { }
+
+    bool operator()(ModuleFile *MF) const {
+      return Removed.count(MF);
+    }
+  };
+}
+
+void ModuleManager::removeModules(ModuleIterator first, ModuleIterator last) {
+  if (first == last)
+    return;
+
+  // Collect the set of module file pointers that we'll be removing.
+  llvm::SmallPtrSet<ModuleFile *, 4> victimSet(first, last);
+
+  // Remove any references to the now-destroyed modules.
+  IsInModuleFileSet checkInSet(victimSet);
+  for (unsigned i = 0, n = Chain.size(); i != n; ++i) {
+    Chain[i]->ImportedBy.remove_if(checkInSet);
+  }
+
+  // Delete the modules and erase them from the various structures.
+  for (ModuleIterator victim = first; victim != last; ++victim) {
+    Modules.erase((*victim)->File);
+    delete *victim;
+  }
+
+  // Remove the modules from the chain.
+  Chain.erase(first, last);
+}
+
 void ModuleManager::addInMemoryBuffer(StringRef FileName, 
                                       llvm::MemoryBuffer *Buffer) {
   
diff --git a/clang/test/Modules/Inputs/Modified/A.h b/clang/test/Modules/Inputs/Modified/A.h
new file mode 100644 (file)
index 0000000..ff833c7
--- /dev/null
@@ -0,0 +1 @@
+int getA();
diff --git a/clang/test/Modules/Inputs/Modified/B.h b/clang/test/Modules/Inputs/Modified/B.h
new file mode 100644 (file)
index 0000000..d1c8bb5
--- /dev/null
@@ -0,0 +1,2 @@
+#include "A.h"
+int getB();
diff --git a/clang/test/Modules/Inputs/Modified/module.map b/clang/test/Modules/Inputs/Modified/module.map
new file mode 100644 (file)
index 0000000..d9aed01
--- /dev/null
@@ -0,0 +1,2 @@
+module A { header "A.h" }
+module B { header "B.h" }
diff --git a/clang/test/Modules/modify-module.m b/clang/test/Modules/modify-module.m
new file mode 100644 (file)
index 0000000..b630ac1
--- /dev/null
@@ -0,0 +1,23 @@
+// Test that if we modify one of the input files used to form a
+// header, that module and dependent modules get rebuilt.
+
+// RUN: rm -rf %t
+// RUN: mkdir -p %t/include
+// RUN: cp %S/Inputs/Modified/A.h %t/include
+// RUN: cp %S/Inputs/Modified/B.h %t/include
+// RUN: cp %S/Inputs/Modified/module.map %t/include
+// RUN: %clang_cc1 -fmodule-cache-path %t/cache -fmodules -I %t/include %s -verify
+// expected-no-diagnostics
+// RUN: touch %t/include/B.h
+// RUN: %clang_cc1 -fmodule-cache-path %t/cache -fmodules -I %t/include %s -verify
+// RUN: echo 'int getA(); int getA2();' > %t/include/A.h
+// RUN: %clang_cc1 -fmodule-cache-path %t/cache -fmodules -I %t/include %s -verify
+
+@__experimental_modules_import B;
+
+int getValue() { return getA() + getB(); }
+
+
+
+
+