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.

llvm-svn: 150645
This commit is contained in:
Douglas Gregor 2012-02-16 01:06:16 +00:00
parent 515a60daff
commit d3b672c385
11 changed files with 154 additions and 5 deletions

View 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.

View 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; }

View 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<

View 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,

View 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,

View 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,

View 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.

View 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) {

View 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;
}

View 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;
}

View 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