From: Faisal Vali Date: Thu, 24 Oct 2013 01:05:22 +0000 (+0000) Subject: Fix an instantiation bug with nested generic lambdas and conversion to fptrs. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=605f91ffd18f35ffcb5fcdd38379bd09b67a6cb8;p=clang 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! git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@193294 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index 9b3afc9990..c843da74cd 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -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(); - QualType FunctionPtrTy; - QualType FunctionTy; + const FunctionProtoType *CallOpProto = + CallOperator->getType()->getAs(); + 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(); + // Get the result of the conversion function which is a pointer-to-function. + PointerTypeLoc PtrToFunctionTL = + ConvTL.getResultLoc().getAs(); + // Do the same for the TypeSourceInfo that is used to name the conversion + // operator. + PointerTypeLoc ConvNamePtrToFunctionTL = + ConvNamePtrToFunctionTSI->getTypeLoc().getAs(); + + // 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 CallOpConvNameTL = + ConvNamePtrToFunctionTL.getPointeeLoc().getAs(); + + // 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::type{} + decltype(a){}) etc.) + // - we can simply use the return type of the call operator, and + // everything should work. + SmallVector 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 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); diff --git a/test/SemaCXX/cxx1y-generic-lambdas.cpp b/test/SemaCXX/cxx1y-generic-lambdas.cpp index 64b9ff1421..3407441770 100644 --- a/test/SemaCXX/cxx1y-generic-lambdas.cpp +++ b/test/SemaCXX/cxx1y-generic-lambdas.cpp @@ -585,6 +585,50 @@ template 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 using FirstType = F; +template F& FirstArg(F& f, Rest& ... r) { return f; }; +template 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{}); + 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