[clang] [clang] Bugfix for choosing the more specialized overload (PR #83279)

Botond István Horváth via cfe-commits cfe-commits at lists.llvm.org
Wed Mar 6 02:29:32 PST 2024


https://github.com/HoBoIs updated https://github.com/llvm/llvm-project/pull/83279

>From 68200ecf3267d1b3940fa73c25c50ee706932a98 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Wed, 28 Feb 2024 13:09:15 +0100
Subject: [PATCH 01/12] Bugfix for choosing the more specialized overload

There was a bug in clang where it couldn't choose which overload candidate is
more specialized if it was comparing a member-function to a non-member
function. Previously, this was detected as an ambigouity, now clang chooses correctly.

This patch fixes the bug by fully implementing CWG2445 and moving the template
transformation described in [temp.func.order] paragraph 3 from
isAtLeastAsSpecializedAs to Sema::getMoreSpecializedTemplate so we have the
transformed parameter list during the whole comperrassion. Also, to be able
to add the correct type for the implicit object parameter
Sema::getMoreSpecializedTemplate has new parameters for the object type.
---
 clang/include/clang/Sema/Sema.h          |   4 +-
 clang/lib/Sema/SemaOverload.cpp          |  12 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp | 263 +++++++++++++++--------
 3 files changed, 186 insertions(+), 93 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index ef4b93fac95ce5..1a2a3a6bebd95e 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9463,7 +9463,9 @@ class Sema final {
   FunctionTemplateDecl *getMoreSpecializedTemplate(
       FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
       TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-      unsigned NumCallArguments2, bool Reversed = false);
+      unsigned NumCallArguments2, QualType ObjType1 = {},
+      QualType ObjType2 = {}, bool Reversed = false);
+
   UnresolvedSetIterator
   getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
                      TemplateSpecCandidateSet &FailedCandidates,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 7d38043890ca20..60138236abf1d8 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10526,14 +10526,24 @@ bool clang::isBetterOverloadCandidate(
   //      according to the partial ordering rules described in 14.5.5.2, or,
   //      if not that,
   if (Cand1IsSpecialization && Cand2IsSpecialization) {
+    const auto *ObjContext1 =
+        dyn_cast<CXXRecordDecl>(Cand1.FoundDecl->getDeclContext());
+    const auto *ObjContext2 =
+        dyn_cast<CXXRecordDecl>(Cand2.FoundDecl->getDeclContext());
     if (FunctionTemplateDecl *BetterTemplate = S.getMoreSpecializedTemplate(
             Cand1.Function->getPrimaryTemplate(),
             Cand2.Function->getPrimaryTemplate(), Loc,
             isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
                                                    : TPOC_Call,
             Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
-            Cand1.isReversed() ^ Cand2.isReversed()))
+            ObjContext1 ? QualType(ObjContext1->getTypeForDecl(), 0)
+                        : QualType{},
+            ObjContext2 ? QualType(ObjContext2->getTypeForDecl(), 0)
+                        : QualType{},
+            Cand1.isReversed() ^ Cand2.isReversed())) {
       return BetterTemplate == Cand1.Function->getPrimaryTemplate();
+    }
+
   }
 
   //   -— F1 and F2 are non-template functions with the same
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 563491f76f5478..2af3c29ae1f1c4 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5333,11 +5333,31 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD,
   return false;
 }
 
+static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context,
+                                                    const CXXMethodDecl *Method,
+                                                    QualType rawType,
+                                                    bool isOtherRvr) {
+  // C++20 [temp.func.order]p3.1, p3.2:
+  //- The type X(M ) is “rvalue reference to cv A” if the optional ref-qualifier
+  //  of M is && or if M has no ref-qualifier and the positionally-corresponding
+  //  parameter of the other transformed template has rvalue reference type;
+  //  if this determination depends recursively upon whether X(M ) is an rvalue
+  //  reference type, it is not considered to have rvalue reference type.
+  //- Otherwise, X(M ) is “lvalue reference to cv A”.
+  assert(Method && !Method->isExplicitObjectMemberFunction() &&
+         "expected a member function with no explicit object parameter");
+
+  rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers());
+  if (Method->getRefQualifier() == RQ_RValue ||
+      (isOtherRvr && Method->getRefQualifier() == RQ_None))
+    return Context.getRValueReferenceType(rawType);
+  return Context.getLValueReferenceType(rawType);
+}
+
 /// If this is a non-static member function,
