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!

llvm-svn: 193294
This commit is contained in:
Faisal Vali 2013-10-24 01:05:22 +00:00
parent e0bc980500
commit 66605d40a7
2 changed files with 151 additions and 41 deletions

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

View 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