iains updated this revision to Diff 442849.
iains marked 25 inline comments as done.
iains added a comment.

rebased, addressed review comments, added another test.


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D126694/new/

https://reviews.llvm.org/D126694

Files:
  clang/include/clang/AST/DeclBase.h
  clang/include/clang/Sema/Sema.h
  clang/lib/AST/Decl.cpp
  clang/lib/AST/TextNodeDumper.cpp
  clang/lib/Sema/Sema.cpp
  clang/lib/Sema/SemaDecl.cpp
  clang/lib/Sema/SemaDeclAttr.cpp
  clang/lib/Sema/SemaDeclCXX.cpp
  clang/lib/Sema/SemaExpr.cpp
  clang/lib/Sema/SemaInit.cpp
  clang/lib/Sema/SemaLambda.cpp
  clang/lib/Sema/SemaModule.cpp
  clang/lib/Sema/SemaOpenMP.cpp
  clang/lib/Sema/SemaStmt.cpp
  clang/lib/Sema/SemaStmtAsm.cpp
  clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
  clang/lib/Serialization/ASTReaderDecl.cpp
  clang/test/AST/ast-dump-decl.c
  clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
  clang/test/CXX/module/module.global.frag/cxx20-10-4-ex2.cpp
  clang/test/CXX/module/module.global.frag/p3-p4.cpp
  clang/test/Modules/cxx-templates.cpp

Index: clang/test/Modules/cxx-templates.cpp
===================================================================
--- clang/test/Modules/cxx-templates.cpp
+++ clang/test/Modules/cxx-templates.cpp
@@ -252,7 +252,7 @@
 // CHECK-DUMP:      ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}>  col:{{.*}} in cxx_templates_common SomeTemplate
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
-// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
+// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate Visible definition
 // CHECK-DUMP-NEXT:     DefinitionData
 // CHECK-DUMP-NEXT:       DefaultConstructor
 // CHECK-DUMP-NEXT:       CopyConstructor
@@ -263,7 +263,7 @@
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char[2]'
 // CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate
 // CHECK-DUMP-NEXT:     TemplateArgument type 'char[1]'
-// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition
+// CHECK-DUMP:        ClassTemplateSpecializationDecl {{.*}} SomeTemplate Visible definition
 // CHECK-DUMP-NEXT:     DefinitionData
 // CHECK-DUMP-NEXT:       DefaultConstructor
 // CHECK-DUMP-NEXT:       CopyConstructor
Index: clang/test/CXX/module/module.global.frag/p3-p4.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.global.frag/p3-p4.cpp
@@ -0,0 +1,54 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm
+// RUN: %clang_cc1 -std=c++20 M.cpp -ast-dump | FileCheck --match-full-lines %s
+// RUN: %clang_cc1 -std=c++20 M-impl.cpp -fmodule-file=M.pcm -fsyntax-only -verify
+
+//--- p3-p4.h
+int f();
+int g();
+int h();
+int j();
+typedef int GMFInt;
+
+//--- M.cpp
+module;
+#include "p3-p4.h"
+
+export module M;
+
+export int use_f() { return f(); }
+
+export using ::h;
+
+namespace N {
+export using ::j;
+}
+
+GMFInt k(int);
+
+// CHECK: |-FunctionDecl {{.*}} <./p3-p4.h:1:1, col:7> col:5 in M.<global> hidden used f 'int ()' ReachableWhenImported
+// CHECK: |-FunctionDecl {{.*}} <line:2:1, col:7> col:5 in M.<global> hidden g 'int ()' ModuleDiscardable
+// CHECK: |-FunctionDecl {{.*}} <line:3:1, col:7> col:5 in M.<global> hidden h 'int ()' ReachableWhenImported
+// CHECK: |-FunctionDecl {{.*}} <line:4:1, col:7> col:5 in M.<global> hidden j 'int ()' ReachableWhenImported
+// CHECK: |-TypedefDecl {{.*}} <line:5:1, col:13> col:13 in M.<global> hidden referenced GMFInt 'int' ReachableWhenImported
+
+// CHECK: |-ExportDecl {{.*}} <M.cpp:6:1, col:34> col:1 in M
+// CHECK-NEXT: | `-FunctionDecl {{.*}} <col:8, col:34> col:12 in M hidden use_f 'int ()' VisibleWhenImported
+
+// CHECK: |-ExportDecl {{.*}} <line:8:1, col:16> col:1 in M
+// CHECK-NEXT: | |-UsingDecl {{.*}} <col:8, col:16> col:16 in M hidden ::h VisibleWhenImported
+// CHECK-NEXT: | `-UsingShadowDecl {{.*}} <col:16> col:16 in M hidden implicit Function {{.*}} 'h' 'int ()' VisibleWhenImported
+
+// CHECK: |-NamespaceDecl {{.*}} <line:10:1, line:12:1> line:10:11 in M hidden N
+// CHECK-NEXT: | `-ExportDecl {{.*}} <line:11:1, col:16> col:1 in M
+// CHECK-NEXT: |   |-UsingDecl {{.*}} <col:8, col:16> col:16 in M hidden ::j VisibleWhenImported
+// CHECK-NEXT: |   `-UsingShadowDecl {{.*}} <col:16> col:16 in M hidden implicit Function {{.*}} 'j' 'int ()' VisibleWhenImported
+
+//--- M-impl.cpp
+import M;
+int a = j();    // expected-error {{missing '#include'; 'j' must be declared before it is used}}
+                // expected-note@p3-p4.h:4 {{declaration here is not visible}}
+int b = N::j(); // should be OK, UsingShadowDecl is visible
Index: clang/test/CXX/module/module.global.frag/cxx20-10-4-ex2.cpp
===================================================================
--- /dev/null
+++ clang/test/CXX/module/module.global.frag/cxx20-10-4-ex2.cpp
@@ -0,0 +1,60 @@
+// RUN: rm -rf %t
+// RUN: split-file %s %t
+// RUN: cd %t
+
+// RUN: %clang_cc1 -std=c++20 std-10-4-ex2-interface.cpp \
+// RUN:  -emit-module-interface -o M.pcm -Wno-unused-value
+// RUN: %clang_cc1 -std=c++20 std-10-4-ex2-implementation.cpp \
+// RUN:  -fmodule-file=M.pcm -fsyntax-only -verify
+//--- std-10-4-ex2.h
+
+namespace N {
+struct X {};
+int d();
+int e();
+inline int f(X, int = d()) { return e(); }
+int g(X);
+int h(X);
+} // namespace N
+
+//--- std-10-4-ex2-interface.cpp
+
+module;
+
+#include "std-10-4-ex2.h"
+
+export module M;
+
+template <typename T> int use_f() {
+  N::X x;           // N::X, N, and  ::  are decl-reachable from use_f
+  return f(x, 123); // N::f is decl-reachable from use_f,
+                    // N::e is indirectly decl-reachable from use_f
+                    //   because it is decl-reachable from N::f, and
+                    // N::d is decl-reachable from use_f
+                    //   because it is decl-reachable from N::f
+                    //   even though it is not used in this call
+}
+
+template <typename T> int use_g() {
+  N::X x;             // N::X, N, and :: are decl-reachable from use_g
+  return g((T(), x)); // N::g is not decl-reachable from use_g
+}
+
+template <typename T> int use_h() {
+  N::X x;             // N::X, N, and :: are decl-reachable from use_h
+  return h((T(), x)); // N::h is not decl-reachable from use_h, but
+                      // N::h is decl-reachable from use_h<int>
+}
+
+int k = use_h<int>();
+// use_h<int> is decl-reachable from k, so
+// N::h is decl-reachable from k
+
+//--- std-10-4-ex2-implementation.cpp
+
+module M;
+
+int a = use_f<int>();
+int b = use_g<int>(); // expected-er...@std-10-4-ex2-interface.cpp:20 {{use of undeclared identifier 'g'}}
+                      // expected-note@-1 {{in instantiation of function template specialization 'use_g<int>' requested here}}
+int c = use_h<int>();
Index: clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
===================================================================
--- clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
+++ clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp
@@ -29,9 +29,8 @@
 #endif
 
 void test_early() {
-  in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
-  // expected-note@* {{not visible}}
-
+  in_header = 1;              // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+                              // expected-note@foo.h:3 {{declaration here is not visible}}
   global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
                               // expected-n...@p2.cpp:16 {{not visible}}
 
@@ -57,9 +56,8 @@
 #endif
 
 void test_late() {
-  in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
-  // expected-note@* {{not visible}}
-
+  in_header = 1;              // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}}
+                              // expected-note@foo.h:3 {{declaration here is not visible}}
   global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}}
   // expected-n...@p2.cpp:16 {{not visible}}
 
