From 97f92ff086454b5ab556c4bf2b2f0c571eb57dc7 Mon Sep 17 00:00:00 2001
From: Bruno Cardoso Lopes <bruno.cardoso@gmail.com>
Date: Wed, 21 Sep 2016 14:54:28 -0700
Subject: [PATCH 1/2] Add NoUndeclaredIncludes attribute and logic

---
 include/clang/Basic/Module.h                       |  4 +++
 include/clang/Lex/ModuleMap.h                      |  7 ++++-
 lib/Basic/Module.cpp                               |  9 +++++-
 lib/Lex/HeaderSearch.cpp                           | 34 ++++++++++++++++++----
 lib/Lex/ModuleMap.cpp                              | 15 +++++++++-
 test/Modules/Inputs/libc-libcxx/include/c++/math.h |  2 ++
 .../libc-libcxx/include/c++/module.modulemap       |  4 +++
 .../Inputs/libc-libcxx/include/c++/stdlib.h        |  1 +
 test/Modules/Inputs/libc-libcxx/include/math.h     |  1 +
 .../Inputs/libc-libcxx/include/module.modulemap    |  4 +++
 test/Modules/Inputs/libc-libcxx/include/stdlib.h   |  1 +
 test/Modules/libc-libcxx.cpp                       |  8 +++++
 12 files changed, 82 insertions(+), 8 deletions(-)
 create mode 100644 test/Modules/Inputs/libc-libcxx/include/c++/math.h
 create mode 100644 test/Modules/Inputs/libc-libcxx/include/c++/module.modulemap
 create mode 100644 test/Modules/Inputs/libc-libcxx/include/c++/stdlib.h
 create mode 100644 test/Modules/Inputs/libc-libcxx/include/math.h
 create mode 100644 test/Modules/Inputs/libc-libcxx/include/module.modulemap
 create mode 100644 test/Modules/Inputs/libc-libcxx/include/stdlib.h
 create mode 100644 test/Modules/libc-libcxx.cpp

diff --git a/include/clang/Basic/Module.h b/include/clang/Basic/Module.h
index 1702fb1..a702381 100644
--- a/include/clang/Basic/Module.h
+++ b/include/clang/Basic/Module.h
@@ -201,6 +201,10 @@ public:
   /// built.
   unsigned ConfigMacrosExhaustive : 1;
 
