Index: clang/include/clang/AST/DeclCXX.h =================================================================== --- clang/include/clang/AST/DeclCXX.h +++ clang/include/clang/AST/DeclCXX.h @@ -1008,8 +1008,13 @@ /// Retrieve the lambda static invoker, the address of which /// is returned by the conversion operator, and the body of which - /// is forwarded to the lambda call operator. + /// is forwarded to the lambda call operator. The version that does not + /// take a calling convention uses the 'default' calling convention for free + /// functions if the Lambda's calling convention was not modified via + /// attribute. Otherwise, it will return the calling convention specified for + /// the lambda. CXXMethodDecl *getLambdaStaticInvoker() const; + CXXMethodDecl *getLambdaStaticInvoker(CallingConv CC) const; /// Retrieve the generic lambda's template parameter list. /// Returns null if the class does not represent a lambda or a generic Index: clang/include/clang/Sema/Sema.h =================================================================== --- clang/include/clang/Sema/Sema.h +++ clang/include/clang/Sema/Sema.h @@ -6461,7 +6461,8 @@ /// 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); + getLambdaConversionFunctionResultType(const FunctionProtoType *CallOpType, + CallingConv CC); /// Define the "body" of the conversion from a lambda object to a /// function pointer. Index: clang/lib/AST/DeclCXX.cpp =================================================================== --- clang/lib/AST/DeclCXX.cpp +++ clang/lib/AST/DeclCXX.cpp @@ -1507,18 +1507,38 @@ } CXXMethodDecl* CXXRecordDecl::getLambdaStaticInvoker() const { - if (!isLambda()) return nullptr; + CXXMethodDecl *CallOp = getLambdaCallOperator(); + CallingConv CC = CallOp->getType()->getAs()->getCallConv(); + return getLambdaStaticInvoker(CC); +} + +static DeclContext::lookup_result +getLambdaStaticInvokers(const CXXRecordDecl &RD) { + assert(RD.isLambda() && "Must be a lambda"); DeclarationName Name = - &getASTContext().Idents.get(getLambdaStaticInvokerName()); - DeclContext::lookup_result Invoker = lookup(Name); - if (Invoker.empty()) return nullptr; - assert(allLookupResultsAreTheSame(Invoker) && - "More than one static invoker operator!"); - NamedDecl *InvokerFun = Invoker.front(); - if (const auto *InvokerTemplate = dyn_cast(InvokerFun)) + &RD.getASTContext().Idents.get(getLambdaStaticInvokerName()); + return RD.lookup(Name); +} + +static CXXMethodDecl *getInvokerAsMethod(NamedDecl *ND) { + if (const auto *InvokerTemplate = dyn_cast(ND)) return cast(InvokerTemplate->getTemplatedDecl()); + return cast(ND); +} + +CXXMethodDecl *CXXRecordDecl::getLambdaStaticInvoker(CallingConv CC) const { + if (!isLambda()) + return nullptr; + DeclContext::lookup_result Invoker = getLambdaStaticInvokers(*this); + + for (NamedDecl *ND : Invoker) { + const FunctionType *FTy = + cast(ND->getAsFunction())->getType()->getAs(); + if (FTy->getCallConv() == CC) + return getInvokerAsMethod(ND); + } - return cast(InvokerFun); + return nullptr; } void CXXRecordDecl::getCaptureFields( @@ -2459,14 +2479,8 @@ bool CXXMethodDecl::isLambdaStaticInvoker() const { const CXXRecordDecl *P = getParent(); - if (P->isLambda()) { - if (const CXXMethodDecl *StaticInvoker = P->getLambdaStaticInvoker()) { - if (StaticInvoker == this) return true; - if (P->isGenericLambda() && this->isFunctionTemplateSpecialization()) - return StaticInvoker == this->getPrimaryTemplate()->getTemplatedDecl(); - } - } - return false; + return P->isLambda() && getDeclName().isIdentifier() && + getName() == getLambdaStaticInvokerName(); } CXXCtorInitializer::CXXCtorInitializer(ASTContext &Context, Index: clang/lib/AST/MicrosoftMangle.cpp =================================================================== --- clang/lib/AST/MicrosoftMangle.cpp +++ clang/lib/AST/MicrosoftMangle.cpp @@ -2252,10 +2252,20 @@ return; } Out << '@'; + } else if (IsInLambda && D && isa(D)) { + // The only lambda conversion operators are to function pointers, which + // can differ by their calling convention and are typically deduced. So + // we make sure that this type gets mangled properly. + mangleType(T->getReturnType(), Range, QMM_Result); } else { QualType ResultType = T->getReturnType(); - if (const auto *AT = - dyn_cast_or_null(ResultType->getContainedAutoType())) { + if (IsInLambda && isa(D)) { + // The only lambda conversion operators are to function pointers, which + // can differ by their calling convention and are typically deduced. So + // we make sure that this type gets mangled properly. + mangleType(ResultType, Range, QMM_Result); + } else if (const auto *AT = dyn_cast_or_null( + ResultType->getContainedAutoType())) { Out << '?'; mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false); Out << '?'; Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -14800,9 +14800,13 @@ SynthesizedFunctionScope Scope(*this, Conv); assert(!Conv->getReturnType()->isUndeducedType()); + QualType ConvRT = Conv->getType()->getAs()->getReturnType(); + CallingConv CC = + ConvRT->getPointeeType()->getAs()->getCallConv(); + CXXRecordDecl *Lambda = Conv->getParent(); FunctionDecl *CallOp = Lambda->getLambdaCallOperator(); - FunctionDecl *Invoker = Lambda->getLambdaStaticInvoker(); + FunctionDecl *Invoker = Lambda->getLambdaStaticInvoker(CC); if (auto *TemplateArgs = Conv->getTemplateSpecializationArgs()) { CallOp = InstantiateFunctionDeclaration( Index: clang/lib/Sema/SemaLambda.cpp =================================================================== --- clang/lib/Sema/SemaLambda.cpp +++ clang/lib/Sema/SemaLambda.cpp @@ -1263,30 +1263,61 @@ PopFunctionScopeInfo(); } +template +static void repeatForLambdaConversionFunctionCallingConvs( + Sema &S, const FunctionProtoType &CallOpProto, Func F) { + CallingConv DefaultFree = S.Context.getDefaultCallingConvention( + CallOpProto.isVariadic(), /*IsCXXMethod=*/false); + CallingConv DefaultMember = S.Context.getDefaultCallingConvention( + CallOpProto.isVariadic(), /*IsCXXMethod=*/true); + CallingConv CallOpCC = CallOpProto.getCallConv(); + + if (CallOpCC == DefaultMember && DefaultMember != DefaultFree) { + F(DefaultFree); + F(DefaultMember); + } else { + F(CallOpCC); + } +} + +// Returns the 'standard' calling convention to be used for the lambda +// conversion function, that is, the 'free' function calling convention unless +// it is overridden by a non-default calling convention attribute. +static CallingConv +getLambdaConversionFunctionCallConv(Sema &S, + const FunctionProtoType *CallOpProto) { + CallingConv DefaultFree = S.Context.getDefaultCallingConvention( + CallOpProto->isVariadic(), /*IsCXXMethod=*/false); + CallingConv DefaultMember = S.Context.getDefaultCallingConvention( + CallOpProto->isVariadic(), /*IsCXXMethod=*/true); + CallingConv CallOpCC = CallOpProto->getCallConv(); + + // If the call-operator hasn't been changed, return both the 'free' and + // 'member' function calling convention. + if (CallOpCC == DefaultMember && DefaultMember != DefaultFree) + return DefaultFree; + return CallOpCC; +} + 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 *CallOpProto, CallingConv CC) { 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 = Qualifiers(); assert(InvokerExtInfo.RefQualifier == RQ_None && - "Lambda's call operator should not have a reference qualifier"); + "Lambda's call operator should not have a reference qualifier"); return Context.getFunctionType(CallOpProto->getReturnType(), CallOpProto->getParamTypes(), InvokerExtInfo); } /// Add a lambda's conversion to function pointer, as described in /// C++11 [expr.prim.lambda]p6. -static void addFunctionPointerConversion(Sema &S, - SourceRange IntroducerRange, +static void addFunctionPointerConversion(Sema &S, SourceRange IntroducerRange, CXXRecordDecl *Class, - CXXMethodDecl *CallOperator) { + CXXMethodDecl *CallOperator, + QualType InvokerFunctionTy) { // This conversion is explicitly disabled if the lambda's function has // pass_object_size attributes on any of its parameters. auto HasPassObjectSizeAttr = [](const ParmVarDecl *P) { @@ -1296,8 +1327,6 @@ return; // Add the conversion to function pointer. - QualType InvokerFunctionTy = S.getLambdaConversionFunctionResultType( - CallOperator->getType()->castAs()); QualType PtrToFunctionTy = S.Context.getPointerType(InvokerFunctionTy); // Create the type of the conversion function. @@ -1442,13 +1471,37 @@ Class->addDecl(Invoke); } +/// Add a lambda's conversion to function pointers, as described in +/// C++11 [expr.prim.lambda]p6. Note that in most cases, this should emit only a +/// single pointer conversion. In the event that the default calling convention +/// for free and member functions is different, it will emit both conventions. +/// FIXME: Implement emitting a version of the operator for EVERY calling +/// convention for MSVC, as described here: +/// https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623. +static void addFunctionPointerConversions(Sema &S, SourceRange IntroducerRange, + CXXRecordDecl *Class, + CXXMethodDecl *CallOperator) { + const FunctionProtoType *CallOpProto = + CallOperator->getType()->castAs(); + + repeatForLambdaConversionFunctionCallingConvs( + S, *CallOpProto, [&](CallingConv CC) { + QualType InvokerFunctionTy = + S.getLambdaConversionFunctionResultType(CallOpProto, CC); + addFunctionPointerConversion(S, IntroducerRange, Class, CallOperator, + InvokerFunctionTy); + }); +} + /// Add a lambda's conversion to block pointer. static void addBlockPointerConversion(Sema &S, SourceRange IntroducerRange, CXXRecordDecl *Class, CXXMethodDecl *CallOperator) { + const FunctionProtoType *CallOpProto = + CallOperator->getType()->castAs(); QualType FunctionTy = S.getLambdaConversionFunctionResultType( - CallOperator->getType()->castAs()); + CallOpProto, getLambdaConversionFunctionCallConv(S, CallOpProto)); QualType BlockPtrTy = S.Context.getBlockPointerType(FunctionTy); FunctionProtoType::ExtProtoInfo ConversionEPI( @@ -1795,8 +1848,8 @@ // to pointer to function having the same parameter and return // types as the closure type's function call operator. if (Captures.empty() && CaptureDefault == LCD_None) - addFunctionPointerConversion(*this, IntroducerRange, Class, - CallOperator); + addFunctionPointerConversions(*this, IntroducerRange, Class, + CallOperator); // Objective-C++: // The closure type for a lambda-expression has a public non-virtual Index: clang/lib/Sema/SemaOverload.cpp =================================================================== --- clang/lib/Sema/SemaOverload.cpp +++ clang/lib/Sema/SemaOverload.cpp @@ -3645,13 +3645,32 @@ return true; } +// Helper for compareConversionFunctions that gets the FunctionType that the +// conversion-operator return value 'points' to, or nullptr. +static const FunctionType * +getConversionOpReturnTyAsFunction(CXXConversionDecl *Conv) { + const FunctionType *ConvFuncTy = Conv->getType()->castAs(); + const PointerType *RetPtrTy = + ConvFuncTy->getReturnType()->getAs(); + + if (!RetPtrTy) + return nullptr; + + return RetPtrTy->getPointeeType()->getAs(); +} + /// Compare the user-defined conversion functions or constructors /// of two user-defined conversion sequences to determine whether any ordering /// is possible. static ImplicitConversionSequence::CompareKind compareConversionFunctions(Sema &S, FunctionDecl *Function1, FunctionDecl *Function2) { - if (!S.getLangOpts().ObjC || !S.getLangOpts().CPlusPlus11) + CXXConversionDecl *Conv1 = dyn_cast_or_null(Function1); + CXXConversionDecl *Conv2 = dyn_cast_or_null(Function2); + if (!Conv1 || !Conv2) + return ImplicitConversionSequence::Indistinguishable; + + if (!Conv1->getParent()->isLambda() || !Conv2->getParent()->isLambda()) return ImplicitConversionSequence::Indistinguishable; // Objective-C++: @@ -3660,15 +3679,7 @@ // respectively, always prefer the conversion to a function pointer, // because the function pointer is more lightweight and is more likely // to keep code working. - CXXConversionDecl *Conv1 = dyn_cast_or_null(Function1); - if (!Conv1) - return ImplicitConversionSequence::Indistinguishable; - - CXXConversionDecl *Conv2 = dyn_cast(Function2); - if (!Conv2) - return ImplicitConversionSequence::Indistinguishable; - - if (Conv1->getParent()->isLambda() && Conv2->getParent()->isLambda()) { + if (S.getLangOpts().ObjC && S.getLangOpts().CPlusPlus11) { bool Block1 = Conv1->getConversionType()->isBlockPointerType(); bool Block2 = Conv2->getConversionType()->isBlockPointerType(); if (Block1 != Block2) @@ -3676,6 +3687,39 @@ : ImplicitConversionSequence::Better; } + // In order to support multiple calling conventions for the lambda conversion + // operator (such as when the free and member function calling convention is + // different), prefer the 'free' mechanism, followed by the calling-convention + // of operator(). The latter is in place to support the MSVC-like solution of + // defining ALL of the possible conversions in regards to calling-convention. + const FunctionType *Conv1FuncRet = getConversionOpReturnTyAsFunction(Conv1); + const FunctionType *Conv2FuncRet = getConversionOpReturnTyAsFunction(Conv2); + + if (Conv1FuncRet && Conv2FuncRet && + Conv1FuncRet->getCallConv() != Conv2FuncRet->getCallConv()) { + CallingConv Conv1CC = Conv1FuncRet->getCallConv(); + CallingConv Conv2CC = Conv2FuncRet->getCallConv(); + + CXXMethodDecl *CallOp = Conv2->getParent()->getLambdaCallOperator(); + const FunctionProtoType *CallOpProto = + CallOp->getType()->getAs(); + + CallingConv CallOpCC = + CallOp->getType()->getAs()->getCallConv(); + CallingConv DefaultFree = S.Context.getDefaultCallingConvention( + CallOpProto->isVariadic(), /*IsCXXMethod=*/false); + CallingConv DefaultMember = S.Context.getDefaultCallingConvention( + CallOpProto->isVariadic(), /*IsCXXMethod=*/true); + + CallingConv PrefOrder[] = {DefaultFree, DefaultMember, CallOpCC}; + for (CallingConv CC : PrefOrder) { + if (Conv1CC == CC) + return ImplicitConversionSequence::Better; + if (Conv2CC == CC) + return ImplicitConversionSequence::Worse; + } + } + return ImplicitConversionSequence::Indistinguishable; } @@ -10153,6 +10197,22 @@ if (Fn->isMultiVersion() && Fn->hasAttr() && !Fn->getAttr()->isDefaultVersion()) return; + if (isa(Fn) && + cast(Fn->getParent())->isLambda()) { + // Don't print candidates other than the one that matches the calling + // convention of the call operator, since that is guaranteed to exist. + const auto *RD = cast(Fn->getParent()); + CXXMethodDecl *CallOp = RD->getLambdaCallOperator(); + CallingConv CallOpCC = + CallOp->getType()->getAs()->getCallConv(); + CXXConversionDecl *ConvD = cast(Fn); + QualType ConvRTy = ConvD->getType()->getAs()->getReturnType(); + CallingConv ConvToCC = + ConvRTy->getPointeeType()->getAs()->getCallConv(); + + if (ConvToCC != CallOpCC) + return; + } std::string FnDesc; std::pair KSPair = Index: clang/lib/Sema/SemaTemplateDeduction.cpp =================================================================== --- clang/lib/Sema/SemaTemplateDeduction.cpp +++ clang/lib/Sema/SemaTemplateDeduction.cpp @@ -5001,8 +5001,12 @@ "failed to deduce lambda return type"); // Build the new return type from scratch. + CallingConv RetTyCC = FD->getReturnType() + ->getPointeeType() + ->castAs() + ->getCallConv(); QualType RetType = getLambdaConversionFunctionResultType( - CallOp->getType()->castAs()); + CallOp->getType()->castAs(), RetTyCC); if (FD->getReturnType()->getAs()) RetType = Context.getPointerType(RetType); else { Index: clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp =================================================================== --- /dev/null +++ clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp @@ -0,0 +1,44 @@ +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu | FileCheck %s --check-prefixes=CHECK,LIN64 +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu -DCC="__attribute__((vectorcall))" | FileCheck %s --check-prefixes=CHECK,VECCALL +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=i386-windows-pc -DWIN32 | FileCheck %s --check-prefixes=WIN32 + +#ifndef CC +#define CC +#endif + +void usage() { + auto lambda = [](int i, float f, double d) CC { return i + f + d; }; + + double (*CC fp)(int, float, double) = lambda; + fp(0, 1.1, 2.2); +#ifdef WIN32 + double (*__attribute__((thiscall)) fp2)(int, float, double) = lambda; + fp2(0, 1.1, 2.2); +#endif // WIN32 +} + +// void usage function, calls convrsion operator. +// LIN64: define void @_Z5usagev() +// VECCALL: define void @_Z5usagev() +// WIN32: define dso_local void @"?usage@@YAXXZ"() +// CHECK: call double (i32, float, double)* @"_ZZ5usagevENK3$_0cvPFdifdEEv" +// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B@?0??usage@@YAXXZ@QBEP6A?A?@@HMN@ZXZ" +// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B@?0??usage@@YAXXZ@QBEP6E?A?@@HMN@ZXZ" +// +// Conversion operator, returns __invoke. +// CHECK: define internal double (i32, float, double)* @"_ZZ5usagevENK3$_0cvPFdifdEEv" +// CHECK: ret double (i32, float, double)* @"_ZZ5usagevEN3$_08__invokeEifd" +// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B@?0??usage@@YAXXZ@QBEP6A?A?@@HMN@ZXZ" +// WIN32: ret double (i32, float, double)* @"?__invoke@@?0??usage@@YAXXZ@CA?A?@@HMN@Z" +// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B@?0??usage@@YAXXZ@QBEP6E?A?@@HMN@ZXZ" +// WIN32: ret double (i32, float, double)* @"?__invoke@@?0??usage@@YAXXZ@CE?A?@@HMN@Z" +// +// __invoke function, calls operator(). Win32 should call both. +// LIN64: define internal double @"_ZZ5usagevEN3$_08__invokeEifd" +// LIN64: call double @"_ZZ5usagevENK3$_0clEifd" +// VECCALL: define internal x86_vectorcallcc double @"_ZZ5usagevEN3$_08__invokeEifd" +// VECCALL: call x86_vectorcallcc double @"_ZZ5usagevENK3$_0clEifd" +// WIN32: define internal double @"?__invoke@@?0??usage@@YAXXZ@CA?A?@@HMN@Z" +// WIN32: call x86_thiscallcc double @"??R@?0??usage@@YAXXZ@QBE?A?@@HMN@Z" +// WIN32: define internal x86_thiscallcc double @"?__invoke@@?0??usage@@YAXXZ@CE?A?@@HMN@Z" +// WIN32: call x86_thiscallcc double @"??R@?0??usage@@YAXXZ@QBE?A?@@HMN@Z" Index: clang/test/SemaCXX/lambda-conversion-op-cc.cpp =================================================================== --- /dev/null +++ clang/test/SemaCXX/lambda-conversion-op-cc.cpp @@ -0,0 +1,190 @@ +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-pc %s -verify -DBAD_CONVERSION +// RUN: %clang_cc1 -fsyntax-only -triple i386-windows-pc %s -verify -DBAD_CONVERSION -DWIN32 +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-pc %s -ast-dump | FileCheck %s --check-prefixes=CHECK,LIN64,NODEF +// RUN: %clang_cc1 -fsyntax-only -triple i386-windows-pc %s -ast-dump -DWIN32 | FileCheck %s --check-prefixes=CHECK,WIN32,NODEF + +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-pc -fdefault-calling-conv=vectorcall %s -verify -DBAD_VEC_CONVERS +// RUN: %clang_cc1 -fsyntax-only -triple x86_64-linux-pc -fdefault-calling-conv=vectorcall %s -ast-dump | FileCheck %s --check-prefixes=CHECK,VECTDEF + +void useage() { + auto normal = [](int, float, double) {}; // #1 + auto vectorcall = [](int, float, double) __attribute__((vectorcall)){}; // #2 +#ifdef WIN32 + auto thiscall = [](int, float, double) __attribute__((thiscall)){}; // #3 +#endif // WIN32 + auto cdecl = [](int, float, double) __attribute__((cdecl)){}; + + auto genericlambda = [](auto a) {}; // #4 + auto genericvectorcalllambda = [](auto a) __attribute__((vectorcall)){}; // #5 + + // None of these should be ambiguous. + (void)+normal; + (void)+vectorcall; +#ifdef WIN32 + (void)+thiscall; +#endif // WIN32 + (void)+cdecl; + +#ifdef BAD_CONVERSION + // expected-error-re@+1 {{invalid argument type {{.*}} to unary expression}} + (void)+genericlambda; + // expected-error-re@+1 {{invalid argument type {{.*}} to unary expression}} + (void)+genericvectorcalllambda; +#endif // BAD_CONVERSION + + // CHECK: VarDecl {{.*}} normal ' + // CHECK: LambdaExpr + // WIN32: CXXMethodDecl {{.*}} operator() 'void (int, float, double) __attribute__((thiscall)) const' + // LIN64: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const' + // VECTDEF: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const' + // NODEF: CXXConversionDecl {{.*}} operator void (*)(int, float, double) 'void + // NODEF: CXXMethodDecl {{.*}} __invoke 'void (int, float, double)' static inline + // VECTDEF: CXXConversionDecl {{.*}} operator void (*)(int, float, double) __attribute__((vectorcall)) 'void + // VECTDEF: CXXMethodDecl {{.*}} __invoke 'void (int, float, double) __attribute__((vectorcall))' static inline + + // CHECK: VarDecl {{.*}} vectorcall ' + // CHECK: LambdaExpr + // CHECK: CXXMethodDecl {{.*}} operator() 'void (int, float, double) __attribute__((vectorcall)) const' + // CHECK: CXXConversionDecl {{.*}} operator void (*)(int, float, double) __attribute__((vectorcall)) 'void + // CHECK: CXXMethodDecl {{.*}} __invoke 'void (int, float, double) __attribute__((vectorcall))' static inline + + // WIN32: VarDecl {{.*}} thiscall ' + // WIN32: LambdaExpr + // WIN32: CXXMethodDecl {{.*}} operator() 'void (int, float, double) __attribute__((thiscall)) const' + // WIN32: CXXConversionDecl {{.*}} operator void (*)(int, float, double) 'void + // WIN32: CXXMethodDecl {{.*}} __invoke 'void (int, float, double)' static inline + + // CHECK: VarDecl {{.*}} cdecl ' + // CHECK: LambdaExpr + // CHECK: CXXMethodDecl {{.*}} operator() 'void (int, float, double) const' + // NODEF: CXXConversionDecl {{.*}} operator void (*)(int, float, double) 'void + // NODEF: CXXMethodDecl {{.*}} __invoke 'void (int, float, double)' static inline + // VECTDEF: CXXConversionDecl {{.*}} operator void (*)(int, float, double) __attribute__((vectorcall)) 'void + // VECTDEF: CXXMethodDecl {{.*}} __invoke 'void (int, float, double) __attribute__((vectorcall))' static inline + + // CHECK: VarDecl {{.*}} genericlambda ' + // CHECK: LambdaExpr + // + // CHECK: FunctionTemplateDecl {{.*}} operator() + // LIN64: CXXMethodDecl {{.*}} operator() 'auto (auto) const' inline + // LIN64: CXXMethodDecl {{.*}} operator() 'void (char) const' inline + // LIN64: CXXMethodDecl {{.*}} operator() 'void (int) const' inline + // WIN32: CXXMethodDecl {{.*}} operator() 'auto (auto) __attribute__((thiscall)) const' inline + // WIN32: CXXMethodDecl {{.*}} operator() 'void (char) __attribute__((thiscall)) const' inline + // WIN32: CXXMethodDecl {{.*}} operator() 'void (int) __attribute__((thiscall)) const' inline + // + // NODEF: FunctionTemplateDecl {{.*}} operator auto (*)(type-parameter-0-0) + // VECDEF: FunctionTemplateDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall)) + // LIN64: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) 'auto (*() const noexcept)(auto)' + // LIN64: CXXConversionDecl {{.*}} operator auto (*)(char) 'void (*() const noexcept)(char)' + // LIN64: CXXConversionDecl {{.*}} operator auto (*)(int) 'void (*() const noexcept)(int)' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) 'auto (*() __attribute__((thiscall)) const noexcept)(auto)' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(char) 'void (*() __attribute__((thiscall)) const noexcept)(char)' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(int) 'void (*() __attribute__((thiscall)) const noexcept)(int)' + // VECDEF: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall)) 'auto (*() const noexcept)(auto)' __attribute__((vectorcall)) + // VECDEF: CXXConversionDecl {{.*}} operator auto (*)(char) __attribute__((vectorcall)) 'void (*() const noexcept)(char)' __attribute__((vectorcall)) + // VECDEF: CXXConversionDecl {{.*}} operator auto (*)(int) __attribute__((vectorcall)) 'void (*() const noexcept)(int)' __attribute__((vectorcall)) + // + // CHECK: FunctionTemplateDecl {{.*}} __invoke + // NODEF: CXXMethodDecl {{.*}} __invoke 'auto (auto)' + // NODEF: CXXMethodDecl {{.*}} __invoke 'void (char)' + // NODEF: CXXMethodDecl {{.*}} __invoke 'void (int)' + // VECDEF: CXXMethodDecl {{.*}} __invoke 'auto (auto) __attribute__((vectorcall))' + // VECDEF: CXXMethodDecl {{.*}} __invoke 'void (char) __attribute__((vectorcall))' + // VECDEF: CXXMethodDecl {{.*}} __invoke 'void (int) __attribute__((vectorcall))' + // + // ONLY WIN32 has the duplicate here. + // WIN32: FunctionTemplateDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((thiscall)) + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((thiscall)) 'auto (*() __attribute__((thiscall)) const noexcept)(auto) __attribute__((thiscall))' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(char) __attribute__((thiscall)) 'void (*() __attribute__((thiscall)) const noexcept)(char) __attribute__((thiscall))' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(int) __attribute__((thiscall)) 'void (*() __attribute__((thiscall)) const noexcept)(int) __attribute__((thiscall))' + // + // WIN32: FunctionTemplateDecl {{.*}} __invoke + // WIN32: CXXMethodDecl {{.*}} __invoke 'auto (auto) __attribute__((thiscall))' + // WIN32: CXXMethodDecl {{.*}} __invoke 'void (char) __attribute__((thiscall))' + // WIN32: CXXMethodDecl {{.*}} __invoke 'void (int) __attribute__((thiscall))' + + // CHECK: VarDecl {{.*}} genericvectorcalllambda ' + // CHECK: LambdaExpr + // CHECK: FunctionTemplateDecl {{.*}} operator() + // CHECK: CXXMethodDecl {{.*}} operator() 'auto (auto) __attribute__((vectorcall)) const' inline + // CHECK: CXXMethodDecl {{.*}} operator() 'void (char) __attribute__((vectorcall)) const' inline + // CHECK: CXXMethodDecl {{.*}} operator() 'void (int) __attribute__((vectorcall)) const' inline + // CHECK: FunctionTemplateDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall)) + // LIN64: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall)) 'auto (*() const noexcept)(auto) __attribute__((vectorcall))' + // LIN64: CXXConversionDecl {{.*}} operator auto (*)(char) __attribute__((vectorcall)) 'void (*() const noexcept)(char) __attribute__((vectorcall))' + // LIN64: CXXConversionDecl {{.*}} operator auto (*)(int) __attribute__((vectorcall)) 'void (*() const noexcept)(int) __attribute__((vectorcall))' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(type-parameter-0-0) __attribute__((vectorcall)) 'auto (*() __attribute__((thiscall)) const noexcept)(auto) __attribute__((vectorcall))' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(char) __attribute__((vectorcall)) 'void (*() __attribute__((thiscall)) const noexcept)(char) __attribute__((vectorcall))' + // WIN32: CXXConversionDecl {{.*}} operator auto (*)(int) __attribute__((vectorcall)) 'void (*() __attribute__((thiscall)) const noexcept)(int) __attribute__((vectorcall))' + // CHECK: FunctionTemplateDecl {{.*}} __invoke + // CHECK: CXXMethodDecl {{.*}} __invoke 'auto (auto) __attribute__((vectorcall))' + // CHECK: CXXMethodDecl {{.*}} __invoke 'void (char) __attribute__((vectorcall))' + // CHECK: CXXMethodDecl {{.*}} __invoke 'void (int) __attribute__((vectorcall))' + + // NODEF: UnaryOperator {{.*}} 'void (*)(int, float, double)' prefix '+' + // NODEF-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int, float, double)' + // NODEF-NEXT: CXXMemberCallExpr {{.*}}'void (*)(int, float, double)' + // VECTDEF: UnaryOperator {{.*}} 'void (*)(int, float, double) __attribute__((vectorcall))' prefix '+' + // VECTDEF-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int, float, double) __attribute__((vectorcall))' + // VECTDEF-NEXT: CXXMemberCallExpr {{.*}}'void (*)(int, float, double) __attribute__((vectorcall))' + + // CHECK: UnaryOperator {{.*}} 'void (*)(int, float, double) __attribute__((vectorcall))' prefix '+' + // CHECK-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int, float, double) __attribute__((vectorcall))' + // CHECK-NEXT: CXXMemberCallExpr {{.*}}'void (*)(int, float, double) __attribute__((vectorcall))' + + // WIN32: UnaryOperator {{.*}} 'void (*)(int, float, double)' prefix '+' + // WIN32-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int, float, double)' + // WIN32-NEXT: CXXMemberCallExpr {{.*}}'void (*)(int, float, double)' + + // NODEF: UnaryOperator {{.*}} 'void (*)(int, float, double)' prefix '+' + // NODEF-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int, float, double)' + // NODEF-NEXT: CXXMemberCallExpr {{.*}}'void (*)(int, float, double)' + // VECTDEF: UnaryOperator {{.*}} 'void (*)(int, float, double) __attribute__((vectorcall))' prefix '+' + // VECTDEF-NEXT: ImplicitCastExpr {{.*}} 'void (*)(int, float, double) __attribute__((vectorcall))' + // VECTDEF-NEXT: CXXMemberCallExpr {{.*}}'void (*)(int, float, double) __attribute__((vectorcall))' + +#ifdef BAD_CONVERSION + // expected-error-re@+2 {{no viable conversion from {{.*}} to 'void (*)(int, float, double) __attribute__((vectorcall))}} + // expected-note@#1 {{candidate function}} + void (*__attribute__((vectorcall)) normal_ptr2)(int, float, double) = normal; + // expected-error-re@+2 {{no viable conversion from {{.*}} to 'void (*)(int, float, double)}} + // expected-note@#2 {{candidate function}} + void (*vectorcall_ptr2)(int, float, double) = vectorcall; +#ifdef WIN32 + void (*__attribute__((thiscall)) thiscall_ptr2)(int, float, double) = thiscall; +#endif // WIN32 + // expected-error-re@+2 {{no viable conversion from {{.*}} to 'void (*)(char) __attribute__((vectorcall))'}} + // expected-note@#4 {{candidate function}} + void(__vectorcall * generic_ptr)(char) = genericlambda; + // expected-error-re@+2 {{no viable conversion from {{.*}} to 'void (*)(char)}} + // expected-note@#5 {{candidate function}} + void (*generic_ptr2)(char) = genericvectorcalllambda; +#endif // BAD_CONVERSION + +#ifdef BAD_VEC_CONVERS + void (*__attribute__((vectorcall)) normal_ptr2)(int, float, double) = normal; + void (*normal_ptr3)(int, float, double) = normal; + // expected-error-re@+2 {{no viable conversion from {{.*}} to 'void (*)(int, float, double) __attribute__((regcall))}} + // expected-note@#1 {{candidate function}} + void (*__attribute__((regcall)) normalptr4)(int, float, double) = normal; + void (*__attribute__((vectorcall)) vectorcall_ptr2)(int, float, double) = vectorcall; + void (*vectorcall_ptr3)(int, float, double) = vectorcall; +#endif // BAD_VEC_CONVERS + + // Required to force emission of the invoker. + void (*normal_ptr)(int, float, double) = normal; + void (*__attribute__((vectorcall)) vectorcall_ptr)(int, float, double) = vectorcall; +#ifdef WIN32 + void (*thiscall_ptr)(int, float, double) = thiscall; +#endif // WIN32 + void (*cdecl_ptr)(int, float, double) = cdecl; + void (*generic_ptr3)(char) = genericlambda; + void (*generic_ptr4)(int) = genericlambda; +#ifdef WIN32 + void (*__attribute__((thiscall)) generic_ptr3b)(char) = genericlambda; + void (*__attribute__((thiscall)) generic_ptr4b)(int) = genericlambda; +#endif + void (*__attribute__((vectorcall)) generic_ptr5)(char) = genericvectorcalllambda; + void (*__attribute__((vectorcall)) generic_ptr6)(int) = genericvectorcalllambda; +}