Index: clang/test/AST/ast-dump-decl.c
===================================================================
--- clang/test/AST/ast-dump-decl.c
+++ clang/test/AST/ast-dump-decl.c
@@ -1,16 +1,16 @@
 // Test without serialization:
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -ast-dump -ast-dump-filter Test %s \
-// RUN: | FileCheck --strict-whitespace %s
+// RUN: | FileCheck --strict-whitespace %s -check-prefix CHECK-NOMODULE
 //
 // Test with serialization:
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -emit-pch -o %t %s
 // RUN: %clang_cc1 -x c -triple x86_64-unknown-unknown -Wno-strict-prototypes -include-pch %t \
 // RUN: -ast-dump-all -ast-dump-filter Test /dev/null \
 // RUN: | sed -e "s/ <undeserialized declarations>//" -e "s/ imported//" \
-// RUN: | FileCheck --strict-whitespace %s
+// RUN: | FileCheck --strict-whitespace %s -check-prefix CHECK-NOMODULE
 //
 // RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -ast-dump %s \
-// RUN: | FileCheck -check-prefix CHECK-TU --strict-whitespace %s
+// RUN: | FileCheck -check-prefix CHECK-TU --strict-whitespace %s -check-prefix CHECK-NOMODULE
 //
 // RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=X \
 // RUN: -triple x86_64-unknown-unknown -Wno-strict-prototypes -fmodule-map-file=%S/Inputs/module.modulemap \
@@ -166,13 +166,16 @@
 // CHECK-MODULE:      FieldDecl{{.*}} TestFieldDeclPrivate 'int' __module_private__
 
 int TestVarDecl;
-// CHECK:      VarDecl{{.*}} TestVarDecl 'int'
+// CHECK-NOMODULE:      VarDecl{{.*}} TestVarDecl 'int'
+// CHECK-MODULE:      VarDecl{{.*}} TestVarDecl 'int' VisibleWhenImported
 
 extern int TestVarDeclSC;
-// CHECK:      VarDecl{{.*}} TestVarDeclSC 'int' extern
+// CHECK-NOMODULE:      VarDecl{{.*}} TestVarDeclSC 'int' extern
+// CHECK-MODULE:      VarDecl{{.*}} TestVarDeclSC 'int' extern VisibleWhenImported
 
 __thread int TestVarDeclThread;
-// CHECK:      VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}}
+// CHECK-NOMODULE:      VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}}
+// CHECK-MODULE:      VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}} VisibleWhenImported
 
 __module_private__ int TestVarDeclPrivate;
 // CHECK-MODULE:      VarDecl{{.*}} TestVarDeclPrivate 'int' __module_private__
Index: clang/lib/Serialization/ASTReaderDecl.cpp
===================================================================
--- clang/lib/Serialization/ASTReaderDecl.cpp
+++ clang/lib/Serialization/ASTReaderDecl.cpp
@@ -621,6 +621,7 @@
     case Decl::ModuleOwnershipKind::VisibleWhenImported:
     case Decl::ModuleOwnershipKind::ReachableWhenImported:
     case Decl::ModuleOwnershipKind::ModulePrivate:
+    case Decl::ModuleOwnershipKind::ModuleDiscardable:
       break;
     }
 
@@ -628,9 +629,10 @@
     // Store the owning submodule ID in the declaration.
     D->setOwningModuleID(SubmoduleID);
 
