Implement conversion to function pointer for generic lambdas without captures.

The general strategy is to create template versions of the conversion function and static invoker and then during template argument deduction of the conversion function, create the corresponding call-operator and static invoker specializations, and when the conversion function is marked referenced generate the body of the conversion function using the corresponding static-invoker specialization.  Similarly, Codegen does something similar - when asked to emit the IR for a specialized static invoker of a generic lambda, it forwards emission to the corresponding call operator. 

This patch has been reviewed in person both by Doug and Richard.  Richard gave me the LGTM.

A few minor changes:
  - per Richard's request i added a simple check to gracefully inform that captures (init, explicit or default) have not been added to generic lambdas just yet (instead of the assertion violation).
  - I removed a few lines of code that added the call operators instantiated parameters to the currentinstantiationscope. Not only did it not handle parameter packs, but it is more relevant in the patch for nested lambdas which will follow this one, and fix that problem more comprehensively.
  - Doug had commented that the original implementation strategy of using the TypeSourceInfo of the call operator to create the static-invoker was flawed and allowed const as a member qualifier to creep into the type of the static-invoker.  I currently kludge around it - but after my initial discussion with Doug, with a follow up session with Richard, I have added a FIXME so that a more elegant solution that involves the use of TrivialTypeSourceInfo call followed by the correct wiring of the template parameters to the functionprototypeloc is forthcoming.

Thanks! 
 

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@191634 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp
index b71aafe..4c73fdd 100644
--- a/lib/Sema/SemaTemplateDeduction.cpp
+++ b/lib/Sema/SemaTemplateDeduction.cpp
@@ -13,6 +13,7 @@
 #include "clang/Sema/TemplateDeduction.h"
 #include "TreeTransform.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/AST/ASTLambda.h"
 #include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
@@ -26,7 +27,6 @@
 
 namespace clang {
   using namespace sema;
-
   /// \brief Various flags that control template argument deduction.
   ///
   /// These flags can be bitwise-OR'd together.
@@ -3607,19 +3607,37 @@
   return TDK_Success;
 }
 
+/// \brief Given a function declaration (e.g. a generic lambda conversion 

+///  function) that contains an 'auto' in its result type, substitute it 

+///  with the same Deduced type that the TypeToReplaceAutoWith was deduced 

+///  with.

+static inline void 
+ReplaceAutoWithinFunctionReturnType(FunctionDecl *F, 
+                                    QualType TypeToReplaceAutoWith, Sema &S) {
+  if (TypeToReplaceAutoWith->getContainedAutoType())
+    TypeToReplaceAutoWith = TypeToReplaceAutoWith->
+        getContainedAutoType()->getDeducedType();
+
+  QualType AutoResultType = F->getResultType();

+  assert(AutoResultType->getContainedAutoType()); 

+  QualType DeducedResultType = S.SubstAutoType(AutoResultType, 

+                                               TypeToReplaceAutoWith);
+  S.Context.adjustDeducedFunctionResultType(F, DeducedResultType);
+}
 /// \brief Deduce template arguments for a templated conversion
 /// function (C++ [temp.deduct.conv]) and, if successful, produce a
 /// conversion function template specialization.
 Sema::TemplateDeductionResult
-Sema::DeduceTemplateArguments(FunctionTemplateDecl *FunctionTemplate,
+Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate,
                               QualType ToType,
                               CXXConversionDecl *&Specialization,
                               TemplateDeductionInfo &Info) {
-  if (FunctionTemplate->isInvalidDecl())
+  if (ConversionTemplate->isInvalidDecl())
     return TDK_Invalid;
 
   CXXConversionDecl *Conv
-    = cast<CXXConversionDecl>(FunctionTemplate->getTemplatedDecl());
+    = cast<CXXConversionDecl>(ConversionTemplate->getTemplatedDecl());
+
   QualType FromType = Conv->getConversionType();
 
   // Canonicalize the types for deduction.
@@ -3675,7 +3693,7 @@
   //   type that is required as the result of the conversion (call it
   //   A) as described in 14.8.2.4.
   TemplateParameterList *TemplateParams
-    = FunctionTemplate->getTemplateParameters();
+    = ConversionTemplate->getTemplateParameters();
   SmallVector<DeducedTemplateArgument, 4> Deduced;
   Deduced.resize(TemplateParams->size());
 
@@ -3703,14 +3721,147 @@
         = DeduceTemplateArgumentsByTypeMatch(*this, TemplateParams,
                                              P, A, Info, Deduced, TDF))
     return Result;