-static void
-AddImplicitObjectParameterType(ASTContext &Context,
-                               CXXMethodDecl *Method,
-                               SmallVectorImpl<QualType> &ArgTypes) {
+static QualType GetImplicitObjectParameterType(ASTContext &Context,
+                                               const CXXMethodDecl *Method,
+                                               QualType rawType) {
   // C++11 [temp.func.order]p3:
   //   [...] The new parameter is of type "reference to cv A," where cv are
   //   the cv-qualifiers of the function template (if any) and A is
@@ -5347,24 +5367,21 @@ AddImplicitObjectParameterType(ASTContext &Context,
   // reference type based on [over.match.funcs]p4.
   assert(Method && Method->isImplicitObjectMemberFunction() &&
          "expected an implicit objet function");
-  QualType ArgTy = Context.getTypeDeclType(Method->getParent());
-  ArgTy = Context.getQualifiedType(ArgTy, Method->getMethodQualifiers());
+  rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers());
   if (Method->getRefQualifier() == RQ_RValue)
-    ArgTy = Context.getRValueReferenceType(ArgTy);
-  else
-    ArgTy = Context.getLValueReferenceType(ArgTy);
-  ArgTypes.push_back(ArgTy);
+    return Context.getRValueReferenceType(rawType);
+  return Context.getLValueReferenceType(rawType);
 }
 
 /// Determine whether the function template \p FT1 is at least as
 /// specialized as \p FT2.
-static bool isAtLeastAsSpecializedAs(Sema &S,
-                                     SourceLocation Loc,
-                                     FunctionTemplateDecl *FT1,
-                                     FunctionTemplateDecl *FT2,
+static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
+                                     const FunctionTemplateDecl *FT1,
+                                     const FunctionTemplateDecl *FT2,
                                      TemplatePartialOrderingContext TPOC,
-                                     unsigned NumCallArguments1,
-                                     bool Reversed) {
+                                     bool Reversed,
+                                     const SmallVector<QualType, 4> &Args1,
+                                     const SmallVector<QualType, 4> &Args2) {
   assert(!Reversed || TPOC == TPOC_Call);
 
   FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -5381,66 +5398,8 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
   //   The types used to determine the ordering depend on the context in which
   //   the partial ordering is done:
   TemplateDeductionInfo Info(Loc);
-  SmallVector<QualType, 4> Args2;
   switch (TPOC) {
-  case TPOC_Call: {
-    //   - In the context of a function call, the function parameter types are
-    //     used.
-    CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
-    CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
-
-    // C++11 [temp.func.order]p3:
-    //   [...] If only one of the function templates is a non-static
-    //   member, that function template is considered to have a new
-    //   first parameter inserted in its function parameter list. The
-    //   new parameter is of type "reference to cv A," where cv are
-    //   the cv-qualifiers of the function template (if any) and A is
-    //   the class of which the function template is a member.
-    //
-    // Note that we interpret this to mean "if one of the function
-    // templates is a non-static member and the other is a non-member";
-    // otherwise, the ordering rules for static functions against non-static
-    // functions don't make any sense.
-    //
-    // C++98/03 doesn't have this provision but we've extended DR532 to cover
-    // it as wording was broken prior to it.
-    SmallVector<QualType, 4> Args1;
-
-    unsigned NumComparedArguments = NumCallArguments1;
-
-    if (!Method2 && Method1 && Method1->isImplicitObjectMemberFunction()) {
-      // Compare 'this' from Method1 against first parameter from Method2.
-      AddImplicitObjectParameterType(S.Context, Method1, Args1);
-      ++NumComparedArguments;
-    } else if (!Method1 && Method2 &&
-               Method2->isImplicitObjectMemberFunction()) {
-      // Compare 'this' from Method2 against first parameter from Method1.
-      AddImplicitObjectParameterType(S.Context, Method2, Args2);
-    } else if (Method1 && Method2 && Reversed &&
-               Method1->isImplicitObjectMemberFunction() &&
-               Method2->isImplicitObjectMemberFunction()) {
-      // Compare 'this' from Method1 against second parameter from Method2
-      // and 'this' from Method2 against second parameter from Method1.
-      AddImplicitObjectParameterType(S.Context, Method1, Args1);
-      AddImplicitObjectParameterType(S.Context, Method2, Args2);
-      ++NumComparedArguments;
-    }
-
-    Args1.insert(Args1.end(), Proto1->param_type_begin(),
-                 Proto1->param_type_end());
-    Args2.insert(Args2.end(), Proto2->param_type_begin(),
-                 Proto2->param_type_end());
-
-    // C++ [temp.func.order]p5:
-    //   The presence of unused ellipsis and default arguments has no effect on
-    //   the partial ordering of function templates.
-    if (Args1.size() > NumComparedArguments)
-      Args1.resize(NumComparedArguments);
-    if (Args2.size() > NumComparedArguments)
-      Args2.resize(NumComparedArguments);
-    if (Reversed)
-      std::reverse(Args2.begin(), Args2.end());
-
+  case TPOC_Call:
     if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(),
                                 Args1.data(), Args1.size(), Info, Deduced,
                                 TDF_None, /*PartialOrdering=*/true) !=
@@ -5448,7 +5407,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
       return false;
 
     break;
-  }
 
   case TPOC_Conversion:
     //   - In the context of a call to a conversion operator, the return types
@@ -5539,6 +5497,14 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
 /// \param NumCallArguments2 The number of arguments in the call to FT2, used
 /// only when \c TPOC is \c TPOC_Call.
 ///
+/// \param RawObjType1 The type of the object parameter of FT1 if a member
+/// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function
+/// template from a member function
+///
+/// \param RawObjType2 The type of the object parameter of FT2 if a member
+/// function only used if \c TPOC is \c TPOC_Call and FT2 is a Function
+/// template from a member function
+///
 /// \param Reversed If \c true, exactly one of FT1 and FT2 is an overload
 /// candidate with a reversed parameter order. In this case, the corresponding
 /// P/A pairs between FT1 and FT2 are reversed.
@@ -5548,13 +5514,113 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
 FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
     TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-    unsigned NumCallArguments2, bool Reversed) {
+    unsigned NumCallArguments2, QualType RawObjType1, QualType RawObjType2,
+    bool Reversed) {
+  SmallVector<QualType, 4> Args1;
+  SmallVector<QualType, 4> Args2;
+  const FunctionDecl *FD1 = FT1->getTemplatedDecl();
+  const FunctionDecl *FD2 = FT2->getTemplatedDecl();
+  bool shouldConvert1 = false;
+  bool shouldConvert2 = false;
+  QualType ObjType1;
+  QualType ObjType2;
+  if (TPOC == TPOC_Call) {
+    const FunctionProtoType *Proto1 =
+        FD1->getType()->getAs<FunctionProtoType>();
+    const FunctionProtoType *Proto2 =
+        FD2->getType()->getAs<FunctionProtoType>();
 
-  bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
-                                          NumCallArguments1, Reversed);
-  bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
-                                          NumCallArguments2, Reversed);
+    //   - In the context of a function call, the function parameter types are
+    //     used.
+    const CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
+    const CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
+
+    if (getLangOpts().CPlusPlus20) {
+      // C++20 [temp.func.order]p3
+      //   [...] Each function template M that is a member function is
+      //   considered to have a new first parameter of type
+      //   X(M), described below, inserted in its function parameter list.
+      //
+      // Note that we interpret "that is a member function" as
+      // "that is a member function with no expicit object argument".
+      // Otherwise the ordering rules for methods with expicit objet arguments
+      // against anything else make no sense.
+      shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
+      shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
+      if (shouldConvert1) {
+        bool isR2 =
+            Method2 && !Method2->isExplicitObjectMemberFunction()
+                ? Method2->getRefQualifier() == RQ_RValue
+                : Proto2->param_type_begin()[0]->isRValueReferenceType();
+        // Compare 'this' from Method1 against first parameter from Method2.
+        ObjType1 = GetImplicitObjectParameterTypeCXX20(this->Context, Method1,
+                                                       RawObjType1, isR2);
+        Args1.push_back(ObjType1);
+      }
+      if (shouldConvert2) {
+        bool isR1 =
+            Method1 && !Method1->isExplicitObjectMemberFunction()
+                ? Method1->getRefQualifier() == RQ_RValue
+                : Proto1->param_type_begin()[0]->isRValueReferenceType();
+        // Compare 'this' from Method2 against first parameter from Method1.
+        ObjType2 = GetImplicitObjectParameterTypeCXX20(this->Context, Method2,
+                                                       RawObjType2, isR1);
+        Args2.push_back(ObjType2);
+      }
+    } else {
+      // C++11 [temp.func.order]p3:
+      //   [...] If only one of the function templates is a non-static
+      //   member, that function template is considered to have a new
+      //   first parameter inserted in its function parameter list. The
+      //   new parameter is of type "reference to cv A," where cv are
+      //   the cv-qualifiers of the function template (if any) and A is
+      //   the class of which the function template is a member.
+      //
+      // Note that we interpret this to mean "if one of the function
+      // templates is a non-static member and the other is a non-member";
+      // otherwise, the ordering rules for static functions against non-static
+      // functions don't make any sense.
+      //
+      // C++98/03 doesn't have this provision but we've extended DR532 to cover
+      // it as wording was broken prior to it.
+      shouldConvert1 =
+          !Method2 && Method1 && Method1->isImplicitObjectMemberFunction();
+      shouldConvert2 =
+          !Method1 && Method2 && Method2->isImplicitObjectMemberFunction();
+      if (shouldConvert1) {
+        // Compare 'this' from Method1 against first parameter from Method2.
+        ObjType1 =
+            GetImplicitObjectParameterType(this->Context, Method1, RawObjType1);
+        Args1.push_back(ObjType1);
+      }
+      if (shouldConvert2) {
+        // Compare 'this' from Method2 against first parameter from Method1.
+        ObjType2 =
+            GetImplicitObjectParameterType(this->Context, Method2, RawObjType2);
+        Args2.push_back(ObjType2);
+      }
+    }
+    unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1;
+
+    Args1.insert(Args1.end(), Proto1->param_type_begin(),
+                 Proto1->param_type_end());
+    Args2.insert(Args2.end(), Proto2->param_type_begin(),
+                 Proto2->param_type_end());
 
+    // C++ [temp.func.order]p5:
+    //   The presence of unused ellipsis and default arguments has no effect on
+    //   the partial ordering of function templates.
+    if (Args1.size() > NumComparedArguments)
+      Args1.resize(NumComparedArguments);
+    if (Args2.size() > NumComparedArguments)
+      Args2.resize(NumComparedArguments);
+    if (Reversed)
+      std::reverse(Args2.begin(), Args2.end());
+  }
+  bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC, Reversed,
+                                          Args1, Args2);
+  bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC, Reversed,
+                                          Args2, Args1);
   // C++ [temp.deduct.partial]p10:
   //   F is more specialized than G if F is at least as specialized as G and G
   //   is not at least as specialized as F.