-    if (ModulePrivate) {
-      // Module-private declarations are never visible, so there is no work to
-      // do.
+    if (ModulePrivate ||
+        ModuleOwnership == Decl::ModuleOwnershipKind::ModuleDiscardable) {
+      // Module-private and unreachable declarations are never visible, so
+      // there is no work to do.
     } else if (Reader.getContext().getLangOpts().ModulesLocalVisibility) {
       // If local visibility is being tracked, this declaration will become
       // hidden and visible as the owning module does.
@@ -641,6 +643,8 @@
       else
         Reader.HiddenNamesMap[Owner].push_back(D);
     }
+  } else if (ModuleOwnership == Decl::ModuleOwnershipKind::ModuleDiscardable) {
+    D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleDiscardable);
   } else if (ModulePrivate) {
     D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate);
   }
Index: clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
===================================================================
--- clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -1004,7 +1004,7 @@
     SemaRef.inferGslPointerAttribute(Typedef);
 
   Typedef->setAccess(D->getAccess());
-  Typedef->setReferenced(D->isReferenced());
+  SemaRef.setDeclReferenced(Typedef, D->isReferenced());
 
   return Typedef;
 }
@@ -1069,7 +1069,7 @@
 Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) {
   auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(),
                                     D->getIdentifier());
-  NewBD->setReferenced(D->isReferenced());
+  SemaRef.setDeclReferenced(NewBD, D->isReferenced());
   SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD);
   return NewBD;
 }
@@ -5213,8 +5213,8 @@
 
   if (!OldVar->isStaticDataMember()) {
     if (OldVar->isUsed(false))
-      NewVar->setIsUsed();
-    NewVar->setReferenced(OldVar->isReferenced());
+      setDeclIsUsed(NewVar);
+    setDeclReferenced(static_cast<Decl *>(NewVar), OldVar->isReferenced());
   }
 
   InstantiateAttrs(TemplateArgs, OldVar, NewVar, LateAttrs, StartingScope);
Index: clang/lib/Sema/SemaStmtAsm.cpp
===================================================================
--- clang/lib/Sema/SemaStmtAsm.cpp
+++ clang/lib/Sema/SemaStmtAsm.cpp
@@ -956,7 +956,7 @@
 
   if (Label->isMSAsmLabel()) {
     // If we have previously created this label implicitly, mark it as used.
-    Label->markUsed(Context);
+    markDeclUsed(Label, Context);
   } else {
     // Otherwise, insert it, but only resolve it if we have seen the label itself.
     std::string InternalName;
Index: clang/lib/Sema/SemaStmt.cpp
===================================================================
--- clang/lib/Sema/SemaStmt.cpp
+++ clang/lib/Sema/SemaStmt.cpp
@@ -2759,7 +2759,7 @@
 
   if (RangeVarType->isDependentType()) {
     // The range is implicitly used as a placeholder when it is dependent.
-    RangeVar->markUsed(Context);
+    markDeclUsed(static_cast<Decl *>(RangeVar), Context);
 
     // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill
     // them in properly when we instantiate the loop.
@@ -3274,7 +3274,7 @@
                                SourceLocation LabelLoc,
                                LabelDecl *TheDecl) {
   setFunctionHasBranchIntoScope();
-  TheDecl->markUsed(Context);
+  markDeclUsed(TheDecl, Context);
   return new (Context) GotoStmt(TheDecl, GotoLoc, LabelLoc);
 }
 
Index: clang/lib/Sema/SemaOpenMP.cpp
===================================================================
--- clang/lib/Sema/SemaOpenMP.cpp
+++ clang/lib/Sema/SemaOpenMP.cpp
@@ -1519,7 +1519,7 @@
                                      SourceLocation Loc,
                                      bool RefersToCapture = false) {
   D->setReferenced();
-  D->markUsed(S.Context);
+  S.markDeclUsed(D, S.Context);
   return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(),
                              SourceLocation(), D, RefersToCapture, Loc, Ty,
                              VK_LValue);
@@ -3146,7 +3146,7 @@
 
     // Mark variable as used.
     VD->setReferenced();
-    VD->markUsed(Context);
+    markDeclUsed(static_cast<Decl *>(VD), Context);
 
     QualType QType = VD->getType();
     if (QType->isDependentType() || QType->isInstantiationDependentType()) {
Index: clang/lib/Sema/SemaModule.cpp
===================================================================
--- clang/lib/Sema/SemaModule.cpp
+++ clang/lib/Sema/SemaModule.cpp
@@ -12,10 +12,14 @@
 //===----------------------------------------------------------------------===//
 
 #include "clang/AST/ASTConsumer.h"
+#include "clang/AST/DeclFriend.h"
 #include "clang/Lex/HeaderSearch.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Sema/SemaInternal.h"
 
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Format.h"
+
 using namespace clang;
 using namespace sema;
 
@@ -83,8 +87,6 @@
     return nullptr;
   }
 
-  // We start in the global module; all those declarations are implicitly
-  // module-private (though they do not have module linkage).
   Module *GlobalModule =
       PushGlobalModuleFragment(ModuleLoc, /*IsImplicit=*/false);
 
@@ -96,8 +98,10 @@
   // provide declarations that are attached to the global module and usable
   // within the module unit.
   //
-  // So the declations in the global module shouldn't be visible by default.
-  TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported);
+  // We are expected to elide GMF decls that are not used in the purview of the
+  // named module.  In order to do that, create GMF decls with a marker that is
+  // updated when a decl is used.
+  TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleDiscardable);
   TU->setLocalOwningModule(GlobalModule);
 
   // FIXME: Consider creating an explicit representation of this declaration.
@@ -900,6 +904,16 @@
         // no declarations (transitively), in which case it's ill-formed.
         diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child,
                                 BlockStart);
+      } else if (getLangOpts().CPlusPlusModules && GlobalModuleFragment) {
+        // An export must be in module purview, so mark any used GMF content.
+        if (auto *USD = dyn_cast<UsingShadowDecl>(Child)) {
+          NamedDecl *Target = USD->getUnderlyingDecl();
+          if (Target->hasOwningModule() &&
+              Target->getOwningModule()->isGlobalModule()) {
+            // Mark the target of the using declaration as reachable.
+            markGMFDeclsReachableFrom(Target);
+          }
+        }
       }
     }
   }
