Fix an instantiation bug with nested generic lambdas and conversion to fptrs.
authorFaisal Vali <faisalv@yahoo.com>
Thu, 24 Oct 2013 01:05:22 +0000 (01:05 +0000)
committerFaisal Vali <faisalv@yahoo.com>
Thu, 24 Oct 2013 01:05:22 +0000 (01:05 +0000)
This patch fixes the typelocs of the conversion-operator and the conversion-operator-name and adds the parameters of the call operator to the FunctionProtoTypeLoc of the respective entities. Thus, when the template declarations (conversion operators) undergo deduction and instantiation/transformation/substitution - they add themselves to the local instantiation scope if needed.

This patch supports the following:

auto L = [](auto b) {
  return [](auto a) ->decltype(a) { return a; };
};
int (*fp)(int) = L(8);

Richard LGTM'd this patch: http://llvm-reviews.chandlerc.com/D1831

Thanks!

llvm-svn: 193294

clang/lib/Sema/SemaLambda.cpp
clang/test/SemaCXX/cxx1y-generic-lambdas.cpp

index 9b3afc9..c843da7 100644 (file)
@@ -866,39 +866,110 @@ static void addFunctionPointerConversion(Sema &S,
                                          CXXRecordDecl *Class,
                                          CXXMethodDecl *CallOperator) {
   // Add the conversion to function pointer.
-  const FunctionProtoType *Proto
-    = CallOperator->getType()->getAs<FunctionProtoType>(); 
-  QualType FunctionPtrTy;
-  QualType FunctionTy;
+  const FunctionProtoType *CallOpProto = 
+      CallOperator->getType()->getAs<FunctionProtoType>();
+  const FunctionProtoType::ExtProtoInfo CallOpExtInfo = 
+      CallOpProto->getExtProtoInfo();   
+  QualType PtrToFunctionTy;
+  QualType InvokerFunctionTy;
   {
-    FunctionProtoType::ExtProtoInfo ExtInfo = Proto->getExtProtoInfo();
+    FunctionProtoType::ExtProtoInfo InvokerExtInfo = CallOpExtInfo;
     CallingConv CC = S.Context.getDefaultCallingConvention(
-        Proto->isVariadic(), /*IsCXXMethod=*/false);
-    ExtInfo.ExtInfo = ExtInfo.ExtInfo.withCallingConv(CC);
-    ExtInfo.TypeQuals = 0;
-    FunctionTy = S.Context.getFunctionType(Proto->getResultType(),
-                                           Proto->getArgTypes(), ExtInfo);
-    FunctionPtrTy = S.Context.getPointerType(FunctionTy);
+        CallOpProto->isVariadic(), /*IsCXXMethod=*/false);
+    InvokerExtInfo.ExtInfo = InvokerExtInfo.ExtInfo.withCallingConv(CC);
+    InvokerExtInfo.TypeQuals = 0;
+    assert(InvokerExtInfo.RefQualifier == RQ_None && 
+        "Lambda's call operator should not have a reference qualifier");
+    InvokerFunctionTy = S.Context.getFunctionType(CallOpProto->getResultType(),
+        CallOpProto->getArgTypes(), InvokerExtInfo);
+    PtrToFunctionTy = S.Context.getPointerType(InvokerFunctionTy);
   }
 
