From: Douglas Gregor Date: Thu, 16 Feb 2012 01:06:16 +0000 (+0000) Subject: Implicitly define a lambda's conversion functions (to function X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f6e2e0291b8964ed41b4325e21dd90b86e791f10;p=clang Implicitly define a lambda's conversion functions (to function 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 --- diff --git a/include/clang/AST/ASTContext.h b/include/clang/AST/ASTContext.h index ded3c4b0aa..ca976f6048 100644 --- a/include/clang/AST/ASTContext.h +++ b/include/clang/AST/ASTContext.h @@ -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 { typedef UsuallyTinyPtrVector CXXMethodVector; llvm::DenseMap OverriddenMethods; + /// \brief Mapping from lambda-to-block-pointer conversion functions to the + /// expression used to copy the lambda object. + llvm::DenseMap LambdaBlockPointerInits; + + friend class CXXConversionDecl; + /// \brief Mapping that stores parameterIndex values for ParmVarDecls /// when that value exceeds the bitfield size of /// ParmVarDeclBits.ParameterIndex. diff --git a/include/clang/AST/DeclCXX.h b/include/clang/AST/DeclCXX.h index c6af92515f..215adb9668 100644 --- a/include/clang/AST/DeclCXX.h +++ b/include/clang/AST/DeclCXX.h @@ -2180,6 +2180,19 @@ public: return getType()->getAs()->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; } diff --git a/include/clang/Basic/DiagnosticSemaKinds.td b/include/clang/Basic/DiagnosticSemaKinds.td index 376a3f31a9..18b0295b30 100644 --- a/include/clang/Basic/DiagnosticSemaKinds.td +++ b/include/clang/Basic/DiagnosticSemaKinds.td @@ -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< diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 0fd4c9f8b1..018acdcab4 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -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, diff --git a/lib/AST/DeclCXX.cpp b/lib/AST/DeclCXX.cpp index 5b9ab4ff5c..b5b3f12f5e 100644 --- a/lib/AST/DeclCXX.cpp +++ b/lib/AST/DeclCXX.cpp @@ -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, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index 447a4b4257..66ec800c72 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -9050,7 +9050,60 @@ bool Sema::isImplicitlyDeleted(FunctionDecl *FD) { (FD->isDefaulted() || FD->isImplicit()) && isa(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, diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index b05f4d5623..7fee518d2c 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -9447,6 +9447,13 @@ void Sema::MarkFunctionReferenced(SourceLocation Loc, FunctionDecl *Func) { else DefineImplicitMoveAssignment(Loc, MethodDecl); } + } else if (isa(MethodDecl) && + MethodDecl->getParent()->isLambda()) { + CXXConversionDecl *Conversion = cast(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. diff --git a/lib/Serialization/ASTReaderDecl.cpp b/lib/Serialization/ASTReaderDecl.cpp index 547dc568e7..6e044fd0db 100644 --- a/lib/Serialization/ASTReaderDecl.cpp +++ b/lib/Serialization/ASTReaderDecl.cpp @@ -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) { diff --git a/lib/Serialization/ASTWriterDecl.cpp b/lib/Serialization/ASTWriterDecl.cpp index 7cd2f4ebed..584ff1c144 100644 --- a/lib/Serialization/ASTWriterDecl.cpp +++ b/lib/Serialization/ASTWriterDecl.cpp @@ -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; } diff --git a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp index 0806828c83..3301b29135 100644 --- a/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp +++ b/test/CXX/expr/expr.prim/expr.prim.lambda/blocks.cpp @@ -13,3 +13,23 @@ void conversion_to_block(int captured) { const auto lambda = [=](int x) { return x + captured; }; int (^b2)(int) = lambda; } + +template +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 boom, + ConstCopyConstructorBoom boom2) { + const auto& lambda1([=] { boom.foo(); }); // okay + + const auto& lambda2([=] { boom2.foo(); }); // expected-note{{in instantiation of member function}} + void (^block)(void) = lambda2; +} diff --git a/test/PCH/cxx11-lambdas.cpp b/test/PCH/cxx11-lambdas.cpp index cc17099f01..c00ec63807 100644 --- a/test/PCH/cxx11-lambdas.cpp +++ b/test/PCH/cxx11-lambdas.cpp @@ -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