]> granicus.if.org Git - clang/commitdiff
Implicitly define a lambda's conversion functions (to function
authorDouglas Gregor <dgregor@apple.com>
Thu, 16 Feb 2012 01:06:16 +0000 (01:06 +0000)
committerDouglas Gregor <dgregor@apple.com>
Thu, 16 Feb 2012 01:06:16 +0000 (01:06 +0000)
pointers and block pointers). We use dummy definitions to keep the
invariant that an implicit, used definition has a body; IR generation
will substitute the actual contents, since they can't be represented
as C++.

For the block pointer case, compute the copy-initialization needed to
capture the lambda object in the block, which IR generation will need
later.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@150645 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/AST/ASTContext.h
include/clang/AST/DeclCXX.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Sema/Sema.h
lib/AST/DeclCXX.cpp
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaExpr.cpp
lib/Serialization/ASTReaderDecl.cpp
lib/Serialization/ASTWriterDecl.cpp
test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp
test/PCH/cxx11-lambdas.cpp

index ded3c4b0aa7dc3194b124656e2083c845338dcb9..ca976f6048ebb573363c502afa3180abe0c580ae 100644 (file)
@@ -55,6 +55,7 @@ namespace clang {
   class CXXABI;
   // Decls
   class DeclContext;
+  class CXXConversionDecl;
   class CXXMethodDecl;
   class CXXRecordDecl;
   class Decl;
@@ -321,6 +322,12 @@ class ASTContext : public llvm::RefCountedBase<ASTContext> {
   typedef UsuallyTinyPtrVector<const CXXMethodDecl> CXXMethodVector;
   llvm::DenseMap<const CXXMethodDecl *, CXXMethodVector> OverriddenMethods;
 
+  /// \brief Mapping from lambda-to-block-pointer conversion functions to the
+  /// expression used to copy the lambda object.
+  llvm::DenseMap<const CXXConversionDecl *, Expr *> LambdaBlockPointerInits;
+  
+  friend class CXXConversionDecl;
+  
   /// \brief Mapping that stores parameterIndex values for ParmVarDecls
   /// when that value exceeds the bitfield size of
   /// ParmVarDeclBits.ParameterIndex.
index c6af92515faa940db5b0dc74396255cbfb15e651..215adb9668b42aa64906cc9bf5d398e92e560657 100644 (file)
@@ -2180,6 +2180,19 @@ public:
     return getType()->getAs<FunctionType>()->getResultType();
   }
 
+  /// \brief Determine whether this conversion function is a conversion from
+  /// a lambda closure type to a block pointer.
+  bool isLambdaToBlockPointerConversion() const;
+  
+  /// \brief For an implicit conversion function that converts a lambda
+  /// closure type to a block pointer, retrieve the expression used to
+  /// copy the closure object into the block.
+  Expr *getLambdaToBlockPointerCopyInit() const;
+  
+  /// \brief Set the copy-initialization expression to be used when converting
+  /// a lambda object to a block pointer.
+  void setLambdaToBlockPointerCopyInit(Expr *Init);
+  
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classof(const CXXConversionDecl *D) { return true; }
index 376a3f31a94a8ad008add9dd6cd84e0b3e13c659..18b0295b30a57355ff99060c0888f740e4ca4cde 100644 (file)
@@ -4071,6 +4071,9 @@ let CategoryName = "Lambda Issue" in {
     "capture of variable '%0' as type %1 calls %select{private|protected}3 "
     "%select{default |copy |move |*ERROR* |*ERROR* |*ERROR* |}2constructor">,
     AccessControl;
+  def note_lambda_to_block_conv : Note<
+    "implicit capture of lambda object due to conversion to block pointer "
+    "here">;
 }
 
 def err_operator_arrow_circular : Error<
index 0fd4c9f8b11b029c907b82966aba098532ba6f54..018acdcab4a6791ea316b3629181b15c54b6c8e7 100644 (file)
@@ -3554,6 +3554,26 @@ public:
   ExprResult ActOnLambdaExpr(SourceLocation StartLoc, Stmt *Body,
                              Scope *CurScope, bool IsInstantiation = false);
 
+  /// \brief Define the "body" of the conversion from a lambda object to a 
+  /// function pointer.
+  ///
+  /// This routine doesn't actually define a sensible body; rather, it fills
+  /// in the initialization expression needed to copy the lambda object into
+  /// the block, and IR generation actually generates the real body of the
+  /// block pointer conversion.
+  void DefineImplicitLambdaToFunctionPointerConversion(
+         SourceLocation CurrentLoc, CXXConversionDecl *Conv);
+
+  /// \brief Define the "body" of the conversion from a lambda object to a 
+  /// block pointer.
+  ///
+  /// This routine doesn't actually define a sensible body; rather, it fills
+  /// in the initialization expression needed to copy the lambda object into
+  /// the block, and IR generation actually generates the real body of the
+  /// block pointer conversion.
+  void DefineImplicitLambdaToBlockPointerConversion(SourceLocation CurrentLoc,
+                                                    CXXConversionDecl *Conv);
+  
   // ParseObjCStringLiteral - Parse Objective-C string literals.
   ExprResult ParseObjCStringLiteral(SourceLocation *AtLocs,
                                     Expr **Strings,
index 5b9ab4ff5c28f8db84658f9ffa75870d080478e2..b5b3f12f5e1ff24766ab5f5d7929b2b323032293 100644 (file)
@@ -1759,6 +1759,21 @@ CXXConversionDecl::Create(ASTContext &C, CXXRecordDecl *RD,
                                    EndLocation);
 }
 
+bool CXXConversionDecl::isLambdaToBlockPointerConversion() const {
+  return isImplicit() && getParent()->isLambda() &&
+         getConversionType()->isBlockPointerType();
+}
+
+Expr *CXXConversionDecl::getLambdaToBlockPointerCopyInit() const {
+  assert(isLambdaToBlockPointerConversion());
+  return getASTContext().LambdaBlockPointerInits[this];
+}
+
+void CXXConversionDecl::setLambdaToBlockPointerCopyInit(Expr *Init) {
+  assert(isLambdaToBlockPointerConversion());
+  getASTContext().LambdaBlockPointerInits[this] = Init;
+}
+
 void LinkageSpecDecl::anchor() { }
 
 LinkageSpecDecl *LinkageSpecDecl::Create(ASTContext &C,
index 447a4b42572bdc71a8a1f77339de29227035bc2c..66ec800c726c29d82381d9e623444385effb0fb4 100644 (file)
@@ -9050,7 +9050,60 @@ bool Sema::isImplicitlyDeleted(FunctionDecl *FD) {
          (FD->isDefaulted() || FD->isImplicit()) &&
          isa<CXXMethodDecl>(FD);
 }
-    
+
+void Sema::DefineImplicitLambdaToFunctionPointerConversion(
+       SourceLocation CurrentLocation,
+       CXXConversionDecl *Conv) 
+{
+  Conv->setUsed();
+  
+  ImplicitlyDefinedFunctionScope Scope(*this, Conv);
+  DiagnosticErrorTrap Trap(Diags);
+  
+  // Introduce a bogus body, which IR generation will override anyway.
+  Conv->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(),
+                                           Conv->getLocation()));
+  
+  if (ASTMutationListener *L = getASTMutationListener()) {
+    L->CompletedImplicitDefinition(Conv);
+  }
+}
+
+void Sema::DefineImplicitLambdaToBlockPointerConversion(
+       SourceLocation CurrentLocation,
+       CXXConversionDecl *Conv) 
+{
+  Conv->setUsed();
+  
+  ImplicitlyDefinedFunctionScope Scope(*this, Conv);
+  DiagnosticErrorTrap Trap(Diags);
+  
+  // Copy-initialize the lambda object as needed to capture
+  Expr *This = ActOnCXXThis(CurrentLocation).take();
+  Expr *DerefThis =CreateBuiltinUnaryOp(CurrentLocation, UO_Deref, This).take();
+  ExprResult Init = PerformCopyInitialization(
+                      InitializedEntity::InitializeBlock(CurrentLocation, 
+                                                         DerefThis->getType(), 
+                                                         /*NRVO=*/false),
+                      CurrentLocation, DerefThis);
+  if (!Init.isInvalid())
+    Init = ActOnFinishFullExpr(Init.take());
+  
+  if (!Init.isInvalid())
+    Conv->setLambdaToBlockPointerCopyInit(Init.take());
+  else {
+    Diag(CurrentLocation, diag::note_lambda_to_block_conv);
+  }
+  
+  // Introduce a bogus body, which IR generation will override anyway.
+  Conv->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(),
+                                           Conv->getLocation()));
+  
+  if (ASTMutationListener *L = getASTMutationListener()) {
+    L->CompletedImplicitDefinition(Conv);
+  }
+}
+
 ExprResult
 Sema::BuildCXXConstructExpr(SourceLocation ConstructLoc, QualType DeclInitType,
                             CXXConstructorDecl *Constructor,
index b05f4d5623db84c60a10e10920821ad55595065a..7fee518d2cc37a3d0c65642beafa8495e28e6668 100644 (file)
@@ -9447,6 +9447,13 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) {
         else
           DefineImplicitMoveAssignment(Loc, MethodDecl);
       }