@@ -935,3 +949,180 @@
          "left the wrong module scope, which is not global module fragment");
   ModuleScopes.pop_back();
 }
+
+// A helper to process a used type to find GMF reachable decls which it uses.
+
+void Sema::findGMFReachableDeclsForType(const QualType& Q) {
+  // If we have a reference, mark the underlying type.
+  auto QR = Q.getNonReferenceType();
+  const Type *T = QR.getTypePtr();
+  assert(T && "missing type pointer?");
+  // Only visit each Type once.
+  if (!GMFTypesVisited.insert(T).second)
+    return;
+  // Return as early as possible if this is not an entity we would need to mark
+  // but typedefs of built-in types need to be caught first (since the typedef
+  // could be in the GMF).
+  if (auto *TD = dyn_cast<TypedefType>(T)) {
+    markGMFDeclsReachableFrom(TD->getDecl());
+  } else if (T->isBuiltinType()) {
+    return;
+  } else if (auto *PT = dyn_cast<PointerType>(T)) {
+    return;
+  } else if (auto *TST = dyn_cast<TemplateSpecializationType>(T)) {
+    if (auto *RT = TST->getAsCXXRecordDecl())
+      markGMFDeclsReachableFrom(RT);
+    TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl();
+    markGMFDeclsReachableFrom(TD);
+  } else if (auto *RD = T->getAsCXXRecordDecl()) {
+    markGMFDeclsReachableFrom(RD);
+  } else if (auto *TP = dyn_cast<TemplateTypeParmType>(T)) {
+    markGMFDeclsReachableFrom(TP->getDecl());
+  } else if (auto *DN = dyn_cast<DependentNameType>(T)) {
+    return;
+  } else if (auto *ST = dyn_cast<SubstTemplateTypeParmType>(T)) {
+    findGMFReachableDeclsForType(ST->getReplacementType());
+  } else if (auto *ET = dyn_cast<ElaboratedType>(T)) {
+    findGMFReachableDeclsForType(ET->getNamedType());
+  } else {
+    llvm::dbgs() << llvm::format("unhandled type: %p ", T);
+    T->dump();
+  }
+}
+
+// A helper to recurse through a stmt/expr tree finding expressions with
+// content that makes GMF decls reachable.
+
+void Sema::findGMFReachableDeclExprs(clang::Stmt *S) {
+
+  if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S)) {
+    markGMFDeclsReachableFrom(DR->getFoundDecl());
+  } else if (DependentScopeDeclRefExpr *DS =
+                 dyn_cast<DependentScopeDeclRefExpr>(S)) {
+    auto *NNS = DS->getQualifier();
+    assert(NNS && (NNS->getKind() == NestedNameSpecifier::TypeSpec) &&
+           "not a type spec");
+    findGMFReachableDeclsForType(QualType(NNS->getAsType(), 0));
+  } else if (UnresolvedLookupExpr *ULE = dyn_cast<UnresolvedLookupExpr>(S)) {
+    for (auto *D : ULE->decls())
+      markGMFDeclsReachableFrom(D);
+  } else if (DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+    if (DS->isSingleDecl())
+      markGMFDeclsReachableFrom(DS->getSingleDecl());
+    else {
+      for (auto *D : DS->decls())
+        markGMFDeclsReachableFrom(D);
+    }
+  }
+
+  // Recurse into sub-expressions.
+  for (auto *CH : S->children())
+    findGMFReachableDeclExprs(CH);
+}
+
+// Starting from decl Orig (which is marked as reachable/visible if it is in
+// the GMF and currently marked as ModuleDiscardable), process other decls
+// reachable from this one and mark them too.
+
+void Sema::markGMFDeclsReachableFrom(Decl *Orig) {
+
+  // Do not alter the ownership kind unless it is ModuleDiscardable.
+  if (!Orig->isModuleDiscardable())
+    return;
+
+  // Only visit each Decl once, when the state is changed.
+  assert(Orig->getOwningModule() && Orig->getOwningModule()->isGlobalModule() &&
+         "should not have a discardable decl outside the GMF");
+  Orig->setModuleOwnershipKind(
+      Decl::ModuleOwnershipKind::ReachableWhenImported);
+
+  // Now process the decl to determine if any dependent decls should also be
+  // updated.
+
+  if (auto FT = dyn_cast<FunctionTemplateDecl>(Orig)) {
+    markGMFDeclsReachableFrom(FT->getTemplatedDecl());
+    for (auto *SP : FT->specializations())
+      markGMFDeclsReachableFrom(SP);
+    for (auto *TA : *FT->getTemplateParameters())
+      markGMFDeclsReachableFrom(TA);
+    return;
+  }
+
+  if (auto *FD = dyn_cast<FunctionDecl>(Orig)) {
+    findGMFReachableDeclsForType(FD->getReturnType());
+    for (ParmVarDecl *P : FD->parameters())
+      markGMFDeclsReachableFrom(P);
+    const FunctionDecl *BodyDecl;
+    if (auto *S = FD->getBody(BodyDecl))
+      findGMFReachableDeclExprs(S);
+    return;
+  }
+
+  if (auto TS = dyn_cast<ClassTemplateSpecializationDecl>(Orig)) {
+    markGMFDeclsReachableFrom(TS->getSpecializedTemplate());
+    if (!TS->isCompleteDefinition())
+      return;
+    for (auto B : TS->bases())
+      findGMFReachableDeclsForType(B.getType());
+    return;
+  }
+
+  if (auto CD = dyn_cast<ClassTemplateDecl>(Orig)) {
+    markGMFDeclsReachableFrom(CD->getTemplatedDecl());
+    for (auto *SP : CD->specializations())
+      markGMFDeclsReachableFrom(SP);
+    for (auto *TA : *CD->getTemplateParameters())
+      markGMFDeclsReachableFrom(TA);
+    return;
+  }
+
+  if (auto *RD = dyn_cast<CXXRecordDecl>(Orig)) {
+    if (!RD->isCompleteDefinition())
+      return;
+    DeclContext *DC = Decl::castToDeclContext(RD);
+    for (auto *D : DC->decls())
+      markGMFDeclsReachableFrom(D);
+    for (auto B : RD->bases())
+      findGMFReachableDeclsForType(B.getType());
+    return;
+  }
+
+  if (auto *TTA = dyn_cast<TemplateTypeParmDecl>(Orig)) {
+    if (TTA->hasDefaultArgument())
+      findGMFReachableDeclsForType(TTA->getDefaultArgument());
+    return;
+  }
+
+  if (auto *PD = dyn_cast<ParmVarDecl>(Orig)) {
+    findGMFReachableDeclsForType(PD->getOriginalType());
+    if (auto *E = PD->getDefaultArg())
+      findGMFReachableDeclExprs(E);
+    return;
+  }
+
+  if (auto *VD = dyn_cast<VarDecl>(Orig)) {
+    findGMFReachableDeclsForType(VD->getType());
+    if (VD->hasInit())
+      findGMFReachableDeclExprs(VD->getInit());
+    return;
+  }
+
+  if (auto *FD = dyn_cast<FieldDecl>(Orig)) {
+    findGMFReachableDeclsForType(FD->getType());
+    if (auto *Init = FD->getInClassInitializer())
+      findGMFReachableDeclExprs(Init);
+    return;
+  }
+
+  if (auto *FD = dyn_cast<FriendDecl>(Orig)) {
+    if (auto *DD = FD->getFriendDecl())
+      markGMFDeclsReachableFrom(DD);
+    return;
+  }
+
+  if (auto *ED = dyn_cast<EnumConstantDecl>(Orig)) {
+    if (auto *E = dyn_cast<EnumDecl>(ED->getDeclContext()))
+      markGMFDeclsReachableFrom(E);
+    return;
+  }
+}
Index: clang/lib/Sema/SemaLambda.cpp
===================================================================
--- clang/lib/Sema/SemaLambda.cpp
+++ clang/lib/Sema/SemaLambda.cpp
@@ -874,7 +874,7 @@
   NewVD->setReferenced(true);
   // FIXME: Pass in a VarDecl::InitializationStyle.
   NewVD->setInitStyle(static_cast<VarDecl::InitializationStyle>(InitStyle));
