https://github.com/Vipul-Cariappa updated 
https://github.com/llvm/llvm-project/pull/210893

>From b2d904db0c0a2832ced8979687bdbefef6777984 Mon Sep 17 00:00:00 2001
From: Vipul Cariappa <[email protected]>
Date: Mon, 20 Jul 2026 11:02:25 +0530
Subject: [PATCH] [clang] delay parsing of all templated & internal-linkage
 functions

The function bodies are only parsed if the function is used.
Otherwise it remains unparsed.
---
 .../clang/Basic/DiagnosticDriverKinds.td      |   5 +
 clang/include/clang/Basic/LangOptions.def     |   1 +
 clang/include/clang/Options/Options.td        |   6 +
 clang/include/clang/Sema/Sema.h               |  26 +-
 clang/lib/Driver/ToolChains/Clang.cpp         |   4 +
 clang/lib/Frontend/CompilerInvocation.cpp     |  25 +
 clang/lib/Parse/ParseCXXInlineMethods.cpp     |  27 +-
 clang/lib/Parse/Parser.cpp                    | 119 ++--
 clang/lib/Sema/Sema.cpp                       |   6 +
 clang/lib/Sema/SemaExpr.cpp                   |   9 +
 clang/lib/Sema/SemaLookup.cpp                 | 100 ++-
 clang/lib/Sema/SemaTemplate.cpp               |  23 +
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  |  10 +
 clang/test/Parser/ParseFunctionsOndemand.cpp  | 602 ++++++++++++++++++
 14 files changed, 880 insertions(+), 83 deletions(-)
 create mode 100644 clang/test/Parser/ParseFunctionsOndemand.cpp

diff --git a/clang/include/clang/Basic/DiagnosticDriverKinds.td 
b/clang/include/clang/Basic/DiagnosticDriverKinds.td
index ae3eecc60fc78..995e2153c81b3 100644
--- a/clang/include/clang/Basic/DiagnosticDriverKinds.td
+++ b/clang/include/clang/Basic/DiagnosticDriverKinds.td
@@ -755,6 +755,11 @@ def warn_drv_fine_grained_bitfield_accesses_ignored : 
Warning<
   "option '-ffine-grained-bitfield-accesses' cannot be enabled together with a 
sanitizer; flag ignored">,
   InGroup<OptionIgnored>;
 
+def warn_drv_parse_functions_ondemand_ignored : Warning<
+  "option '-fparse-functions-ondemand' is not supported when generating a "
+  "%select{precompiled header|module}0; flag ignored">,
+  InGroup<OptionIgnored>;
+
 def err_drv_profile_instrument_use_path_with_no_kind : Error<
   "option '-fprofile-instrument-use-path=' requires 
-fprofile-instrument-use=<kind>">;
 
