/// 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
/// 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.
}
CXXMethodDecl* CXXRecordDecl::getLambdaStaticInvoker() const {
- if (!isLambda()) return nullptr;
+ CXXMethodDecl *CallOp = getLambdaCallOperator();
+ CallingConv CC = CallOp->getType()->getAs<FunctionType>()->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<FunctionTemplateDecl>(InvokerFun))
+ &RD.getASTContext().Idents.get(getLambdaStaticInvokerName());
+ return RD.lookup(Name);
+}
+
+static CXXMethodDecl *getInvokerAsMethod(NamedDecl *ND) {
+ if (const auto *InvokerTemplate = dyn_cast<FunctionTemplateDecl>(ND))
return cast<CXXMethodDecl>(InvokerTemplate->getTemplatedDecl());
+ return cast<CXXMethodDecl>(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<ValueDecl>(ND->getAsFunction())->getType()->getAs<FunctionType>();
+ if (FTy->getCallConv() == CC)
+ return getInvokerAsMethod(ND);
+ }
- return cast<CXXMethodDecl>(InvokerFun);
+ return nullptr;
}
void CXXRecordDecl::getCaptureFields(
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,
return;
}
Out << '@';
+ } else if (IsInLambda && D && isa<CXXConversionDecl>(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<AutoType>(ResultType->getContainedAutoType())) {
+ if (IsInLambda && isa<CXXConversionDecl>(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<AutoType>(
+ ResultType->getContainedAutoType())) {
Out << '?';
mangleQualifiers(ResultType.getLocalQualifiers(), /*IsMember=*/false);
Out << '?';
SynthesizedFunctionScope Scope(*this, Conv);
assert(!Conv->getReturnType()->isUndeducedType());
+ QualType ConvRT = Conv->getType()->getAs<FunctionType>()->getReturnType();
+ CallingConv CC =
+ ConvRT->getPointeeType()->getAs<FunctionType>()->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(
PopFunctionScopeInfo();
}
+template <typename Func>
+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) {
return;
// Add the conversion to function pointer.
- QualType InvokerFunctionTy = S.getLambdaConversionFunctionResultType(
- CallOperator->getType()->castAs<FunctionProtoType>());
QualType PtrToFunctionTy = S.Context.getPointerType(InvokerFunctionTy);
// Create the type of the conversion function.
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<FunctionProtoType>();
+
+ 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<FunctionProtoType>();
QualType FunctionTy = S.getLambdaConversionFunctionResultType(
- CallOperator->getType()->castAs<FunctionProtoType>());
+ CallOpProto, getLambdaConversionFunctionCallConv(S, CallOpProto));
QualType BlockPtrTy = S.Context.getBlockPointerType(FunctionTy);
FunctionProtoType::ExtProtoInfo ConversionEPI(
// 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
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<FunctionType>();
+ const PointerType *RetPtrTy =
+ ConvFuncTy->getReturnType()->getAs<PointerType>();
+
+ if (!RetPtrTy)
+ return nullptr;
+
+ return RetPtrTy->getPointeeType()->getAs<FunctionType>();
+}
+
/// 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<CXXConversionDecl>(Function1);
+ CXXConversionDecl *Conv2 = dyn_cast_or_null<CXXConversionDecl>(Function2);
+ if (!Conv1 || !Conv2)
+ return ImplicitConversionSequence::Indistinguishable;
+
+ if (!Conv1->getParent()->isLambda() || !Conv2->getParent()->isLambda())
return ImplicitConversionSequence::Indistinguishable;
// Objective-C++:
// 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<CXXConversionDecl>(Function1);
- if (!Conv1)
- return ImplicitConversionSequence::Indistinguishable;
-
- CXXConversionDecl *Conv2 = dyn_cast<CXXConversionDecl>(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)
: 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<FunctionProtoType>();
+
+ CallingConv CallOpCC =
+ CallOp->getType()->getAs<FunctionType>()->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;
}
if (Fn->isMultiVersion() && Fn->hasAttr<TargetAttr>() &&
!Fn->getAttr<TargetAttr>()->isDefaultVersion())
return;
+ if (isa<CXXConversionDecl>(Fn) &&
+ cast<CXXRecordDecl>(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<CXXRecordDecl>(Fn->getParent());
+ CXXMethodDecl *CallOp = RD->getLambdaCallOperator();
+ CallingConv CallOpCC =
+ CallOp->getType()->getAs<FunctionType>()->getCallConv();
+ CXXConversionDecl *ConvD = cast<CXXConversionDecl>(Fn);
+ QualType ConvRTy = ConvD->getType()->getAs<FunctionType>()->getReturnType();
+ CallingConv ConvToCC =
+ ConvRTy->getPointeeType()->getAs<FunctionType>()->getCallConv();
+
+ if (ConvToCC != CallOpCC)
+ return;
+ }
std::string FnDesc;
std::pair<OverloadCandidateKind, OverloadCandidateSelect> KSPair =
"failed to deduce lambda return type");
// Build the new return type from scratch.
+ CallingConv RetTyCC = FD->getReturnType()
+ ->getPointeeType()
+ ->castAs<FunctionType>()
+ ->getCallConv();
QualType RetType = getLambdaConversionFunctionResultType(
- CallOp->getType()->castAs<FunctionProtoType>());
+ CallOp->getType()->castAs<FunctionProtoType>(), RetTyCC);
if (FD->getReturnType()->getAs<PointerType>())
RetType = Context.getPointerType(RetType);
else {
--- /dev/null
+// 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<lambda_0>@?0??usage@@YAXXZ@QBEP6A?A?<auto>@@HMN@ZXZ"
+// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ@QBEP6E?A?<auto>@@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<lambda_0>@?0??usage@@YAXXZ@QBEP6A?A?<auto>@@HMN@ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ@CA?A?<auto>@@HMN@Z"
+// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ@QBEP6E?A?<auto>@@HMN@ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ@CE?A?<auto>@@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@<lambda_0>@?0??usage@@YAXXZ@CA?A?<auto>@@HMN@Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ@QBE?A?<auto>@@HMN@Z"
+// WIN32: define internal x86_thiscallcc double @"?__invoke@<lambda_0>@?0??usage@@YAXXZ@CE?A?<auto>@@HMN@Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ@QBE?A?<auto>@@HMN@Z"
--- /dev/null
+// 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;
+}