+    } else if (isa<CXXConversionDecl>(MethodDecl) &&
+               MethodDecl->getParent()->isLambda()) {
+      CXXConversionDecl *Conversion = cast<CXXConversionDecl>(MethodDecl);
+      if (Conversion->isLambdaToBlockPointerConversion())
+        DefineImplicitLambdaToBlockPointerConversion(Loc, Conversion);
+      else
+        DefineImplicitLambdaToFunctionPointerConversion(Loc, Conversion);
     } else if (MethodDecl->isVirtual())
       MarkVTableUsed(Loc, MethodDecl->getParent());
   }
@@ -10041,7 +10048,7 @@ static void MarkExprReferenced(Sema &SemaRef, SourceLocation Loc,
   }
 
   SemaRef.MarkAnyDeclReferenced(Loc, D);
-}
+} 
 
 /// \brief Perform reference-marking and odr-use handling for a
 /// BlockDeclRefExpr.
index 547dc568e7a33678a8cccca37641ff9796c75a9c..6e044fd0dba2a7df53b11763dd0ac3fe28784f6e 100644 (file)
@@ -1206,6 +1206,8 @@ void ASTDeclReader::VisitCXXDestructorDecl(CXXDestructorDecl *D) {
 void ASTDeclReader::VisitCXXConversionDecl(CXXConversionDecl *D) {
   VisitCXXMethodDecl(D);
   D->IsExplicitSpecified = Record[Idx++];
+  if (D->isLambdaToBlockPointerConversion())
+    D->setLambdaToBlockPointerCopyInit(Reader.ReadExpr(F));
 }
 
 void ASTDeclReader::VisitImportDecl(ImportDecl *D) {
index 7cd2f4ebed21a6a6a6bb1297d0cd263397ba110a..584ff1c1442a0063824d224e157dd7d19a6adc00 100644 (file)
@@ -961,6 +961,8 @@ void ASTDeclWriter::VisitCXXDestructorDecl(CXXDestructorDecl *D) {
 void ASTDeclWriter::VisitCXXConversionDecl(CXXConversionDecl *D) {
   VisitCXXMethodDecl(D);
   Record.push_back(D->IsExplicitSpecified);
+  if (D->isLambdaToBlockPointerConversion())
+    Writer.AddStmt(D->getLambdaToBlockPointerCopyInit());
   Code = serialization::DECL_CXX_CONVERSION;
 }
 
index 0806828c83e4ff08c0280eec3cdde56c9ea33da0..3301b29135d6bc662a20bf60123bf566c5c9476d 100644 (file)
@@ -13,3 +13,23 @@ void conversion_to_block(int captured) {
   const auto lambda = [=](int x) { return x + captured; };
   int (^b2)(int) = lambda;
 }
+
+template<typename T>
+class ConstCopyConstructorBoom {
+public:
+  ConstCopyConstructorBoom(ConstCopyConstructorBoom&);
+
+  ConstCopyConstructorBoom(const ConstCopyConstructorBoom&) {
+    T *ptr = 1; // expected-error{{cannot initialize a variable of type 'float *' with an rvalue of type 'int'}}
+  }
+
+  void foo() const;
+};
+
+void conversion_to_block_init(ConstCopyConstructorBoom<int> boom,
+                              ConstCopyConstructorBoom<float> boom2) {
+  const auto& lambda1([=] { boom.foo(); }); // okay
+
+  const auto& lambda2([=] { boom2.foo(); }); // expected-note{{in instantiation of member function}}
+  void (^block)(void) = lambda2;
+}
index cc17099f01c3830cc80e06a78a9a3a5b679d14ff..c00ec638075204fefcbeaf6689b9430ca95a93ca 100644 (file)
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -pedantic-errors -std=c++11 -emit-pch %s -o %t-cxx11
-// RUN: %clang_cc1 -ast-print -pedantic-errors -std=c++11 -include-pch %t-cxx11  %s | FileCheck -check-prefix=CHECK-PRINT %s
+// RUN: %clang_cc1 -pedantic-errors -fblocks -std=c++11 -emit-pch %s -o %t-cxx11
+// RUN: %clang_cc1 -ast-print -pedantic-errors -fblocks -std=c++11 -include-pch %t-cxx11  %s | FileCheck -check-prefix=CHECK-PRINT %s
 
 #ifndef HEADER_INCLUDED
 
@@ -26,6 +26,13 @@ inline int sum_array(int n) {
 
   return lambda(n);
 }
+
+inline int to_block_pointer(int n) {
+  auto lambda = [=](int m) { return n + m; };
+  int (^block)(int) = lambda;
+  return block(17);
+}
+
 #else
 
 // CHECK-PRINT: T add_slowly
@@ -33,7 +40,7 @@ inline int sum_array(int n) {
 template float add_slowly(const float&, const float&);
 
 int add(int x, int y) {
-  return add_int_slowly_twice(x, y) + sum_array(4);
+  return add_int_slowly_twice(x, y) + sum_array(4) + to_block_pointer(5);
 }
 
 // CHECK-PRINT: inline int add_int_slowly_twice