Implement support for module requirements, which indicate the language
features needed for a particular module to be available. This allows
mixed-language modules, where certain headers only work under some
language variants (e.g., in C++, std.tuple might only be available in
C++11 mode).


git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@147387 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp
index 69a62d3..feec5f0 100644
--- a/lib/Basic/Module.cpp
+++ b/lib/Basic/Module.cpp
@@ -13,7 +13,11 @@
 //===----------------------------------------------------------------------===//
 #include "clang/Basic/Module.h"
 #include "clang/Basic/FileManager.h"
+#include "clang/Basic/LangOptions.h"
+#include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringSwitch.h"
 using namespace clang;
 
 Module::~Module() {
@@ -25,6 +29,36 @@
   
 }
 
+/// \brief Determine whether a translation unit built using the current
+/// language options has the given feature.
+static bool hasFeature(StringRef Feature, const LangOptions &LangOpts) {
+  return llvm::StringSwitch<bool>(Feature)
+           .Case("blocks", LangOpts.Blocks)
+           .Case("cplusplus", LangOpts.CPlusPlus)
+           .Case("cplusplus11", LangOpts.CPlusPlus0x)
+           .Case("objc", LangOpts.ObjC1)
+           .Case("objc_arc", LangOpts.ObjCAutoRefCount)
+           .Default(false);
+}
+
+bool 
+Module::isAvailable(const LangOptions &LangOpts, StringRef &Feature) const {
+  if (IsAvailable)
+    return true;
+
+  for (const Module *Current = this; Current; Current = Current->Parent) {
+    for (unsigned I = 0, N = Current->Requires.size(); I != N; ++I) {
+      if (!hasFeature(Current->Requires[I], LangOpts)) {
+        Feature = Current->Requires[I];
+        return false;
+      }
+    }
+  }
+
+  llvm_unreachable("could not find a reason why module is unavailable");
+  return false;
+}
+
 bool Module::isSubModuleOf(Module *Other) const {
   const Module *This = this;
   do {
@@ -72,6 +106,35 @@
   return Umbrella.dyn_cast<const DirectoryEntry *>();
 }
 
+void Module::addRequirement(StringRef Feature, const LangOptions &LangOpts) {
+  Requires.push_back(Feature);
+
+  // If this feature is currently available, we're done.
+  if (hasFeature(Feature, LangOpts))
+    return;
+
+  if (!IsAvailable)
+    return;
+
+  llvm::SmallVector<Module *, 2> Stack;
+  Stack.push_back(this);
+  while (!Stack.empty()) {
+    Module *Current = Stack.back();
+    Stack.pop_back();
+
+    if (!Current->IsAvailable)
+      continue;
+
+    Current->IsAvailable = false;
+    for (llvm::StringMap<Module *>::iterator Sub = Current->SubModules.begin(),
+                                          SubEnd = Current->SubModules.end();
+         Sub != SubEnd; ++Sub) {
+      if (Sub->second->IsAvailable)
+        Stack.push_back(Sub->second);
+    }
+  }
+}
+
 static void printModuleId(llvm::raw_ostream &OS, const ModuleId &Id) {
   for (unsigned I = 0, N = Id.size(); I != N; ++I) {
     if (I)
@@ -87,6 +150,17 @@
   if (IsExplicit)
     OS << "explicit ";
   OS << "module " << Name << " {\n";
+
+  if (!Requires.empty()) {
+    OS.indent(Indent + 2);
+    OS << "requires ";
+    for (unsigned I = 0, N = Requires.size(); I != N; ++I) {
+      if (I)
+        OS << ", ";
+      OS << Requires[I];
+    }
+    OS << "\n";
+  }
   
   if (const FileEntry *UmbrellaHeader = getUmbrellaHeader()) {
     OS.indent(Indent + 2);