diff --git a/clang/include/clang/Basic/LangOptions.def 
b/clang/include/clang/Basic/LangOptions.def
index 3d63b9677e4df..648832c72be32 100644
--- a/clang/include/clang/Basic/LangOptions.def
+++ b/clang/include/clang/Basic/LangOptions.def
@@ -340,6 +340,7 @@ ENUM_LANGOPT(AddressSpaceMapMangling , 
AddrSpaceMapMangling, 2, ASMM_Target, Not
 LANGOPT(IncludeDefaultHeader, 1, 0, NotCompatible, "Include default header 
file for OpenCL")
 LANGOPT(DeclareOpenCLBuiltins, 1, 0, NotCompatible, "Declare OpenCL builtin 
functions")
 LANGOPT(DelayedTemplateParsing , 1, 0, Benign, "delayed template parsing")
+LANGOPT(ParseFunctionsOnDemand , 1, 0, Benign, "on-demand function parsing")
 LANGOPT(BlocksRuntimeOptional , 1, 0, NotCompatible, "optional blocks runtime")
 LANGOPT(
     CompleteMemberPointers, 1, 0, NotCompatible,
diff --git a/clang/include/clang/Options/Options.td 
b/clang/include/clang/Options/Options.td
index 41848b18f2e1b..81682b2c0b3bf 100644
--- a/clang/include/clang/Options/Options.td
+++ b/clang/include/clang/Options/Options.td
@@ -3618,6 +3618,12 @@ defm delayed_template_parsing : 
BoolFOption<"delayed-template-parsing",
           "Parse templated function definitions at the end of the translation 
unit">,
   NegFlag<SetFalse, [], [], "Disable delayed template parsing">,
   BothFlags<[], [ClangOption, CLOption]>>;
+defm parse_functions_ondemand : BoolFOption<"parse-functions-ondemand",
+  LangOpts<"ParseFunctionsOnDemand">, DefaultFalse,
+  PosFlag<SetTrue, [], [ClangOption, CC1Option],
+          "Parse internal-linkage function definitions on demand">,
+  NegFlag<SetFalse, [], [], "Disable on-demand function parsing">,
+  BothFlags<[], [ClangOption, CLOption]>>;
 def fms_memptr_rep_EQ : Joined<["-"], "fms-memptr-rep=">, Group<f_Group>,
   Visibility<[ClangOption, CC1Option]>,
   Values<"single,multiple,virtual">, NormalizedValuesScope<"LangOptions">,
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 8a30f6319bcef..6030f22016e34 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -12554,6 +12554,7 @@ class Sema final : public SemaBase {
   void MarkAsLateParsedTemplate(FunctionDecl *FD, Decl *FnD,
                                 CachedTokens &Toks);
   void UnmarkAsLateParsedTemplate(FunctionDecl *FD);
+  bool ParseLateFunctionDefinition(FunctionDecl *FD);
   bool IsInsideALocalClassWithinATemplateFunction();
 
   /// We've found a use of a templated declaration that would trigger an
@@ -14229,27 +14230,18 @@ class Sema final : public SemaBase {
         return;
 
       // Restore the set of pending vtables.
-      assert(S.VTableUses.empty() &&
-             "VTableUses should be empty before it is discarded.");
       S.VTableUses.swap(S.SavedVTableUses.back());
+      S.VTableUses.insert(S.VTableUses.end(), S.SavedVTableUses.back().begin(),
+                          S.SavedVTableUses.back().end());
       S.SavedVTableUses.pop_back();
 
       // Restore the set of pending implicit instantiations.
-      if ((S.TUKind != TU_Prefix || !S.LangOpts.PCHInstantiateTemplates) &&
-          AtEndOfTU) {
-        assert(S.PendingInstantiations.empty() &&
-               "PendingInstantiations should be empty before it is 
discarded.");
-        S.PendingInstantiations.swap(S.SavedPendingInstantiations.back());
-        S.SavedPendingInstantiations.pop_back();
-      } else {
-        // Template instantiations in the PCH may be delayed until the TU.
-        S.PendingInstantiations.swap(S.SavedPendingInstantiations.back());
-        S.PendingInstantiations.insert(
-            S.PendingInstantiations.end(),
-            S.SavedPendingInstantiations.back().begin(),
-            S.SavedPendingInstantiations.back().end());
-        S.SavedPendingInstantiations.pop_back();
-      }
+      S.PendingInstantiations.swap(S.SavedPendingInstantiations.back());
+      S.PendingInstantiations.insert(
+          S.PendingInstantiations.end(),
+          S.SavedPendingInstantiations.back().begin(),
+          S.SavedPendingInstantiations.back().end());
+      S.SavedPendingInstantiations.pop_back();
     }
 
     GlobalEagerInstantiationScope(const GlobalEagerInstantiationScope &) =
diff --git a/clang/lib/Driver/ToolChains/Clang.cpp 
b/clang/lib/Driver/ToolChains/Clang.cpp
index 660e61d7c5de3..734de2e98d1f6 100644
--- a/clang/lib/Driver/ToolChains/Clang.cpp
+++ b/clang/lib/Driver/ToolChains/Clang.cpp
@@ -7846,6 +7846,10 @@ void Clang::ConstructJob(Compilation &C, const JobAction 
&JA,
     CmdArgs.push_back("-fdelayed-template-parsing");
   }
 
+  if (Args.hasFlag(options::OPT_fparse_functions_ondemand,
+                   options::OPT_fno_parse_functions_ondemand, false))
+    CmdArgs.push_back("-fparse-functions-ondemand");
+
   if (Args.hasFlag(options::OPT_fpch_validate_input_files_content,
                    options::OPT_fno_pch_validate_input_files_content, false))
     CmdArgs.push_back("-fvalidate-ast-input-files-content");
diff --git a/clang/lib/Frontend/CompilerInvocation.cpp 
b/clang/lib/Frontend/CompilerInvocation.cpp
index 6562cb3a9b135..baab5bd4d83cc 100644
--- a/clang/lib/Frontend/CompilerInvocation.cpp
+++ b/clang/lib/Frontend/CompilerInvocation.cpp
@@ -4126,6 +4126,11 @@ bool CompilerInvocation::ParseLangArgs(LangOptions 
&Opts, ArgList &Args,
 #include "clang/Options/Options.inc"
 #undef LANG_OPTION_WITH_MARSHALLING
 
+  // delayed parsing has incompatible lookup semantics with ondemand parsing
+  if (Opts.ParseFunctionsOnDemand && Opts.DelayedTemplateParsing)
+    Diags.Report(diag::err_drv_argument_not_allowed_with)
+        << "-fparse-functions-ondemand" << "-fdelayed-template-parsing";
+
   // "Modules semantics" (e.g. cross-translation-unit declaration merging) are
   // needed for both Clang (header) modules and C++20 modules, so enable them
   // for either.
@@ -5140,6 +5145,26 @@ bool CompilerInvocation::CreateFromArgsImpl(
   if (Res.getFrontendOpts().ProgramAction == frontend::RewriteObjC)
     LangOpts.ObjCExceptions = 1;
 
+  if (LangOpts.ParseFunctionsOnDemand) {
+    switch (Res.getFrontendOpts().ProgramAction) {
+    case frontend::GeneratePCH:
+      LangOpts.ParseFunctionsOnDemand = false;
+      Diags.Report(diag::warn_drv_parse_functions_ondemand_ignored)
+          << /*precompiled header*/ 0;
+      break;
+    case frontend::GenerateModule:
+    case frontend::GenerateModuleInterface:
+    case frontend::GenerateReducedModuleInterface:
+    case frontend::GenerateHeaderUnit:
+      LangOpts.ParseFunctionsOnDemand = false;
+      Diags.Report(diag::warn_drv_parse_functions_ondemand_ignored)
+          << /*module*/ 1;
+      break;
+    default:
+      break;
+    }
+  }
+
   for (auto Warning : Res.getDiagnosticOpts().Warnings) {
     if (Warning == "misexpect" &&
         !Diags.isIgnored(diag::warn_profile_data_misexpect, SourceLocation())) 
{
diff --git a/clang/lib/Parse/ParseCXXInlineMethods.cpp 
b/clang/lib/Parse/ParseCXXInlineMethods.cpp
index be531e567046e..8a589970a99bf 100644
--- a/clang/lib/Parse/ParseCXXInlineMethods.cpp
+++ b/clang/lib/Parse/ParseCXXInlineMethods.cpp
@@ -144,24 +144,31 @@ NamedDecl *Parser::ParseCXXInlineMethodDef(
     return FnD;
   }
 
+  FunctionDecl *FD = FnD ? FnD->getAsFunction() : nullptr;
+  bool IsDelayedTemplateFunction =
+      Actions.CurContext->isDependentContext() ||
+      (TemplateInfo.Kind != ParsedTemplateKind::NonTemplate &&
+       TemplateInfo.Kind != ParsedTemplateKind::ExplicitSpecialization);
+
   // In delayed template parsing mode, if we are within a class template
   // or if we are about to parse function member template then consume
   // the tokens and store them for parsing at the end of the translation unit.
-  if (getLangOpts().DelayedTemplateParsing &&
-      D.getFunctionDefinitionKind() == FunctionDefinitionKind::Definition &&
+  // In on-demand parsing mode, internal-linkage member definitions are also
+  // handled in the same way.
+  if (D.getFunctionDefinitionKind() == FunctionDefinitionKind::Definition &&
       !D.getDeclSpec().hasConstexprSpecifier() &&
-      !(FnD && FnD->getAsFunction() &&
-        FnD->getAsFunction()->getReturnType()->getContainedAutoType()) &&
-      ((Actions.CurContext->isDependentContext() ||
-        (TemplateInfo.Kind != ParsedTemplateKind::NonTemplate &&
-         TemplateInfo.Kind != ParsedTemplateKind::ExplicitSpecialization)) &&
-       !Actions.IsInsideALocalClassWithinATemplateFunction())) {
+      !(FD && FD->getReturnType()->getContainedAutoType()) &&
+      !Actions.IsInsideALocalClassWithinATemplateFunction() &&
+      ((getLangOpts().DelayedTemplateParsing && IsDelayedTemplateFunction) ||
+       (getLangOpts().ParseFunctionsOnDemand &&
+        ((FD && !FD->isExternallyVisible() &&
+          FD->isDefinedOutsideFunctionOrMethod()) ||
+         IsDelayedTemplateFunction)))) {
 
     CachedTokens Toks;
     LexTemplateFunctionForLateParsing(Toks);
 
-    if (FnD) {
-      FunctionDecl *FD = FnD->getAsFunction();
+    if (FD) {
       Actions.CheckForFunctionRedefinition(FD);
       Actions.MarkAsLateParsedTemplate(FD, FnD, Toks);
     }
diff --git a/clang/lib/Parse/Parser.cpp b/clang/lib/Parse/Parser.cpp
index 6a21acd9dc4ef..c6b6c5cc4a890 100644
--- a/clang/lib/Parse/Parser.cpp
+++ b/clang/lib/Parse/Parser.cpp
@@ -26,6 +26,7 @@
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/SemaCodeCompletion.h"
+#include "clang/Sema/SemaOpenMP.h"
 #include "llvm/ADT/STLForwardCompat.h"
 #include "llvm/Support/Path.h"
 #include "llvm/Support/TimeProfiler.h"
@@ -1234,51 +1235,82 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator 
&D,
         Diag(AL.getLoc(), diag::warn_attribute_on_function_definition) << AL;
   }
 
-  // In delayed template parsing mode, for function template we consume the
-  // tokens and store them for late parsing at the end of the translation unit.
-  if (getLangOpts().DelayedTemplateParsing && Tok.isNot(tok::equal) &&
-      TemplateInfo.Kind == ParsedTemplateKind::Template &&
-      LateParsedAttrs->empty() && Actions.canDelayFunctionBody(D)) {
-    MultiTemplateParamsArg 
TemplateParameterLists(*TemplateInfo.TemplateParams);
+  MultiTemplateParamsArg TemplateParameterLists(
+      TemplateInfo.TemplateParams ? *TemplateInfo.TemplateParams
+                                  : MultiTemplateParamsArg{});
 
-    ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
-                                   Scope::CompoundStmtScope);
-    Scope *ParentScope = getCurScope()->getParent();
+  bool CanDelayFunctionBody = Actions.canDelayFunctionBody(D);
+  bool IsDelayedTemplateFunction =
+      getLangOpts().DelayedTemplateParsing &&
+      TemplateInfo.Kind == ParsedTemplateKind::Template && 
CanDelayFunctionBody;
+  bool ShouldProbeInternalLinkage = getLangOpts().ParseFunctionsOnDemand &&
+                                    !CurParsedObjCImpl && CanDelayFunctionBody;
 
-    D.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
-    Decl *DP = Actions.HandleDeclarator(ParentScope, D,
-                                        TemplateParameterLists);
-    D.complete(DP);
-    D.getMutableDeclSpec().abort();
+  // Enter a scope for the function body.
+  ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
+                                 Scope::CompoundStmtScope);
+  Scope *ParentScope = getCurScope()->getParent();
 
-    if (SkipFunctionBodies && (!DP || Actions.canSkipFunctionBody(DP)) &&
-        trySkippingFunctionBody()) {
-      BodyScope.Exit();
-      return Actions.ActOnSkippedFunctionBody(DP);
-    }
+  D.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
+
+  // Check and handle if we are in an `omp begin/end declare variant` scope.
+  SmallVector<FunctionDecl *, 4> Bases;
+  if (getLangOpts().OpenMP && Actions.OpenMP().isInOpenMPDeclareVariantScope())
+    Actions.OpenMP().ActOnStartOfFunctionDefinitionInOpenMPDeclareVariantScope(
+        ParentScope, D, TemplateParameterLists, Bases);
+
+  Decl *FuncDecl =
+      Actions.HandleDeclarator(ParentScope, D, TemplateParameterLists);
+
+  // Break out of the ParsingDeclarator context before we parse the body.
+  D.complete(FuncDecl);
+
+  // Break out of the ParsingDeclSpec context, too.  This const_cast is
+  // safe because we're always the sole owner.
+  D.getMutableDeclSpec().abort();
+
+  // Finish the OpenMP declare-variant handling on any early-return path.
+  auto FinishDeclareVariant = [&](Decl *D) {
+    if (!Bases.empty())
+      Actions.OpenMP()
+          .ActOnFinishedFunctionDefinitionInOpenMPDeclareVariantScope(D, 
Bases);
+  };
+
+  // For function templates in delayed template parsing mode, and for
+  // internal-linkage functions in on-demand parsing mode, consume the tokens
+  // and store them for late parsing.
+  if (Tok.isNot(tok::equal) && LateParsedAttrs->empty() &&
+      (IsDelayedTemplateFunction || ShouldProbeInternalLinkage)) {
+    FunctionDecl *FnD = FuncDecl ? FuncDecl->getAsFunction() : nullptr;
+    bool ShouldDelayFunction =
+        !FnD->isUsed(/*CheckUsedAttr=*/true) &&
+        FnD->isDefinedOutsideFunctionOrMethod() &&
+        (IsDelayedTemplateFunction || (FnD && !FnD->isExternallyVisible()) ||
+         TemplateInfo.Kind == ParsedTemplateKind::Template);
+    if (ShouldDelayFunction) {
+      if (SkipFunctionBodies &&
+          (!FuncDecl || Actions.canSkipFunctionBody(FuncDecl)) &&
+          trySkippingFunctionBody()) {
+        BodyScope.Exit();
+        FinishDeclareVariant(FuncDecl);
+        return Actions.ActOnSkippedFunctionBody(FuncDecl);
+      }
 
-    CachedTokens Toks;
-    LexTemplateFunctionForLateParsing(Toks);
+      CachedTokens Toks;
+      LexTemplateFunctionForLateParsing(Toks);
 
-    if (DP) {
-      FunctionDecl *FnD = DP->getAsFunction();
-      Actions.CheckForFunctionRedefinition(FnD);
-      Actions.MarkAsLateParsedTemplate(FnD, DP, Toks);
+      if (FnD) {
+        Actions.CheckForFunctionRedefinition(FnD);
+        Actions.MarkAsLateParsedTemplate(FnD, FuncDecl, Toks);
+      }
+      FinishDeclareVariant(FuncDecl);
+      return FuncDecl;
     }
-    return DP;
   }
+
   if (CurParsedObjCImpl && !TemplateInfo.TemplateParams &&
       (Tok.is(tok::l_brace) || Tok.is(tok::kw_try) || Tok.is(tok::colon)) &&
       Actions.CurContext->isTranslationUnit()) {
-    ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
-                                   Scope::CompoundStmtScope);
-    Scope *ParentScope = getCurScope()->getParent();
-
-    D.setFunctionDefinitionKind(FunctionDefinitionKind::Definition);
-    Decl *FuncDecl = Actions.HandleDeclarator(ParentScope, D,
-                                              MultiTemplateParamsArg());
-    D.complete(FuncDecl);
-    D.getMutableDeclSpec().abort();
     if (FuncDecl) {
       // Consume the tokens and store them for later parsing.
       StashAwayMethodOrFunctionBodyTokens(FuncDecl);
@@ -1288,10 +1320,6 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator 
&D,
     // FIXME: Should we really fall through here?
   }
 
-  // Enter a scope for the function body.
-  ParseScope BodyScope(this, Scope::FnScope | Scope::DeclScope |
-                                 Scope::CompoundStmtScope);
-
   // Parse function body eagerly if it is either '= delete;' or '= default;' as
   // ActOnStartOfFunctionDef needs to know whether the function is deleted.
   StringLiteral *DeletedMessage = nullptr;
@@ -1336,11 +1364,9 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator 
&D,
   // Tell the actions module that we have entered a function definition with 
the
   // specified Declarator for the function.
   SkipBodyInfo SkipBody;
-  Decl *Res = Actions.ActOnStartOfFunctionDef(getCurScope(), D,
-                                              TemplateInfo.TemplateParams
-                                                  ? 
*TemplateInfo.TemplateParams
-                                                  : MultiTemplateParamsArg(),
+  Decl *Res = Actions.ActOnStartOfFunctionDef(getCurScope(), FuncDecl,
                                               &SkipBody, BodyKind);
+  FinishDeclareVariant(Res);
 
   if (SkipBody.ShouldSkip) {
     // Do NOT enter SkipFunctionBody if we already consumed the tokens.
@@ -1361,13 +1387,6 @@ Decl *Parser::ParseFunctionDefinition(ParsingDeclarator 
&D,
     return Res;
   }
 
-  // Break out of the ParsingDeclarator context before we parse the body.
-  D.complete(Res);
-
-  // Break out of the ParsingDeclSpec context, too.  This const_cast is
-  // safe because we're always the sole owner.
-  D.getMutableDeclSpec().abort();
-
   if (BodyKind != Sema::FnBodyKind::Other) {
     Actions.SetFunctionBodyKind(Res, KWLoc, BodyKind, DeletedMessage);
     Stmt *GeneratedBody = Res ? Res->getBody() : nullptr;
diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp
index 9f962912148ab..cdc17bae67a18 100644
--- a/clang/lib/Sema/Sema.cpp
+++ b/clang/lib/Sema/Sema.cpp
@@ -1276,6 +1276,12 @@ void 
Sema::ActOnEndOfTranslationUnitFragment(TUFragmentKind Kind) {
   {
     llvm::TimeTraceScope TimeScope("PerformPendingInstantiations");
     PerformPendingInstantiations();
+
+    // PerformPendingInstantiations can serialize more definitions
+    while (!VTableUses.empty() || !PendingInstantiations.empty()) {
+      DefineUsedVTables();
+      PerformPendingInstantiations();
+    }
   }
 
   emitDeferredDiags();
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index 67d9ac4ad5cff..6b1aa597d2754 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -19018,6 +19018,15 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, 
FunctionDecl *Func,
   if (getLangOpts().CUDA)
     CUDA().CheckCall(Loc, Func);
 
+  if (const FunctionDecl *Definition;
+      getLangOpts().ParseFunctionsOnDemand && NeedDefinition &&
+      Func->hasBody(Definition) && !Definition->getBody() &&
+      Definition->isLateTemplateParsed()) {
+    FunctionDecl *LateParsedFD = const_cast<FunctionDecl *>(Definition);
+    LateParsedFD->setInstantiationIsPending(true);
+    PendingInstantiations.push_back(std::make_pair(LateParsedFD, Loc));
+  }
+
   // If we need a definition, try to create one.
   if (NeedDefinition && !Func->getBody()) {
     runWithSufficientStackSpace(Loc, [&] {
diff --git a/clang/lib/Sema/SemaLookup.cpp b/clang/lib/Sema/SemaLookup.cpp
index 43129800e9813..38280a9e82546 100644
--- a/clang/lib/Sema/SemaLookup.cpp
+++ b/clang/lib/Sema/SemaLookup.cpp
@@ -12,6 +12,7 @@
 
//===----------------------------------------------------------------------===//
 
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
@@ -1125,6 +1126,85 @@ static void DeclareImplicitMemberFunctionsWithName(Sema 
&S,
   }
 }
 
+static bool IsVisibleAtLateParsedLookupPoint(Sema &S, SourceLocation LookupLoc,
+                                             NamedDecl *D) {
+  if (!S.getLangOpts().ParseFunctionsOnDemand)
+    return true;
+
+  if (D->isImplicit())
+    return true;
+
+  // We are substituting template types.
+  // Example:
+  // template <typename T>
+  // typename T::result foo(T &callback) { return callback(); }
+  // struct CallMe {
+  //   using result = int;
+  //   result operator()() { return 1; }
+  // } callback;
+  // foo(callback);
+  //
+  // Lookup of CallMe::result needs to succeesd even thought
+  // CallMe::result is declared "after" the template being parsed
+  if (!S.CodeSynthesisContexts.empty() && isa<TypeDecl>(D)) {
+    switch (S.CodeSynthesisContexts.back().Kind) {
+    case Sema::CodeSynthesisContext::ExplicitTemplateArgumentSubstitution:
+    case Sema::CodeSynthesisContext::DeducedTemplateArgumentSubstitution:
+    case Sema::CodeSynthesisContext::LambdaExpressionSubstitution:
+    case Sema::CodeSynthesisContext::PriorTemplateArgumentSubstitution:
+    case Sema::CodeSynthesisContext::ConstraintSubstitution:
+    case Sema::CodeSynthesisContext::ParameterMappingSubstitution:
+      return true;
+    default:
+      break;
+    }
+  }
+
+  // lambda call operator is accessible from everywhere
+  if (auto *CXXMD = dyn_cast<CXXMethodDecl>(D);
+      CXXMD && isLambdaCallOperator(CXXMD))
+    return true;
+
+  auto *FD = dyn_cast_or_null<FunctionDecl>(S.CurContext);
+  if (!FD || !FD->isLateTemplateParsed())
+    return true;
+
+  SourceLocation DeclLoc = D->getCanonicalDecl()->getLocation();
+
+  // can this be an assert on valid source locations at this point?
+  if (DeclLoc.isInvalid() || LookupLoc.isInvalid())
+    return true;
+
+  if (DeclLoc == LookupLoc ||
+      S.getSourceManager().isBeforeInTranslationUnit(DeclLoc, LookupLoc))
+    return true;
+
+  const CXXRecordDecl *EnclosingRD = nullptr;
+  if (auto *CXXMD = dyn_cast<CXXMethodDecl>(FD))
+    EnclosingRD = CXXMD->getParent();
+  else if (FD->getFriendObjectKind() != Decl::FriendObjectKind::FOK_None)
+    EnclosingRD = dyn_cast<CXXRecordDecl>(FD->getLexicalDeclContext());
+
+  for (auto *RD = dyn_cast_or_null<RecordDecl>(
+           EnclosingRD ? EnclosingRD->getDefinition() : nullptr);
+       RD; RD = isa_and_nonnull<RecordDecl>(RD->getParent())
+                    ? dyn_cast<RecordDecl>(RD->getParent())->getDefinition()
+                    : nullptr) {
+    if (RD->getEndLoc().isInvalid() ||
+        S.getSourceManager().isBeforeInTranslationUnit(DeclLoc,
+                                                       RD->getEndLoc()))
+      return true;
+  }
+  return false;
+}
+
+static bool IsVisibleAtLateParsedLookupPoint(Sema &S, LookupResult &R,
+                                             NamedDecl *D) {
+  auto res =
+      IsVisibleAtLateParsedLookupPoint(S, R.getLookupNameInfo().getLoc(), D);
+  return res;
+}
+
 // Adds all qualifying matches for a name within a decl context to the
 // given lookup result.  Returns true if any matches were found.
 static bool LookupDirect(Sema &S, LookupResult &R, const DeclContext *DC) {
@@ -1139,8 +1219,10 @@ static bool LookupDirect(Sema &S, LookupResult &R, const 
DeclContext *DC) {
   DeclContext::lookup_result DR = DC->lookup(R.getLookupName());
   for (NamedDecl *D : DR) {
     if ((D = R.getAcceptableDecl(D))) {
-      R.addDecl(D);
-      Found = true;
+      if (IsVisibleAtLateParsedLookupPoint(S, R, D)) {
+        R.addDecl(D);
+        Found = true;
+      }
     }
   }
 
@@ -1347,7 +1429,8 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
     bool SearchNamespaceScope = true;
     // Check whether the IdResolver has anything in this scope.
     for (; I != IEnd && S->isDeclScope(*I); ++I) {
-      if (NamedDecl *ND = R.getAcceptableDecl(*I)) {
+      if (NamedDecl *ND = R.getAcceptableDecl(*I);
+          ND && IsVisibleAtLateParsedLookupPoint(*this, R, ND)) {
         if (NameKind == LookupRedeclarationWithLinkage &&
             !(*I)->isTemplateParameter()) {
           // If it's a template parameter, we still find it, so we can diagnose
@@ -1500,7 +1583,8 @@ bool Sema::CppLookupName(LookupResult &R, Scope *S) {
     // Check whether the IdResolver has anything in this scope.
     bool Found = false;
     for (; I != IEnd && S->isDeclScope(*I); ++I) {
-      if (NamedDecl *ND = R.getAcceptableDecl(*I)) {
+      if (NamedDecl *ND = R.getAcceptableDecl(*I);
+          ND && IsVisibleAtLateParsedLookupPoint(*this, R, ND)) {
         // We found something.  Look for anything else in our scope
         // with this same name and in an acceptable identifier
         // namespace, so that we can construct an overload set if we
@@ -2243,7 +2327,8 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool 
AllowBuiltinCreation,
     for (IdentifierResolver::iterator I = IdResolver.begin(Name),
                                    IEnd = IdResolver.end();
          I != IEnd; ++I)
-      if (NamedDecl *D = R.getAcceptableDecl(*I)) {
+      if (NamedDecl *D = R.getAcceptableDecl(*I);
+          D && IsVisibleAtLateParsedLookupPoint(*this, R, D)) {
         if (NameKind == LookupRedeclarationWithLinkage) {
           // Determine whether this (or a previous) declaration is
           // out-of-scope.
@@ -2297,7 +2382,8 @@ bool Sema::LookupName(LookupResult &R, Scope *S, bool 
AllowBuiltinCreation,
             }
 
             // If the declaration is in the right namespace and visible, add 
it.
-            if (NamedDecl *LastD = R.getAcceptableDecl(*LastI))
+            if (NamedDecl *LastD = R.getAcceptableDecl(*LastI);
+                LastD && IsVisibleAtLateParsedLookupPoint(*this, R, LastD))
               R.addDecl(LastD);
           }
 
@@ -3919,6 +4005,8 @@ void Sema::ArgumentDependentLookup(DeclarationName Name, 
SourceLocation Loc,
       if (!isa<FunctionDecl>(Underlying) &&
           !isa<FunctionTemplateDecl>(Underlying))
         continue;
+      if (!IsVisibleAtLateParsedLookupPoint(*this, Loc, D))
+        continue;
 
       // The declaration is visible to argument-dependent lookup if either
       // it's ordinarily visible or declared as a friend in an associated
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 643392833759d..2adba04703d98 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -11758,6 +11758,14 @@ void Sema::MarkAsLateParsedTemplate(FunctionDecl *FD, 
Decl *FnD,
   LateParsedTemplateMap.insert(std::make_pair(FD, std::move(LPT)));
 
   FD->setLateTemplateParsed(true);
+
+  if (getLangOpts().ParseFunctionsOnDemand && !FD->instantiationIsPending() &&
+      FD->isUsed(/*CheckUsedAttr=*/false)) {
+    // This function already has a declaration, and the declaration has been
+    // MarkFunctionReferenced
+    FD->setInstantiationIsPending(true);
+    PendingInstantiations.push_back(std::make_pair(FD, FD->getLocation()));
+  }
 }
 
 void Sema::UnmarkAsLateParsedTemplate(FunctionDecl *FD) {
@@ -11766,6 +11774,21 @@ void Sema::UnmarkAsLateParsedTemplate(FunctionDecl 
*FD) {
   FD->setLateTemplateParsed(false);
 }
 
+bool Sema::ParseLateFunctionDefinition(FunctionDecl *FD) {
+  if (!LateTemplateParser)
+    return false;
+
+  if (FD->isFromASTFile() && ExternalSource)
+    ExternalSource->ReadLateParsedTemplates(LateParsedTemplateMap);
+
+  auto LPTIter = LateParsedTemplateMap.find(FD);
+  if (LPTIter == LateParsedTemplateMap.end())
+    return false;
+
+  LateTemplateParser(OpaqueParser, *LPTIter->second);
+  return FD->getBody() != nullptr;
+}
+
 bool Sema::IsInsideALocalClassWithinATemplateFunction() {
   DeclContext *DC = CurContext;
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index f1f97ca125f46..4ad1de0aad34f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -7275,6 +7275,16 @@ void Sema::PerformPendingInstantiations(bool LocalOnly, 
bool AtEndOfTU) {
 
     // Instantiate function definitions
     if (FunctionDecl *Function = dyn_cast<FunctionDecl>(Inst.first)) {
+      if (Function->isLateTemplateParsed() &&
+          (Function->getTemplatedKind() == FunctionDecl::TK_NonTemplate ||
+           Function->getTemplateSpecializationKind() ==
+               TSK_ExplicitSpecialization)) {
+        ParseLateFunctionDefinition(Function);
+        if (Function->isDefined())
+          Function->setInstantiationIsPending(false);
+        continue;
+      }
+
       bool DefinitionRequired = Function->getTemplateSpecializationKind() ==
                                 TSK_ExplicitInstantiationDefinition;
       if (Function->isMultiVersion()) {
diff --git a/clang/test/Parser/ParseFunctionsOndemand.cpp 
b/clang/test/Parser/ParseFunctionsOndemand.cpp
new file mode 100644
index 0000000000000..f21d721a1488e
--- /dev/null
+++ b/clang/test/Parser/ParseFunctionsOndemand.cpp
@@ -0,0 +1,602 @@
+// RUN: %clang_cc1 -fparse-functions-ondemand -fsyntax-only -verify -Wall 
-Wextra -Werror -ferror-limit 0 -std=c++11 %s
+// RUN: not %clang_cc1 -fparse-functions-ondemand -fdelayed-template-parsing 
-fsyntax-only -std=c++11 %s 2>&1 | FileCheck %s --check-prefix=MUTEX
+
+// MUTEX: error: invalid argument '-fparse-functions-ondemand' not allowed 
with '-fdelayed-template-parsing'
+
+// Internal-linkage bodies are parsed only when referenced; external ones 
eagerly.
+namespace InternalFunctionBodies {
+static void static_diagnoses_on_use() {
+  undeclared_static(); // expected-error {{use of undeclared identifier 
'undeclared_static'}}
+}
+
+static void prior_static();
+void prior_static() {
+  undeclared_prior_static(); // expected-error {{use of undeclared identifier 
'undeclared_prior_static'}}
+}
+
+static void unreferenced_prior_static();
+void unreferenced_prior_static() {
+  undeclared_prior_static(); // this should not give an error
+}
+
+namespace {
+void anon_namespace_function() {
+  undeclared_anon_namespace(); // expected-error {{use of undeclared 
identifier 'undeclared_anon_namespace'}}
+}
+
+void unreferenced_anon_namespace_function() {
+  undeclared_anon_namespace(); // this should not give an error
+}
+} // namespace
+
+void external_function_diagnoses_eagerly() {
+  undeclared_external(); // expected-error {{use of undeclared identifier 
'undeclared_external'}}
+}
+
+static void unreferenced_static_body_is_delayed() {
+  undeclared_but_unreferenced(); // this should not give an error
+}
+
+__attribute__((used)) static int attribute_used_body_is_parsed() {
+  return undeclared_attribute_used(); // expected-error {{use of undeclared 
identifier 'undeclared_attribute_used'}}
+}
+
+void trigger_internal_function_bodies() {
+  static_diagnoses_on_use();
+  prior_static();
+  anon_namespace_function();
+}
+} // namespace InternalFunctionBodies
+
+// A delayed body resolves overloads using only those declared before it.
+namespace SourceOrderOverloads {
+struct One {};
+struct Two {}; // expected-note 2 {{candidate constructor}}
+
+static One foo_hidden_later(int);
+static void call_before_later_overload() {
+  Two value = foo_hidden_later(0.0); // expected-error {{no viable conversion 
from 'One' to 'Two'}}
+}
+static Two foo_hidden_later(double);
+
+static Two foo_forward_declared(double);
+static void call_forward_declared_overload() {
+  Two _ = foo_forward_declared(0.0);
+}
+static Two foo_forward_declared(double);
+
+static One foo_both_before(int);
+static Two foo_both_before(double);
+static void call_both_overloads_before() {
+  Two _ = foo_both_before(0.0);
+}
+
+void trigger_source_order_overloads() {
+  call_before_later_overload();
+  call_forward_declared_overload();
+  call_both_overloads_before();
+}
+} // namespace SourceOrderOverloads
+
+// A delayed body sees namespace-scope names only if declared before it.
+namespace NamespaceScopeSourceOrder {
+static int later_variable_hidden() {
+  return later_variable; // expected-error {{use of undeclared identifier 
'later_variable'}}
+}
+static int later_variable;
+
+static int later_function_hidden() {
+  return later_function(); // expected-error {{use of undeclared identifier 
'later_function'}}
+}
+static int later_function();
+
+static int builtin_lookup_still_works() {
+  return __builtin_abs(-1);
+}
+
+void trigger_namespace_scope_source_order() {
+  later_variable_hidden();
+  later_function_hidden();
+  builtin_lookup_still_works();
+}
+} // namespace NamespaceScopeSourceOrder
+
+// Member bodies are delayed too; when parsed they see the whole class but only
+// prior namespace-scope names.
+namespace ClassMethodLookup {
+struct NormalClass {
+  void sees_later_method() {
+    later_method();
+  }
+  void later_method();
+
+  int sees_later_static_member() {
+    return later_static_member;
+  }
+  static int later_static_member;
+};
+
+namespace {
+struct InternalClass {
+  void unreferenced_body_is_delayed() {
+    undeclared_in_method(); // this should not give an error
+  }
+
+  void sees_later_member() {
+    later_member();
+  }
+  void later_member() {}
+
+  void diagnoses_on_use() {
+    undeclared_in_referenced_method(); // expected-error {{use of undeclared 
identifier 'undeclared_in_referenced_method'}}
+  }
+
+  void later_global_hidden() {
+    later_global_after_class; // expected-error {{use of undeclared identifier 
'later_global_after_class'}}
+  }
+
+  static int static_member_later_global_hidden() {
+    return later_global_after_class; // expected-error {{use of undeclared 
identifier 'later_global_after_class'}}
+  }
+
+  static int static_member_sees_later_static_member() {
+    return later_static_member;
+  }
+  static int later_static_member;
+};
+} // namespace
+
+int InternalClass::later_static_member = 10;
+int later_global_after_class = 10;
+
+void trigger_class_method_lookup() {
+  InternalClass object;
+  object.sees_later_member();
+  object.diagnoses_on_use();
+  object.later_global_hidden();
+  InternalClass::static_member_later_global_hidden();
+  InternalClass::static_member_sees_later_static_member();
+}
+} // namespace ClassMethodLookup
+
+// Out-of-line member bodies see namespace-scope names declared before the
+// out-of-line definition.
+namespace OutOfClassMethods {
+namespace {
+struct S {
+  int sees_prior_global();
+  int hides_later_global();
+};
+
+int prior_global; // expected-note {{'prior_global' declared here}}
+
+int S::sees_prior_global() {
+  return prior_global;
+}
+
+int S::hides_later_global() {
+  return later_global; // expected-error {{use of undeclared identifier 
'later_global'}}
+}
+
+int later_global; // expected-note 2 {{'OutOfClassMethods::later_global' 
declared here}}
+} // namespace
+
+void trigger_out_of_class_methods() {
+  S s;
+  s.sees_prior_global();
+  s.hides_later_global();
+}
+} // namespace OutOfClassMethods
+
+// Inline friend and friend-class member bodies follow the same prior-only
+// namespace-scope visibility.
+namespace FriendFunctionsAndClasses {
+int prior_global;
+
+namespace {
+struct FriendFunctionPrior {
+  friend int friend_function_prior(FriendFunctionPrior) {
+    return prior_global;
+  }
+};
+
+struct FriendFunctionLater {
+  friend int friend_function_later(FriendFunctionLater) {
+    return later_global; // expected-error {{use of undeclared identifier 
'later_global'}}
+  }
+};
+
+struct Host {
+  friend struct FriendClass;
+};
+
+struct FriendClass {
+  static int sees_prior_global() {
+    return prior_global;
+  }
+  static int hides_later_global() {
+    return later_global; // expected-error {{use of undeclared identifier 
'later_global'}}
+  }
+};
+} // namespace
+
+int later_global;
+
+void trigger_friend_functions_and_classes() {
+  friend_function_prior(FriendFunctionPrior{});
+  friend_function_later(FriendFunctionLater{});
+  FriendClass::sees_prior_global();
+  FriendClass::hides_later_global();
+}
+} // namespace FriendFunctionsAndClasses
+
+// ADL from a delayed body finds only functions declared before it.
+namespace ADLLookup {
+struct One {};
+struct Two {};
+
+namespace Hidden {
+struct A {};
+}
+
+static void adl_later_function_hidden() {
+  Hidden::A a;
+  Two value = adl_target(a); // expected-error {{use of undeclared identifier 
'adl_target'}}
+}
+
+namespace Hidden {
+One adl_target(A);
+}
+
+namespace Visible {
+struct A {};
+One adl_target(A);
+}
+
+static void adl_prior_function_visible() {
+  Visible::A a;
+  One _ = adl_target(a);
+}
+
+void trigger_adl_lookup() {
+  adl_later_function_hidden();
+  adl_prior_function_visible();
+}
+} // namespace ADLLookup
+
+// Qualified lookup from a delayed body finds only members declared before it.
+namespace QualifiedLookup {
+struct One {};
+struct Two {};
+
+namespace Hidden {}
+
+static void qualified_later_function_hidden() {
+  Hidden::h(); // expected-error {{no member named 'h' in namespace 
'QualifiedLookup::Hidden'}}
+}
+
+namespace Hidden {
+One h();
+}
+
+namespace Visible {
+One h();
+}
+
+static void qualified_prior_function_visible() {
+  One _ = Visible::h();
+}
+
+void trigger_qualified_lookup() {
+  qualified_later_function_hidden();
+  qualified_prior_function_visible();
+}
+} // namespace QualifiedLookup
+
+// Default arguments, noexcept, and trailing-return are parsed eagerly (at the
+// declaration), so they cannot see later names; delete/default bodies are 
fine.
+namespace DeclarationParts {
+static int default_argument(int value = default_argument_later_global) { // 
expected-error {{use of undeclared identifier 'default_argument_later_global'}}
+  return value;
+}
+int default_argument_later_global;
+
+static void noexcept_expr() noexcept(noexcept(noexcept_later_global)) {} // 
expected-error {{use of undeclared identifier 'noexcept_later_global'}}
+int noexcept_later_global;
+
+static auto trailing_return() -> decltype(trailing_return_later_global) { // 
expected-error {{use of undeclared identifier 'trailing_return_later_global'}}
+  return 0;
+}
+int trailing_return_later_global;
+
+void deleted_function() = delete;
+
+struct DefaultedConstructor {
+  DefaultedConstructor() = default;
+};
+} // namespace DeclarationParts
+
+// Local-class member bodies are parsed with their enclosing function's body.
+namespace LocalClassCases {
+void local_class_method_diagnoses_on_use() {
+  struct S {
+    void f() {
+      undeclared_in_local_class(); // expected-error {{use of undeclared 
identifier 'undeclared_in_local_class'}}
+    }
+  };
+
+  S s;
+  s.f();
+}
+
+void local_class_method_sees_local_typedef() {
+  typedef int T;
+  struct S {
+    T f() { return 0; }
+  };
+
+  S s;
+  s.f();
+}
+
+// A local-class member body can name a sibling local declaration, so it must 
be
+// parsed with its enclosing function rather than deferred and re-parsed at the
+// end of the TU (where the enclosing scope is gone).
+void local_class_method_sees_sibling_local_class() {
+  struct Helper {
+    int value() { return 7; }
+  };
+  struct User {
+    int use() {
+      Helper h; // must still resolve 'Helper'
+      return h.value();
+    }
+  };
+
+  User u;
+  u.use();
+}
+
+// Same, but the referencing member is reached through the vtable of a locally
+// constructed object.
+void local_class_virtual_method_sees_sibling_local_class() {
+  struct Helper {
+    int value() { return 7; }
+  };
+  struct Base {
+    virtual int get() { return 0; }
+    virtual ~Base() {}
+  };
+  struct Derived : Base {
+    int get() override {
+      Helper h; // must still resolve 'Helper'
+      return h.value();
+    }
+  };
+
+  Derived d;
+  (void)d;
+}
+} // namespace LocalClassCases
+
+// A hidden friend defined inline in a class template is late-parsed on demand.
+// Its body is parsed in the complete-class context, so it must see members of
+// the enclosing class regardless of source order -- including members declared
+// after it, as in libstdc++'s forward_list iterator comparison operators.
+namespace HiddenFriendMemberOrder {
+template <class T>
+struct Iter {
+  friend bool operator==(const Iter &a, const Iter &b) {
+    return a.node == b.node; // 'node' is declared later in the same class
+  }
+  friend bool operator!=(const Iter &a, const Iter &b) {
+    return a.node != b.node;
+  }
+  void *node;
+};
+
+bool trigger_hidden_friend_member_order() {
+  Iter<int> a{}, b{};
+  return a == b || a != b;
+}
+} // namespace HiddenFriendMemberOrder
+
+namespace VirtualFunctions {
+// Constructing an object of a polymorphic internal-linkage class emits its
+// vtable, which references every virtual function. So all virtual function
+// bodies are parsed when the object is used, while unreferenced non-virtual
+// members stay unparsed.
+namespace {
+struct UsedObject {
+  virtual void first_virtual() {
+    undeclared_in_first_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_first_virtual'}}
+  }
+  virtual void second_virtual() {
+    undeclared_in_second_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_second_virtual'}}
+  }
+  void unreferenced_non_virtual() {
+    undeclared_in_non_virtual(); // this should not give an error
+  }
+};
+
+// No object is ever created, so none of the virtual bodies are parsed.
+struct NeverInstantiated {
+  virtual void unused_first_virtual() {
+    undeclared_in_unused_first(); // this should not give an error
+  }
+  virtual void unused_second_virtual() {
+    undeclared_in_unused_second(); // this should not give an error
+  }
+};
+
+// Only a pointer is formed; without constructing an object the vtable is not
+// emitted and the virtual bodies stay unparsed.
+struct OnlyPointerUsed {
+  virtual void pointer_virtual() {
+    undeclared_in_pointer_virtual(); // this should not give an error
+  }
+};
+
+// Calling a single virtual function still emits the vtable, so every virtual
+// body is parsed -- not just the one that was called.
+struct CallOneVirtual {
+  virtual void called_virtual() {
+    undeclared_in_called_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_called_virtual'}}
+  }
+  virtual void other_virtual() {
+    undeclared_in_other_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_other_virtual'}}
+  }
+};
+
+// Using a derived object emits both the derived and base vtables, so virtual
+// bodies from the whole hierarchy are parsed.
+struct Base {
+  virtual void base_virtual() {
+    undeclared_in_base_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_base_virtual'}}
+  }
+};
+
+struct Derived : Base {
+  void base_virtual() override {
+    undeclared_in_override_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_override_virtual'}}
+  }
+  virtual void derived_virtual() {
+    undeclared_in_derived_virtual(); // expected-error {{use of undeclared 
identifier 'undeclared_in_derived_virtual'}}
+  }
+};
+} // namespace
+
+void trigger_virtual_functions() {
+  UsedObject used;
+  (void)used;
+
+  CallOneVirtual call;
+  call.called_virtual();
+
+  Derived derived;
+  (void)derived;
+
+  OnlyPointerUsed *pointer = nullptr;
+  (void)pointer;
+}
+} // namespace VirtualFunctions
+
+// A vtable whose first use is inside an on-demand-deferred internal function 
is
+// only marked used while that body is parsed at the end of the TU, after the
+// initial vtable-defining pass. Its virtual bodies must still be parsed (here,
+// diagnosed) rather than emitted unparsed.
+namespace VTableUsedInDeferredFunction {
+namespace {
+struct Polymorphic {
+  virtual int poly_method() {
+    undeclared_in_poly_method(); // expected-error {{use of undeclared 
identifier 'undeclared_in_poly_method'}}
+    return 0;
+  }
+};
+}
+
+// Internal linkage, so this body is deferred; constructing the object here is
+// the first use of Polymorphic's vtable.
+static Polymorphic &get_instance() {
+  static Polymorphic instance;
+  return instance;
+}
+
+void trigger_vtable_used_in_deferred_function() {
+  get_instance();
+}
+} // namespace VTableUsedInDeferredFunction
+
+// An explicit function-template specialization is not TK_NonTemplate, so the
+// on-demand machinery cannot re-parse it. Even when it has internal linkage
+// (its template argument is an anonymous-namespace type) it must be parsed
+// eagerly rather than deferred; otherwise its body (here, diagnosed) would be
+// left unparsed and emitted empty.
+namespace ExplicitSpecialization {
+namespace {
+struct InternalField {};
+struct InternalField2 {};
+}
+struct Parser {
+  template <class T> bool parse(T &field);
+};
+template <>
+bool Parser::parse(InternalField &field) {
+  (void)field;
+  undeclared_in_explicit_specialization(); // this should not error
+  return false;
+}
+template <>
+bool Parser::parse(InternalField2 &field) {
+  (void)field;
+  undeclared_in_explicit_specialization(); // expected-error {{use of 
undeclared identifier 'undeclared_in_explicit_specialization'}}
+  return false;
+}
+} // namespace ExplicitSpecialization
+
+void trigger_explici_specialization() {
+    ExplicitSpecialization::Parser p;
+    ExplicitSpecialization::InternalField2 f;
+    p.parse(f);
+}
+
+// A function template's body is deferred on demand (it is late-parsed), but 
its
+// signature is still substituted at call sites. That substitution -- here 
SFINAE
+// on decltype(Builder()) -- must not be subject to the late-parsed body's
+// source-order visibility restriction, otherwise a capturing lambda's call
+// operator (declared at the call site) is wrongly hidden and the call fails to
+// resolve. (A non-capturing lambda avoids the restriction, so a capturing one 
is
+// used here.)
+namespace SFINAEWithCapturingLambda {
+struct Diag {
+  int x;
+};
+struct Emitter {
+  void emit(Diag &&) {}
+  template <class T> void emit(T Builder, decltype(Builder()) * = nullptr) {
+    emit(Builder());
+  }
+};
+void trigger_sfinae_with_capturing_lambda(Emitter &E) {
+  int local = 0;
+  E.emit([&]() { return Diag{local}; });
+}
+} // namespace SFINAEWithCapturingLambda
+
+// Anonymous-namespace function templates are still parsed on instantiation, 
and
+// dependent lookups in their signatures and bodies must resolve normally.
+namespace AnonymousNamespaceTemplateLookup {
+namespace {
+template <typename T> void call_foo(T &t) { t.foo(); }
+
+template <typename T> typename T::result call_bar(T &t) {
+  return t.bar();
+}
+
+struct T {
+  using result = int;
+
+  void foo() {}
+
+  result bar() { return 0; }
+};
+} // namespace
+
+void trigger_anonymous_namespace_template_lookup() {
+  T t;
+  call_foo(t);
+  (void)call_bar(t);
+}
+} // namespace AnonymousNamespaceTemplateLookup
+
+// Template bodies are still parsed on instantiation.
+namespace TemplateCases {
+template <class T>
+void template_body_diagnoses_on_instantiation() {
+  undeclared_in_template(); // expected-error {{use of undeclared identifier 
'undeclared_in_template'}}
+}
+
+void trigger_template_cases() {
+  template_body_diagnoses_on_instantiation<int>();
+}
+} // namespace TemplateCases

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to