-  NewVD->markUsed(Context);
+  markDeclUsed(static_cast<Decl *>(NewVD), Context);
   NewVD->setInit(Init);
   if (NewVD->isParameterPack())
     getCurLambda()->LocalPacks.push_back(NewVD);
@@ -1975,7 +1975,7 @@
         Lambda->lookup(
           Context.DeclarationNames.getCXXOperatorName(OO_Call)).front());
   CallOperator->setReferenced();
-  CallOperator->markUsed(Context);
+  markDeclUsed(CallOperator, Context);
 
   ExprResult Init = PerformCopyInitialization(
       InitializedEntity::InitializeLambdaToBlock(ConvLocation, Src->getType()),
Index: clang/lib/Sema/SemaInit.cpp
===================================================================
--- clang/lib/Sema/SemaInit.cpp
+++ clang/lib/Sema/SemaInit.cpp
@@ -4692,7 +4692,7 @@
   FunctionDecl *Function = Best->Function;
   // This is the overload that will be used for this initialization step if we
   // use this initialization. Mark it as referenced.
-  Function->setReferenced();
+  S.setDeclReferenced(Function);
 
   // Compute the returned type and value kind of the conversion.
   QualType cv3T3;
@@ -5363,7 +5363,7 @@
   }
 
   FunctionDecl *Function = Best->Function;
-  Function->setReferenced();
+  S.setDeclReferenced(Function);
   bool HadMultipleCandidates = (CandidateSet.size() > 1);
 
   if (isa<CXXConstructorDecl>(Function)) {
Index: clang/lib/Sema/SemaExpr.cpp
===================================================================
--- clang/lib/Sema/SemaExpr.cpp
+++ clang/lib/Sema/SemaExpr.cpp
@@ -47,6 +47,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
+#include "clang/Sema/Sema.h"
 #include "clang/Sema/SemaFixItUtils.h"
 #include "clang/Sema/SemaInternal.h"
 #include "clang/Sema/Template.h"
@@ -15790,7 +15791,7 @@
 /// ActOnAddrLabel - Parse the GNU address of label extension: "&&foo".
 ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc,
                                 LabelDecl *TheDecl) {
-  TheDecl->markUsed(Context);
+  markDeclUsed(TheDecl, Context);
   // Create the AST node.  The address of a label always has type 'void*'.
   return new (Context) AddrLabelExpr(OpLoc, LabLoc, TheDecl,
                                      Context.getPointerType(Context.VoidTy));
@@ -17875,7 +17876,7 @@
                                   bool MightBeOdrUse) {
   assert(Func && "No function?");
 
-  Func->setReferenced();
+  setDeclReferenced(Func);
 
   // Recursive functions aren't really used until they're used from some other
   // context.
@@ -18108,7 +18109,7 @@
       }
     }
 
-    Func->markUsed(Context);
+    markDeclUsed(Func, Context);
   }
 }
 
@@ -18179,7 +18180,7 @@
     }
   }
 
-  Var->markUsed(SemaRef.Context);
+  SemaRef.markDeclUsed(Var, SemaRef.Context);
 }
 
 void Sema::MarkCaptureUsedInEnclosingContext(VarDecl *Capture,
@@ -19335,7 +19336,7 @@
   assert((!E || isa<DeclRefExpr>(E) || isa<MemberExpr>(E) ||
           isa<FunctionParmPackExpr>(E)) &&
          "Invalid Expr argument to DoMarkVarDeclReferenced");
-  Var->setReferenced();
+  SemaRef.setDeclReferenced(Var);
 
   if (Var->isInvalidDecl())
     return;
@@ -19611,7 +19612,7 @@
     MarkFunctionReferenced(Loc, FD, MightBeOdrUse);
     return;
   }
