From 08191e917b90c971dcbcbecf8f89ec1e661fa3b0 Mon Sep 17 00:00:00 2001
From: Ilya Biryukov <ibiryukov@google.com>
Date: Wed, 20 Dec 2017 14:32:38 +0000
Subject: [PATCH] [Frontend] Handle skipped bodies in template instantiations

Summary:
- Fixed an assert in Sema::InstantiateFunctionDefinition and added
  support for instantiating a function template with skipped body.
- Properly call setHasSkippedBody for FunctionTemplateDecl passed to
  Sema::ActOnSkippedFunctionBody.

Reviewers: sepavloff, bkramer

Reviewed By: sepavloff

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D41237

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@321174 91177308-0d34-0410-b5e6-96231b3b80d8
---
 lib/Sema/SemaDecl.cpp                    |  6 ++++--
 lib/Sema/SemaTemplateInstantiateDecl.cpp | 23 ++++++++++++--------
 test/Index/skipped-bodies-templates.cpp  | 27 ++++++++++++++++++++++++
 3 files changed, 45 insertions(+), 11 deletions(-)
 create mode 100644 test/Index/skipped-bodies-templates.cpp

diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index c60cc52f41..a1fc725f8d 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -12179,9 +12179,11 @@ bool Sema::canSkipFunctionBody(Decl *D) {
 }
 
 Decl *Sema::ActOnSkippedFunctionBody(Decl *Decl) {
-  if (FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Decl))
+  if (!Decl)
+    return nullptr;
+  if (FunctionDecl *FD = Decl->getAsFunction())
     FD->setHasSkippedBody();
-  else if (ObjCMethodDecl *MD = dyn_cast_or_null<ObjCMethodDecl>(Decl))
+  else if (ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(Decl))
     MD->setHasSkippedBody();
   return Decl;
 }
diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 3febf5debe..b926e573ad 100644
--- a/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -3855,7 +3855,8 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   }
 
   // Note, we should never try to instantiate a deleted function template.
-  assert((Pattern || PatternDecl->isDefaulted()) &&
+  assert((Pattern || PatternDecl->isDefaulted() ||
+          PatternDecl->hasSkippedBody()) &&
          "unexpected kind of function template definition");
 
   // C++1y [temp.explicit]p10:
@@ -3940,16 +3941,20 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
       }
     }
 
-    // Instantiate the function body.
-    StmtResult Body = SubstStmt(Pattern, TemplateArgs);
+    if (PatternDecl->hasSkippedBody()) {
+      ActOnSkippedFunctionBody(Function);
+    } else {
+      // Instantiate the function body.
+      StmtResult Body = SubstStmt(Pattern, TemplateArgs);
 
-    if (Body.isInvalid())
-      Function->setInvalidDecl();
+      if (Body.isInvalid())
+        Function->setInvalidDecl();
 
-    // FIXME: finishing the function body while in an expression evaluation
-    // context seems wrong. Investigate more.
-    ActOnFinishFunctionBody(Function, Body.get(),
-                            /*IsInstantiation=*/true);
+      // FIXME: finishing the function body while in an expression evaluation
+      // context seems wrong. Investigate more.
+      ActOnFinishFunctionBody(Function, Body.get(),
+                              /*IsInstantiation=*/true);
+    }
 
     PerformDependentDiagnostics(PatternDecl, TemplateArgs);
 
diff --git a/test/Index/skipped-bodies-templates.cpp b/test/Index/skipped-bodies-templates.cpp
new file mode 100644
index 0000000000..d8fbc28ebc
--- /dev/null
+++ b/test/Index/skipped-bodies-templates.cpp
@@ -0,0 +1,27 @@
+// RUN: env CINDEXTEST_SKIP_FUNCTION_BODIES=1 c-index-test -test-load-source all %s 2>&1 \
+// RUN: | FileCheck %s
+
+
+template <class T>
+struct Foo {
+  inline int with_body() {
+    return 100;
+  }
+
+  inline int without_body();
+};
+
+
+int bar = Foo<int>().with_body() + Foo<int>().without_body();
+// CHECK-NOT: warning: inline function 'Foo<int>::with_body' is not defined
+// CHECK: warning: inline function 'Foo<int>::without_body' is not defined
+
+template <class T>
+inline int with_body() { return 10; }
+
+template <class T>
+inline int without_body();
+
+int baz = with_body<int>() + without_body<int>();
+// CHECK-NOT: warning: inline function 'with_body<int>' is not defined
+// CHECK: warning: inline function 'without_body<int>' is not defined
-- 
2.40.0