@@ -5568,12 +5634,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   //   ... and if G has a trailing function parameter pack for which F does not
   //   have a corresponding parameter, and if F does not have a trailing
   //   function parameter pack, then F is more specialized than G.
-  FunctionDecl *FD1 = FT1->getTemplatedDecl();
-  FunctionDecl *FD2 = FT2->getTemplatedDecl();
-  unsigned NumParams1 = FD1->getNumParams();
-  unsigned NumParams2 = FD2->getNumParams();
-  bool Variadic1 = NumParams1 && FD1->parameters().back()->isParameterPack();
-  bool Variadic2 = NumParams2 && FD2->parameters().back()->isParameterPack();
+
+  SmallVector<QualType> param1;
+  param1.reserve(FD1->param_size() + shouldConvert1);
+  if (shouldConvert1)
+    param1.push_back(ObjType1);
+  for (const auto &x : FD1->parameters())
+    param1.push_back(x->getType());
+
+  SmallVector<QualType> param2;
+  param2.reserve(FD2->param_size() + shouldConvert2);
+  if (shouldConvert2)
+    param2.push_back(ObjType2);
+  for (const auto &x : FD2->parameters())
+    param2.push_back(x->getType());
+
+  unsigned NumParams1 = param1.size();
+  unsigned NumParams2 = param2.size();
+
+  bool Variadic1 =
+      FD1->param_size() && FD1->parameters().back()->isParameterPack();
+  bool Variadic2 =
+      FD2->param_size() && FD2->parameters().back()->isParameterPack();
   if (Variadic1 != Variadic2) {
     if (Variadic1 && NumParams1 > NumParams2)
       return FT2;
@@ -5584,8 +5666,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   // This a speculative fix for CWG1432 (Similar to the fix for CWG1395) that
   // there is no wording or even resolution for this issue.
   for (int i = 0, e = std::min(NumParams1, NumParams2); i < e; ++i) {
-    QualType T1 = FD1->getParamDecl(i)->getType().getCanonicalType();
-    QualType T2 = FD2->getParamDecl(i)->getType().getCanonicalType();
+    QualType T1 = param1[i].getCanonicalType();
+    QualType T2 = param2[i].getCanonicalType();
     auto *TST1 = dyn_cast<TemplateSpecializationType>(T1);
     auto *TST2 = dyn_cast<TemplateSpecializationType>(T2);
     if (!TST1 || !TST2)
@@ -5644,8 +5726,7 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   // Any top-level cv-qualifiers modifying a parameter type are deleted when
   // forming the function type.
   for (unsigned i = 0; i < NumParams1; ++i)
-    if (!Context.hasSameUnqualifiedType(FD1->getParamDecl(i)->getType(),
-                                        FD2->getParamDecl(i)->getType()))
+    if (!Context.hasSameUnqualifiedType(param1[i], param2[i]))
       return nullptr;
 
   // C++20 [temp.func.order]p6.3:

>From 3cb51452a25227afb0750ae65fcca9bf281433ac Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Botond=20Istv=C3=A1n=20Horv=C3=A1th?=
 <56926027+HoBoIs at users.noreply.github.com>
Date: Wed, 28 Feb 2024 16:50:30 +0100
Subject: [PATCH 02/12] Formatting SemaOverload.cpp

---
 clang/lib/Sema/SemaOverload.cpp | 1 -
 1 file changed, 1 deletion(-)

diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 60138236abf1d8..74d714ad308af1 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10543,7 +10543,6 @@ bool clang::isBetterOverloadCandidate(
             Cand1.isReversed() ^ Cand2.isReversed())) {
       return BetterTemplate == Cand1.Function->getPrimaryTemplate();
     }
-
   }
 
   //   -— F1 and F2 are non-template functions with the same

>From 54e7fb1ae193196538423a0c1cbb62fc518f77ef Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Wed, 28 Feb 2024 18:46:07 +0100
Subject: [PATCH 03/12] Unified the branches for newer and older standards

---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 97 +++++++++---------------
 1 file changed, 37 insertions(+), 60 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 2af3c29ae1f1c4..f7526933be12f4 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5333,10 +5333,10 @@ bool Sema::CheckIfFunctionSpecializationIsImmediate(FunctionDecl *FD,
   return false;
 }
 