-
+

+  // Create an Instantiation Scope for finalizing the operator.

+  LocalInstantiationScope InstScope(*this);

+  

+  CXXMethodDecl *LambdaCallOpSpec = 0;

+  bool GenericLambdaCallOperatorHasDeducedReturnType = false;
+  

+  // Having successfully deduced and matched the type of the conversion

+  // function against the destination type, if the destination type

+  // is a ptr-to-function and the source type is a generic lambda conversion

+  // to ptr-to-function, we know that the parameters of the destination 

+  // ptr-to-function have matched successfully against those of our 

+  // lambda's conversion function.  

+  // For instance:

+  //  int (*fp)(int) = [](auto a) { return a; };

+  //     [template<class T> operator id<auto(*)(T)>() const]

+  // If it is indeed the conversion operator of a generic lambda then if

+  // not already done, create the corresponding specializations of the call 

+  // operator and the static-invoker; and if the return type is auto, 

+  // deduce the return type, and then check and see if it matches the ToType.

+

+  const bool IsGenericLambdaConversionOperator = 

+      isLambdaConversionOperator(Conv);

+  if (IsGenericLambdaConversionOperator) {

+    const Type *FromTypePtr = P.getTypePtr();

+    const Type *ToTypePtr = A.getTypePtr();

+

+    assert(P->isPointerType()); 

+    FromTypePtr = P->getPointeeType().getTypePtr();

+    assert(A->isPointerType());

+    ToTypePtr = A->getPointeeType().getTypePtr();

+    

+    CXXRecordDecl *LambdaClass = Conv->getParent();

+    assert(LambdaClass && LambdaClass->isGenericLambda()); 

+

+    const FunctionType *ToFunType = ToTypePtr->getAs<FunctionType>();

+

+    // The specialization of the Generic Lambda Call Op, instantiated

+    // using the deduced parameters from the conversion function

+    // i.e.

+    // auto L = [](auto a) { return f(a); };

+    // int (*fp)(int) = L;

+    //

+

+    CXXMethodDecl *CallOp = LambdaClass->getLambdaCallOperator();

+    QualType CallOpResultType = CallOp->getResultType(); 

+    GenericLambdaCallOperatorHasDeducedReturnType = 

+        CallOpResultType->getContainedAutoType();

+    FunctionTemplateDecl *CallOpTemplate = 

+        CallOp->getDescribedFunctionTemplate();

+

+    TemplateDeductionInfo OpInfo(Info.getLocation()); 

+    FunctionDecl *CallOpSpec = 0;

+    // Use the deduced arguments so far, to specialize our generic

+    // lambda's call operator.

+    if (TemplateDeductionResult Result

+                  = FinishTemplateArgumentDeduction(CallOpTemplate, Deduced, 

+                                                    0, CallOpSpec, OpInfo))

+      return Result;

+ 

+    bool HadToDeduceReturnTypeDuringCurrentCall = false;

+    // If we need to deduce the return type, do so (instantiates the callop).

+    if (GenericLambdaCallOperatorHasDeducedReturnType && 

+        CallOpSpec->getResultType()->isUndeducedType()) {

+      HadToDeduceReturnTypeDuringCurrentCall = true;

+      DeduceReturnType(CallOpSpec, CallOpSpec->getPointOfInstantiation(),

+                      /*Diagnose*/ true);

+    }

+    

+    LambdaCallOpSpec = cast<CXXMethodDecl>(CallOpSpec);

+    

+    // Check to see if the return type of the destination ptr-to-function

+    // matches the return type of the call operator.

+    if (!Context.hasSameType(LambdaCallOpSpec->getResultType(), 

+        ToFunType->getResultType()))

+      return TDK_NonDeducedMismatch;

+    // Since we have succeeded in matching the source and destination

+    // ptr-to-functions (now including return type), and have successfully 

+    // specialized our corresponding call operator, we are ready to

+    // specialize the static invoker with the deduced arguments of our

+    // ptr-to-function.

+    FunctionDecl *InvokerSpecialization = 0;

+    FunctionTemplateDecl *InvokerTemplate = LambdaClass->

+                    getLambdaStaticInvoker()->getDescribedFunctionTemplate();

+

+    TemplateDeductionResult Result

+      = FinishTemplateArgumentDeduction(InvokerTemplate, Deduced, 0, 

+            InvokerSpecialization, Info);

+    assert(Result == TDK_Success);

+    // Set the result type to match the corresponding call operator

+    // specialization's result type.

+    if (GenericLambdaCallOperatorHasDeducedReturnType && 

+        InvokerSpecialization->getResultType()->isUndeducedType())

+      ReplaceAutoWithinFunctionReturnType(InvokerSpecialization,

+                                LambdaCallOpSpec->getResultType(), *this);

+    
+    // Ensure that static invoker doesn't have a const qualifier.
+    // FIXME: When creating the InvokerTemplate in SemaLambda.cpp 
+    // do not use the CallOperator's TypeSourceInfo which allows
+    // the const qualifier to leak through. 
+    const FunctionProtoType *InvokerFPT = InvokerSpecialization->
+                    getType().getTypePtr()->castAs<FunctionProtoType>();
+    FunctionProtoType::ExtProtoInfo EPI = InvokerFPT->getExtProtoInfo();
+    EPI.TypeQuals = 0;
+    InvokerSpecialization->setType(Context.getFunctionType(
+        InvokerFPT->getResultType(), InvokerFPT->getArgTypes(),EPI));

+    

+    // Since the original conversion operator's parameters are the same 

+    // entities as the lambda's call operator's, we introduce a mapping

+    // from the generic to the specialized parameters of the call operators.

+    // This only needs to be done in the absence of return type deduction,

+    // since deducing the return type entails instantiation which adds

+    // the parameter mapping to the CurrentInstantiationScope. 

+    // This is necessary when transforming nested lambdas that do not

+    // capture.

+    // FIXME: This will be fixed once nested lambdas and capturing

+    // is implemented since it does require handling parameter 

+    // packs correctly which might require careful calls to

+    // SemaTemplateInstantiate::addInstantiatedParametersToScope.

+    // if (!HadToDeduceReturnTypeDuringCurrentCall) { ... }
+  }
+  

+  

   // Finish template argument deduction.
-  LocalInstantiationScope InstScope(*this);
-  FunctionDecl *Spec = 0;
-  TemplateDeductionResult Result
-    = FinishTemplateArgumentDeduction(FunctionTemplate, Deduced, 0, Spec,
-                                      Info);
-  Specialization = cast_or_null<CXXConversionDecl>(Spec);
+  FunctionDecl *ConversionSpec = 0;

+  TemplateDeductionResult Result

+        = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0, 

+              ConversionSpec, Info);

+  Specialization = cast_or_null<CXXConversionDecl>(ConversionSpec);

+  if (Result == TDK_Success && GenericLambdaCallOperatorHasDeducedReturnType) {

+    // Set the return type of the conversion specialization, since even 

+    // though we have ensured that the return types are compatible, if 

+    // there is an auto in the return type of this conversion function, 

+    // replace it permanently with the return type of the deduced lambda

+    // so we don't try and deduce against it.

+    assert(LambdaCallOpSpec);

+    if (ConversionSpec->getResultType()->isUndeducedType())

+      ReplaceAutoWithinFunctionReturnType(ConversionSpec, 

+                                          LambdaCallOpSpec->getResultType(),

+                                         *this);

+  } 
   return Result;
 }