[modules] Don't pass interesting decls to the consumer for a module file that's
passed on the command line but never actually used. We consider a (top-level)
module to be used if any part of it is imported, either by the current
translation unit, or by any part of a top-level module that is itself used.

(Put another way, a module is used if an implicit modules build would have
loaded its .pcm file.)

llvm-svn: 275481
diff --git a/clang/lib/Serialization/ASTReader.cpp b/clang/lib/Serialization/ASTReader.cpp
index b35bd7b..d4bcdac 100644
--- a/clang/lib/Serialization/ASTReader.cpp
+++ b/clang/lib/Serialization/ASTReader.cpp
@@ -2691,6 +2691,8 @@
     case EAGERLY_DESERIALIZED_DECLS:
       // FIXME: Skip reading this record if our ASTConsumer doesn't care
       // about "interesting" decls (for instance, if we're building a module).
+      // FIXME: Store this somewhere per-module and defer until
+      // markModuleReferenced is called.
       for (unsigned I = 0, N = Record.size(); I != N; ++I)
         EagerlyDeserializedDecls.push_back(getGlobalDeclID(F, Record[I]));
       break;
@@ -3361,6 +3363,9 @@
 void ASTReader::makeModuleVisible(Module *Mod,
                                   Module::NameVisibilityKind NameVisibility,
                                   SourceLocation ImportLoc) {
+  // If we import anything from the module in any way, then it is used.
+  markModuleUsed(Mod);
+
   llvm::SmallPtrSet<Module *, 4> Visited;
   SmallVector<Module *, 4> Stack;
   Stack.push_back(Mod);
@@ -6727,10 +6732,95 @@
     Decl *D = InterestingDecls.front();
     InterestingDecls.pop_front();
 
+    // If we have found an interesting ImportDecl, then its imported module
+    // is considered used.
+    if (auto *ID = dyn_cast<ImportDecl>(D))
+      markModuleUsed(ID->getImportedModule());
+
     PassInterestingDeclToConsumer(D);
   }
 }
 
+void ASTReader::markModuleUsed(Module *M) {
+  M = M->getTopLevelModule();
+  // Mark that interesting decls in this module should now be passed to the
+  // consumer, and pass any pending decls.
+  auto MInterestingDecls =
+      UnimportedModuleInterestingDecls.insert(std::make_pair(M, nullptr)).first;
+  if (auto *Decls = MInterestingDecls->second) {
+    MInterestingDecls->second = nullptr;
+    for (auto *D : *Decls) {
+      Module *Owner = D->getImportedOwningModule();
+      if (Owner)
+        Owner = Owner->getTopLevelModule();
+      if (Owner != M) {
+        // Mark that this decl has been handed to the consumer in its original
+        // module, and stop if it's already been removed from there.
+        auto OwnerIt = UnimportedModuleInterestingDecls.find(Owner);
+        if (OwnerIt == UnimportedModuleInterestingDecls.end() ||
+            !OwnerIt->second)
+          continue;
+        auto NewEnd =
+            std::remove(OwnerIt->second->begin(), OwnerIt->second->end(), D);
+        if (NewEnd == OwnerIt->second->end())
+          continue;
+        OwnerIt->second->erase(NewEnd, OwnerIt->second->end());
+      }
+      InterestingDecls.push_back(D);
+    }
+  }
+}
+
+void ASTReader::addInterestingDecl(Decl *D,
+                                   llvm::Optional<Module *> OwnerOverride) {
+  Module *Owner = D->getImportedOwningModule();
+  if (Owner)
+    Owner = Owner->getTopLevelModule();
+  Module *ExportedBy = OwnerOverride ? *OwnerOverride : Owner;
+  if (ExportedBy)
+    ExportedBy = ExportedBy->getTopLevelModule();
+
+  auto It = ExportedBy ? UnimportedModuleInterestingDecls.find(ExportedBy)
+                       : UnimportedModuleInterestingDecls.end();
+  if (It == UnimportedModuleInterestingDecls.end())
+    It = UnimportedModuleInterestingDecls.insert(
+             std::make_pair(ExportedBy, new (Context) ModuleInterestingDecls))
+         .first;
+  ModuleInterestingDecls *Interesting = It->second;
+
+  // If this declaration's module has been imported, hand it to the consumer.
+  if (!ExportedBy || !Interesting) {
+    if (Owner != ExportedBy) {
+      // Mark that this decl has been handed to the consumer in its original
+      // module, and stop if it's already been removed from there.
+      auto OwnerIt = UnimportedModuleInterestingDecls.find(Owner);
+      if (OwnerIt == UnimportedModuleInterestingDecls.end() || !OwnerIt->second)
+        return;
+      auto NewEnd =
+          std::remove(OwnerIt->second->begin(), OwnerIt->second->end(), D);
+      if (NewEnd == OwnerIt->second->end())
+        return;
+      OwnerIt->second->erase(NewEnd, OwnerIt->second->end());
+    }
+    InterestingDecls.push_back(D);
+    return;
+  }
+  assert(Owner && "re-export of unowned decl");
+
+  // If this is a re-export of another module's decl, check whether the decl
+  // has already been handed to the consumer.
+  if (Owner != ExportedBy) {
+    auto OwnerIt = UnimportedModuleInterestingDecls.find(Owner);
+    if (OwnerIt != UnimportedModuleInterestingDecls.end() &&
+        (!OwnerIt->second ||
+         std::find(OwnerIt->second->begin(), OwnerIt->second->end(), D) ==
+             OwnerIt->second->end()))
+      return;
+  }
+
+  Interesting->push_back(D);
+}
+
 void ASTReader::PassInterestingDeclToConsumer(Decl *D) {
   if (ObjCImplDecl *ImplD = dyn_cast<ObjCImplDecl>(D))
     PassObjCImplDeclToConsumer(ImplD, Consumer);
diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp
index 5442399..21db402 100644
--- a/clang/lib/Serialization/ASTReaderDecl.cpp
+++ b/clang/lib/Serialization/ASTReaderDecl.cpp
@@ -3462,6 +3462,13 @@
   }
   assert(Idx == Record.size());
 
