]> granicus.if.org Git - clang/commitdiff
Rework the Sema/AST/IRgen dance for the lambda closure type's
authorDouglas Gregor <dgregor@apple.com>
Fri, 17 Feb 2012 03:02:34 +0000 (03:02 +0000)
committerDouglas Gregor <dgregor@apple.com>
Fri, 17 Feb 2012 03:02:34 +0000 (03:02 +0000)
conversion to function pointer. Rather than having IRgen synthesize
the body of this function, we instead introduce a static member
function "__invoke" with the same signature as the lambda's
operator() in the AST. Sema then generates a body for the conversion
to function pointer which simply returns the address of __invoke. This
approach makes it easier to evaluate a call to the conversion function
as a constant, makes the linkage of the __invoke function follow the
normal rules for member functions, and may make life easier down the
road if we ever want to constexpr'ify some of lambdas.

Note that IR generation is responsible for filling in the body of
__invoke (Sema just adds a dummy body), because the body can't
generally be expressed in C++.

Eli, please review!

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

include/clang/AST/DeclCXX.h
lib/AST/DeclCXX.cpp
lib/CodeGen/CGClass.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/CodeGen/CodeGenFunction.h
lib/Sema/SemaDeclCXX.cpp
lib/Sema/SemaLambda.cpp
test/CodeGenCXX/lambda-expressions.cpp

index 215adb9668b42aa64906cc9bf5d398e92e560657..c371c08560f89538a170a2c537851847352749ad 100644 (file)
@@ -1565,6 +1565,15 @@ public:
 
   bool hasInlineBody() const;
 
+  /// \brief Determine whether this is a lambda closure type's static member
+  /// function that is used for the result of the lambda's conversion to
+  /// function pointer (for a lambda with no captures).
+  ///
+  /// The function itself, if used, will have a placeholder body that will be
+  /// supplied by IR generation to either forward to the function call operator
+  /// or clone the function call operator.
+  bool isLambdaStaticInvoker() const;
+  
   // Implement isa/cast/dyncast/etc.
   static bool classof(const Decl *D) { return classofKind(D->getKind()); }
   static bool classof(const CXXMethodDecl *D) { return true; }
index b5b3f12f5e1ff24766ab5f5d7929b2b323032293..a4d82206258d74c8fbbf5b1d943dc8f229169ae0 100644 (file)
@@ -1455,6 +1455,12 @@ bool CXXMethodDecl::hasInlineBody() const {
   return CheckFn->hasBody(fn) && !fn->isOutOfLine();
 }
 
+bool CXXMethodDecl::isLambdaStaticInvoker() const {
+  return getParent()->isLambda() && 
+         getIdentifier() && getIdentifier()->getName() == "__invoke";
+}
+
+
 CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context,
                                        TypeSourceInfo *TInfo, bool IsVirtual,
                                        SourceLocation L, Expr *Init,
index ed44679a88621df5c183670818d8d5a6f721043f..7e94af391f93f02925aeab5732ecdb037f7021c5 100644 (file)
@@ -1725,26 +1725,15 @@ void CodeGenFunction::EmitLambdaToBlockPointerBody(FunctionArgList &Args) {
   CGM.ErrorUnsupported(CurFuncDecl, "lambda conversion to block");
 }
 