-  FunctionProtoType::ExtProtoInfo ExtInfo(S.Context.getDefaultCallingConvention(
+  // Create the type of the conversion function.
+  FunctionProtoType::ExtProtoInfo ConvExtInfo(
+      S.Context.getDefaultCallingConvention(
       /*IsVariadic=*/false, /*IsCXXMethod=*/true));
-  ExtInfo.TypeQuals = Qualifiers::Const;
-  QualType ConvTy = S.Context.getFunctionType(FunctionPtrTy, None, ExtInfo);
+  // The conversion function is always const.
+  ConvExtInfo.TypeQuals = Qualifiers::Const;
+  QualType ConvTy = 
+      S.Context.getFunctionType(PtrToFunctionTy, None, ConvExtInfo);
 
   SourceLocation Loc = IntroducerRange.getBegin();
-  DeclarationName Name
+  DeclarationName ConversionName
     = S.Context.DeclarationNames.getCXXConversionFunctionName(
-        S.Context.getCanonicalType(FunctionPtrTy));
-  DeclarationNameLoc NameLoc;
-  NameLoc.NamedType.TInfo = S.Context.getTrivialTypeSourceInfo(FunctionPtrTy,
-                                                               Loc);
+        S.Context.getCanonicalType(PtrToFunctionTy));
+  DeclarationNameLoc ConvNameLoc;
+  // Construct a TypeSourceInfo for the conversion function, and wire
+  // all the parameters appropriately for the FunctionProtoTypeLoc 
+  // so that everything works during transformation/instantiation of 
+  // generic lambdas.
+  // The main reason for wiring up the parameters of the conversion
+  // function with that of the call operator is so that constructs
+  // like the following work:
+  // auto L = [](auto b) {                <-- 1
+  //   return [](auto a) -> decltype(a) { <-- 2
+  //      return a;
+  //   };
+  // };
+  // int (*fp)(int) = L(5);  
+  // Because the trailing return type can contain DeclRefExprs that refer
+  // to the original call operator's variables, we hijack the call 
+  // operators ParmVarDecls below.
+  TypeSourceInfo *ConvNamePtrToFunctionTSI = 
+      S.Context.getTrivialTypeSourceInfo(PtrToFunctionTy, Loc);
+  ConvNameLoc.NamedType.TInfo = ConvNamePtrToFunctionTSI;
+
+  // The conversion function is a conversion to a pointer-to-function.
+  TypeSourceInfo *ConvTSI = S.Context.getTrivialTypeSourceInfo(ConvTy, Loc);
+  FunctionProtoTypeLoc ConvTL = 
+      ConvTSI->getTypeLoc().getAs<FunctionProtoTypeLoc>();
+  // Get the result of the conversion function which is a pointer-to-function.
+  PointerTypeLoc PtrToFunctionTL = 
+      ConvTL.getResultLoc().getAs<PointerTypeLoc>();
+  // Do the same for the TypeSourceInfo that is used to name the conversion
+  // operator.
+  PointerTypeLoc ConvNamePtrToFunctionTL = 
+      ConvNamePtrToFunctionTSI->getTypeLoc().getAs<PointerTypeLoc>();
+  
+  // Get the underlying function types that the conversion function will
+  // be converting to (should match the type of the call operator).
+  FunctionProtoTypeLoc CallOpConvTL = 
+      PtrToFunctionTL.getPointeeLoc().getAs<FunctionProtoTypeLoc>();
+  FunctionProtoTypeLoc CallOpConvNameTL = 
+    ConvNamePtrToFunctionTL.getPointeeLoc().getAs<FunctionProtoTypeLoc>();
+  
+  // Wire up the FunctionProtoTypeLocs with the call operator's parameters.
+  // These parameter's are essentially used to transform the name and
+  // the type of the conversion operator.  By using the same parameters
+  // as the call operator's we don't have to fix any back references that
+  // the trailing return type of the call operator's uses (such as 
+  // decltype(some_type<decltype(a)>::type{} + decltype(a){}) etc.)
+  // - we can simply use the return type of the call operator, and 
+  // everything should work. 
+  SmallVector<ParmVarDecl *, 4> InvokerParams;
+  for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I) {
+    ParmVarDecl *From = CallOperator->getParamDecl(I);
+
+    InvokerParams.push_back(ParmVarDecl::Create(S.Context, 
+           // Temporarily add to the TU. This is set to the invoker below.
+                                             S.Context.getTranslationUnitDecl(),
+                                             From->getLocStart(),
+                                             From->getLocation(),
+                                             From->getIdentifier(),
+                                             From->getType(),
+                                             From->getTypeSourceInfo(),
+                                             From->getStorageClass(),
+                                             /*DefaultArg=*/0));
+    CallOpConvTL.setArg(I, From);
+    CallOpConvNameTL.setArg(I, From);
+  }
+
   CXXConversionDecl *Conversion 
     = CXXConversionDecl::Create(S.Context, Class, Loc, 
-                                DeclarationNameInfo(Name, Loc, NameLoc),
+                                DeclarationNameInfo(ConversionName, 
+                                  Loc, ConvNameLoc),
                                 ConvTy, 
-                                S.Context.getTrivialTypeSourceInfo(ConvTy, 
-                                                                   Loc),
+                                ConvTSI,
                                 /*isInline=*/true, /*isExplicit=*/false,
                                 /*isConstexpr=*/false, 
                                 CallOperator->getBody()->getLocEnd());
