r193294 - Fix an instantiation bug with nested generic lambdas and conversion to fptrs.
Faisal Vali
faisalv at yahoo.com
Wed Oct 23 18:05:23 PDT 2013
Author: faisalv
Date: Wed Oct 23 20:05:22 2013
New Revision: 193294
URL: http://llvm.org/viewvc/llvm-project?rev=193294&view=rev
Log:
Fix an instantiation bug with nested generic lambdas and conversion to fptrs.
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!
Modified:
cfe/trunk/lib/Sema/SemaLambda.cpp
cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp
Modified: cfe/trunk/lib/Sema/SemaLambda.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaLambda.cpp?rev=193294&r1=193293&r2=193294&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaLambda.cpp (original)
+++ cfe/trunk/lib/Sema/SemaLambda.cpp Wed Oct 23 20:05:22 2013
@@ -866,39 +866,110 @@ static void addFunctionPointerConversion
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
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
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
// 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);
Modified: cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp?rev=193294&r1=193293&r2=193294&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp (original)
+++ cfe/trunk/test/SemaCXX/cxx1y-generic-lambdas.cpp Wed Oct 23 20:05:22 2013
@@ -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
More information about the cfe-commits
mailing list