-static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context,
-                                                    const CXXMethodDecl *Method,
-                                                    QualType rawType,
-                                                    bool isOtherRvr) {
+static QualType GetImplicitObjectParameterType(ASTContext &Context,
+                                               const CXXMethodDecl *Method,
+                                               QualType RawType,
+                                               bool IsOtherRvr) {
   // C++20 [temp.func.order]p3.1, p3.2:
   //- The type X(M ) is “rvalue reference to cv A” if the optional ref-qualifier
   //  of M is && or if M has no ref-qualifier and the positionally-corresponding
@@ -5344,20 +5344,7 @@ static QualType GetImplicitObjectParameterTypeCXX20(ASTContext &Context,
   //  if this determination depends recursively upon whether X(M ) is an rvalue
   //  reference type, it is not considered to have rvalue reference type.
   //- Otherwise, X(M ) is “lvalue reference to cv A”.
-  assert(Method && !Method->isExplicitObjectMemberFunction() &&
-         "expected a member function with no explicit object parameter");
-
-  rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers());
-  if (Method->getRefQualifier() == RQ_RValue ||
-      (isOtherRvr && Method->getRefQualifier() == RQ_None))
-    return Context.getRValueReferenceType(rawType);
-  return Context.getLValueReferenceType(rawType);
-}
-
-/// If this is a non-static member function,
-static QualType GetImplicitObjectParameterType(ASTContext &Context,
-                                               const CXXMethodDecl *Method,
-                                               QualType rawType) {
+  //
   // C++11 [temp.func.order]p3:
   //   [...] The new parameter is of type "reference to cv A," where cv are
   //   the cv-qualifiers of the function template (if any) and A is
@@ -5365,12 +5352,15 @@ static QualType GetImplicitObjectParameterType(ASTContext &Context,
   //
   // The standard doesn't say explicitly, but we pick the appropriate kind of
   // reference type based on [over.match.funcs]p4.
-  assert(Method && Method->isImplicitObjectMemberFunction() &&
-         "expected an implicit objet function");
-  rawType = Context.getQualifiedType(rawType, Method->getMethodQualifiers());
-  if (Method->getRefQualifier() == RQ_RValue)
-    return Context.getRValueReferenceType(rawType);
-  return Context.getLValueReferenceType(rawType);
+
+  assert(Method && !Method->isExplicitObjectMemberFunction() &&
+         "expected a member function with no explicit object parameter");
+
+  RawType = Context.getQualifiedType(RawType, Method->getMethodQualifiers());
+  if (Method->getRefQualifier() == RQ_RValue ||
+      (IsOtherRvr && Method->getRefQualifier() == RQ_None))
+    return Context.getRValueReferenceType(RawType);
+  return Context.getLValueReferenceType(RawType);
 }
 
 /// Determine whether the function template \p FT1 is at least as
@@ -5547,34 +5537,11 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
       // against anything else make no sense.
       shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
       shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
-      if (shouldConvert1) {
-        bool isR2 =
-            Method2 && !Method2->isExplicitObjectMemberFunction()
-                ? Method2->getRefQualifier() == RQ_RValue
-                : Proto2->param_type_begin()[0]->isRValueReferenceType();
-        // Compare 'this' from Method1 against first parameter from Method2.
-        ObjType1 = GetImplicitObjectParameterTypeCXX20(this->Context, Method1,
-                                                       RawObjType1, isR2);
-        Args1.push_back(ObjType1);
-      }
-      if (shouldConvert2) {
-        bool isR1 =
-            Method1 && !Method1->isExplicitObjectMemberFunction()
-                ? Method1->getRefQualifier() == RQ_RValue
-                : Proto1->param_type_begin()[0]->isRValueReferenceType();
-        // Compare 'this' from Method2 against first parameter from Method1.
-        ObjType2 = GetImplicitObjectParameterTypeCXX20(this->Context, Method2,
-                                                       RawObjType2, isR1);
-        Args2.push_back(ObjType2);
-      }
     } else {
       // C++11 [temp.func.order]p3:
       //   [...] If only one of the function templates is a non-static
       //   member, that function template is considered to have a new
-      //   first parameter inserted in its function parameter list. The
-      //   new parameter is of type "reference to cv A," where cv are
-      //   the cv-qualifiers of the function template (if any) and A is
-      //   the class of which the function template is a member.
+      //   first parameter inserted in its function parameter list.
       //
       // Note that we interpret this to mean "if one of the function
       // templates is a non-static member and the other is a non-member";
@@ -5587,18 +5554,28 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
           !Method2 && Method1 && Method1->isImplicitObjectMemberFunction();
       shouldConvert2 =
           !Method1 && Method2 && Method2->isImplicitObjectMemberFunction();
-      if (shouldConvert1) {
-        // Compare 'this' from Method1 against first parameter from Method2.
-        ObjType1 =
-            GetImplicitObjectParameterType(this->Context, Method1, RawObjType1);
-        Args1.push_back(ObjType1);
-      }
-      if (shouldConvert2) {
-        // Compare 'this' from Method2 against first parameter from Method1.
-        ObjType2 =
-            GetImplicitObjectParameterType(this->Context, Method2, RawObjType2);
-        Args2.push_back(ObjType2);
-      }
+    }
+    if (shouldConvert1) {
+      bool isR2 =
+          getLangOpts().CPlusPlus20 &&
+          (shouldConvert1
+               ? Method2->getRefQualifier() == RQ_RValue
+               : Proto2->param_type_begin()[0]->isRValueReferenceType());
+      // Compare 'this' from Method1 against first parameter from Method2.
+      ObjType1 = GetImplicitObjectParameterType(this->Context, Method1,
+                                                RawObjType1, isR2);
+      Args1.push_back(ObjType1);
+    }
+    if (shouldConvert2) {
+      bool isR1 =
+          getLangOpts().CPlusPlus20 &&
+          (shouldConvert2
+               ? Method1->getRefQualifier() == RQ_RValue
+               : Proto1->param_type_begin()[0]->isRValueReferenceType());
+      // Compare 'this' from Method2 against first parameter from Method1.
+      ObjType2 = GetImplicitObjectParameterType(this->Context, Method2,
+                                                RawObjType2, isR1);
+      Args2.push_back(ObjType2);
     }
     unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1;
 

>From e377e6e320d5c280e9364bbd14be456aab57ea46 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Thu, 29 Feb 2024 15:35:12 +0100
Subject: [PATCH 04/12] minor fix + tests + release notes

---
 clang/docs/ReleaseNotes.rst                |  6 ++++
 clang/lib/Sema/SemaTemplateDeduction.cpp   | 12 +++----
 clang/test/SemaCXX/overload-template.cpp   | 25 +++++++++++++++
 clang/test/SemaCXX/overloaded-operator.cpp | 37 ++++++++++++++++++++++
 4 files changed, 74 insertions(+), 6 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 7e16b9f0c67dbd..6a1b93db9f6486 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -290,6 +290,12 @@ Bug Fixes to C++ Support
   lookup searches the bases of an incomplete class.
 - Fix a crash when an unresolved overload set is encountered on the RHS of a ``.*`` operator.
   (`#53815 <https://github.com/llvm/llvm-project/issues/53815>`_)
+- Fix a bug where overload resolution falsly reported an ambiguity when it was compearing
+  a member-function against a non member function or a member-function with an
+  explicit object parameter against a member function with no explicit object parameter
+  when one of the function had more specialized templates.
+  Fixes (`#82509 <https://github.com/llvm/llvm-project/issues/82509>`_)
+  and (`#74494 <https://github.com/llvm/llvm-project/issues/74494>`_)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index f7526933be12f4..04e46ab69876f6 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5556,25 +5556,25 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
           !Method1 && Method2 && Method2->isImplicitObjectMemberFunction();
     }
     if (shouldConvert1) {
-      bool isR2 =
+      bool isRValRef2 =
           getLangOpts().CPlusPlus20 &&
-          (shouldConvert1
+          (shouldConvert2
                ? Method2->getRefQualifier() == RQ_RValue
                : Proto2->param_type_begin()[0]->isRValueReferenceType());
       // Compare 'this' from Method1 against first parameter from Method2.
       ObjType1 = GetImplicitObjectParameterType(this->Context, Method1,
-                                                RawObjType1, isR2);
+                                                RawObjType1, isRValRef2);
       Args1.push_back(ObjType1);
     }
     if (shouldConvert2) {
-      bool isR1 =
+      bool isRValRef1 =
           getLangOpts().CPlusPlus20 &&
-          (shouldConvert2
+          (shouldConvert1
                ? Method1->getRefQualifier() == RQ_RValue
                : Proto1->param_type_begin()[0]->isRValueReferenceType());
       // Compare 'this' from Method2 against first parameter from Method1.
       ObjType2 = GetImplicitObjectParameterType(this->Context, Method2,
-                                                RawObjType2, isR1);
+                                                RawObjType2, isRValRef1);
       Args2.push_back(ObjType2);
     }
     unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1;