-void CodeGenFunction::EmitLambdaThunkBody(llvm::Function *Fn,
-                                          const CGFunctionInfo &FnInfo,
-                                          const CXXRecordDecl *Lambda) {
+void CodeGenFunction::EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD) {
+  const CXXRecordDecl *Lambda = MD->getParent();
   DeclarationName Name
     = getContext().DeclarationNames.getCXXOperatorName(OO_Call);
   DeclContext::lookup_const_result Calls = Lambda->lookup(Name);
-  CXXMethodDecl *MD = cast<CXXMethodDecl>(*Calls.first++);
+  CXXMethodDecl *CallOperator = cast<CXXMethodDecl>(*Calls.first++);
   const FunctionProtoType *FPT = MD->getType()->getAs<FunctionProtoType>();
   QualType ResultType = FPT->getResultType();
 
-  // Begin function
-  FunctionArgList FunctionArgs;
-  for (FunctionDecl::param_const_iterator I = MD->param_begin(),
-       E = MD->param_end(); I != E; ++I) {
-    ParmVarDecl *Param = *I;
-    FunctionArgs.push_back(Param);
-  }
-  StartFunction(GlobalDecl(), ResultType, Fn, FnInfo, FunctionArgs,
-                SourceLocation());
-
   // Start building arguments for forwarding call
   CallArgList CallArgs;
 
@@ -1760,7 +1749,7 @@ void CodeGenFunction::EmitLambdaThunkBody(llvm::Function *Fn,
   }
 
   // Get the address of the call operator.
-  GlobalDecl GD(MD);
+  GlobalDecl GD(CallOperator);
   const CGFunctionInfo &CalleeFnInfo = CGM.getTypes().getFunctionInfo(GD);
   llvm::Type *Ty =
     CGM.getTypes().GetFunctionType(CalleeFnInfo, FPT->isVariadic());
@@ -1769,54 +1758,24 @@ void CodeGenFunction::EmitLambdaThunkBody(llvm::Function *Fn,
   // Determine whether we have a return value slot to use.
   ReturnValueSlot Slot;
   if (!ResultType->isVoidType() &&
-      FnInfo.getReturnInfo().getKind() == ABIArgInfo::Indirect &&
+      CurFnInfo->getReturnInfo().getKind() == ABIArgInfo::Indirect &&
       hasAggregateLLVMType(CurFnInfo->getReturnType()))
     Slot = ReturnValueSlot(ReturnValue, ResultType.isVolatileQualified());
   
   // Now emit our call.
-  RValue RV = EmitCall(CalleeFnInfo, Callee, Slot, CallArgs, MD);
+  RValue RV = EmitCall(CalleeFnInfo, Callee, Slot, CallArgs, CallOperator);
 
   // Forward the returned value
   if (!ResultType->isVoidType() && Slot.isNull())
     EmitReturnOfRValue(RV, ResultType);
-
-  // End the function.
-  FinishFunction();
 }
 
-llvm::Constant *
-CodeGenFunction::EmitLambdaConvertedFnPtr(const CXXMethodDecl *MD) {
-  QualType FnTy = MD->getResultType()->getPointeeType();
-  CanQual<FunctionProtoType> CanFnTy =
-      CGM.getContext().getCanonicalType(FnTy)->getAs<FunctionProtoType>();
-  llvm::FunctionType *FnLLVMTy = cast<llvm::FunctionType>(CGM.getTypes().ConvertType(FnTy));
-  const CXXRecordDecl *Lambda = MD->getParent();
-  const CGFunctionInfo &FnInfo = CGM.getTypes().getFunctionInfo(CanFnTy);
-
-  if (CanFnTy->isVariadic()) {
+void CodeGenFunction::EmitLambdaStaticInvokeFunction(const CXXMethodDecl *MD) {
+  if (MD->isVariadic()) {
     // FIXME: Making this work correctly is nasty because it requires either
     // cloning the body of the call operator or making the call operator forward.
     CGM.ErrorUnsupported(MD, "lambda conversion to variadic function");
-    return llvm::UndefValue::get(FnLLVMTy->getPointerTo());
   }
 
-  // Build a declaration for the function which this function will
-  // return a pointer to.
-  // FIXME: Should the "thunk" actually be part of the AST?  That would allow
-  // the conversion to function pointer to be constexpr...
-  std::string MangledName =
-      (llvm::Twine(CurFn->getName()) + "_lambdacallthunk").str();
-  llvm::Function *Fn =
-      llvm::Function::Create(FnLLVMTy, llvm::Function::InternalLinkage,
-                            MangledName, &CGM.getModule());
-
-  // Emit the definition of the new function.
-  CodeGenFunction(CGM).EmitLambdaThunkBody(Fn, FnInfo, Lambda);
-  return Fn;
-}
-
-void CodeGenFunction::EmitLambdaToFunctionPointerBody(FunctionArgList &Args) {
-  const CXXMethodDecl *MD = cast<CXXMethodDecl>(CurFuncDecl);
-  EmitReturnOfRValue(RValue::get(EmitLambdaConvertedFnPtr(MD)),
-                     MD->getResultType());
+  EmitLambdaDelegatingInvokeBody(MD);
 }
index 1034de736c126d986ea5034f2b913949eb1f9ace..28c5935de0491faecebfc1fbe9bf89290e1328e9 100644 (file)
@@ -448,13 +448,15 @@ void CodeGenFunction::GenerateCode(GlobalDecl GD, llvm::Function *Fn,
            FD->hasAttr<CUDAGlobalAttr>())
     CGM.getCUDARuntime().EmitDeviceStubBody(*this, Args);
   else if (isa<CXXConversionDecl>(FD) &&
-           cast<CXXConversionDecl>(FD)->getParent()->isLambda()) {
-    // The lambda conversion operators are special; the semantics can't be
-    // expressed in the AST, so IRGen needs to special-case them.
-    if (cast<CXXConversionDecl>(FD)->isLambdaToBlockPointerConversion())
-      EmitLambdaToBlockPointerBody(Args);
-    else
-      EmitLambdaToFunctionPointerBody(Args);
+           cast<CXXConversionDecl>(FD)->isLambdaToBlockPointerConversion()) {
+    // The lambda conversion to block pointer is special; the semantics can't be
+    // expressed in the AST, so IRGen needs to special-case it.
+    EmitLambdaToBlockPointerBody(Args);
+  } else if (isa<CXXMethodDecl>(FD) &&
+             cast<CXXMethodDecl>(FD)->isLambdaStaticInvoker()) {
+    // The lambda "__invoke" function is special, because it forwards or
+    // clones the body of the function call operator (but is actually static).
+    EmitLambdaStaticInvokeFunction(cast<CXXMethodDecl>(FD));
   }
   else
     EmitFunctionBody(Args);
index 2b9b8ab7b8808f61e60c6a0171e77a8086f12f27..427ee08e69e1d693804d5239cbaa25810afa690b 100644 (file)
@@ -1377,11 +1377,8 @@ public:
   void EmitFunctionBody(FunctionArgList &Args);
 
   void EmitLambdaToBlockPointerBody(FunctionArgList &Args);
-  void EmitLambdaToFunctionPointerBody(FunctionArgList &Args);
-  llvm::Constant *EmitLambdaConvertedFnPtr(const CXXMethodDecl *MD);
-  void EmitLambdaThunkBody(llvm::Function *Fn,
-                           const CGFunctionInfo &FnInfo,
-                           const CXXRecordDecl *Lambda);
+  void EmitLambdaDelegatingInvokeBody(const CXXMethodDecl *MD);
+  void EmitLambdaStaticInvokeFunction(const CXXMethodDecl *MD);
 
   /// EmitReturnBlock - Emit the unified return block, trying to avoid its
   /// emission when possible.
index 66ec800c726c29d82381d9e623444385effb0fb4..82e59ddafeef3d07d038199290ea3f960b4c6a2c 100644 (file)
@@ -9051,21 +9051,52 @@ bool Sema::isImplicitlyDeleted(FunctionDecl *FD) {
          isa<CXXMethodDecl>(FD);
 }
 
+/// \brief Mark the call operator of the given lambda closure type as "used".
+static void markLambdaCallOperatorUsed(Sema &S, CXXRecordDecl *Lambda) {
+  CXXMethodDecl *CallOperator 
+  = cast<CXXMethodDecl>(
+      *Lambda->lookup(
+        S.Context.DeclarationNames.getCXXOperatorName(OO_Call)).first);
+  CallOperator->setReferenced();
+  CallOperator->setUsed();
+}
+
 void Sema::DefineImplicitLambdaToFunctionPointerConversion(
        SourceLocation CurrentLocation,
        CXXConversionDecl *Conv) 
 {
+  CXXRecordDecl *Lambda = Conv->getParent();
+  
+  // Make sure that the lambda call operator is marked used.
+  markLambdaCallOperatorUsed(*this, Lambda);
+  
   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(),
+  // Return the address of the __invoke function.
+  DeclarationName InvokeName = &Context.Idents.get("__invoke");
+  CXXMethodDecl *Invoke 
+    = cast<CXXMethodDecl>(*Lambda->lookup(InvokeName).first);
+  Expr *FunctionRef = BuildDeclRefExpr(Invoke, Invoke->getType(),
+                                       VK_LValue, Conv->getLocation()).take();
+  assert(FunctionRef && "Can't refer to __invoke function?");
+  Stmt *Return = ActOnReturnStmt(Conv->getLocation(), FunctionRef).take();
+  Conv->setBody(new (Context) CompoundStmt(Context, &Return, 1, 
+                                           Conv->getLocation(),
                                            Conv->getLocation()));
+    
+  // Fill in the __invoke function with a dummy implementation. IR generation
+  // will fill in the actual details.
+  Invoke->setUsed();
+  Invoke->setReferenced();
+  Invoke->setBody(new (Context) CompoundStmt(Context, 0, 0, Conv->getLocation(),
+                                             Conv->getLocation()));
   
   if (ASTMutationListener *L = getASTMutationListener()) {
     L->CompletedImplicitDefinition(Conv);
+    L->CompletedImplicitDefinition(Invoke);
   }
 }
 
@@ -9073,6 +9104,8 @@ void Sema::DefineImplicitLambdaToBlockPointerConversion(
        SourceLocation CurrentLocation,
        CXXConversionDecl *Conv) 
 {
+  // Make sure that the lambda call operator is marked used.
+  markLambdaCallOperatorUsed(*this, Conv->getParent());
   Conv->setUsed();
   
   ImplicitlyDefinedFunctionScope Scope(*this, Conv);
index 4e5935701b3a8b94fc153dd64190cf6f92bcd24f..2af946288f4032bc312f50ffff84ccbfadfc34ad 100644 (file)
@@ -372,17 +372,18 @@ static void addFunctionPointerConversion(Sema &S,
                                          SourceRange IntroducerRange,
                                          CXXRecordDecl *Class,
                                          CXXMethodDecl *CallOperator) {
+  // Add the conversion to function pointer.
   const FunctionProtoType *Proto
     = CallOperator->getType()->getAs<FunctionProtoType>(); 
   QualType FunctionPtrTy;
+  QualType FunctionTy;
   {
     FunctionProtoType::ExtProtoInfo ExtInfo = Proto->getExtProtoInfo();
     ExtInfo.TypeQuals = 0;
-    QualType FunctionTy
-      = S.Context.getFunctionType(Proto->getResultType(),
-                                  Proto->arg_type_begin(),
-                                  Proto->getNumArgs(),
-                                  ExtInfo);
+    FunctionTy = S.Context.getFunctionType(Proto->getResultType(),
+                                           Proto->arg_type_begin(),
+                                           Proto->getNumArgs(),
+                                           ExtInfo);
     FunctionPtrTy = S.Context.getPointerType(FunctionTy);
   }
   
@@ -409,6 +410,34 @@ static void addFunctionPointerConversion(Sema &S,
   Conversion->setAccess(AS_public);
   Conversion->setImplicit(true);
   Class->addDecl(Conversion);
+  
+  // Add a non-static member function "__invoke" that will be the result of
+  // the conversion.
+  Name = &S.Context.Idents.get("__invoke");
+  CXXMethodDecl *Invoke
+    = CXXMethodDecl::Create(S.Context, Class, Loc, 
+                            DeclarationNameInfo(Name, Loc), FunctionTy, 
+                            CallOperator->getTypeSourceInfo(),
+                            /*IsStatic=*/true, SC_Static, /*IsInline=*/true,
+                            /*IsConstexpr=*/false, 
+                            CallOperator->getBody()->getLocEnd());
+  SmallVector<ParmVarDecl *, 4> InvokeParams;
+  for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
+    ParmVarDecl *From = CallOperator->getParamDecl(I);
+    InvokeParams.push_back(ParmVarDecl::Create(S.Context, Invoke,
+                                               From->getLocStart(),
+                                               From->getLocation(),
+                                               From->getIdentifier(),
+                                               From->getType(),
+                                               From->getTypeSourceInfo(),
+                                               From->getStorageClass(),
+                                               From->getStorageClassAsWritten(),
+                                               /*DefaultArg=*/0));
+  }
+  Invoke->setParams(InvokeParams);
+  Invoke->setAccess(AS_private);
+  Invoke->setImplicit(true);
+  Class->addDecl(Invoke);
 }
 
 /// \brief Add a lambda's conversion to block pointer.
index 237687037d701ede199eb691dc3de4d4f1ea119c..e1aab1889c034579d16544b4beb83cfaa4c3c0da 100644 (file)
@@ -47,8 +47,26 @@ int e(E a, E b, bool cond) { [a,b,cond](){ return (cond ? a : b).x; }(); }
 // CHECK: invoke i32 @"_ZZ1e1ES_bENK3$_4clEv"
 // CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev"
 // CHECK: call void @"_ZZ1e1ES_bEN3$_4D1Ev"
+
 // CHECK: define internal i32 @"_ZZ1e1ES_bENK3$_4clEv"
 // CHECK: trunc i8
 // CHECK: load i32*
 // CHECK: ret i32
+
+void f() {
+  // CHECK: define void @_Z1fv()
+  // CHECK: {{call.*_5cvPFiiiEEv}}
+  // CHECK-NEXT: store i32 (i32, i32)*
+  // CHECK-NEXT: ret void
+  int (*fp)(int, int) = [](int x, int y){ return x + y; };
+}
+
+// CHECK: define internal i32 @"_ZZ1fvEN3$_58__invokeEii"
+// CHECK: store i32
+// CHECK-NEXT: store i32
+// CHECK-NEXT: load i32*
+// CHECK-NEXT: load i32*
+// CHECK-NEXT: call i32 @"_ZZ1fvENK3$_5clEii"
+// CHECK-NEXT: ret i32
+
 // CHECK: define internal void @"_ZZ1e1ES_bEN3$_4D2Ev"