Implement template support for CapturedStmt

- Sema tests added and CodeGen tests are pending

Differential Revision: http://llvm-reviews.chandlerc.com/D728



git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@181101 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index da83220..74c9ec2 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -16,6 +16,7 @@
 
 #include "clang/AST/DeclGroup.h"
 #include "clang/AST/StmtIterator.h"
+#include "clang/Basic/CapturedStmt.h"
 #include "clang/Basic/IdentifierTable.h"
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
@@ -1982,16 +1983,16 @@
   /// \brief The number of variable captured, including 'this'.
   unsigned NumCaptures;
 
-  /// \brief The implicit outlined function.
-  CapturedDecl *TheCapturedDecl;
+  /// \brief The pointer part is the implicit the outlined function and the 
+  /// int part is the captured region kind, 'CR_Default' etc.
+  llvm::PointerIntPair<CapturedDecl *, 1, CapturedRegionKind> CapDeclAndKind;
 
   /// \brief The record for captured variables, a RecordDecl or CXXRecordDecl.
   RecordDecl *TheRecordDecl;
 
   /// \brief Construct a captured statement.
-  CapturedStmt(Stmt *S, ArrayRef<Capture> Captures,
-               ArrayRef<Expr *> CaptureInits,
-               CapturedDecl *CD, RecordDecl *RD);
+  CapturedStmt(Stmt *S, CapturedRegionKind Kind, ArrayRef<Capture> Captures,
+               ArrayRef<Expr *> CaptureInits, CapturedDecl *CD, RecordDecl *RD);
 
   /// \brief Construct an empty captured statement.
   CapturedStmt(EmptyShell Empty, unsigned NumCaptures);
@@ -2006,6 +2007,7 @@
 
 public:
   static CapturedStmt *Create(ASTContext &Context, Stmt *S,
+                              CapturedRegionKind Kind,
                               ArrayRef<Capture> Captures,
                               ArrayRef<Expr *> CaptureInits,
                               CapturedDecl *CD, RecordDecl *RD);
@@ -2020,11 +2022,36 @@
   }
 
   /// \brief Retrieve the outlined function declaration.
-  CapturedDecl *getCapturedDecl() const { return TheCapturedDecl; }
+  CapturedDecl *getCapturedDecl() { return CapDeclAndKind.getPointer(); }
+  const CapturedDecl *getCapturedDecl() const {
+    return const_cast<CapturedStmt *>(this)->getCapturedDecl();
+  }
+
+  /// \brief Set the outlined function declaration.
+  void setCapturedDecl(CapturedDecl *D) {
+    assert(D && "null CapturedDecl");
+    CapDeclAndKind.setPointer(D);
+  }
+
+  /// \brief Retrieve the captured region kind.
+  CapturedRegionKind getCapturedRegionKind() const {
+    return CapDeclAndKind.getInt();
+  }
+
+  /// \brief Set the captured region kind.
+  void setCapturedRegionKind(CapturedRegionKind Kind) {
+    CapDeclAndKind.setInt(Kind);
+  }
 
   /// \brief Retrieve the record declaration for captured variables.
   const RecordDecl *getCapturedRecordDecl() const { return TheRecordDecl; }
 
+  /// \brief Set the record declaration for captured variables.
+  void setCapturedRecordDecl(RecordDecl *D) {
+    assert(D && "null RecordDecl");
+    TheRecordDecl = D;
+  }
+
   /// \brief True if this variable has been captured.
   bool capturesVariable(const VarDecl *Var) const;
 
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index f947573..78b9f1e 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -2798,7 +2798,7 @@
   void ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope,
                                 CapturedRegionKind Kind, unsigned NumParams);
   StmtResult ActOnCapturedRegionEnd(Stmt *S);
-  void ActOnCapturedRegionError(bool IsInstantiation = false);
+  void ActOnCapturedRegionError();
   RecordDecl *CreateCapturedStmtRecordDecl(CapturedDecl *&CD,
                                            SourceLocation Loc,
                                            unsigned NumParams);
diff --git a/lib/AST/Stmt.cpp b/lib/AST/Stmt.cpp
index 0db97d2..5b29c07 100644
--- a/lib/AST/Stmt.cpp
+++ b/lib/AST/Stmt.cpp
@@ -1049,12 +1049,13 @@
       + FirstCaptureOffset);
 }
 