-  D->setReferenced();
+  setDeclReferenced(D);
 }
 
 namespace {
Index: clang/lib/Sema/SemaDeclCXX.cpp
===================================================================
--- clang/lib/Sema/SemaDeclCXX.cpp
+++ clang/lib/Sema/SemaDeclCXX.cpp
@@ -8796,7 +8796,7 @@
       return;
     }
     FD->setBody(Body.get());
-    FD->markUsed(Context);
+    markDeclUsed(FD, Context);
   }
 
   // The exception specification is needed because we are defining the
@@ -13503,7 +13503,7 @@
                            ? Constructor->getEndLoc()
                            : Constructor->getLocation();
   Constructor->setBody(new (Context) CompoundStmt(Loc));
-  Constructor->markUsed(Context);
+  markDeclUsed(Constructor, Context);
 
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(Constructor);
@@ -13682,7 +13682,7 @@
   }
 
   Constructor->setBody(new (Context) CompoundStmt(InitLoc));
-  Constructor->markUsed(Context);
+  markDeclUsed(Constructor, Context);
 
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(Constructor);
@@ -13793,7 +13793,7 @@
                            ? Destructor->getEndLoc()
                            : Destructor->getLocation();
   Destructor->setBody(new (Context) CompoundStmt(Loc));
-  Destructor->markUsed(Context);
+  markDeclUsed(Destructor, Context);
 
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(Destructor);
@@ -14652,7 +14652,7 @@
     assert(!Body.isInvalid() && "Compound statement creation cannot fail");
   }
   CopyAssignOperator->setBody(Body.getAs<Stmt>());
-  CopyAssignOperator->markUsed(Context);
+  markDeclUsed(CopyAssignOperator, Context);
 
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(CopyAssignOperator);
@@ -15022,7 +15022,7 @@
     assert(!Body.isInvalid() && "Compound statement creation cannot fail");
   }
   MoveAssignOperator->setBody(Body.getAs<Stmt>());
-  MoveAssignOperator->markUsed(Context);
+  markDeclUsed(MoveAssignOperator, Context);
 
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(MoveAssignOperator);
@@ -15166,7 +15166,7 @@
     Sema::CompoundScopeRAII CompoundScope(*this);
     CopyConstructor->setBody(
         ActOnCompoundStmt(Loc, Loc, None, /*isStmtExpr=*/false).getAs<Stmt>());
-    CopyConstructor->markUsed(Context);
+    markDeclUsed(CopyConstructor, Context);
   }
 
   if (ASTMutationListener *L = getASTMutationListener()) {
@@ -15292,7 +15292,7 @@
     Sema::CompoundScopeRAII CompoundScope(*this);
     MoveConstructor->setBody(ActOnCompoundStmt(
         Loc, Loc, None, /*isStmtExpr=*/ false).getAs<Stmt>());
-    MoveConstructor->markUsed(Context);
+    markDeclUsed(MoveConstructor, Context);
   }
 
   if (ASTMutationListener *L = getASTMutationListener()) {
@@ -15343,7 +15343,7 @@
   // Fill in the __invoke function with a dummy implementation. IR generation
   // will fill in the actual details. Update its type in case it contained
   // an 'auto'.
-  Invoker->markUsed(Context);
+  markDeclUsed(Invoker, Context);
   Invoker->setReferenced();
   Invoker->setType(Conv->getReturnType()->getPointeeType());
   Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation()));
@@ -15355,7 +15355,7 @@
   Stmt *Return = BuildReturnStmt(Conv->getLocation(), FunctionRef).get();
   Conv->setBody(CompoundStmt::Create(Context, Return, FPOptionsOverride(),
                                      Conv->getLocation(), Conv->getLocation()));
-  Conv->markUsed(Context);
+  markDeclUsed(Conv, Context);
   Conv->setReferenced();
 
   if (ASTMutationListener *L = getASTMutationListener()) {
@@ -15410,7 +15410,7 @@
   Stmt *ReturnS = Return.get();
   Conv->setBody(CompoundStmt::Create(Context, ReturnS, FPOptionsOverride(),
                                      Conv->getLocation(), Conv->getLocation()));
-  Conv->markUsed(Context);
+  markDeclUsed(Conv, Context);
 
   // We're done; notify the mutation listener, if any.
   if (ASTMutationListener *L = getASTMutationListener()) {
Index: clang/lib/Sema/SemaDeclAttr.cpp
===================================================================
--- clang/lib/Sema/SemaDeclAttr.cpp
+++ clang/lib/Sema/SemaDeclAttr.cpp
@@ -2012,7 +2012,7 @@
     LookupResult LR(S, target, Sema::LookupOrdinaryName);
     if (S.LookupQualifiedName(LR, S.getCurLexicalContext()))
       for (NamedDecl *ND : LR)
-        ND->markUsed(S.Context);
+        S.markDeclUsed(ND, S.Context);
   }
 
   D->addAttr(::new (S.Context) AliasAttr(S.Context, AL, Str));
Index: clang/lib/Sema/SemaDecl.cpp
===================================================================
--- clang/lib/Sema/SemaDecl.cpp
+++ clang/lib/Sema/SemaDecl.cpp
@@ -4114,7 +4114,7 @@
 
   // Merge "used" flag.
   if (Old->getMostRecentDecl()->isUsed(false))
-    New->setIsUsed();
+    setDeclIsUsed(New);
 
   // Merge attributes from the parameters.  These can mismatch with K&R
   // declarations.
@@ -4530,7 +4530,7 @@
 
   // Merge "used" flag.
   if (Old->getMostRecentDecl()->isUsed(false))
-    New->setIsUsed();
+    setDeclIsUsed(New);
 
   // Keep a chain of previous declarations.
   New->setPreviousDecl(Old);
Index: clang/lib/Sema/Sema.cpp
===================================================================
--- clang/lib/Sema/Sema.cpp
+++ clang/lib/Sema/Sema.cpp
@@ -1105,6 +1105,9 @@
   assert(DelayedDiagnostics.getCurrentPool() == nullptr
          && "reached end of translation unit with a pool attached?");
 
+  //  We have finished checking if decls in a GMF need to be made reachable.
+  GMFTypesVisited.clear();
+
   // If code completion is enabled, don't perform any end-of-translation-unit
   // work.
   if (PP.isCodeCompletionEnabled())
Index: clang/lib/AST/TextNodeDumper.cpp
===================================================================
--- clang/lib/AST/TextNodeDumper.cpp
+++ clang/lib/AST/TextNodeDumper.cpp
@@ -12,6 +12,7 @@
 
 #include "clang/AST/TextNodeDumper.h"
 #include "clang/AST/APValue.h"
+#include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclFriend.h"
 #include "clang/AST/DeclOpenMP.h"
 #include "clang/AST/DeclTemplate.h"
@@ -57,6 +58,29 @@
   llvm_unreachable("Decl that isn't part of DeclNodes.inc!");
 }
 