diff --git a/clang/test/SemaCXX/overload-template.cpp b/clang/test/SemaCXX/overload-template.cpp
index 0a23788ef3da6a..0fe13c479cce22 100644
--- a/clang/test/SemaCXX/overload-template.cpp
+++ b/clang/test/SemaCXX/overload-template.cpp
@@ -1,4 +1,5 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -std=c++23 -verify -fsyntax-only %s
 
 enum copy_traits { movable = 1 };
 
@@ -33,3 +34,27 @@ void ReproducesBugSimply() {
   InsertRow(3, B{}); // expected-error {{no matching function for call to 'InsertRow'}}
 }
 
+#if __cplusplus >= 202302L
+namespace overloadCheck{
+  template<typename T>
+  concept AlwaysTrue = true;
+
+  struct S {
+    int f(AlwaysTrue auto) { return 1; }
+    void f(this S&&, auto) {}
+
+    void g(auto) {}
+    int g(this S&&,AlwaysTrue auto) {return 1;}
+
+    int h(AlwaysTrue auto) { return 1; } //expected-note {{previous definition is here}}
+    int h(this S&&,AlwaysTrue auto) { // expected-error {{class member cannot be redeclared}} 
+      return 1;
+    }
+  };
+
+  int main() {
+    int x = S{}.f(0);
+    int y = S{}.g(0);
+  }
+}
+#endif
diff --git a/clang/test/SemaCXX/overloaded-operator.cpp b/clang/test/SemaCXX/overloaded-operator.cpp
index 887848c29b83c5..49311625d7ab2d 100644
--- a/clang/test/SemaCXX/overloaded-operator.cpp
+++ b/clang/test/SemaCXX/overloaded-operator.cpp
@@ -645,3 +645,40 @@ class b {
 
 
 }
+
+#if __cplusplus >= 202002L
+namespace nw{
+  template<class T>
+  concept AlwaysTrue=true;
+
+  struct S{
+    template<class T>
+    void operator+(const T&)const{}
+
+    template<AlwaysTrue T>
+    int operator-(const T&)const{return 0;}
+
+    template<AlwaysTrue T>
+    int operator*(const T&)const{ // expected-note {{candidate function}}
+      return 0;
+    }
+  };
+
+  template<AlwaysTrue T>
+  int operator+(const S&, const T&){return 0;}
+
+  template<class T>
+  void operator-(const S&, const T&){}
+
+  template<AlwaysTrue T>
+  int operator*(const S&, const T&){ // expected-note {{candidate function}}
+    return 0;
+  }
+
+  void foo(){
+    int a = S{} + 1;
+    int b = S{} - 1;
+    int c = S{} * 1; // expected-error {{use of overloaded operator '*' is ambiguous (with operand types 'S' and 'int')}}
+  }
+}
+#endif

>From 74e69bf423645f98e342710f8e251365cdb7cd3b Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Fri, 1 Mar 2024 14:40:35 +0100
Subject: [PATCH 05/12] Set DR CWG2445 status

---
 clang/www/cxx_dr_status.html | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 8b638e06f4aab6..5973d825433463 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -14478,7 +14478,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/2445.html">2445</a></td>
     <td>C++20</td>
     <td>Partial ordering with rewritten candidates</td>
-    <td class="unknown" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 18</td>
   </tr>
   <tr id="2446">
     <td><a href="https://cplusplus.github.io/CWG/issues/2446.html">2446</a></td>

>From d8c5aa26f7213e98f8030e1613efd5136b44b1a5 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Fri, 1 Mar 2024 16:38:24 +0100
Subject: [PATCH 06/12] Added test to dr24xx.cpp, auto-generated
 cxx_dr_status.html

---
 clang/test/CXX/drs/dr24xx.cpp | 32 ++++++++++++++++++++++++++++++++
 clang/www/cxx_dr_status.html  |  2 +-
 2 files changed, 33 insertions(+), 1 deletion(-)

diff --git a/clang/test/CXX/drs/dr24xx.cpp b/clang/test/CXX/drs/dr24xx.cpp
index ae8dda3351f48e..e756f43985517b 100644
--- a/clang/test/CXX/drs/dr24xx.cpp
+++ b/clang/test/CXX/drs/dr24xx.cpp
@@ -68,3 +68,35 @@ template<A> struct X {};
 X<1> x;
 #endif
 }
+namespace dr2445 { // dr2445: 19
+#if __cplusplus >= 202002L
+  template <typename> constexpr bool F = false;
+  template <typename T> struct A { };
+
+  template <typename T, typename U>
+  bool operator==(T, A<U *>);
+
+  template <typename T, typename U>
+  bool operator!=(A<T>, U) {
+   static_assert(F<T>, "Isn't this less specialized?");
+   return false;
+  }
+
+  bool f(A<int> ax, A<int *> ay) { return ay != ax; }
+
+  template<class T> concept AlwaysTrue=true;
+  template <class T> struct B {
+    template <AlwaysTrue U>
+    bool operator==(const B<U>&)const;
+  };
+
+
+  template <typename U>
+  bool operator==(const B<int>&,const B<U>&) {
+   static_assert(F<int>, "Isn't this less specialized?");
+   return false;
+  }
+
+  bool g(B<int> bx, B<int *> by) { return bx == by; }
+#endif
+}
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 5973d825433463..7f288a084edf81 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -14478,7 +14478,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/2445.html">2445</a></td>
     <td>C++20</td>
     <td>Partial ordering with rewritten candidates</td>