@@ -912,7 +983,7 @@ static void addFunctionPointerConversion(Sema &S,
             CallOperator->getDescribedFunctionTemplate();
     FunctionTemplateDecl *ConversionTemplate =
                   FunctionTemplateDecl::Create(S.Context, Class,
-                                      Loc, Name,
+                                      Loc, ConversionName,
                                       TemplateCallOperator->getTemplateParameters(),
                                       Conversion);
     ConversionTemplate->setAccess(AS_public);
@@ -923,7 +994,8 @@ static void addFunctionPointerConversion(Sema &S,
     Class->addDecl(Conversion);
   // Add a non-static member function that will be the result of
   // the conversion with a certain unique ID.
-  Name = &S.Context.Idents.get(getLambdaStaticInvokerName());
+  DeclarationName InvokerName = &S.Context.Idents.get(
+                                                 getLambdaStaticInvokerName());
   // FIXME: Instead of passing in the CallOperator->getTypeSourceInfo()
   // we should get a prebuilt TrivialTypeSourceInfo from Context
   // using FunctionTy & Loc and get its TypeLoc as a FunctionProtoTypeLoc
@@ -931,34 +1003,28 @@ static void addFunctionPointerConversion(Sema &S,
   // loop below and then use its Params to set Invoke->setParams(...) below.
   // This would avoid the 'const' qualifier of the calloperator from 
   // contaminating the type of the invoker, which is currently adjusted 
-  // in SemaTemplateDeduction.cpp:DeduceTemplateArguments.
+  // in SemaTemplateDeduction.cpp:DeduceTemplateArguments.  Fixing the
+  // trailing return type of the invoker would require a visitor to rebuild
+  // the trailing return type and adjusting all back DeclRefExpr's to refer
+  // to the new static invoker parameters - not the call operator's.
   CXXMethodDecl *Invoke
     = CXXMethodDecl::Create(S.Context, Class, Loc, 
-                            DeclarationNameInfo(Name, Loc), FunctionTy, 
-                            CallOperator->getTypeSourceInfo(),
+                            DeclarationNameInfo(InvokerName, Loc), 
+                            InvokerFunctionTy,
+                            CallOperator->getTypeSourceInfo(), 
                             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(),
-                                               /*DefaultArg=*/0));
-  }
-  Invoke->setParams(InvokeParams);
+  for (unsigned I = 0, N = CallOperator->getNumParams(); I != N; ++I)
+    InvokerParams[I]->setOwningFunction(Invoke);
+  Invoke->setParams(InvokerParams);
   Invoke->setAccess(AS_private);
   Invoke->setImplicit(true);
   if (Class->isGenericLambda()) {
     FunctionTemplateDecl *TemplateCallOperator = 
             CallOperator->getDescribedFunctionTemplate();
     FunctionTemplateDecl *StaticInvokerTemplate = FunctionTemplateDecl::Create(
-                          S.Context, Class, Loc, Name,
+                          S.Context, Class, Loc, InvokerName,
                           TemplateCallOperator->getTemplateParameters(),
                           Invoke);
     StaticInvokerTemplate->setAccess(AS_private);
index 64b9ff1..3407441 100644 (file)
@@ -585,6 +585,50 @@ template<class T> void foo(T) {
 template void foo(int); 
 } // end ns nested_generic_lambdas_123
 
+namespace nested_fptr_235 {
+int test()
+{
+  auto L = [](auto b) {
+    return [](auto a) ->decltype(a) { return a; };
+  };
+  int (*fp)(int) = L(8);
+  fp(5);
+  L(3);
+  char (*fc)(char) = L('a');
+  fc('b');
+  L('c');
+  double (*fd)(double) = L(3.14);
+  fd(3.14);
+  fd(6.26);
+  return 0;
+}
+int run = test();
+}
+
+
+namespace fptr_with_decltype_return_type {
+template<class F, class ... Ts> using FirstType = F;
+template<class F, class ... Rest> F& FirstArg(F& f, Rest& ... r) { return f; };
+template<class ... Ts> auto vfun(Ts&& ... ts) {
+  print(ts...);
+  return FirstArg(ts...);
+}
+int test()
+{
+ {
+   auto L = [](auto ... As) {
+    return [](auto b) ->decltype(b) {   
+      vfun([](decltype(As) a) -> decltype(a) { return a; } ...)(FirstType<decltype(As)...>{});
+      return decltype(b){};
+    };
+   };
+   auto LL = L(1, 'a', 3.14, "abc");
+   LL("dim");
+ }
+  return 0;
+}
+int run = test();
+}
 
 } // end ns nested_non_capturing_lambda_tests