+  // If we have deserialized a declaration that has a definition the
+  // AST consumer might need to know about, queue it.
+  // We don't pass it to the consumer immediately because we may be in recursive
+  // loading, and some declarations may still be initializing.
+  if (isConsumerInterestedIn(D, Reader.hasPendingBody()))
+    addInterestingDecl(D);
+
   // Load any relevant update records.
   PendingUpdateRecords.push_back(std::make_pair(ID, D));
 
@@ -3470,13 +3477,6 @@
     if (Class->isThisDeclarationADefinition())
       loadObjCCategories(ID, Class);
   
-  // If we have deserialized a declaration that has a definition the
-  // AST consumer might need to know about, queue it.
-  // We don't pass it to the consumer immediately because we may be in recursive
-  // loading, and some declarations may still be initializing.
-  if (isConsumerInterestedIn(D, Reader.hasPendingBody()))
-    InterestingDecls.push_back(D);
-
   return D;
 }
 
@@ -3511,7 +3511,7 @@
       // we need to hand it off to the consumer.
       if (!WasInteresting &&
           isConsumerInterestedIn(D, Reader.hasPendingBody())) {
-        InterestingDecls.push_back(D);
+        addInterestingDecl(D);
         WasInteresting = true;
       }
     }
@@ -3945,6 +3945,7 @@
         // The declaration is now visible.
         Exported->Hidden = false;
       }
+      Reader.addInterestingDecl(Exported, Owner);
       break;
     }
 
diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp
index 0940dd2..6bc7d72 100644
--- a/clang/lib/Serialization/ASTWriterDecl.cpp
+++ b/clang/lib/Serialization/ASTWriterDecl.cpp
@@ -2124,7 +2124,7 @@
   // ImportDecl is used by codegen to determine the set of imported modules to
   // search for inputs for automatic linking; include it if it has a semantic
   // effect.
-  if (isa<ImportDecl>(D) && !WritingModule)
+  if (isa<ImportDecl>(D))
     return true;
 
   return Context.DeclMustBeEmitted(D);