From 78f3c1f12f22bb890addc680a9955297d5155aff Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Tue, 2 Jan 2018 23:52:42 +0000 Subject: [PATCH] Fix and simplify handling of return type for (generic) lambda conversion function to function pointer. Previously, we would: * compute the type of the conversion function and static invoker as a side-effect of template argument deduction for a conversion * re-compute the type as part of deduced return type deduction when building the conversion function itself Neither of these turns out to be quite correct. There are other ways to reach a declaration of the conversion function than in a conversion (such as an explicit call or friend declaration), and performing auto deduction causes the function type to be rebuilt in the context of the lambda closure type (which is different from the context in which it originally appeared, resulting in spurious substitution failures for constructs that are valid in one context but not the other, such as the use of an enclosing class's "this" pointer). This patch switches us to use a different strategy: as before, we use the declared type of the operator() to form the type of the conversion function and invoker, but we now populate that type as part of return type deduction for the conversion function. And the invoker is now treated as simply being an implementation detail of building the conversion function, and isn't given special treatment by template argument deduction for the conversion function any more. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@321683 91177308-0d34-0410-b5e6-96231b3b80d8 --- include/clang/Sema/Sema.h | 8 + lib/Sema/SemaDeclCXX.cpp | 70 ++++----- lib/Sema/SemaLambda.cpp | 55 +++---- lib/Sema/SemaOverload.cpp | 3 - lib/Sema/SemaStmt.cpp | 7 + lib/Sema/SemaTemplateDeduction.cpp | 177 +++++------------------ lib/Sema/SemaTemplateInstantiateDecl.cpp | 24 +++ test/SemaCXX/cxx1y-generic-lambdas.cpp | 18 ++- test/SemaCXX/lambda-expressions.cpp | 25 ++++ 9 files changed, 164 insertions(+), 223 deletions(-) diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 9cbe8e5cd6..055d14f2d0 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -5547,6 +5547,11 @@ public: ExprResult BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc, sema::LambdaScopeInfo *LSI); + /// Get the return type to use for a lambda's conversion function(s) to + /// function pointer type, given the type of the call operator. + QualType + getLambdaConversionFunctionResultType(const FunctionProtoType *CallOpType); + /// \brief Define the "body" of the conversion from a lambda object to a /// function pointer. /// @@ -7760,6 +7765,9 @@ public: void InstantiateExceptionSpec(SourceLocation PointOfInstantiation, FunctionDecl *Function); + FunctionDecl *InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, + const TemplateArgumentList *Args, + SourceLocation Loc); void InstantiateFunctionDefinition(SourceLocation PointOfInstantiation, FunctionDecl *Function, bool Recursive = false, diff --git a/lib/Sema/SemaDeclCXX.cpp b/lib/Sema/SemaDeclCXX.cpp index ceded02e39..5e24649c71 100644 --- a/lib/Sema/SemaDeclCXX.cpp +++ b/lib/Sema/SemaDeclCXX.cpp @@ -12215,30 +12215,27 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion( SourceLocation CurrentLocation, CXXConversionDecl *Conv) { SynthesizedFunctionScope Scope(*this, Conv); + assert(!Conv->getReturnType()->isUndeducedType()); CXXRecordDecl *Lambda = Conv->getParent(); - CXXMethodDecl *CallOp = Lambda->getLambdaCallOperator(); - // If we are defining a specialization of a conversion to function-ptr - // cache the deduced template arguments for this specialization - // so that we can use them to retrieve the corresponding call-operator - // and static-invoker. - const TemplateArgumentList *DeducedTemplateArgs = nullptr; - - // Retrieve the corresponding call-operator specialization. - if (Lambda->isGenericLambda()) { - assert(Conv->isFunctionTemplateSpecialization()); - FunctionTemplateDecl *CallOpTemplate = - CallOp->getDescribedFunctionTemplate(); - DeducedTemplateArgs = Conv->getTemplateSpecializationArgs(); - void *InsertPos = nullptr; - FunctionDecl *CallOpSpec = CallOpTemplate->findSpecialization( - DeducedTemplateArgs->asArray(), - InsertPos); - assert(CallOpSpec && - "Conversion operator must have a corresponding call operator"); - CallOp = cast(CallOpSpec); + FunctionDecl *CallOp = Lambda->getLambdaCallOperator(); + FunctionDecl *Invoker = Lambda->getLambdaStaticInvoker(); + + if (auto *TemplateArgs = Conv->getTemplateSpecializationArgs()) { + CallOp = InstantiateFunctionDeclaration( + CallOp->getDescribedFunctionTemplate(), TemplateArgs, CurrentLocation); + if (!CallOp) + return; + + Invoker = InstantiateFunctionDeclaration( + Invoker->getDescribedFunctionTemplate(), TemplateArgs, CurrentLocation); + if (!Invoker) + return; } + if (CallOp->isInvalidDecl()) + return; + // Mark the call operator referenced (and add to pending instantiations // if necessary). // For both the conversion and static-invoker template specializations @@ -12246,39 +12243,24 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion( // to the PendingInstantiations. MarkFunctionReferenced(CurrentLocation, CallOp); - // Retrieve the static invoker... - CXXMethodDecl *Invoker = Lambda->getLambdaStaticInvoker(); - // ... and get the corresponding specialization for a generic lambda. - if (Lambda->isGenericLambda()) { - assert(DeducedTemplateArgs && - "Must have deduced template arguments from Conversion Operator"); - FunctionTemplateDecl *InvokeTemplate = - Invoker->getDescribedFunctionTemplate(); - void *InsertPos = nullptr; - FunctionDecl *InvokeSpec = InvokeTemplate->findSpecialization( - DeducedTemplateArgs->asArray(), - InsertPos); - assert(InvokeSpec && - "Must have a corresponding static invoker specialization"); - Invoker = cast(InvokeSpec); - } + // Fill in the __invoke function with a dummy implementation. IR generation + // will fill in the actual details. Update its type in case it contained + // an 'auto'. + Invoker->markUsed(Context); + Invoker->setReferenced(); + Invoker->setType(Conv->getReturnType()->getPointeeType()); + Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation())); + // Construct the body of the conversion function { return __invoke; }. Expr *FunctionRef = BuildDeclRefExpr(Invoker, Invoker->getType(), - VK_LValue, Conv->getLocation()).get(); + VK_LValue, Conv->getLocation()).get(); assert(FunctionRef && "Can't refer to __invoke function?"); Stmt *Return = BuildReturnStmt(Conv->getLocation(), FunctionRef).get(); Conv->setBody(CompoundStmt::Create(Context, Return, Conv->getLocation(), Conv->getLocation())); - Conv->markUsed(Context); Conv->setReferenced(); - // Fill in the __invoke function with a dummy implementation. IR generation - // will fill in the actual details. - Invoker->markUsed(Context); - Invoker->setReferenced(); - Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation())); - if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Conv); L->CompletedImplicitDefinition(Invoker); diff --git a/lib/Sema/SemaLambda.cpp b/lib/Sema/SemaLambda.cpp index cbfc330ca6..19d2de7197 100644 --- a/lib/Sema/SemaLambda.cpp +++ b/lib/Sema/SemaLambda.cpp @@ -1167,6 +1167,24 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope, PopFunctionScopeInfo(); } +QualType Sema::getLambdaConversionFunctionResultType( + const FunctionProtoType *CallOpProto) { + // The function type inside the pointer type is the same as the call + // operator with some tweaks. The calling convention is the default free + // function convention, and the type qualifications are lost. + const FunctionProtoType::ExtProtoInfo CallOpExtInfo = + CallOpProto->getExtProtoInfo(); + FunctionProtoType::ExtProtoInfo InvokerExtInfo = CallOpExtInfo; + CallingConv CC = Context.getDefaultCallingConvention( + 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"); + return Context.getFunctionType(CallOpProto->getReturnType(), + CallOpProto->getParamTypes(), InvokerExtInfo); +} + /// \brief Add a lambda's conversion to function pointer, as described in /// C++11 [expr.prim.lambda]p6. static void addFunctionPointerConversion(Sema &S, @@ -1182,25 +1200,9 @@ static void addFunctionPointerConversion(Sema &S, return; // Add the conversion to function pointer. - const FunctionProtoType *CallOpProto = - CallOperator->getType()->getAs(); - const FunctionProtoType::ExtProtoInfo CallOpExtInfo = - CallOpProto->getExtProtoInfo(); - QualType PtrToFunctionTy; - QualType InvokerFunctionTy; - { - FunctionProtoType::ExtProtoInfo InvokerExtInfo = CallOpExtInfo; - CallingConv CC = S.Context.getDefaultCallingConvention( - 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->getReturnType(), - CallOpProto->getParamTypes(), InvokerExtInfo); - PtrToFunctionTy = S.Context.getPointerType(InvokerFunctionTy); - } + QualType InvokerFunctionTy = S.getLambdaConversionFunctionResultType( + CallOperator->getType()->castAs()); + QualType PtrToFunctionTy = S.Context.getPointerType(InvokerFunctionTy); // Create the type of the conversion function. FunctionProtoType::ExtProtoInfo ConvExtInfo( @@ -1357,19 +1359,8 @@ static void addBlockPointerConversion(Sema &S, SourceRange IntroducerRange, CXXRecordDecl *Class, CXXMethodDecl *CallOperator) { - const FunctionProtoType *Proto = - CallOperator->getType()->getAs(); - - // The function type inside the block pointer type is the same as the call - // operator with some tweaks. The calling convention is the default free - // function convention, and the type qualifications are lost. - FunctionProtoType::ExtProtoInfo BlockEPI = Proto->getExtProtoInfo(); - BlockEPI.ExtInfo = - BlockEPI.ExtInfo.withCallingConv(S.Context.getDefaultCallingConvention( - Proto->isVariadic(), /*IsCXXMethod=*/false)); - BlockEPI.TypeQuals = 0; - QualType FunctionTy = S.Context.getFunctionType( - Proto->getReturnType(), Proto->getParamTypes(), BlockEPI); + QualType FunctionTy = S.getLambdaConversionFunctionResultType( + CallOperator->getType()->castAs()); QualType BlockPtrTy = S.Context.getBlockPointerType(FunctionTy); FunctionProtoType::ExtProtoInfo ConversionEPI( diff --git a/lib/Sema/SemaOverload.cpp b/lib/Sema/SemaOverload.cpp index 07d931552f..f2b1963df1 100644 --- a/lib/Sema/SemaOverload.cpp +++ b/lib/Sema/SemaOverload.cpp @@ -11328,9 +11328,6 @@ Sema::ResolveSingleFunctionTemplateSpecialization(OverloadExpr *ovl, return Matched; } - - - // Resolve and fix an overloaded expression that can be resolved // because it identifies a single function template specialization. // diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp index 4474d62949..1ebc36716a 100644 --- a/lib/Sema/SemaStmt.cpp +++ b/lib/Sema/SemaStmt.cpp @@ -14,6 +14,7 @@ #include "clang/Sema/SemaInternal.h" #include "clang/AST/ASTContext.h" #include "clang/AST/ASTDiagnostic.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/CharUnits.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclObjC.h" @@ -3228,6 +3229,12 @@ bool Sema::DeduceFunctionTypeFromReturnExpr(FunctionDecl *FD, SourceLocation ReturnLoc, Expr *&RetExpr, AutoType *AT) { + // If this is the conversion function for a lambda, we choose to deduce it + // type from the corresponding call operator, not from the synthesized return + // statement within it. See Sema::DeduceReturnType. + if (isLambdaConversionOperator(FD)) + return false; + TypeLoc OrigResultType = getReturnTypeLoc(FD); QualType Deduced; diff --git a/lib/Sema/SemaTemplateDeduction.cpp b/lib/Sema/SemaTemplateDeduction.cpp index f8ee602516..3a0f2ff000 100644 --- a/lib/Sema/SemaTemplateDeduction.cpp +++ b/lib/Sema/SemaTemplateDeduction.cpp @@ -3940,117 +3940,6 @@ Sema::TemplateDeductionResult Sema::DeduceTemplateArguments( 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 TypeToReplaceAutoWith. Be careful to pass in the type you want -/// to replace 'auto' with and not the actual result type you want -/// to set the function to. -static inline void -SubstAutoWithinFunctionReturnType(FunctionDecl *F, - QualType TypeToReplaceAutoWith, Sema &S) { - assert(!TypeToReplaceAutoWith->getContainedAutoType()); - QualType AutoResultType = F->getReturnType(); - assert(AutoResultType->getContainedAutoType()); - QualType DeducedResultType = S.SubstAutoType(AutoResultType, - TypeToReplaceAutoWith); - S.Context.adjustDeducedFunctionResultType(F, DeducedResultType); -} - -/// \brief Given a specialized conversion operator of a generic lambda -/// create the corresponding specializations of the call operator and -/// the static-invoker. If the return type of the call operator is auto, -/// deduce its return type and check if that matches the -/// return type of the destination function ptr. - -static inline Sema::TemplateDeductionResult -SpecializeCorrespondingLambdaCallOperatorAndInvoker( - CXXConversionDecl *ConversionSpecialized, - SmallVectorImpl &DeducedArguments, - QualType ReturnTypeOfDestFunctionPtr, - TemplateDeductionInfo &TDInfo, - Sema &S) { - - CXXRecordDecl *LambdaClass = ConversionSpecialized->getParent(); - assert(LambdaClass && LambdaClass->isGenericLambda()); - - CXXMethodDecl *CallOpGeneric = LambdaClass->getLambdaCallOperator(); - QualType CallOpResultType = CallOpGeneric->getReturnType(); - const bool GenericLambdaCallOperatorHasDeducedReturnType = - CallOpResultType->getContainedAutoType(); - - FunctionTemplateDecl *CallOpTemplate = - CallOpGeneric->getDescribedFunctionTemplate(); - - FunctionDecl *CallOpSpecialized = nullptr; - // Use the deduced arguments of the conversion function, to specialize our - // generic lambda's call operator. - if (Sema::TemplateDeductionResult Result - = S.FinishTemplateArgumentDeduction(CallOpTemplate, - DeducedArguments, - 0, CallOpSpecialized, TDInfo)) - return Result; - - // If we need to deduce the return type, do so (instantiates the callop). - if (GenericLambdaCallOperatorHasDeducedReturnType && - CallOpSpecialized->getReturnType()->isUndeducedType()) - S.DeduceReturnType(CallOpSpecialized, - CallOpSpecialized->getPointOfInstantiation(), - /*Diagnose*/ true); - - // Check to see if the return type of the destination ptr-to-function - // matches the return type of the call operator. - if (!S.Context.hasSameType(CallOpSpecialized->getReturnType(), - ReturnTypeOfDestFunctionPtr)) - return Sema::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 *InvokerSpecialized = nullptr; - FunctionTemplateDecl *InvokerTemplate = LambdaClass-> - getLambdaStaticInvoker()->getDescribedFunctionTemplate(); - -#ifndef NDEBUG - Sema::TemplateDeductionResult LLVM_ATTRIBUTE_UNUSED Result = -#endif - S.FinishTemplateArgumentDeduction(InvokerTemplate, DeducedArguments, 0, - InvokerSpecialized, TDInfo); - assert(Result == Sema::TDK_Success && - "If the call operator succeeded so should the invoker!"); - // Set the result type to match the corresponding call operator - // specialization's result type. - if (GenericLambdaCallOperatorHasDeducedReturnType && - InvokerSpecialized->getReturnType()->isUndeducedType()) { - // Be sure to get the type to replace 'auto' with and not - // the full result type of the call op specialization - // to substitute into the 'auto' of the invoker and conversion - // function. - // For e.g. - // int* (*fp)(int*) = [](auto* a) -> auto* { return a; }; - // We don't want to subst 'int*' into 'auto' to get int**. - - QualType TypeToReplaceAutoWith = CallOpSpecialized->getReturnType() - ->getContainedAutoType() - ->getDeducedType(); - SubstAutoWithinFunctionReturnType(InvokerSpecialized, - TypeToReplaceAutoWith, S); - SubstAutoWithinFunctionReturnType(ConversionSpecialized, - TypeToReplaceAutoWith, S); - } - - // 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 = InvokerSpecialized-> - getType().getTypePtr()->castAs(); - FunctionProtoType::ExtProtoInfo EPI = InvokerFPT->getExtProtoInfo(); - EPI.TypeQuals = 0; - InvokerSpecialized->setType(S.Context.getFunctionType( - InvokerFPT->getReturnType(), InvokerFPT->getParamTypes(), EPI)); - return Sema::TDK_Success; -} /// \brief Deduce template arguments for a templated conversion /// function (C++ [temp.deduct.conv]) and, if successful, produce a /// conversion function template specialization. @@ -4158,35 +4047,6 @@ Sema::DeduceTemplateArguments(FunctionTemplateDecl *ConversionTemplate, = FinishTemplateArgumentDeduction(ConversionTemplate, Deduced, 0, ConversionSpecialized, Info); Specialization = cast_or_null(ConversionSpecialized); - - // If the conversion operator is being invoked on a lambda closure to convert - // to a ptr-to-function, use the deduced arguments from the conversion - // function to specialize the corresponding call operator. - // e.g., int (*fp)(int) = [](auto a) { return a; }; - if (Result == TDK_Success && isLambdaConversionOperator(ConversionGeneric)) { - - // Get the return type of the destination ptr-to-function we are converting - // to. This is necessary for matching the lambda call operator's return - // type to that of the destination ptr-to-function's return type. - assert(A->isPointerType() && - "Can only convert from lambda to ptr-to-function"); - const FunctionType *ToFunType = - A->getPointeeType().getTypePtr()->getAs(); - const QualType DestFunctionPtrReturnType = ToFunType->getReturnType(); - - // Create the corresponding specializations of the call operator and - // the static-invoker; and if the return type is auto, - // deduce the return type and check if it matches the - // DestFunctionPtrReturnType. - // For instance: - // auto L = [](auto a) { return f(a); }; - // int (*fp)(int) = L; - // char (*fp2)(int) = L; <-- Not OK. - - Result = SpecializeCorrespondingLambdaCallOperatorAndInvoker( - Specialization, Deduced, DestFunctionPtrReturnType, - Info, *this); - } return Result; } @@ -4536,6 +4396,43 @@ bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc, bool Diagnose) { assert(FD->getReturnType()->isUndeducedType()); + // For a lambda's conversion operator, deduce any 'auto' or 'decltype(auto)' + // within the return type from the call operator's type. + if (isLambdaConversionOperator(FD)) { + CXXRecordDecl *Lambda = cast(FD)->getParent(); + FunctionDecl *CallOp = Lambda->getLambdaCallOperator(); + + // For a generic lambda, instantiate the call operator if needed. + if (auto *Args = FD->getTemplateSpecializationArgs()) { + CallOp = InstantiateFunctionDeclaration( + CallOp->getDescribedFunctionTemplate(), Args, Loc); + if (!CallOp || CallOp->isInvalidDecl()) + return true; + + // We might need to deduce the return type by instantiating the definition + // of the operator() function. + if (CallOp->getReturnType()->isUndeducedType()) + InstantiateFunctionDefinition(Loc, CallOp); + } + + if (CallOp->isInvalidDecl()) + return true; + assert(!CallOp->getReturnType()->isUndeducedType() && + "failed to deduce lambda return type"); + + // Build the new return type from scratch. + QualType RetType = getLambdaConversionFunctionResultType( + CallOp->getType()->castAs()); + if (FD->getReturnType()->getAs()) + RetType = Context.getPointerType(RetType); + else { + assert(FD->getReturnType()->getAs()); + RetType = Context.getBlockPointerType(RetType); + } + Context.adjustDeducedFunctionResultType(FD, RetType); + return false; + } + if (FD->getTemplateInstantiationPattern()) InstantiateFunctionDefinition(Loc, FD); diff --git a/lib/Sema/SemaTemplateInstantiateDecl.cpp b/lib/Sema/SemaTemplateInstantiateDecl.cpp index eb0770a952..ab68e7e671 100644 --- a/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -3729,6 +3729,30 @@ TemplateDeclInstantiator::InitMethodInstantiation(CXXMethodDecl *New, return false; } +/// Instantiate (or find existing instantiation of) a function template with a +/// given set of template arguments. +/// +/// Usually this should not be used, and template argument deduction should be +/// used in its place. +FunctionDecl * +Sema::InstantiateFunctionDeclaration(FunctionTemplateDecl *FTD, + const TemplateArgumentList *Args, + SourceLocation Loc) { + FunctionDecl *FD = FTD->getTemplatedDecl(); + + sema::TemplateDeductionInfo Info(Loc); + InstantiatingTemplate Inst( + *this, Loc, FTD, Args->asArray(), + CodeSynthesisContext::ExplicitTemplateArgumentSubstitution, Info); + if (Inst.isInvalid()) + return nullptr; + + ContextRAII SavedContext(*this, FD); + MultiLevelTemplateArgumentList MArgs(*Args); + + return cast_or_null(SubstDecl(FD, FD->getParent(), MArgs)); +} + /// In the MS ABI, we need to instantiate default arguments of dllexported /// default constructors along with the constructor definition. This allows IR /// gen to emit a constructor closure which calls the default constructor with diff --git a/test/SemaCXX/cxx1y-generic-lambdas.cpp b/test/SemaCXX/cxx1y-generic-lambdas.cpp index 9f3c77591a..49386e7f43 100644 --- a/test/SemaCXX/cxx1y-generic-lambdas.cpp +++ b/test/SemaCXX/cxx1y-generic-lambdas.cpp @@ -181,7 +181,7 @@ int test() { int (*fp2)(int) = [](auto b) -> int { return b; }; int (*fp3)(char) = [](auto c) -> int { return c; }; char (*fp4)(int) = [](auto d) { return d; }; //expected-error{{no viable conversion}}\ - //expected-note{{candidate template ignored}} + //expected-note{{candidate function[with $0 = int]}} char (*fp5)(char) = [](auto e) -> int { return e; }; //expected-error{{no viable conversion}}\ //expected-note{{candidate template ignored}} @@ -207,12 +207,22 @@ int variadic_test() { } // end ns namespace conversion_operator { -void test() { - auto L = [](auto a) -> int { return a; }; + void test() { + auto L = [](auto a) -> int { return a; }; // expected-error {{cannot initialize}} int (*fp)(int) = L; int (&fp2)(int) = [](auto a) { return a; }; // expected-error{{non-const lvalue}} int (&&fp3)(int) = [](auto a) { return a; }; // expected-error{{no viable conversion}}\ //expected-note{{candidate}} + + using F = int(int); + using G = int(void*); + L.operator F*(); + L.operator G*(); // expected-note-re {{instantiation of function template specialization '{{.*}}::operator()'}} + + // Here, the conversion function is named 'operator auto (*)(int)', and + // there is no way to write that name in valid C++. + auto M = [](auto a) -> auto { return a; }; + M.operator F*(); // expected-error {{no member named 'operator int (*)(int)'}} } } } @@ -992,4 +1002,4 @@ namespace PR32638 { void test() { [](auto x) noexcept(noexcept(x)) { } (0); } -} \ No newline at end of file +} diff --git a/test/SemaCXX/lambda-expressions.cpp b/test/SemaCXX/lambda-expressions.cpp index efbb47681a..de77467b6d 100644 --- a/test/SemaCXX/lambda-expressions.cpp +++ b/test/SemaCXX/lambda-expressions.cpp @@ -583,3 +583,28 @@ namespace PR25627_dont_odr_use_local_consts { (void) [] { X x; }; } } + +namespace ConversionOperatorDoesNotHaveDeducedReturnType { + auto x = [](int){}; + auto y = [](auto) -> void {}; + using T = decltype(x); + using U = decltype(y); + using ExpectedTypeT = void (*)(int); + template + using ExpectedTypeU = void (*)(T); + + struct X { + friend T::operator ExpectedTypeT() const; + + // Formally, this is invalid, because the return type of the conversion + // function for a generic lambda expression is an unspecified decltype + // type, which this should not match. However, this declaration is + // functionally equivalent to that one, so we're permitted to choose to + // accept this. + template + friend U::operator ExpectedTypeU() const; + }; + + // This used to crash in return type deduction for the conversion opreator. + struct A { int n; void f() { +[](decltype(n)) {}; } }; +} -- 2.50.1