-    <td class="unreleased" align="center">Clang 18</td>
+    <td class="unreleased" align="center">Clang 19</td>
   </tr>
   <tr id="2446">
     <td><a href="https://cplusplus.github.io/CWG/issues/2446.html">2446</a></td>

>From d70ad7da5228383ff761759629b0430e2e4106eb Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Mon, 4 Mar 2024 14:11:14 +0100
Subject: [PATCH 07/12] added tests for rvalref and volatile

---
 clang/test/CXX/drs/dr24xx.cpp | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/clang/test/CXX/drs/dr24xx.cpp b/clang/test/CXX/drs/dr24xx.cpp
index e756f43985517b..4534ed26e56d07 100644
--- a/clang/test/CXX/drs/dr24xx.cpp
+++ b/clang/test/CXX/drs/dr24xx.cpp
@@ -68,6 +68,7 @@ template<A> struct X {};
 X<1> x;
 #endif
 }
+
 namespace dr2445 { // dr2445: 19
 #if __cplusplus >= 202002L
   template <typename> constexpr bool F = false;
@@ -98,5 +99,33 @@ namespace dr2445 { // dr2445: 19
   }
 
   bool g(B<int> bx, B<int *> by) { return bx == by; }
+
+  struct C{
+    template<AlwaysTrue T>
+    int operator+(T){return 0;}
+    template<class T>
+    void operator-(T){}
+  };
+  template<class T>
+  void operator+(C&&,T){}
+  template<AlwaysTrue T>
+  int operator-(C&&,T){return 0;}
+
+  void t(int* iptr){
+    int x1 = C{} + iptr;
+    int x2 = C{} - iptr;
+  }
+
+  struct D{
+    template<AlwaysTrue T>
+    int operator+(T) volatile {return 1;}
+  };
+
+  template<class T>
+  void operator+(volatile D&,T) {}
+
+  int foo(volatile D& d){
+    return d + 1;
+  }
 #endif
 }

>From 9de8a5a861c16a0570b3f91967e906e916113e3e Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Tue, 5 Mar 2024 11:50:37 +0100
Subject: [PATCH 08/12] removed a now unsed parameter

---
 clang/include/clang/Sema/Sema.h          |  3 +--
 clang/lib/Sema/SemaOverload.cpp          |  2 +-
 clang/lib/Sema/SemaTemplateDeduction.cpp | 12 ++++--------
 3 files changed, 6 insertions(+), 11 deletions(-)

diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1a2a3a6bebd95e..3e85f96f468e2b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9463,8 +9463,7 @@ class Sema final {
   FunctionTemplateDecl *getMoreSpecializedTemplate(
       FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
       TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-      unsigned NumCallArguments2, QualType ObjType1 = {},
-      QualType ObjType2 = {}, bool Reversed = false);
+      QualType ObjType1 = {}, QualType ObjType2 = {}, bool Reversed = false);
 
   UnresolvedSetIterator
   getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index 74d714ad308af1..e9dace7ccbf10a 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10535,7 +10535,7 @@ bool clang::isBetterOverloadCandidate(
             Cand2.Function->getPrimaryTemplate(), Loc,
             isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
                                                    : TPOC_Call,
-            Cand1.ExplicitCallArguments, Cand2.ExplicitCallArguments,
+            Cand1.ExplicitCallArguments,
             ObjContext1 ? QualType(ObjContext1->getTypeForDecl(), 0)
                         : QualType{},
             ObjContext2 ? QualType(ObjContext2->getTypeForDecl(), 0)
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 04e46ab69876f6..9f8e7d9d17deff 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5484,9 +5484,6 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
 /// \param NumCallArguments1 The number of arguments in the call to FT1, used
 /// only when \c TPOC is \c TPOC_Call.
 ///
-/// \param NumCallArguments2 The number of arguments in the call to FT2, used
-/// only when \c TPOC is \c TPOC_Call.
-///
 /// \param RawObjType1 The type of the object parameter of FT1 if a member
 /// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function
 /// template from a member function
@@ -5504,8 +5501,7 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
 FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
     TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-    unsigned NumCallArguments2, QualType RawObjType1, QualType RawObjType2,
-    bool Reversed) {
+    QualType RawObjType1, QualType RawObjType2, bool Reversed) {
   SmallVector<QualType, 4> Args1;
   SmallVector<QualType, 4> Args2;
   const FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -5791,8 +5787,8 @@ UnresolvedSetIterator Sema::getMostSpecialized(
     FunctionTemplateDecl *Challenger
       = cast<FunctionDecl>(*I)->getPrimaryTemplate();
     assert(Challenger && "Not a function template specialization?");
-    if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger,
-                                                  Loc, TPOC_Other, 0, 0),
+    if (isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger, Loc,
+                                                  TPOC_Other, 0),
                        Challenger)) {
       Best = I;
       BestTemplate = Challenger;
@@ -5807,7 +5803,7 @@ UnresolvedSetIterator Sema::getMostSpecialized(
       = cast<FunctionDecl>(*I)->getPrimaryTemplate();
     if (I != Best &&
         !isSameTemplate(getMoreSpecializedTemplate(BestTemplate, Challenger,
-                                                   Loc, TPOC_Other, 0, 0),
+                                                   Loc, TPOC_Other, 0),
                         BestTemplate)) {
       Ambiguous = true;
       break;

>From f7dc5a937ea9a399e6bfa1651b2b1eeba00471f8 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Tue, 5 Mar 2024 15:44:30 +0100
Subject: [PATCH 09/12] Made the resolution of the DR retroactive

---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 64 +++++++-----------------
 1 file changed, 17 insertions(+), 47 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 9f8e7d9d17deff..6f46eda20643b5 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5344,14 +5344,6 @@ static QualType GetImplicitObjectParameterType(ASTContext &Context,
   //  if this determination depends recursively upon whether X(M ) is an rvalue
   //  reference type, it is not considered to have rvalue reference type.
   //- Otherwise, X(M ) is “lvalue reference to cv A”.
-  //
-  // C++11 [temp.func.order]p3:
-  //   [...] The new parameter is of type "reference to cv A," where cv are
-  //   the cv-qualifiers of the function template (if any) and A is
-  //   the class of which the function template is a member.
-  //
-  // The standard doesn't say explicitly, but we pick the appropriate kind of
-  // reference type based on [over.match.funcs]p4.
 
   assert(Method && !Method->isExplicitObjectMemberFunction() &&
          "expected a member function with no explicit object parameter");
@@ -5520,43 +5512,22 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     //     used.
     const CXXMethodDecl *Method1 = dyn_cast<CXXMethodDecl>(FD1);
     const CXXMethodDecl *Method2 = dyn_cast<CXXMethodDecl>(FD2);
-
-    if (getLangOpts().CPlusPlus20) {
-      // C++20 [temp.func.order]p3
-      //   [...] Each function template M that is a member function is
-      //   considered to have a new first parameter of type
-      //   X(M), described below, inserted in its function parameter list.
-      //
-      // Note that we interpret "that is a member function" as
-      // "that is a member function with no expicit object argument".
-      // Otherwise the ordering rules for methods with expicit objet arguments
-      // against anything else make no sense.
-      shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
-      shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
-    } else {
-      // C++11 [temp.func.order]p3:
-      //   [...] If only one of the function templates is a non-static
-      //   member, that function template is considered to have a new
-      //   first parameter inserted in its function parameter list.
-      //
-      // Note that we interpret this to mean "if one of the function
-      // templates is a non-static member and the other is a non-member";
-      // otherwise, the ordering rules for static functions against non-static
-      // functions don't make any sense.
-      //
-      // C++98/03 doesn't have this provision but we've extended DR532 to cover
-      // it as wording was broken prior to it.
-      shouldConvert1 =
-          !Method2 && Method1 && Method1->isImplicitObjectMemberFunction();
-      shouldConvert2 =
-          !Method1 && Method2 && Method2->isImplicitObjectMemberFunction();
-    }
+    // C++20 [temp.func.order]p3
+    //   [...] Each function template M that is a member function is
+    //   considered to have a new first parameter of type
+    //   X(M), described below, inserted in its function parameter list.
+    //
+    // Note that we interpret "that is a member function" as
+    // "that is a member function with no expicit object argument".
+    // Otherwise the ordering rules for methods with expicit objet arguments
+    // against anything else make no sense.
+    shouldConvert1 = Method1 && !Method1->isExplicitObjectMemberFunction();
+    shouldConvert2 = Method2 && !Method2->isExplicitObjectMemberFunction();
     if (shouldConvert1) {
       bool isRValRef2 =
-          getLangOpts().CPlusPlus20 &&
-          (shouldConvert2
-               ? Method2->getRefQualifier() == RQ_RValue
-               : Proto2->param_type_begin()[0]->isRValueReferenceType());
+          shouldConvert2
+              ? Method2->getRefQualifier() == RQ_RValue
+              : Proto2->param_type_begin()[0]->isRValueReferenceType();
       // Compare 'this' from Method1 against first parameter from Method2.
       ObjType1 = GetImplicitObjectParameterType(this->Context, Method1,
                                                 RawObjType1, isRValRef2);
@@ -5564,10 +5535,9 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     }
     if (shouldConvert2) {
       bool isRValRef1 =
-          getLangOpts().CPlusPlus20 &&
-          (shouldConvert1
-               ? Method1->getRefQualifier() == RQ_RValue
-               : Proto1->param_type_begin()[0]->isRValueReferenceType());
+          shouldConvert1
+              ? Method1->getRefQualifier() == RQ_RValue
+              : Proto1->param_type_begin()[0]->isRValueReferenceType();
       // Compare 'this' from Method2 against first parameter from Method1.
       ObjType2 = GetImplicitObjectParameterType(this->Context, Method2,
                                                 RawObjType2, isRValRef1);

>From f9ae5112909e3b0f073b2bd7fefa7915d05d6de7 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Tue, 5 Mar 2024 17:47:45 +0100
Subject: [PATCH 10/12] Changed the resizing from if (x<size) resize(x) to
 resize(min(x,size))

---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 6f46eda20643b5..c7ff0eaec6b89a 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5543,7 +5543,7 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
                                                 RawObjType2, isRValRef1);
       Args2.push_back(ObjType2);
     }
-    unsigned NumComparedArguments = NumCallArguments1 + shouldConvert1;
+    size_t NumComparedArguments = NumCallArguments1 + shouldConvert1;
 
     Args1.insert(Args1.end(), Proto1->param_type_begin(),
                  Proto1->param_type_end());
@@ -5553,10 +5553,9 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     // C++ [temp.func.order]p5:
     //   The presence of unused ellipsis and default arguments has no effect on
     //   the partial ordering of function templates.
-    if (Args1.size() > NumComparedArguments)
-      Args1.resize(NumComparedArguments);
-    if (Args2.size() > NumComparedArguments)
-      Args2.resize(NumComparedArguments);
+    Args1.resize(std::min(Args1.size(), NumComparedArguments));
+    Args2.resize(std::min(Args2.size(), NumComparedArguments));
+
     if (Reversed)
       std::reverse(Args2.begin(), Args2.end());
   }

>From 95ab9b3824e9d82180c5f3fde9f7c50d2ec06ba5 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Wed, 6 Mar 2024 11:17:57 +0100
Subject: [PATCH 11/12] Renamed variables as suggested + typo correction in
 relase notes

---
 clang/docs/ReleaseNotes.rst              |  2 +-
 clang/include/clang/Sema/Sema.h          |  2 +-
 clang/lib/Sema/SemaOverload.cpp          |  8 +++---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 34 ++++++++++++------------
 4 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 6a1b93db9f6486..2b208b3a691e7f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -290,7 +290,7 @@ Bug Fixes to C++ Support
   lookup searches the bases of an incomplete class.
 - Fix a crash when an unresolved overload set is encountered on the RHS of a ``.*`` operator.
   (`#53815 <https://github.com/llvm/llvm-project/issues/53815>`_)
-- Fix a bug where overload resolution falsly reported an ambiguity when it was compearing
+- Fix a bug where overload resolution falsely reported an ambiguity when it was comparing
   a member-function against a non member function or a member-function with an
   explicit object parameter against a member function with no explicit object parameter
   when one of the function had more specialized templates.
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 3e85f96f468e2b..27f68bd07df43b 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -9463,7 +9463,7 @@ class Sema final {
   FunctionTemplateDecl *getMoreSpecializedTemplate(
       FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
       TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-      QualType ObjType1 = {}, QualType ObjType2 = {}, bool Reversed = false);
+      QualType RawObj1Ty = {}, QualType RawObj2Ty = {}, bool Reversed = false);
 
   UnresolvedSetIterator
   getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp
index e9dace7ccbf10a..ac73d6871b3aec 100644
--- a/clang/lib/Sema/SemaOverload.cpp
+++ b/clang/lib/Sema/SemaOverload.cpp
@@ -10526,9 +10526,9 @@ bool clang::isBetterOverloadCandidate(
   //      according to the partial ordering rules described in 14.5.5.2, or,
   //      if not that,
   if (Cand1IsSpecialization && Cand2IsSpecialization) {
-    const auto *ObjContext1 =
+    const auto *Obj1Context =
         dyn_cast<CXXRecordDecl>(Cand1.FoundDecl->getDeclContext());
-    const auto *ObjContext2 =
+    const auto *Obj2Context =
         dyn_cast<CXXRecordDecl>(Cand2.FoundDecl->getDeclContext());
     if (FunctionTemplateDecl *BetterTemplate = S.getMoreSpecializedTemplate(
             Cand1.Function->getPrimaryTemplate(),
@@ -10536,9 +10536,9 @@ bool clang::isBetterOverloadCandidate(
             isa<CXXConversionDecl>(Cand1.Function) ? TPOC_Conversion
                                                    : TPOC_Call,
             Cand1.ExplicitCallArguments,
-            ObjContext1 ? QualType(ObjContext1->getTypeForDecl(), 0)
+            Obj1Context ? QualType(Obj1Context->getTypeForDecl(), 0)
                         : QualType{},
-            ObjContext2 ? QualType(ObjContext2->getTypeForDecl(), 0)
+            Obj2Context ? QualType(Obj2Context->getTypeForDecl(), 0)
                         : QualType{},
             Cand1.isReversed() ^ Cand2.isReversed())) {
       return BetterTemplate == Cand1.Function->getPrimaryTemplate();
diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index c7ff0eaec6b89a..096ba091514d5e 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5362,8 +5362,8 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
                                      const FunctionTemplateDecl *FT2,
                                      TemplatePartialOrderingContext TPOC,
                                      bool Reversed,
-                                     const SmallVector<QualType, 4> &Args1,
-                                     const SmallVector<QualType, 4> &Args2) {
+                                     const SmallVector<QualType> &Args1,
+                                     const SmallVector<QualType> &Args2) {
   assert(!Reversed || TPOC == TPOC_Call);
 
   FunctionDecl *FD1 = FT1->getTemplatedDecl();
@@ -5476,11 +5476,11 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
 /// \param NumCallArguments1 The number of arguments in the call to FT1, used
 /// only when \c TPOC is \c TPOC_Call.
 ///
-/// \param RawObjType1 The type of the object parameter of FT1 if a member
+/// \param RawObj1Ty The type of the object parameter of FT1 if a member
 /// function only used if \c TPOC is \c TPOC_Call and FT1 is a Function
 /// template from a member function
 ///
-/// \param RawObjType2 The type of the object parameter of FT2 if a member
+/// \param RawObj2Ty The type of the object parameter of FT2 if a member
 /// function only used if \c TPOC is \c TPOC_Call and FT2 is a Function
 /// template from a member function
 ///
@@ -5493,15 +5493,15 @@ static bool isAtLeastAsSpecializedAs(Sema &S, SourceLocation Loc,
 FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
     FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
     TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
-    QualType RawObjType1, QualType RawObjType2, bool Reversed) {
-  SmallVector<QualType, 4> Args1;
-  SmallVector<QualType, 4> Args2;
+    QualType RawObj1Ty, QualType RawObj2Ty, bool Reversed) {
+  SmallVector<QualType> Args1;
+  SmallVector<QualType> Args2;
   const FunctionDecl *FD1 = FT1->getTemplatedDecl();
   const FunctionDecl *FD2 = FT2->getTemplatedDecl();
   bool shouldConvert1 = false;
   bool shouldConvert2 = false;
-  QualType ObjType1;
-  QualType ObjType2;
+  QualType Obj1Ty;
+  QualType Obj2Ty;
   if (TPOC == TPOC_Call) {
     const FunctionProtoType *Proto1 =
         FD1->getType()->getAs<FunctionProtoType>();
@@ -5529,9 +5529,9 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
               ? Method2->getRefQualifier() == RQ_RValue
               : Proto2->param_type_begin()[0]->isRValueReferenceType();
       // Compare 'this' from Method1 against first parameter from Method2.
-      ObjType1 = GetImplicitObjectParameterType(this->Context, Method1,
-                                                RawObjType1, isRValRef2);
-      Args1.push_back(ObjType1);
+      Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1,
+                                                RawObj1Ty, isRValRef2);
+      Args1.push_back(Obj1Ty);
     }
     if (shouldConvert2) {
       bool isRValRef1 =
@@ -5539,9 +5539,9 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
               ? Method1->getRefQualifier() == RQ_RValue
               : Proto1->param_type_begin()[0]->isRValueReferenceType();
       // Compare 'this' from Method2 against first parameter from Method1.
-      ObjType2 = GetImplicitObjectParameterType(this->Context, Method2,
-                                                RawObjType2, isRValRef1);
-      Args2.push_back(ObjType2);
+      Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2,
+                                                RawObj2Ty, isRValRef1);
+      Args2.push_back(Obj2Ty);
     }
     size_t NumComparedArguments = NumCallArguments1 + shouldConvert1;
 
@@ -5580,14 +5580,14 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
   SmallVector<QualType> param1;
   param1.reserve(FD1->param_size() + shouldConvert1);
   if (shouldConvert1)
-    param1.push_back(ObjType1);
+    param1.push_back(Obj1Ty);
   for (const auto &x : FD1->parameters())
     param1.push_back(x->getType());
 
   SmallVector<QualType> param2;
   param2.reserve(FD2->param_size() + shouldConvert2);
   if (shouldConvert2)
-    param2.push_back(ObjType2);
+    param2.push_back(Obj2Ty);
   for (const auto &x : FD2->parameters())
     param2.push_back(x->getType());
 

>From 59fe48af87f4e3f6f71c6fabbe506047c3352137 Mon Sep 17 00:00:00 2001
From: Botond Istvan Horvath <horvath.botond.istvan at gmail.com>
Date: Wed, 6 Mar 2024 11:29:11 +0100
Subject: [PATCH 12/12] formatting

---
 clang/lib/Sema/SemaTemplateDeduction.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp
index 096ba091514d5e..f14861640f549a 100644
--- a/clang/lib/Sema/SemaTemplateDeduction.cpp
+++ b/clang/lib/Sema/SemaTemplateDeduction.cpp
@@ -5529,8 +5529,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
               ? Method2->getRefQualifier() == RQ_RValue
               : Proto2->param_type_begin()[0]->isRValueReferenceType();
       // Compare 'this' from Method1 against first parameter from Method2.
-      Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1,
-                                                RawObj1Ty, isRValRef2);
+      Obj1Ty = GetImplicitObjectParameterType(this->Context, Method1, RawObj1Ty,
+                                              isRValRef2);
       Args1.push_back(Obj1Ty);
     }
     if (shouldConvert2) {
@@ -5539,8 +5539,8 @@ FunctionTemplateDecl *Sema::getMoreSpecializedTemplate(
               ? Method1->getRefQualifier() == RQ_RValue
               : Proto1->param_type_begin()[0]->isRValueReferenceType();
       // Compare 'this' from Method2 against first parameter from Method1.
-      Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2,
-                                                RawObj2Ty, isRValRef1);
+      Obj2Ty = GetImplicitObjectParameterType(this->Context, Method2, RawObj2Ty,
+                                              isRValRef1);
       Args2.push_back(Obj2Ty);
     }
     size_t NumComparedArguments = NumCallArguments1 + shouldConvert1;



More information about the cfe-commits mailing list