[clang] ec809e4 - PR47372: Fix Lambda invoker calling conventions

Erich Keane via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 30 06:40:18 PDT 2020


Author: Erich Keane
Date: 2020-10-30T06:39:55-07:00
New Revision: ec809e4cfe0be08b1bab7ba7e6ff905b9430571a

URL: https://github.com/llvm/llvm-project/commit/ec809e4cfe0be08b1bab7ba7e6ff905b9430571a
DIFF: https://github.com/llvm/llvm-project/commit/ec809e4cfe0be08b1bab7ba7e6ff905b9430571a.diff

LOG: PR47372: Fix Lambda invoker calling conventions

As mentioned in the defect, the lambda static invoker does not follow
the calling convention of the lambda itself, which seems wrong. This
patch ensures that the calling convention of operator() is passed onto
the invoker and conversion-operator type.

This is accomplished by extracting the calling-convention determination
code out into a separate function in order to better reflect the 'thiscall'
work, as well as somewhat better support the future implementation of
https://devblogs.microsoft.com/oldnewthing/20150220-00/?p=44623

For any target (basically just win32) that has a different free and
static function calling convention, this generates BOTH alternatives.
This required some work to get the Windows mangler to work correctly for
this, as well as some tie-breaking for the unary operators.

Differential Revision: https://reviews.llvm.org/D89559

Added: 
    clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp
    clang/test/SemaCXX/lambda-conversion-op-cc.cpp

Modified: 
    clang/include/clang/AST/DeclCXX.h
    clang/include/clang/Sema/Sema.h
    clang/lib/AST/DeclCXX.cpp
    clang/lib/AST/MicrosoftMangle.cpp
    clang/lib/Sema/SemaDeclCXX.cpp
    clang/lib/Sema/SemaLambda.cpp
    clang/lib/Sema/SemaOverload.cpp
    clang/lib/Sema/SemaTemplateDeduction.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index fc0632f07b51..496caf871a91 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -1008,8 +1008,13 @@ class CXXRecordDecl : public RecordDecl {
 
   /// 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

diff  --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 61ee743a7ab9..4be3c23652e1 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -6597,7 +6597,8 @@ class Sema final {
   /// 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.

diff  --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 3f522450e870..9f8248722002 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -1507,18 +1507,38 @@ CXXMethodDecl *CXXRecordDecl::getLambdaCallOperator() const {
 }
 
 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(
@@ -2459,14 +2479,8 @@ bool CXXMethodDecl::hasInlineBody() const {
 
 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,

diff  --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index 8878b6e5e94f..3a263d487b75 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -2260,10 +2260,20 @@ void MicrosoftCXXNameMangler::mangleFunctionType(const FunctionType *T,
       return;
     }
     Out << '@';
+  } else if (IsInLambda && D && isa<CXXConversionDecl>(D)) {
+    // The only lambda conversion operators are to function pointers, which
+    // can 
diff er 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 
diff er 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 << '?';

diff  --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index bc119725e4a0..f340b5229d47 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -14811,9 +14811,13 @@ void Sema::DefineImplicitLambdaToFunctionPointerConversion(
   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(

diff  --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp
index 0b081f39299e..8242e89eb111 100644
--- a/clang/lib/Sema/SemaLambda.cpp
+++ b/clang/lib/Sema/SemaLambda.cpp
@@ -1263,30 +1263,61 @@ void Sema::ActOnLambdaError(SourceLocation StartLoc, Scope *CurScope,
   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) {
@@ -1296,8 +1327,6 @@ static void addFunctionPointerConversion(Sema &S,
     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.
@@ -1442,13 +1471,37 @@ static void addFunctionPointerConversion(Sema &S,
     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 
diff erent, 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(
@@ -1795,8 +1848,8 @@ ExprResult Sema::BuildLambdaExpr(SourceLocation StartLoc, SourceLocation EndLoc,
     //   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

diff  --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index ae106ff4557a..277ef5af9130 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -3645,13 +3645,32 @@ Sema::DiagnoseMultipleUserDefinedConversion(Expr *From, QualType ToType) {
   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++:
@@ -3660,15 +3679,7 @@ compareConversionFunctions(Sema &S, FunctionDecl *Function1,
   //   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)
@@ -3676,6 +3687,39 @@ compareConversionFunctions(Sema &S, FunctionDecl *Function1,
                     : 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
+  // 
diff erent), 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;
 }
 
@@ -10180,6 +10224,22 @@ void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
   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 =

diff  --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 886377108e3f..d137aec82b8c 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5022,8 +5022,12 @@ bool Sema::DeduceReturnType(FunctionDecl *FD, SourceLocation Loc,
            "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 {

diff  --git a/clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp b/clang/test/CodeGenCXX/lambda-conversion-op-cc.cpp
new file mode 100644
index 000000000000..c2618925de15
--- /dev/null
+++ b/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<lambda_0>@?0??usage@@YAXXZ at QBEP6A?A?<auto>@@HMN at ZXZ"
+// WIN32: call x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6E?A?<auto>@@HMN at 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 at QBEP6A?A?<auto>@@HMN at ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CA?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_thiscallcc double (i32, float, double)* @"??B<lambda_0>@?0??usage@@YAXXZ at QBEP6E?A?<auto>@@HMN at ZXZ"
+// WIN32: ret double (i32, float, double)* @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CE?A?<auto>@@HMN at 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 at CA?A?<auto>@@HMN at Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"
+// WIN32: define internal x86_thiscallcc double @"?__invoke@<lambda_0>@?0??usage@@YAXXZ at CE?A?<auto>@@HMN at Z"
+// WIN32: call x86_thiscallcc double @"??R<lambda_0>@?0??usage@@YAXXZ at QBE?A?<auto>@@HMN at Z"

diff  --git a/clang/test/SemaCXX/lambda-conversion-op-cc.cpp b/clang/test/SemaCXX/lambda-conversion-op-cc.cpp
new file mode 100644
index 000000000000..77aa18cc8875
--- /dev/null
+++ b/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 at +1 {{invalid argument type {{.*}} to unary expression}}
+  (void)+genericlambda;
+  // expected-error-re at +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 at +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 at +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 at +2 {{no viable conversion from {{.*}} to 'void (*)(char) __attribute__((vectorcall))'}}
+  // expected-note@#4 {{candidate function}}
+  void(__vectorcall * generic_ptr)(char) = genericlambda;
+  // expected-error-re at +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 at +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;
+}


        


More information about the cfe-commits mailing list