+static void dumpModuleOwnership(raw_ostream &OS, const Decl *D) {
+  switch (D->getModuleOwnershipKind()) {
+  case Decl::ModuleOwnershipKind::Unowned:
+    // OS << " Unowned";
+    break;
+  case Decl::ModuleOwnershipKind::Visible:
+    OS << " Visible";
+    break;
+  case Decl::ModuleOwnershipKind::VisibleWhenImported:
+    OS << " VisibleWhenImported";
+    break;
+  case Decl::ModuleOwnershipKind::ReachableWhenImported:
+    OS << " ReachableWhenImported";
+    break;
+  case Decl::ModuleOwnershipKind::ModulePrivate:
+    OS << " __module_private__";
+    break;
+  case Decl::ModuleOwnershipKind::ModuleDiscardable:
+    OS << " ModuleDiscardable";
+    break;
+  }
+}
+
 TextNodeDumper::TextNodeDumper(raw_ostream &OS, const ASTContext &Context,
                                bool ShowColors)
     : TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors),
@@ -1615,8 +1639,7 @@
 void TextNodeDumper::VisitTypedefDecl(const TypedefDecl *D) {
   dumpName(D);
   dumpType(D->getUnderlyingType());
-  if (D->isModulePrivate())
-    OS << " __module_private__";
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitEnumDecl(const EnumDecl *D) {
@@ -1627,8 +1650,7 @@
       OS << " struct";
   }
   dumpName(D);
-  if (D->isModulePrivate())
-    OS << " __module_private__";
+  dumpModuleOwnership(OS, D);
   if (D->isFixed())
     dumpType(D->getIntegerType());
 }
@@ -1636,8 +1658,7 @@
 void TextNodeDumper::VisitRecordDecl(const RecordDecl *D) {
   OS << ' ' << D->getKindName();
   dumpName(D);
-  if (D->isModulePrivate())
-    OS << " __module_private__";
+  dumpModuleOwnership(OS, D);
   if (D->isCompleteDefinition())
     OS << " definition";
 }
@@ -1666,8 +1687,7 @@
     OS << " inline";
   if (D->isVirtualAsWritten())
     OS << " virtual";
-  if (D->isModulePrivate())
-    OS << " __module_private__";
+  dumpModuleOwnership(OS, D);
 
   if (D->isPure())
     OS << " pure";
@@ -1744,8 +1764,7 @@
   dumpType(D->getType());
   if (D->isMutable())
     OS << " mutable";
-  if (D->isModulePrivate())
-    OS << " __module_private__";
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitVarDecl(const VarDecl *D) {
@@ -1764,8 +1783,7 @@
     OS << " tls_dynamic";
     break;
   }
-  if (D->isModulePrivate())
-    OS << " __module_private__";
+  dumpModuleOwnership(OS, D);
   if (D->isNRVOVariable())
     OS << " nrvo";
   if (D->isInline())
@@ -1941,6 +1959,7 @@
     {
       ColorScope Color(OS, ShowColors, DeclKindNameColor);
       OS << "DefinitionData";
+      dumpModuleOwnership(OS, D);
     }
 #define FLAG(fn, name)                                                         \
   if (D->fn())                                                                 \
@@ -1980,6 +1999,7 @@
       FLAG(hasConstexprDefaultConstructor, constexpr);
       FLAG(needsImplicitDefaultConstructor, needs_implicit);
       FLAG(defaultedDefaultConstructorIsConstexpr, defaulted_is_constexpr);
+      dumpModuleOwnership(OS, D);
     });
 
     AddChild([=] {
@@ -1998,6 +2018,7 @@
       if (!D->needsOverloadResolutionForCopyConstructor())
         FLAG(defaultedCopyConstructorIsDeleted, defaulted_is_deleted);
       FLAG(implicitCopyConstructorHasConstParam, implicit_has_const_param);
+      dumpModuleOwnership(OS, D);
     });
 
     AddChild([=] {
@@ -2015,6 +2036,7 @@
            needs_overload_resolution);
       if (!D->needsOverloadResolutionForMoveConstructor())
         FLAG(defaultedMoveConstructorIsDeleted, defaulted_is_deleted);
+      dumpModuleOwnership(OS, D);
     });
 
     AddChild([=] {
@@ -2030,6 +2052,7 @@
       FLAG(needsImplicitCopyAssignment, needs_implicit);
       FLAG(needsOverloadResolutionForCopyAssignment, needs_overload_resolution);
       FLAG(implicitCopyAssignmentHasConstParam, implicit_has_const_param);
+      dumpModuleOwnership(OS, D);
     });
 
     AddChild([=] {
@@ -2044,6 +2067,7 @@
       FLAG(hasUserDeclaredMoveAssignment, user_declared);
       FLAG(needsImplicitMoveAssignment, needs_implicit);
       FLAG(needsOverloadResolutionForMoveAssignment, needs_overload_resolution);
+      dumpModuleOwnership(OS, D);
     });
 
     AddChild([=] {
@@ -2061,6 +2085,7 @@
       FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution);
       if (!D->needsOverloadResolutionForDestructor())
         FLAG(defaultedDestructorIsDeleted, defaulted_is_deleted);
+      dumpModuleOwnership(OS, D);
     });
   });
 
@@ -2072,20 +2097,24 @@
       dumpType(I.getType());
       if (I.isPackExpansion())
         OS << "...";