+  /// \brief Whether files in this module can only include files from used
+  /// modules.
+  unsigned NoUndeclaredIncludes : 1;
+
   /// \brief Describes the visibility of the various names within a
   /// particular module.
   enum NameVisibilityKind {
diff --git a/include/clang/Lex/ModuleMap.h b/include/clang/Lex/ModuleMap.h
index 8a704db..579eee2 100644
--- a/include/clang/Lex/ModuleMap.h
+++ b/include/clang/Lex/ModuleMap.h
@@ -171,7 +171,8 @@ private:
 
   /// \brief The set of attributes that can be attached to a module.
   struct Attributes {
-    Attributes() : IsSystem(), IsExternC(), IsExhaustive() {}
+    Attributes()
+        : IsSystem(), IsExternC(), IsExhaustive(), NoUndeclaredIncludes() {}
 
     /// \brief Whether this is a system module.
     unsigned IsSystem : 1;
@@ -181,6 +182,10 @@ private:
 
     /// \brief Whether this is an exhaustive set of configuration macros.
     unsigned IsExhaustive : 1;
+
+    /// \brief Whether files in this module can only include files from
+    /// explicit dependencies of the module.
+    unsigned NoUndeclaredIncludes : 1;
   };
 
   /// \brief A directory for which framework modules can be inferred.
diff --git a/lib/Basic/Module.cpp b/lib/Basic/Module.cpp
index 8578947..80bbc24 100644
--- a/lib/Basic/Module.cpp
+++ b/lib/Basic/Module.cpp
@@ -33,7 +33,7 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
       IsExplicit(IsExplicit), IsSystem(false), IsExternC(false),
       IsInferred(false), InferSubmodules(false), InferExplicitSubmodules(false),
       InferExportWildcard(false), ConfigMacrosExhaustive(false),
-      NameVisibility(Hidden) {
+      NoUndeclaredIncludes(false), NameVisibility(Hidden) {
   if (Parent) {
     if (!Parent->isAvailable())
       IsAvailable = false;
@@ -41,6 +41,8 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
       IsSystem = true;
     if (Parent->IsExternC)
       IsExternC = true;
+    if (Parent->NoUndeclaredIncludes)
+      NoUndeclaredIncludes = true;
     IsMissingRequirement = Parent->IsMissingRequirement;
     
     Parent->SubModuleIndex[Name] = Parent->SubModules.size();
@@ -181,6 +183,11 @@ bool Module::directlyUses(const Module *Requested) const {
   for (auto *Use : Top->DirectUses)
     if (Requested->isSubModuleOf(Use))
       return true;
+
+  // Anyone is allowed to use our builtin stddef.h and its accompanying module.
+  if (!Requested->Parent && Requested->Name == "_Builtin_stddef_max_align_t")
+    return true;
+
   return false;
 }
 
diff --git a/lib/Lex/HeaderSearch.cpp b/lib/Lex/HeaderSearch.cpp
index c2c909e..991c5ae 100644
--- a/lib/Lex/HeaderSearch.cpp
+++ b/lib/Lex/HeaderSearch.cpp
@@ -413,6 +413,12 @@ getTopFrameworkDir(FileManager &FileMgr, StringRef DirName,
   return TopFrameworkDir;
 }
 
+static bool needModuleLookup(Module *RequestingModule,
+                             bool HasSuggestedModule) {
+  return HasSuggestedModule ||
+         (RequestingModule && RequestingModule->NoUndeclaredIncludes);
+}
+
 /// DoFrameworkLookup - Do a lookup of the specified file in the current
 /// DirectoryLookup, which is a framework directory.
 const FileEntry *DirectoryLookup::DoFrameworkLookup(
@@ -508,7 +514,7 @@ const FileEntry *DirectoryLookup::DoFrameworkLookup(
   }
 
   // If we found the header and are allowed to suggest a module, do so now.
-  if (FE && SuggestedModule) {
+  if (FE && needModuleLookup(RequestingModule, SuggestedModule)) {
     // Find the framework in which this header occurs.
     StringRef FrameworkPath = FE->getDir()->getName();
     bool FoundFramework = false;
@@ -1165,13 +1171,31 @@ HeaderSearch::findModuleForHeader(const FileEntry *File) const {
   return ModMap.findModuleForHeader(File);
 }
 
+static bool suggestModule(HeaderSearch &HS, const FileEntry *File,
+                          Module *RequestingModule,
+                          ModuleMap::KnownHeader *SuggestedModule) {
+  ModuleMap::KnownHeader Module = HS.findModuleForHeader(File);
+  if (SuggestedModule)
+    *SuggestedModule = Module;
+
+  // If this module specifies [no_undeclared_includes], we cannot find any
+  // file that's in a non-dependency module.
+  if (RequestingModule && Module && RequestingModule->NoUndeclaredIncludes) {
+    HS.getModuleMap().resolveUses(RequestingModule, /*Complain*/false);
+    if (!RequestingModule->directlyUses(Module.getModule()))
+      return false;
+  }
+
+  return true;
+}
+
 bool HeaderSearch::findUsableModuleForHeader(
     const FileEntry *File, const DirectoryEntry *Root, Module *RequestingModule,
     ModuleMap::KnownHeader *SuggestedModule, bool IsSystemHeaderDir) {
-  if (File && SuggestedModule) {
+  if (File && needModuleLookup(RequestingModule, SuggestedModule)) {
     // If there is a module that corresponds to this header, suggest it.
     hasModuleMap(File->getName(), Root, IsSystemHeaderDir);
-    *SuggestedModule = findModuleForHeader(File);
+    return suggestModule(*this, File, RequestingModule, SuggestedModule);
   }
   return true;
 }
@@ -1180,7 +1204,7 @@ bool HeaderSearch::findUsableModuleForFrameworkHeader(
     const FileEntry *File, StringRef FrameworkName, Module *RequestingModule,
     ModuleMap::KnownHeader *SuggestedModule, bool IsSystemFramework) {
   // If we're supposed to suggest a module, look for one now.
-  if (SuggestedModule) {
+  if (needModuleLookup(RequestingModule, SuggestedModule)) {
     // Find the top-level framework based on this framework.
     SmallVector<std::string, 4> SubmodulePath;
     const DirectoryEntry *TopFrameworkDir
@@ -1197,7 +1221,7 @@ bool HeaderSearch::findUsableModuleForFrameworkHeader(
     // important so that we're consistent about whether this header
     // corresponds to a module. Possibly we should lock down framework modules
     // so that this is not possible.
-    *SuggestedModule = findModuleForHeader(File);
+    return suggestModule(*this, File, RequestingModule, SuggestedModule);
   }
   return true;
 }
diff --git a/lib/Lex/ModuleMap.cpp b/lib/Lex/ModuleMap.cpp
index 4b49083..756a17c 100644
--- a/lib/Lex/ModuleMap.cpp
+++ b/lib/Lex/ModuleMap.cpp
@@ -674,6 +674,8 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
           Attrs.IsSystem |= inferred->second.Attrs.IsSystem;
           Attrs.IsExternC |= inferred->second.Attrs.IsExternC;
           Attrs.IsExhaustive |= inferred->second.Attrs.IsExhaustive;
+          Attrs.NoUndeclaredIncludes |=
+              inferred->second.Attrs.NoUndeclaredIncludes;
           ModuleMapFile = inferred->second.ModuleMapFile;
         }
       }
@@ -711,6 +713,7 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
   Result->IsSystem |= Attrs.IsSystem;
   Result->IsExternC |= Attrs.IsExternC;
   Result->ConfigMacrosExhaustive |= Attrs.IsExhaustive;
+  Result->NoUndeclaredIncludes |= Attrs.NoUndeclaredIncludes;
   Result->Directory = FrameworkDir;
 
   // umbrella header "umbrella-header-name"
@@ -1309,7 +1312,9 @@ namespace {
     /// \brief The 'extern_c' attribute.
     AT_extern_c,
     /// \brief The 'exhaustive' attribute.
-    AT_exhaustive
+    AT_exhaustive,
+    /// \brief The 'no_undeclared_includes' attribute.
+    AT_no_undeclared_includes
   };
 }
 
@@ -1479,6 +1484,9 @@ void ModuleMapParser::parseModuleDecl() {
     ActiveModule->IsSystem = true;
   if (Attrs.IsExternC)
     ActiveModule->IsExternC = true;
+  if (Attrs.NoUndeclaredIncludes ||
+      (!ActiveModule->Parent && ModuleName == "Darwin"))
+    ActiveModule->NoUndeclaredIncludes = true;
   ActiveModule->Directory = Directory;
 
   bool Done = false;
@@ -2375,6 +2383,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
       = llvm::StringSwitch<AttributeKind>(Tok.getString())
           .Case("exhaustive", AT_exhaustive)
           .Case("extern_c", AT_extern_c)
+          .Case("no_undeclared_includes", AT_no_undeclared_includes)
           .Case("system", AT_system)
           .Default(AT_unknown);
     switch (Attribute) {
@@ -2394,6 +2403,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
     case AT_exhaustive:
       Attrs.IsExhaustive = true;
       break;
+
+    case AT_no_undeclared_includes:
+      Attrs.NoUndeclaredIncludes = true;
+      break;
     }
     consumeToken();
 
diff --git a/test/Modules/Inputs/libc-libcxx/include/c++/math.h b/test/Modules/Inputs/libc-libcxx/include/c++/math.h
new file mode 100644
index 0000000..890db37
--- /dev/null
+++ b/test/Modules/Inputs/libc-libcxx/include/c++/math.h
@@ -0,0 +1,2 @@
+#include_next <math.h>
+template<typename T> T abs(T t) { return (t < 0) ? -t : t; }
diff --git a/test/Modules/Inputs/libc-libcxx/include/c++/module.modulemap b/test/Modules/Inputs/libc-libcxx/include/c++/module.modulemap
new file mode 100644
index 0000000..fd15779
--- /dev/null
+++ b/test/Modules/Inputs/libc-libcxx/include/c++/module.modulemap
@@ -0,0 +1,4 @@
+module "libc++" {
+  module math { header "math.h" export * }
+  module stdlib { header "stdlib.h" export * }
+}
diff --git a/test/Modules/Inputs/libc-libcxx/include/c++/stdlib.h b/test/Modules/Inputs/libc-libcxx/include/c++/stdlib.h
new file mode 100644
index 0000000..6e736cb
--- /dev/null
+++ b/test/Modules/Inputs/libc-libcxx/include/c++/stdlib.h
@@ -0,0 +1 @@
+#include_next "stdlib.h"
diff --git a/test/Modules/Inputs/libc-libcxx/include/math.h b/test/Modules/Inputs/libc-libcxx/include/math.h
new file mode 100644
index 0000000..380ab41
--- /dev/null
+++ b/test/Modules/Inputs/libc-libcxx/include/math.h
@@ -0,0 +1 @@
+int abs(int);
diff --git a/test/Modules/Inputs/libc-libcxx/include/module.modulemap b/test/Modules/Inputs/libc-libcxx/include/module.modulemap
new file mode 100644
index 0000000..744f545
--- /dev/null
+++ b/test/Modules/Inputs/libc-libcxx/include/module.modulemap
@@ -0,0 +1,4 @@
+module libc [no_undeclared_includes] {
+  module math { header "math.h" export * }
+  module stdlib { header "stdlib.h" export * }
+}
diff --git a/test/Modules/Inputs/libc-libcxx/include/stdlib.h b/test/Modules/Inputs/libc-libcxx/include/stdlib.h
new file mode 100644
index 0000000..a84546e
--- /dev/null
+++ b/test/Modules/Inputs/libc-libcxx/include/stdlib.h
@@ -0,0 +1 @@
+#include <math.h>
diff --git a/test/Modules/libc-libcxx.cpp b/test/Modules/libc-libcxx.cpp
new file mode 100644
index 0000000..8d0f4e0
--- /dev/null
+++ b/test/Modules/libc-libcxx.cpp
@@ -0,0 +1,8 @@
+// RUN: rm -rf %t
+// RUN: %clang_cc1 -x c++ -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -I %S/Inputs/libc-libcxx/include/c++ -I %S/Inputs/libc-libcxx/include %s -verify
+// expected-no-diagnostics
+
+#include "math.h"
+
+int n = abs(0);
+float f = abs<float>(0.f);