-CapturedStmt::CapturedStmt(Stmt *S, ArrayRef<Capture> Captures,
+CapturedStmt::CapturedStmt(Stmt *S, CapturedRegionKind Kind,
+                           ArrayRef<Capture> Captures,
                            ArrayRef<Expr *> CaptureInits,
                            CapturedDecl *CD,
                            RecordDecl *RD)
   : Stmt(CapturedStmtClass), NumCaptures(Captures.size()),
-    TheCapturedDecl(CD), TheRecordDecl(RD) {
+    CapDeclAndKind(CD, Kind), TheRecordDecl(RD) {
   assert( S && "null captured statement");
   assert(CD && "null captured declaration for captured statement");
   assert(RD && "null record declaration for captured statement");
@@ -1074,11 +1075,12 @@
 
 CapturedStmt::CapturedStmt(EmptyShell Empty, unsigned NumCaptures)
   : Stmt(CapturedStmtClass, Empty), NumCaptures(NumCaptures),
-    TheCapturedDecl(0), TheRecordDecl(0) {
+    CapDeclAndKind(0, CR_Default), TheRecordDecl(0) {
   getStoredStmts()[NumCaptures] = 0;
 }
 
 CapturedStmt *CapturedStmt::Create(ASTContext &Context, Stmt *S,
+                                   CapturedRegionKind Kind,
                                    ArrayRef<Capture> Captures,
                                    ArrayRef<Expr *> CaptureInits,
                                    CapturedDecl *CD,
@@ -1102,7 +1104,7 @@
   }
 
   void *Mem = Context.Allocate(Size);
-  return new (Mem) CapturedStmt(S, Captures, CaptureInits, CD, RD);
+  return new (Mem) CapturedStmt(S, Kind, Captures, CaptureInits, CD, RD);
 }
 
 CapturedStmt *CapturedStmt::CreateDeserialized(ASTContext &Context,
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index 42d4590..16cc43a 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -2981,7 +2981,8 @@
 }
 
 void Sema::ActOnCapturedRegionStart(SourceLocation Loc, Scope *CurScope,
-                                    CapturedRegionKind Kind, unsigned NumParams) {
+                                    CapturedRegionKind Kind,
+                                    unsigned NumParams) {
   CapturedDecl *CD = 0;
   RecordDecl *RD = CreateCapturedStmtRecordDecl(CD, Loc, NumParams);
 
@@ -2996,13 +2997,10 @@
   PushExpressionEvaluationContext(PotentiallyEvaluated);
 }
 
-void Sema::ActOnCapturedRegionError(bool IsInstantiation) {
+void Sema::ActOnCapturedRegionError() {
   DiscardCleanupsInEvaluationContext();
   PopExpressionEvaluationContext();
 
-  if (!IsInstantiation)
-    PopDeclContext();
-
   CapturedRegionScopeInfo *RSI = getCurCapturedRegion();
   RecordDecl *Record = RSI->TheRecordDecl;
   Record->setInvalidDecl();
@@ -3014,6 +3012,7 @@
   ActOnFields(/*Scope=*/0, Record->getLocation(), Record, Fields,
               SourceLocation(), SourceLocation(), /*AttributeList=*/0);
 
+  PopDeclContext();
   PopFunctionScopeInfo();
 }
 
@@ -3027,12 +3026,16 @@
   CapturedDecl *CD = RSI->TheCapturedDecl;
   RecordDecl *RD = RSI->TheRecordDecl;
 
-  CapturedStmt *Res = CapturedStmt::Create(getASTContext(), S, Captures,
+  CapturedStmt *Res = CapturedStmt::Create(getASTContext(), S,
+                                           RSI->CapRegionKind, Captures,
                                            CaptureInits, CD, RD);
 
   CD->setBody(Res->getCapturedStmt());
   RD->completeDefinition();
 
+  DiscardCleanupsInEvaluationContext();
+  PopExpressionEvaluationContext();
+
   PopDeclContext();
   PopFunctionScopeInfo();
 
diff --git a/lib/Sema/TreeTransform.h b/lib/Sema/TreeTransform.h
index 99cb282..0fcde81 100644
--- a/lib/Sema/TreeTransform.h
+++ b/lib/Sema/TreeTransform.h
@@ -9449,7 +9449,18 @@
 template<typename Derived>
 StmtResult
 TreeTransform<Derived>::TransformCapturedStmt(CapturedStmt *S) {
-  llvm_unreachable("not implement yet");
+  SourceLocation Loc = S->getLocStart();
+  unsigned NumParams = S->getCapturedDecl()->getNumParams();
+  getSema().ActOnCapturedRegionStart(Loc, /*CurScope*/0,
+                                     S->getCapturedRegionKind(), NumParams);
+  StmtResult Body = getDerived().TransformStmt(S->getCapturedStmt());
+
+  if (Body.isInvalid()) {
+    getSema().ActOnCapturedRegionError();
+    return StmtError();
+  }
+
+  return getSema().ActOnCapturedRegionEnd(Body.take());
 }
 
 } // end namespace clang
diff --git a/lib/Serialization/ASTReaderStmt.cpp b/lib/Serialization/ASTReaderStmt.cpp
index 5d72614..e1357ba 100644
--- a/lib/Serialization/ASTReaderStmt.cpp
+++ b/lib/Serialization/ASTReaderStmt.cpp
@@ -382,8 +382,9 @@
 
 void ASTStmtReader::VisitCapturedStmt(CapturedStmt *S) {
   VisitStmt(S);
-  S->TheCapturedDecl = ReadDeclAs<CapturedDecl>(Record, Idx);
-  S->TheRecordDecl = ReadDeclAs<RecordDecl>(Record, Idx);
+  S->setCapturedDecl(ReadDeclAs<CapturedDecl>(Record, Idx));
+  S->setCapturedRegionKind(static_cast<CapturedRegionKind>(Record[Idx++]));
+  S->setCapturedRecordDecl(ReadDeclAs<RecordDecl>(Record, Idx));
 
   // Capture inits
   for (CapturedStmt::capture_init_iterator I = S->capture_init_begin(),
@@ -393,7 +394,7 @@
 
   // Body
   S->setCapturedStmt(Reader.ReadSubStmt());
-  S->TheCapturedDecl->setBody(S->getCapturedStmt());
+  S->getCapturedDecl()->setBody(S->getCapturedStmt());
 
   // Captures
   for (CapturedStmt::capture_iterator I = S->capture_begin(),
diff --git a/lib/Serialization/ASTWriterStmt.cpp b/lib/Serialization/ASTWriterStmt.cpp
index 7d28247..5f7ac01 100644
--- a/lib/Serialization/ASTWriterStmt.cpp
+++ b/lib/Serialization/ASTWriterStmt.cpp
@@ -290,7 +290,10 @@
   // NumCaptures
   Record.push_back(std::distance(S->capture_begin(), S->capture_end()));
 
+  // CapturedDecl and captured region kind
   Writer.AddDeclRef(S->getCapturedDecl(), Record);
+  Record.push_back(S->getCapturedRegionKind());
+
   Writer.AddDeclRef(S->getCapturedRecordDecl(), Record);
 
   // Capture inits
diff --git a/test/SemaCXX/captured-statements.cpp b/test/SemaCXX/captured-statements.cpp
index 15879a1..dbb18a7 100644
--- a/test/SemaCXX/captured-statements.cpp
+++ b/test/SemaCXX/captured-statements.cpp
@@ -50,3 +50,117 @@
     { b(); }        // OK
   }
 };
+
+template <typename T>
+void template_capture_var() {
+  T x; // expected-error{{declaration of reference variable 'x' requires an initializer}}
+  #pragma clang _debug captured
+  {
+    (void)x;
+  }
+}
+
+template <typename T>
+class Val {
+  T v;
+public:
+  void set(const T &v0) {
+    #pragma clang __debug captured
+    {
+      v = v0;
+    }
+  }
+};
+
+void test_capture_var() {
+  template_capture_var<int>(); // OK
+  template_capture_var<int&>(); // expected-note{{in instantiation of function template specialization 'template_capture_var<int &>' requested here}}
+
+  Val<float> Obj;
+  Obj.set(0.0f); // OK
+}
+
+template <typename S, typename T>
+S template_capture_var(S x, T y) {
+  #pragma clang _debug captured
+  {
+    x++;
+    y++;  // expected-error{{read-only variable is not assignable}}
+  }
+
+  return x;
+}
+
+// Check if can recover from a template error.
+void test_capture_var_error() {
+  template_capture_var<int, int>(0, 1); // OK
+  template_capture_var<int, const int>(0, 1); // expected-note{{in instantiation of function template specialization 'template_capture_var<int, const int>' requested here}}
+  template_capture_var<int, int>(0, 1); // OK
+}
+
+template <typename T>
+void template_capture_in_lambda() {
+  T x, y;
+  [=, &y]() {
+    #pragma clang __debug captured
+    {
+      y += x;
+    }
+  }();
+}
+
+void test_lambda() {
+  template_capture_in_lambda<int>(); // OK
+}
+
+struct Foo {
+  void foo() { }
+  static void bar() { }
+};
+
+template <typename T>
+void template_capture_func(T &t) {
+  #pragma clang __debug captured
+  {
+    t.foo();
+  }
+
+  #pragma clang __debug captured
+  {
+    T::bar();
+  }
+}
+
+void test_template_capture_func() {
+  Foo Obj;
+  template_capture_func(Obj);
+}
+
+template <typename T>
+T captured_sum(const T &a, const T &b) {
+  T result;
+
+  #pragma clang __debug captured
+  {
+    result = a + b;
+  }
+
+  return result;
+}
+
+template <typename T, typename... Args>
+T captured_sum(const T &a, const Args&... args) {
+  T result;
+
+  #pragma clang __debug captured
+  {
+    result = a + captured_sum(args...);
+  }
+
+  return result;
+}
+
+void test_capture_variadic() {
+  (void)captured_sum(1, 2, 3); // OK
+  (void)captured_sum(1, 2, 3, 4, 5); // OK
+}