From aa874ab2417a1a9b2dc68bb132b47f7e19d99acf Mon Sep 17 00:00:00 2001
From: Vassil Vassilev <v.g.vassilev@gmail.com>
Date: Mon, 27 Mar 2023 19:43:33 +0000
Subject: [PATCH] [cxxmodules] Implement a module attribute 'optional' to allow
 missing headers.

This deals with the fact that our modulemaps include headers which can vary
across library versions and that attribute is a way to express this.
---
 interpreter/cling/include/cling/std.modulemap     |  4 ++--
 .../src/tools/clang/include/clang/Basic/Module.h  |  3 +++
 .../src/tools/clang/include/clang/Lex/ModuleMap.h |  5 ++++-
 .../llvm/src/tools/clang/lib/Basic/Module.cpp     |  3 ++-
 .../llvm/src/tools/clang/lib/Lex/ModuleMap.cpp    | 15 ++++++++++++++-
 5 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/interpreter/cling/include/cling/std.modulemap b/interpreter/cling/include/cling/std.modulemap
index d5bb22388e1..6f150355d8a 100644
--- a/interpreter/cling/include/cling/std.modulemap
+++ b/interpreter/cling/include/cling/std.modulemap
@@ -429,8 +429,8 @@ module "std" [system] {
     export *
     header "bits/basic_ios.h"
   }
-  module "bits/chrono.h" {
-    requires cplusplus20
+  module "bits/chrono.h" [optional] {
+    requires cplusplus17
     export *
     header "bits/chrono.h"
   }
diff --git a/interpreter/llvm/src/tools/clang/include/clang/Basic/Module.h b/interpreter/llvm/src/tools/clang/include/clang/Basic/Module.h
index 3476b05d2e9..f62e657fef7 100644
--- a/interpreter/llvm/src/tools/clang/include/clang/Basic/Module.h
+++ b/interpreter/llvm/src/tools/clang/include/clang/Basic/Module.h
@@ -303,6 +303,9 @@ public:
   /// and headers from used modules.
   unsigned NoUndeclaredIncludes : 1;
 
+  /// Whether the submodule is allowed to have missing headers.
+  unsigned IsOptional: 1;
+
   /// Whether this module came from a "private" module map, found next
   /// to a regular (public) module map.
   unsigned ModuleMapIsPrivate : 1;
diff --git a/interpreter/llvm/src/tools/clang/include/clang/Lex/ModuleMap.h b/interpreter/llvm/src/tools/clang/include/clang/Lex/ModuleMap.h
index 41f85a1f572..26ed708c49e 100644
--- a/interpreter/llvm/src/tools/clang/include/clang/Lex/ModuleMap.h
+++ b/interpreter/llvm/src/tools/clang/include/clang/Lex/ModuleMap.h
@@ -241,9 +241,12 @@ private:
     /// and headers from used modules.
     unsigned NoUndeclaredIncludes : 1;
 
+    /// Whether we can have a submodule with missing header files.
+    unsigned IsOptional : 1;
+
     Attributes()
         : IsSystem(false), IsExternC(false), IsExhaustive(false),
-          NoUndeclaredIncludes(false) {}
+          NoUndeclaredIncludes(false), IsOptional(false) {}
   };
 
   /// A directory for which framework modules can be inferred.
diff --git a/interpreter/llvm/src/tools/clang/lib/Basic/Module.cpp b/interpreter/llvm/src/tools/clang/lib/Basic/Module.cpp
index 07ee82ceb60..ff91ddcc880 100644
--- a/interpreter/llvm/src/tools/clang/lib/Basic/Module.cpp
+++ b/interpreter/llvm/src/tools/clang/lib/Basic/Module.cpp
@@ -43,7 +43,7 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
       IsSystem(false), IsExternC(false), IsInferred(false),
       InferSubmodules(false), InferExplicitSubmodules(false),
       InferExportWildcard(false), ConfigMacrosExhaustive(false),
-      NoUndeclaredIncludes(false), ModuleMapIsPrivate(false),
+      NoUndeclaredIncludes(false), IsOptional(false), ModuleMapIsPrivate(false),
       NameVisibility(Hidden) {
   if (Parent) {
     IsAvailable = Parent->isAvailable();
@@ -51,6 +51,7 @@ Module::Module(StringRef Name, SourceLocation DefinitionLoc, Module *Parent,
     IsSystem = Parent->IsSystem;
     IsExternC = Parent->IsExternC;
     NoUndeclaredIncludes = Parent->NoUndeclaredIncludes;
+    IsOptional = Parent->IsOptional;
     ModuleMapIsPrivate = Parent->ModuleMapIsPrivate;
 
     Parent->SubModuleIndex[Name] = Parent->SubModules.size();
diff --git a/interpreter/llvm/src/tools/clang/lib/Lex/ModuleMap.cpp b/interpreter/llvm/src/tools/clang/lib/Lex/ModuleMap.cpp
index f9af7c2a24f..7feb88508b9 100644
--- a/interpreter/llvm/src/tools/clang/lib/Lex/ModuleMap.cpp
+++ b/interpreter/llvm/src/tools/clang/lib/Lex/ModuleMap.cpp
@@ -274,6 +274,8 @@ void ModuleMap::resolveHeader(Module *Mod,
     // this was supposed to modularize the builtin header alone.
   } else if (Header.Kind == Module::HK_Excluded) {
     // Ignore missing excluded header files. They're optional anyway.
+  } else if (Mod->IsOptional) {
+     // Optional submodules can have missing headers.
   } else {
     // If we find a module that has a missing header, we mark this module as
     // unavailable and store the header directive for displaying diagnostics.
@@ -1037,6 +1039,7 @@ Module *ModuleMap::inferFrameworkModule(const DirectoryEntry *FrameworkDir,
   Result->IsExternC |= Attrs.IsExternC;
   Result->ConfigMacrosExhaustive |= Attrs.IsExhaustive;
   Result->NoUndeclaredIncludes |= Attrs.NoUndeclaredIncludes;
+  Result->IsOptional |= Attrs.IsOptional;
   Result->Directory = FrameworkDir;
 
   // Chop off the first framework bit, as that is implied.
@@ -1749,7 +1752,10 @@ namespace {
     AT_exhaustive,
 
     /// The 'no_undeclared_includes' attribute.
-    AT_no_undeclared_includes
+    AT_no_undeclared_includes,
+
+    /// The 'optional' attribute.
+    AT_optional
   };
 
 } // namespace
@@ -2005,6 +2011,8 @@ void ModuleMapParser::parseModuleDecl() {
     ActiveModule->IsSystem = true;
   if (Attrs.IsExternC)
     ActiveModule->IsExternC = true;
+  if (Attrs.IsOptional)
+    ActiveModule->IsOptional = true;
   if (Attrs.NoUndeclaredIncludes ||
       (!ActiveModule->Parent && ModuleName == "Darwin"))
     ActiveModule->NoUndeclaredIncludes = true;
@@ -2905,6 +2913,7 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
           .Case("exhaustive", AT_exhaustive)
           .Case("extern_c", AT_extern_c)
           .Case("no_undeclared_includes", AT_no_undeclared_includes)
+          .Case("optional", AT_optional)
           .Case("system", AT_system)
           .Default(AT_unknown);
     switch (Attribute) {
@@ -2928,6 +2937,10 @@ bool ModuleMapParser::parseOptionalAttributes(Attributes &Attrs) {
     case AT_no_undeclared_includes:
       Attrs.NoUndeclaredIncludes = true;
       break;
+
+    case AT_optional:
+      Attrs.IsOptional = true;
+      break;
     }
     consumeToken();
 
-- 
GitLab