Commit fcea7fbd authored by Richard Smith's avatar Richard Smith
Browse files

CWG2445: For function template partial ordering, take reversal of

function arguments into account when forming P/A pairs.
parent 7cddd15e
Loading
Loading
Loading
Loading
+2 −0
Original line number Diff line number Diff line
@@ -850,6 +850,8 @@ class Sema;
      return static_cast<OverloadCandidateRewriteKind>(RewriteKind);
    }

    bool isReversed() const { return getRewriteKind() & CRK_Reversed; }

    /// hasAmbiguousConversion - Returns whether this overload
    /// candidate requires an ambiguous conversion or not.
    bool hasAmbiguousConversion() const {
+4 −6
Original line number Diff line number Diff line
@@ -7964,12 +7964,10 @@ public:
                                        SourceLocation ReturnLoc,
                                        Expr *&RetExpr, AutoType *AT);
  FunctionTemplateDecl *getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                                   FunctionTemplateDecl *FT2,
                                                   SourceLocation Loc,
                                           TemplatePartialOrderingContext TPOC,
                                                   unsigned NumCallArguments1,
                                                   unsigned NumCallArguments2);
  FunctionTemplateDecl *getMoreSpecializedTemplate(
      FunctionTemplateDecl *FT1, FunctionTemplateDecl *FT2, SourceLocation Loc,
      TemplatePartialOrderingContext TPOC, unsigned NumCallArguments1,
      unsigned NumCallArguments2, bool Reversed = false);
  UnresolvedSetIterator
  getMostSpecialized(UnresolvedSetIterator SBegin, UnresolvedSetIterator SEnd,
                     TemplateSpecCandidateSet &FailedCandidates,
+12 −13
Original line number Diff line number Diff line
@@ -8392,7 +8392,7 @@ public:
          // We interpret "same parameter-type-list" as applying to the
          // "synthesized candidate, with the order of the two parameters
          // reversed", not to the original function.
          bool Reversed = C->RewriteKind & CRK_Reversed;
          bool Reversed = C->isReversed();
          QualType FirstParamType = C->Function->getParamDecl(Reversed ? 1 : 0)
                                        ->getType()
                                        .getUnqualifiedType();
@@ -9478,7 +9478,7 @@ bool clang::isBetterOverloadCandidate(
    case ImplicitConversionSequence::Worse:
      if (Cand1.Function && Cand1.Function == Cand2.Function &&
          (Cand2.RewriteKind & CRK_Reversed) != 0) {
          Cand2.isReversed()) {
        // Work around large-scale breakage caused by considering reversed
        // forms of operator== in C++20:
        //
@@ -9566,14 +9566,13 @@ bool clang::isBetterOverloadCandidate(
  //      according to the partial ordering rules described in 14.5.5.2, or,
  //      if not that,
  if (Cand1IsSpecialization && Cand2IsSpecialization) {
    if (FunctionTemplateDecl *BetterTemplate
          = S.getMoreSpecializedTemplate(Cand1.Function->getPrimaryTemplate(),
                                         Cand2.Function->getPrimaryTemplate(),
                                         Loc,
    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.ExplicitCallArguments, Cand2.ExplicitCallArguments,
            Cand1.isReversed() ^ Cand2.isReversed()))
      return BetterTemplate == Cand1.Function->getPrimaryTemplate();
  }
@@ -11298,7 +11297,7 @@ CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
  unsigned ConvIdx = 0;
  unsigned ArgIdx = 0;
  ArrayRef<QualType> ParamTypes;
  bool Reversed = Cand->RewriteKind & CRK_Reversed;
  bool Reversed = Cand->isReversed();
  if (Cand->IsSurrogate) {
    QualType ConvType
@@ -13232,7 +13231,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
      // We found a built-in operator or an overloaded operator.
      FunctionDecl *FnDecl = Best->Function;
      bool IsReversed = (Best->RewriteKind & CRK_Reversed);
      bool IsReversed = Best->isReversed();
      if (IsReversed)
        std::swap(Args[0], Args[1]);
@@ -13264,7 +13263,7 @@ ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
          // formally ambiguous, and allowing it is an extension.
          for (OverloadCandidate &Cand : CandidateSet) {
            if (Cand.Viable && Cand.Function == FnDecl &&
                Cand.RewriteKind & CRK_Reversed) {
                Cand.isReversed()) {
              for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) {
                if (CompareImplicitConversionSequences(
                        *this, OpLoc, Cand.Conversions[ArgIdx],
+20 −4
Original line number Diff line number Diff line
@@ -4847,7 +4847,10 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
                                     FunctionTemplateDecl *FT1,
                                     FunctionTemplateDecl *FT2,
                                     TemplatePartialOrderingContext TPOC,
                                     unsigned NumCallArguments1) {
                                     unsigned NumCallArguments1,
                                     bool Reversed) {
  assert(!Reversed || TPOC == TPOC_Call);

  FunctionDecl *FD1 = FT1->getTemplatedDecl();
  FunctionDecl *FD2 = FT2->getTemplatedDecl();
  const FunctionProtoType *Proto1 = FD1->getType()->getAs<FunctionProtoType>();
@@ -4896,6 +4899,12 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
    } else if (!Method1 && Method2 && !Method2->isStatic()) {
      // Compare 'this' from Method2 against first parameter from Method1.
      AddImplicitObjectParameterType(S.Context, Method2, Args2);
    } else if (Method1 && Method2 && Reversed) {
      // 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(),
@@ -4910,6 +4919,8 @@ static bool isAtLeastAsSpecializedAs(Sema &S,
      Args1.resize(NumComparedArguments);
    if (Args2.size() > NumComparedArguments)
      Args2.resize(NumComparedArguments);
    if (Reversed)
      std::reverse(Args2.begin(), Args2.end());
    if (DeduceTemplateArguments(S, TemplateParams, Args2.data(), Args2.size(),
                                Args1.data(), Args1.size(), Info, Deduced,
                                TDF_None, /*PartialOrdering=*/true))
@@ -5028,6 +5039,10 @@ static bool isVariadicFunctionTemplate(FunctionTemplateDecl *FunTmpl) {
/// \param NumCallArguments2 The number of arguments in the call to FT2, used
/// only when \c TPOC is \c TPOC_Call.
///
/// \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.
///
/// \returns the more specialized function template. If neither
/// template is more specialized, returns NULL.
FunctionTemplateDecl *
@@ -5036,7 +5051,8 @@ Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
                                 SourceLocation Loc,
                                 TemplatePartialOrderingContext TPOC,
                                 unsigned NumCallArguments1,
                                 unsigned NumCallArguments2) {
                                 unsigned NumCallArguments2,
                                 bool Reversed) {

  auto JudgeByConstraints = [&] () -> FunctionTemplateDecl * {
    llvm::SmallVector<const Expr *, 3> AC1, AC2;
@@ -5053,9 +5069,9 @@ Sema::getMoreSpecializedTemplate(FunctionTemplateDecl *FT1,
  };

  bool Better1 = isAtLeastAsSpecializedAs(*this, Loc, FT1, FT2, TPOC,
                                          NumCallArguments1);
                                          NumCallArguments1, Reversed);
  bool Better2 = isAtLeastAsSpecializedAs(*this, Loc, FT2, FT1, TPOC,
                                          NumCallArguments2);
                                          NumCallArguments2, Reversed);

  if (Better1 != Better2) // We have a clear winner
    return Better1 ? FT1 : FT2;
+10 −1
Original line number Diff line number Diff line
// RUN: %clang_cc1 -fsyntax-only -verify %s
// RUN: %clang_cc1 -fsyntax-only -verify -std=c++2a %s

// Make sure we accept this
template<class X>struct A{typedef X Y;};
@@ -15,3 +15,12 @@ template<class X>bool operator==(B<X>*,typename B<X>::Y); // \
// expected-note{{candidate template ignored: substitution failure [with X = int]}}
int a(B<int> x) { return operator==(&x,1); } // expected-error{{no matching function for call to 'operator=='}} \
// expected-note{{in instantiation of function template specialization}}

// Ensure we take parameter list reversal into account in partial oredring.
namespace CompareOrdering {
  template<typename T> struct A {};
  template<typename T> int operator<=>(A<T>, int) = delete;
  template<typename T> int operator<=>(int, A<T*>);
  // OK, selects the more-specialized reversed function.
  bool b = A<int*>() < 0;
}