+      dumpModuleOwnership(OS, D);
     });
   }
 }
 
 void TextNodeDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) {
   dumpName(D);
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitClassTemplateDecl(const ClassTemplateDecl *D) {
   dumpName(D);
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitVarTemplateDecl(const VarTemplateDecl *D) {
   dumpName(D);
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) {
@@ -2109,6 +2138,7 @@
   if (D->isParameterPack())
     OS << " ...";
   dumpName(D);
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitNonTypeTemplateParmDecl(
@@ -2118,6 +2148,7 @@
   if (D->isParameterPack())
     OS << " ...";
   dumpName(D);
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitTemplateTemplateParmDecl(
@@ -2126,6 +2157,7 @@
   if (D->isParameterPack())
     OS << " ...";
   dumpName(D);
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) {
@@ -2133,6 +2165,7 @@
   if (D->getQualifier())
     D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy());
   OS << D->getDeclName();
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) {
@@ -2160,6 +2193,7 @@
 void TextNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *D) {
   OS << ' ';
   dumpBareDeclRef(D->getTargetDecl());
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitConstructorUsingShadowDecl(
@@ -2206,6 +2240,7 @@
 void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) {
   if (TypeSourceInfo *T = D->getFriendType())
     dumpType(T->getType());
+  dumpModuleOwnership(OS, D);
 }
 
 void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) {
Index: clang/lib/AST/Decl.cpp
===================================================================
--- clang/lib/AST/Decl.cpp
+++ clang/lib/AST/Decl.cpp
@@ -590,6 +590,7 @@
   case Decl::ModuleOwnershipKind::Unowned:
   case Decl::ModuleOwnershipKind::ReachableWhenImported:
   case Decl::ModuleOwnershipKind::ModulePrivate:
+  case Decl::ModuleOwnershipKind::ModuleDiscardable:
     return false;
   case Decl::ModuleOwnershipKind::Visible:
   case Decl::ModuleOwnershipKind::VisibleWhenImported:
Index: clang/include/clang/Sema/Sema.h
===================================================================
--- clang/include/clang/Sema/Sema.h
+++ clang/include/clang/Sema/Sema.h
@@ -2272,7 +2272,55 @@
 
   bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind);
 
+  /// When checking for Global Module Fragment decls that need to be made
+  /// reachable we also need to consider the types used.  This set is used to
+  /// ensure that we only visit each type once.
+  llvm::SmallPtrSet<const clang::Type *, 32> GMFTypesVisited;
+
+  /// Examine a type, unwrapping any decls that might be in the GMF.
+  void findGMFReachableDeclsForType(const QualType& Q);
+
+  /// Walk a statement/expression tree looking for cases that require GMF decls
+  /// to be made reachable.
+  void findGMFReachableDeclExprs(clang::Stmt *S);
+
+  /// Starting from a decl D mark any additional decls in the GMF this makes
+  /// reachable.
+  void markGMFDeclsReachableFrom(Decl *D);
+
+  // We have marked some decl as used (or referenced) and therefore (if we are
+  // in a module purview, and there is a GMF) we need to see if that use or
+  // reference requires us to make GMF decl(s) reachable.
+  void handleGMFDeclsReachableFrom(Decl *D) {
+    // Try to use an early exit for the cases we do not need to consider.
+    if (!getLangOpts().CPlusPlusModules || !GlobalModuleFragment ||
+        !isCurrentModulePurview())
+      return;
+
+    // We only need to make the decls reachable, not visible.
+    markGMFDeclsReachableFrom(D);
+  }
+
 public:
+  /// To implement C++20 GMF elision semantics, we initially mark decls in the
+  /// Global Module Fragment as "ModuleDiscardable".  If the decl is then used
+  /// within the module purview, we reset that to visible.
+  void setDeclIsUsed(Decl *D) {
+    D->setIsUsed();
+    handleGMFDeclsReachableFrom(D);
+  }
+
+  /// Likewise when the decl is marked used and notifies mutation listeners.
+  void markDeclUsed(Decl *D, ASTContext &C) {
+    D->markUsed(C);
+    handleGMFDeclsReachableFrom(D);
+  }
+
+  void setDeclReferenced(Decl *D, bool R = true) {
+    D->setReferenced(R);
+    handleGMFDeclsReachableFrom(D);
+  }
+
   /// Get the module unit whose scope we are currently within.
   Module *getCurrentModule() const {
     return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module;
Index: clang/include/clang/AST/DeclBase.h
===================================================================
--- clang/include/clang/AST/DeclBase.h
+++ clang/include/clang/AST/DeclBase.h
@@ -232,9 +232,12 @@
 
     /// This declaration has an owning module, but is only visible to
     /// lookups that occur within that module.
-    /// The discarded declarations in global module fragment belongs
-    /// to this group too.
-    ModulePrivate
+    ModulePrivate,
+
+    /// This declaration is part of a Global Module Fragment, it is permitted
+    /// to discard it and therefore it is not reachable or visible to importers
+    /// of the named module of which the GMF is part.
+    ModuleDiscardable
   };
 
 protected:
@@ -594,6 +597,12 @@
 
   void setReferenced(bool R = true) { Referenced = R; }
 
+  /// Whether this declaration should be retained if it is used.
+  bool isModuleDiscardable() const {
+    return getModuleOwnershipKind() ==
+           Decl::ModuleOwnershipKind::ModuleDiscardable;
+  }
+
   /// Whether this declaration is a top-level declaration (function,
   /// global variable, etc.) that is lexically inside an objc container
   /// definition.
@@ -633,9 +642,10 @@
     return getModuleOwnershipKind() > ModuleOwnershipKind::VisibleWhenImported;
   }
 
-  /// FIXME: Implement discarding declarations actually in global module
-  /// fragment. See [module.global.frag]p3,4 for details.
-  bool isDiscardedInGlobalModuleFragment() const { return false; }
+  /// See [module.global.frag]p3,4 for details.
+  bool isDiscardedInGlobalModuleFragment() const {
+    return getModuleOwnershipKind() == ModuleOwnershipKind::ModuleDiscardable;
+  }
 
   /// Return true if this declaration has an attribute which acts as
   /// definition of the entity, such as 'alias' or 'ifunc'.
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to