r375306 - [c++20] Add rewriting from comparison operators to <=> / ==.

Peter Collingbourne via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 13 15:12:14 PST 2020


On Fri, Oct 18, 2019 at 5:02 PM Richard Smith via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

> Author: rsmith
> Date: Fri Oct 18 17:04:43 2019
> New Revision: 375306
>
> URL: http://llvm.org/viewvc/llvm-project?rev=375306&view=rev
> Log:
> [c++20] Add rewriting from comparison operators to <=> / ==.
>
> This adds support for rewriting <, >, <=, and >= to a normal or reversed
> call to operator<=>, for rewriting != to a normal or reversed call to
> operator==, and for rewriting <=> and == to reversed forms of those same
> operators.
>
> Note that this is a breaking change for various C++17 code patterns,
> including some in use in LLVM. The most common patterns (where an
> operator== becomes ambiguous with a reversed form of itself) are still
> accepted under this patch, as an extension (with a warning). I'm hopeful
> that we can get the language rules fixed before C++20 ships, and the
> extension warning is aimed primarily at providing data to inform that
> decision.
>

Hi Richard, it looks like this change caused us to start rejecting the
following input:

struct S {
  bool operator==(int rhs) const;
  bool operator!=(int rhs) const;
  operator int() const;
};

void f(S a, S b) {
  a != b;
}

with:

$ clang -std=gnu++2a -fsyntax-only /tmp/test.cpp
/tmp/test.cpp:8:5: error: use of overloaded operator '!=' is ambiguous
(with operand types 'S' and 'S')
  a != b;
  ~ ^  ~
/tmp/test.cpp:2:8: note: candidate function (with reversed parameter order)
  bool operator==(int rhs) const;
       ^
/tmp/test.cpp:3:8: note: candidate function
  bool operator!=(int rhs) const;
       ^
/tmp/test.cpp:2:8: note: candidate function
  bool operator==(int rhs) const;
       ^

It looks like this was intentional, but I wanted to make sure. The code
pattern isn't very common (I see only one instance in Android), but I
figured that you might want to be aware of it in case it would inform any
changes to the language.

Peter


> Added:
>
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
>
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
>
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
>     cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp
> Modified:
>     cfe/trunk/include/clang/AST/ExprCXX.h
>     cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
>     cfe/trunk/include/clang/Basic/OperatorKinds.h
>     cfe/trunk/include/clang/Sema/Overload.h
>     cfe/trunk/include/clang/Sema/Sema.h
>     cfe/trunk/lib/AST/ExprCXX.cpp
>     cfe/trunk/lib/Frontend/FrontendActions.cpp
>     cfe/trunk/lib/Sema/SemaExpr.cpp
>     cfe/trunk/lib/Sema/SemaOverload.cpp
>     cfe/trunk/lib/Sema/SemaTemplate.cpp
>     cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
>     cfe/trunk/lib/Sema/TreeTransform.h
>     cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
>     cfe/trunk/test/PCH/cxx2a-compare.cpp
>     cfe/trunk/test/SemaCXX/compare-cxx2a.cpp
>     cfe/trunk/test/SemaCXX/self-comparison.cpp
>     cfe/trunk/www/cxx_status.html
>
> Modified: cfe/trunk/include/clang/AST/ExprCXX.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ExprCXX.h?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/AST/ExprCXX.h (original)
> +++ cfe/trunk/include/clang/AST/ExprCXX.h Fri Oct 18 17:04:43 2019
> @@ -220,6 +220,40 @@ public:
>    }
>  };
>
> +/// Represents a call to a CUDA kernel function.
> +class CUDAKernelCallExpr final : public CallExpr {
> +  friend class ASTStmtReader;
> +
> +  enum { CONFIG, END_PREARG };
> +
> +  // CUDAKernelCallExpr has some trailing objects belonging
> +  // to CallExpr. See CallExpr for the details.
> +
> +  CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,
> +                     QualType Ty, ExprValueKind VK, SourceLocation RP,
> +                     unsigned MinNumArgs);
> +
> +  CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);
> +
> +public:
> +  static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,
> +                                    CallExpr *Config, ArrayRef<Expr *>
> Args,
> +                                    QualType Ty, ExprValueKind VK,
> +                                    SourceLocation RP, unsigned
> MinNumArgs = 0);
> +
> +  static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,
> +                                         unsigned NumArgs, EmptyShell
> Empty);
> +
> +  const CallExpr *getConfig() const {
> +    return cast_or_null<CallExpr>(getPreArg(CONFIG));
> +  }
> +  CallExpr *getConfig() { return
> cast_or_null<CallExpr>(getPreArg(CONFIG)); }
> +
> +  static bool classof(const Stmt *T) {
> +    return T->getStmtClass() == CUDAKernelCallExprClass;
> +  }
> +};
> +
>  /// A rewritten comparison expression that was originally written using
>  /// operator syntax.
>  ///
> @@ -310,40 +344,6 @@ public:
>    }
>  };
>
> -/// Represents a call to a CUDA kernel function.
> -class CUDAKernelCallExpr final : public CallExpr {
> -  friend class ASTStmtReader;
> -
> -  enum { CONFIG, END_PREARG };
> -
> -  // CUDAKernelCallExpr has some trailing objects belonging
> -  // to CallExpr. See CallExpr for the details.
> -
> -  CUDAKernelCallExpr(Expr *Fn, CallExpr *Config, ArrayRef<Expr *> Args,
> -                     QualType Ty, ExprValueKind VK, SourceLocation RP,
> -                     unsigned MinNumArgs);
> -
> -  CUDAKernelCallExpr(unsigned NumArgs, EmptyShell Empty);
> -
> -public:
> -  static CUDAKernelCallExpr *Create(const ASTContext &Ctx, Expr *Fn,
> -                                    CallExpr *Config, ArrayRef<Expr *>
> Args,
> -                                    QualType Ty, ExprValueKind VK,
> -                                    SourceLocation RP, unsigned
> MinNumArgs = 0);
> -
> -  static CUDAKernelCallExpr *CreateEmpty(const ASTContext &Ctx,
> -                                         unsigned NumArgs, EmptyShell
> Empty);
> -
> -  const CallExpr *getConfig() const {
> -    return cast_or_null<CallExpr>(getPreArg(CONFIG));
> -  }
> -  CallExpr *getConfig() { return
> cast_or_null<CallExpr>(getPreArg(CONFIG)); }
> -
> -  static bool classof(const Stmt *T) {
> -    return T->getStmtClass() == CUDAKernelCallExprClass;
> -  }
> -};
> -
>  /// Abstract class common to all of the C++ "named"/"keyword" casts.
>  ///
>  /// This abstract class is inherited by all of the classes
>
> Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
> +++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Fri Oct 18
> 17:04:43 2019
> @@ -3769,7 +3769,8 @@ def note_ovl_too_many_candidates : Note<
>      "pass -fshow-overloads=all to show them">;
>
>  def select_ovl_candidate_kind : TextSubstitution<
> -  "%select{function|function|constructor|"
> +  "%select{function|function|function (with reversed parameter order)|"
> +    "constructor|"
>      "constructor (the implicit default constructor)|"
>      "constructor (the implicit copy constructor)|"
>      "constructor (the implicit move constructor)|"
> @@ -3990,6 +3991,13 @@ def err_ovl_ambiguous_oper_unary : Error
>    "use of overloaded operator '%0' is ambiguous (operand type %1)">;
>  def err_ovl_ambiguous_oper_binary : Error<
>    "use of overloaded operator '%0' is ambiguous (with operand types %1
> and %2)">;
> +def ext_ovl_ambiguous_oper_binary_reversed : ExtWarn<
> +  "ISO C++20 considers use of overloaded operator '%0' (with operand
> types %1 "
> +  "and %2) to be ambiguous despite there being a unique best viable
> function">,
> +  InGroup<DiagGroup<"ambiguous-reversed-operator">>, SFINAEFailure;
> +def note_ovl_ambiguous_oper_binary_reversed_candidate : Note<
> +  "ambiguity is between a regular call to this operator and a call with
> the "
> +  "argument order reversed">;
>  def err_ovl_no_viable_oper : Error<"no viable overloaded '%0'">;
>  def note_assign_lhs_incomplete : Note<"type %0 is incomplete">;
>  def err_ovl_deleted_oper : Error<
> @@ -3997,6 +4005,9 @@ def err_ovl_deleted_oper : Error<
>  def err_ovl_deleted_special_oper : Error<
>    "object of type %0 cannot be %select{constructed|copied|moved|assigned|"
>    "assigned|destroyed}1 because its %sub{select_special_member_kind}1 is
> implicitly deleted">;
> +def err_ovl_rewrite_equalequal_not_bool : Error<
> +  "return type %0 of selected 'operator==' function for rewritten "
> +  "'%1' comparison is not 'bool'">;
>  def err_ovl_no_viable_subscript :
>      Error<"no viable overloaded operator[] for type %0">;
>  def err_ovl_no_oper :
> @@ -9961,6 +9972,8 @@ def err_std_compare_type_not_supported :
>     "member '%2' is missing|"
>     "the type is not trivially copyable|"
>     "the type does not have the expected form}1">;
> +def note_rewriting_operator_as_spaceship : Note<
> +  "while rewriting comparison as call to 'operator<=>' declared here">;
>
>  // Memory Tagging Extensions (MTE) diagnostics
>  def err_memtag_arg_null_or_pointer : Error<
>
> Modified: cfe/trunk/include/clang/Basic/OperatorKinds.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/OperatorKinds.h?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Basic/OperatorKinds.h (original)
> +++ cfe/trunk/include/clang/Basic/OperatorKinds.h Fri Oct 18 17:04:43 2019
> @@ -30,6 +30,25 @@ enum OverloadedOperatorKind : int {
>  /// the preceding "operator" keyword.
>  const char *getOperatorSpelling(OverloadedOperatorKind Operator);
>
> +/// Get the other overloaded operator that the given operator can be
> rewritten
> +/// into, if any such operator exists.
> +inline OverloadedOperatorKind
> +getRewrittenOverloadedOperator(OverloadedOperatorKind Kind) {
> +  switch (Kind) {
> +  case OO_Less:
> +  case OO_LessEqual:
> +  case OO_Greater:
> +  case OO_GreaterEqual:
> +    return OO_Spaceship;
> +
> +  case OO_ExclaimEqual:
> +    return OO_EqualEqual;
> +
> +  default:
> +    return OO_None;
> +  }
> +}
> +
>  } // end namespace clang
>
>  #endif
>
> Modified: cfe/trunk/include/clang/Sema/Overload.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Overload.h?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/Overload.h (original)
> +++ cfe/trunk/include/clang/Sema/Overload.h Fri Oct 18 17:04:43 2019
> @@ -71,6 +71,30 @@ class Sema;
>      OCD_ViableCandidates
>    };
>
> +  /// The parameter ordering that will be used for the candidate. This is
> +  /// used to represent C++20 binary operator rewrites that reverse the
> order
> +  /// of the arguments. If the parameter ordering is Reversed, the Args
> list is
> +  /// reversed (but obviously the ParamDecls for the function are not).
> +  ///
> +  /// After forming an OverloadCandidate with reversed parameters, the
> list
> +  /// of conversions will (as always) be indexed by argument, so will be
> +  /// in reverse parameter order.
> +  enum class OverloadCandidateParamOrder : char { Normal, Reversed };
> +
> +  /// The kinds of rewrite we perform on overload candidates. Note that
> the
> +  /// values here are chosen to serve as both bitflags and as a rank
> (lower
> +  /// values are preferred by overload resolution).
> +  enum OverloadCandidateRewriteKind : unsigned {
> +    /// Candidate is not a rewritten candidate.
> +    CRK_None = 0x0,
> +
> +    /// Candidate is a rewritten candidate with a different operator name.
> +    CRK_DifferentOperator = 0x1,
> +
> +    /// Candidate is a rewritten candidate with a reversed order of
> parameters.
> +    CRK_Reversed = 0x2,
> +  };
> +
>    /// ImplicitConversionKind - The kind of implicit conversion used to
>    /// convert an argument to a parameter's type. The enumerator values
>    /// match with the table titled 'Conversions' in [over.ics.scs] and are
> listed
> @@ -757,7 +781,8 @@ class Sema;
>      CXXConversionDecl *Surrogate;
>
>      /// The conversion sequences used to convert the function arguments
> -    /// to the function parameters.
> +    /// to the function parameters. Note that these are indexed by
> argument,
> +    /// so may not match the parameter order of Function.
>      ConversionSequenceList Conversions;
>
>      /// The FixIt hints which can be used to fix the Bad candidate.
> @@ -783,6 +808,9 @@ class Sema;
>      /// True if the candidate was found using ADL.
>      CallExpr::ADLCallKind IsADLCandidate : 1;
>
> +    /// Whether this is a rewritten candidate, and if so, of what kind?
> +    OverloadCandidateRewriteKind RewriteKind : 2;
> +
>      /// FailureKind - The reason why this candidate is not viable.
>      /// Actually an OverloadFailureKind.
>      unsigned char FailureKind;
> @@ -838,7 +866,8 @@ class Sema;
>
>    private:
>      friend class OverloadCandidateSet;
> -    OverloadCandidate() : IsADLCandidate(CallExpr::NotADL) {}
> +    OverloadCandidate()
> +        : IsADLCandidate(CallExpr::NotADL), RewriteKind(CRK_None) {}
>    };
>
>    /// OverloadCandidateSet - A set of overload candidates, used in C++
> @@ -867,9 +896,54 @@ class Sema;
>        CSK_InitByConstructor,
>      };
>
> +    /// Information about operator rewrites to consider when adding
> operator
> +    /// functions to a candidate set.
> +    struct OperatorRewriteInfo {
> +      OperatorRewriteInfo()
> +          : OriginalOperator(OO_None), AllowRewrittenCandidates(false) {}
> +      OperatorRewriteInfo(OverloadedOperatorKind Op, bool AllowRewritten)
> +          : OriginalOperator(Op),
> AllowRewrittenCandidates(AllowRewritten) {}
> +
> +      /// The original operator as written in the source.
> +      OverloadedOperatorKind OriginalOperator;
> +      /// Whether we should include rewritten candidates in the overload
> set.
> +      bool AllowRewrittenCandidates;
> +
> +      /// Would use of this function result in a rewrite using a different
> +      /// operator?
> +      bool isRewrittenOperator(const FunctionDecl *FD) {
> +        return OriginalOperator &&
> +               FD->getDeclName().getCXXOverloadedOperator() !=
> OriginalOperator;
> +      }
> +
> +      bool isAcceptableCandidate(const FunctionDecl *FD) {
> +        return AllowRewrittenCandidates || !isRewrittenOperator(FD);
> +      }
> +
> +      /// Determine the kind of rewrite that should be performed for this
> +      /// candidate.
> +      OverloadCandidateRewriteKind
> +      getRewriteKind(const FunctionDecl *FD, OverloadCandidateParamOrder
> PO) {
> +        OverloadCandidateRewriteKind CRK = CRK_None;
> +        if (isRewrittenOperator(FD))
> +          CRK = OverloadCandidateRewriteKind(CRK | CRK_DifferentOperator);
> +        if (PO == OverloadCandidateParamOrder::Reversed)
> +          CRK = OverloadCandidateRewriteKind(CRK | CRK_Reversed);
> +        return CRK;
> +      }
> +
> +      /// Determine whether we should consider looking for and adding
> reversed
> +      /// candidates for operator Op.
> +      bool shouldAddReversed(OverloadedOperatorKind Op);
> +
> +      /// Determine whether we should add a rewritten candidate for \p FD
> with
> +      /// reversed parameter order.
> +      bool shouldAddReversed(ASTContext &Ctx, const FunctionDecl *FD);
> +    };
> +
>    private:
>      SmallVector<OverloadCandidate, 16> Candidates;
> -    llvm::SmallPtrSet<Decl *, 16> Functions;
> +    llvm::SmallPtrSet<uintptr_t, 16> Functions;
>
>      // Allocator for ConversionSequenceLists. We store the first few of
> these
>      // inline to avoid allocation for small sets.
> @@ -877,6 +951,7 @@ class Sema;
>
>      SourceLocation Loc;
>      CandidateSetKind Kind;
> +    OperatorRewriteInfo RewriteInfo;
>
>      constexpr static unsigned NumInlineBytes =
>          24 * sizeof(ImplicitConversionSequence);
> @@ -915,19 +990,24 @@ class Sema;
>      void destroyCandidates();
>
>    public:
> -    OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
> -        : Loc(Loc), Kind(CSK) {}
> +    OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK,
> +                         OperatorRewriteInfo RewriteInfo = {})
> +        : Loc(Loc), Kind(CSK), RewriteInfo(RewriteInfo) {}
>      OverloadCandidateSet(const OverloadCandidateSet &) = delete;
>      OverloadCandidateSet &operator=(const OverloadCandidateSet &) =
> delete;
>      ~OverloadCandidateSet() { destroyCandidates(); }
>
>      SourceLocation getLocation() const { return Loc; }
>      CandidateSetKind getKind() const { return Kind; }
> +    OperatorRewriteInfo getRewriteInfo() const { return RewriteInfo; }
>
>      /// Determine when this overload candidate will be new to the
>      /// overload set.
> -    bool isNewCandidate(Decl *F) {
> -      return Functions.insert(F->getCanonicalDecl()).second;
> +    bool isNewCandidate(Decl *F, OverloadCandidateParamOrder PO =
> +                                     OverloadCandidateParamOrder::Normal)
> {
> +      uintptr_t Key = reinterpret_cast<uintptr_t>(F->getCanonicalDecl());
> +      Key |= static_cast<uintptr_t>(PO);
> +      return Functions.insert(Key).second;
>      }
>
>      /// Clear out all of the candidates.
>
> Modified: cfe/trunk/include/clang/Sema/Sema.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/include/clang/Sema/Sema.h (original)
> +++ cfe/trunk/include/clang/Sema/Sema.h Fri Oct 18 17:04:43 2019
> @@ -159,6 +159,8 @@ namespace clang {
>    class OMPClause;
>    struct OMPVarListLocTy;
>    struct OverloadCandidate;
> +  enum class OverloadCandidateParamOrder : char;
> +  enum OverloadCandidateRewriteKind : unsigned;
>    class OverloadCandidateSet;
>    class OverloadExpr;
>    class ParenListExpr;
> @@ -3019,7 +3021,8 @@ public:
>                              bool AllowExplicit = true,
>                              bool AllowExplicitConversion = false,
>                              ADLCallKind IsADLCandidate =
> ADLCallKind::NotADL,
> -                            ConversionSequenceList EarlyConversions =
> None);
> +                            ConversionSequenceList EarlyConversions =
> None,
> +                            OverloadCandidateParamOrder PO = {});
>    void AddFunctionCandidates(const UnresolvedSetImpl &Functions,
>                        ArrayRef<Expr *> Args,
>                        OverloadCandidateSet &CandidateSet,
> @@ -3032,7 +3035,8 @@ public:
>                            Expr::Classification ObjectClassification,
>                            ArrayRef<Expr *> Args,
>                            OverloadCandidateSet& CandidateSet,
> -                          bool SuppressUserConversion = false);
> +                          bool SuppressUserConversion = false,
> +                          OverloadCandidateParamOrder PO = {});
>    void AddMethodCandidate(CXXMethodDecl *Method,
>                            DeclAccessPair FoundDecl,
>                            CXXRecordDecl *ActingContext, QualType
> ObjectType,
> @@ -3041,7 +3045,8 @@ public:
>                            OverloadCandidateSet& CandidateSet,
>                            bool SuppressUserConversions = false,
>                            bool PartialOverloading = false,
> -                          ConversionSequenceList EarlyConversions = None);
> +                          ConversionSequenceList EarlyConversions = None,
> +                          OverloadCandidateParamOrder PO = {});
>    void AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
>                                    DeclAccessPair FoundDecl,
>                                    CXXRecordDecl *ActingContext,
> @@ -3051,23 +3056,22 @@ public:
>                                    ArrayRef<Expr *> Args,
>                                    OverloadCandidateSet& CandidateSet,
>                                    bool SuppressUserConversions = false,
> -                                  bool PartialOverloading = false);
> +                                  bool PartialOverloading = false,
> +                                  OverloadCandidateParamOrder PO = {});
>    void AddTemplateOverloadCandidate(
>        FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
>        TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *>
> Args,
>        OverloadCandidateSet &CandidateSet, bool SuppressUserConversions =
> false,
>        bool PartialOverloading = false, bool AllowExplicit = true,
> -      ADLCallKind IsADLCandidate = ADLCallKind::NotADL);
> -  bool CheckNonDependentConversions(FunctionTemplateDecl
> *FunctionTemplate,
> -                                    ArrayRef<QualType> ParamTypes,
> -                                    ArrayRef<Expr *> Args,
> -                                    OverloadCandidateSet &CandidateSet,
> -                                    ConversionSequenceList &Conversions,
> -                                    bool SuppressUserConversions,
> -                                    CXXRecordDecl *ActingContext =
> nullptr,
> -                                    QualType ObjectType = QualType(),
> -                                    Expr::Classification
> -                                        ObjectClassification = {});
> +      ADLCallKind IsADLCandidate = ADLCallKind::NotADL,
> +      OverloadCandidateParamOrder PO = {});
> +  bool CheckNonDependentConversions(
> +      FunctionTemplateDecl *FunctionTemplate, ArrayRef<QualType>
> ParamTypes,
> +      ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
> +      ConversionSequenceList &Conversions, bool SuppressUserConversions,
> +      CXXRecordDecl *ActingContext = nullptr, QualType ObjectType =
> QualType(),
> +      Expr::Classification ObjectClassification = {},
> +      OverloadCandidateParamOrder PO = {});
>    void AddConversionCandidate(
>        CXXConversionDecl *Conversion, DeclAccessPair FoundDecl,
>        CXXRecordDecl *ActingContext, Expr *From, QualType ToType,
> @@ -3084,10 +3088,14 @@ public:
>                               const FunctionProtoType *Proto,
>                               Expr *Object, ArrayRef<Expr *> Args,
>                               OverloadCandidateSet& CandidateSet);
> +  void AddNonMemberOperatorCandidates(
> +      const UnresolvedSetImpl &Functions, ArrayRef<Expr *> Args,
> +      OverloadCandidateSet &CandidateSet,
> +      TemplateArgumentListInfo *ExplicitTemplateArgs = nullptr);
>    void AddMemberOperatorCandidates(OverloadedOperatorKind Op,
>                                     SourceLocation OpLoc, ArrayRef<Expr *>
> Args,
> -                                   OverloadCandidateSet& CandidateSet,
> -                                   SourceRange OpRange = SourceRange());
> +                                   OverloadCandidateSet &CandidateSet,
> +                                   OverloadCandidateParamOrder PO = {});
>    void AddBuiltinCandidate(QualType *ParamTys, ArrayRef<Expr *> Args,
>                             OverloadCandidateSet& CandidateSet,
>                             bool IsAssignmentOperator = false,
> @@ -3103,9 +3111,10 @@ public:
>                                              bool PartialOverloading =
> false);
>
>    // Emit as a 'note' the specific overload candidate
> -  void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
> -                             QualType DestType = QualType(),
> -                             bool TakingAddress = false);
> +  void NoteOverloadCandidate(
> +      NamedDecl *Found, FunctionDecl *Fn,
> +      OverloadCandidateRewriteKind RewriteKind =
> OverloadCandidateRewriteKind(),
> +      QualType DestType = QualType(), bool TakingAddress = false);
>
>    // Emit as a series of 'note's all template and non-templates
> identified by
>    // the expression Expr
> @@ -3237,7 +3246,8 @@ public:
>                                     BinaryOperatorKind Opc,
>                                     const UnresolvedSetImpl &Fns,
>                                     Expr *LHS, Expr *RHS,
> -                                   bool RequiresADL = true);
> +                                   bool RequiresADL = true,
> +                                   bool AllowRewrittenCandidates = true);
>
>    ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
>                                                  SourceLocation RLoc,
> @@ -7665,6 +7675,9 @@ public:
>        // We are substituting template arguments into a constraint
> expression.
>        ConstraintSubstitution,
>
> +      /// We are rewriting a comparison operator in terms of an
> operator<=>.
> +      RewritingOperatorAsSpaceship,
> +
>        /// Added for Template instantiation observation.
>        /// Memoization means we are _not_ instantiating a template because
>        /// it is already instantiated (but we entered a context where we
>
> Modified: cfe/trunk/lib/AST/ExprCXX.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprCXX.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/AST/ExprCXX.cpp (original)
> +++ cfe/trunk/lib/AST/ExprCXX.cpp Fri Oct 18 17:04:43 2019
> @@ -79,7 +79,7 @@ CXXRewrittenBinaryOperator::getDecompose
>      Result.RHS = BO->getRHS();
>      Result.InnerBinOp = BO;
>    } else if (auto *BO = dyn_cast<CXXOperatorCallExpr>(E)) {
> -    assert(!SkippedNot || BO->getOperator() == OO_Equal);
> +    assert(!SkippedNot || BO->getOperator() == OO_EqualEqual);
>      assert(BO->isInfixBinaryOp());
>      switch (BO->getOperator()) {
>      case OO_Less: Result.Opcode = BO_LT; break;
> @@ -107,7 +107,7 @@ CXXRewrittenBinaryOperator::getDecompose
>      return Result;
>
>    // Otherwise, we expect a <=> to now be on the LHS.
> -  E = Result.InnerBinOp->IgnoreImplicit();
> +  E = Result.LHS->IgnoreImplicit();
>    if (auto *BO = dyn_cast<BinaryOperator>(E)) {
>      assert(BO->getOpcode() == BO_Cmp);
>      Result.LHS = BO->getLHS();
>
> Modified: cfe/trunk/lib/Frontend/FrontendActions.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Frontend/FrontendActions.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Frontend/FrontendActions.cpp (original)
> +++ cfe/trunk/lib/Frontend/FrontendActions.cpp Fri Oct 18 17:04:43 2019
> @@ -415,6 +415,8 @@ private:
>        return "DeclaringSpecialMember";
>      case CodeSynthesisContext::DefiningSynthesizedFunction:
>        return "DefiningSynthesizedFunction";
> +    case CodeSynthesisContext::RewritingOperatorAsSpaceship:
> +      return "RewritingOperatorAsSpaceship";
>      case CodeSynthesisContext::Memoization:
>        return "Memoization";
>      case CodeSynthesisContext::ConstraintsCheck:
>
> Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaExpr.cpp Fri Oct 18 17:04:43 2019
> @@ -13310,6 +13310,13 @@ static ExprResult BuildOverloadedBinOp(S
>      S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(),
>                                     RHS->getType(), Functions);
>
> +  // In C++20 onwards, we may have a second operator to look up.
> +  if (S.getLangOpts().CPlusPlus2a) {
> +    if (OverloadedOperatorKind ExtraOp =
> getRewrittenOverloadedOperator(OverOp))
> +      S.LookupOverloadedOperatorName(ExtraOp, Sc, LHS->getType(),
> +                                     RHS->getType(), Functions);
> +  }
> +
>    // Build the (potentially-overloaded, potentially-dependent)
>    // binary operation.
>    return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS);
>
> Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaOverload.cpp Fri Oct 18 17:04:43 2019
> @@ -848,6 +848,25 @@ llvm::Optional<unsigned> DeductionFailur
>    }
>  }
>
> +bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
> +    OverloadedOperatorKind Op) {
> +  if (!AllowRewrittenCandidates)
> +    return false;
> +  return Op == OO_EqualEqual || Op == OO_Spaceship;
> +}
> +
> +bool OverloadCandidateSet::OperatorRewriteInfo::shouldAddReversed(
> +    ASTContext &Ctx, const FunctionDecl *FD) {
> +  if (!shouldAddReversed(FD->getDeclName().getCXXOverloadedOperator()))
> +    return false;
> +  // Don't bother adding a reversed candidate that can never be a better
> +  // match than the non-reversed version.
> +  return FD->getNumParams() != 2 ||
> +         !Ctx.hasSameUnqualifiedType(FD->getParamDecl(0)->getType(),
> +                                     FD->getParamDecl(1)->getType()) ||
> +         FD->hasAttr<EnableIfAttr>();
> +}
> +
>  void OverloadCandidateSet::destroyCandidates() {
>    for (iterator i = begin(), e = end(); i != e; ++i) {
>      for (auto &C : i->Conversions)
> @@ -6056,7 +6075,8 @@ void Sema::AddOverloadCandidate(
>      FunctionDecl *Function, DeclAccessPair FoundDecl, ArrayRef<Expr *>
> Args,
>      OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
>      bool PartialOverloading, bool AllowExplicit, bool
> AllowExplicitConversions,
> -    ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions) {
> +    ADLCallKind IsADLCandidate, ConversionSequenceList EarlyConversions,
> +    OverloadCandidateParamOrder PO) {
>    const FunctionProtoType *Proto
>      =
> dyn_cast<FunctionProtoType>(Function->getType()->getAs<FunctionType>());
>    assert(Proto && "Functions without a prototype cannot be overloaded");
> @@ -6075,25 +6095,14 @@ void Sema::AddOverloadCandidate(
>        AddMethodCandidate(Method, FoundDecl, Method->getParent(),
> QualType(),
>                           Expr::Classification::makeSimpleLValue(), Args,
>                           CandidateSet, SuppressUserConversions,
> -                         PartialOverloading, EarlyConversions);
> +                         PartialOverloading, EarlyConversions, PO);
>        return;
>      }
>      // We treat a constructor like a non-member function, since its object
>      // argument doesn't participate in overload resolution.
>    }
>
> -  if (!CandidateSet.isNewCandidate(Function))
> -    return;
> -
> -  // C++ [over.match.oper]p3:
> -  //   if no operand has a class type, only those non-member functions in
> the
> -  //   lookup set that have a first parameter of type T1 or "reference to
> -  //   (possibly cv-qualified) T1", when T1 is an enumeration type, or
> (if there
> -  //   is a right operand) a second parameter of type T2 or "reference to
> -  //   (possibly cv-qualified) T2", when T2 is an enumeration type, are
> -  //   candidate functions.
> -  if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&
> -      !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))
> +  if (!CandidateSet.isNewCandidate(Function, PO))
>      return;
>
>    // C++11 [class.copy]p11: [DR1402]
> @@ -6108,12 +6117,25 @@ void Sema::AddOverloadCandidate(
>    EnterExpressionEvaluationContext Unevaluated(
>        *this, Sema::ExpressionEvaluationContext::Unevaluated);
>
> +  // C++ [over.match.oper]p3:
> +  //   if no operand has a class type, only those non-member functions in
> the
> +  //   lookup set that have a first parameter of type T1 or "reference to
> +  //   (possibly cv-qualified) T1", when T1 is an enumeration type, or
> (if there
> +  //   is a right operand) a second parameter of type T2 or "reference to
> +  //   (possibly cv-qualified) T2", when T2 is an enumeration type, are
> +  //   candidate functions.
> +  if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&
> +      !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))
> +    return;
> +
>    // Add this candidate
>    OverloadCandidate &Candidate =
>        CandidateSet.addCandidate(Args.size(), EarlyConversions);
>    Candidate.FoundDecl = FoundDecl;
>    Candidate.Function = Function;
>    Candidate.Viable = true;
> +  Candidate.RewriteKind =
> +      CandidateSet.getRewriteInfo().getRewriteKind(Function, PO);
>    Candidate.IsSurrogate = false;
>    Candidate.IsADLCandidate = IsADLCandidate;
>    Candidate.IgnoreObjectArgument = false;
> @@ -6213,7 +6235,9 @@ void Sema::AddOverloadCandidate(
>    // Determine the implicit conversion sequences for each of the
>    // arguments.
>    for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
> -    if (Candidate.Conversions[ArgIdx].isInitialized()) {
> +    unsigned ConvIdx =
> +        PO == OverloadCandidateParamOrder::Reversed ? 1 - ArgIdx : ArgIdx;
> +    if (Candidate.Conversions[ConvIdx].isInitialized()) {
>        // We already formed a conversion sequence for this parameter during
>        // template argument deduction.
>      } else if (ArgIdx < NumParams) {
> @@ -6222,12 +6246,12 @@ void Sema::AddOverloadCandidate(
>        // (13.3.3.1) that converts that argument to the corresponding
>        // parameter of F.
>        QualType ParamType = Proto->getParamType(ArgIdx);
> -      Candidate.Conversions[ArgIdx] = TryCopyInitialization(
> +      Candidate.Conversions[ConvIdx] = TryCopyInitialization(
>            *this, Args[ArgIdx], ParamType, SuppressUserConversions,
>            /*InOverloadResolution=*/true,
>            /*AllowObjCWritebackConversion=*/
>            getLangOpts().ObjCAutoRefCount, AllowExplicitConversions);
> -      if (Candidate.Conversions[ArgIdx].isBad()) {
> +      if (Candidate.Conversions[ConvIdx].isBad()) {
>          Candidate.Viable = false;
>          Candidate.FailureKind = ovl_fail_bad_conversion;
>          return;
> @@ -6236,7 +6260,7 @@ void Sema::AddOverloadCandidate(
>        // (C++ 13.3.2p2): For the purposes of overload resolution, any
>        // argument for which there is no corresponding parameter is
>        // considered to ""match the ellipsis" (C+ 13.3.3.1.3).
> -      Candidate.Conversions[ArgIdx].setEllipsis();
> +      Candidate.Conversions[ConvIdx].setEllipsis();
>      }
>    }
>
> @@ -6583,9 +6607,10 @@ void Sema::AddFunctionCandidates(const U
>          FunctionArgs = Args.slice(1);
>        }
>        if (FunTmpl) {
> -        AddTemplateOverloadCandidate(
> -            FunTmpl, F.getPair(), ExplicitTemplateArgs, FunctionArgs,
> -            CandidateSet, SuppressUserConversions, PartialOverloading);
> +        AddTemplateOverloadCandidate(FunTmpl, F.getPair(),
> +                                     ExplicitTemplateArgs, FunctionArgs,
> +                                     CandidateSet,
> SuppressUserConversions,
> +                                     PartialOverloading);
>        } else {
>          AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet,
>                               SuppressUserConversions, PartialOverloading);
> @@ -6596,12 +6621,12 @@ void Sema::AddFunctionCandidates(const U
>
>  /// AddMethodCandidate - Adds a named decl (which is some kind of
>  /// method) as a method candidate to the given overload set.
> -void Sema::AddMethodCandidate(DeclAccessPair FoundDecl,
> -                              QualType ObjectType,
> +void Sema::AddMethodCandidate(DeclAccessPair FoundDecl, QualType
> ObjectType,
>                                Expr::Classification ObjectClassification,
>                                ArrayRef<Expr *> Args,
> -                              OverloadCandidateSet& CandidateSet,
> -                              bool SuppressUserConversions) {
> +                              OverloadCandidateSet &CandidateSet,
> +                              bool SuppressUserConversions,
> +                              OverloadCandidateParamOrder PO) {
>    NamedDecl *Decl = FoundDecl.getDecl();
>    CXXRecordDecl *ActingContext =
> cast<CXXRecordDecl>(Decl->getDeclContext());
>
> @@ -6614,11 +6639,11 @@ void Sema::AddMethodCandidate(DeclAccess
>      AddMethodTemplateCandidate(TD, FoundDecl, ActingContext,
>                                 /*ExplicitArgs*/ nullptr, ObjectType,
>                                 ObjectClassification, Args, CandidateSet,
> -                               SuppressUserConversions);
> +                               SuppressUserConversions, false, PO);
>    } else {
>      AddMethodCandidate(cast<CXXMethodDecl>(Decl), FoundDecl,
> ActingContext,
>                         ObjectType, ObjectClassification, Args,
> CandidateSet,
> -                       SuppressUserConversions);
> +                       SuppressUserConversions, false, None, PO);
>    }
>  }
>
> @@ -6637,14 +6662,15 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>                           OverloadCandidateSet &CandidateSet,
>                           bool SuppressUserConversions,
>                           bool PartialOverloading,
> -                         ConversionSequenceList EarlyConversions) {
> +                         ConversionSequenceList EarlyConversions,
> +                         OverloadCandidateParamOrder PO) {
>    const FunctionProtoType *Proto
>      =
> dyn_cast<FunctionProtoType>(Method->getType()->getAs<FunctionType>());
>    assert(Proto && "Methods without a prototype cannot be overloaded");
>    assert(!isa<CXXConstructorDecl>(Method) &&
>           "Use AddOverloadCandidate for constructors");
>
> -  if (!CandidateSet.isNewCandidate(Method))
> +  if (!CandidateSet.isNewCandidate(Method, PO))
>      return;
>
>    // C++11 [class.copy]p23: [DR1402]
> @@ -6663,6 +6689,8 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>        CandidateSet.addCandidate(Args.size() + 1, EarlyConversions);
>    Candidate.FoundDecl = FoundDecl;
>    Candidate.Function = Method;
> +  Candidate.RewriteKind =
> +      CandidateSet.getRewriteInfo().getRewriteKind(Method, PO);
>    Candidate.IsSurrogate = false;
>    Candidate.IgnoreObjectArgument = false;
>    Candidate.ExplicitCallArguments = Args.size();
> @@ -6698,12 +6726,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>      // The implicit object argument is ignored.
>      Candidate.IgnoreObjectArgument = true;
>    else {
> +    unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 :
> 0;
>      // Determine the implicit conversion sequence for the object
>      // parameter.
> -    Candidate.Conversions[0] = TryObjectArgumentInitialization(
> +    Candidate.Conversions[ConvIdx] = TryObjectArgumentInitialization(
>          *this, CandidateSet.getLocation(), ObjectType,
> ObjectClassification,
>          Method, ActingContext);
> -    if (Candidate.Conversions[0].isBad()) {
> +    if (Candidate.Conversions[ConvIdx].isBad()) {
>        Candidate.Viable = false;
>        Candidate.FailureKind = ovl_fail_bad_conversion;
>        return;
> @@ -6722,7 +6751,9 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>    // Determine the implicit conversion sequences for each of the
>    // arguments.
>    for (unsigned ArgIdx = 0; ArgIdx < Args.size(); ++ArgIdx) {
> -    if (Candidate.Conversions[ArgIdx + 1].isInitialized()) {
> +    unsigned ConvIdx =
> +        PO == OverloadCandidateParamOrder::Reversed ? 0 : (ArgIdx + 1);
> +    if (Candidate.Conversions[ConvIdx].isInitialized()) {
>        // We already formed a conversion sequence for this parameter during
>        // template argument deduction.
>      } else if (ArgIdx < NumParams) {
> @@ -6731,13 +6762,13 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>        // (13.3.3.1) that converts that argument to the corresponding
>        // parameter of F.
>        QualType ParamType = Proto->getParamType(ArgIdx);
> -      Candidate.Conversions[ArgIdx + 1]
> +      Candidate.Conversions[ConvIdx]
>          = TryCopyInitialization(*this, Args[ArgIdx], ParamType,
>                                  SuppressUserConversions,
>                                  /*InOverloadResolution=*/true,
>                                  /*AllowObjCWritebackConversion=*/
>                                    getLangOpts().ObjCAutoRefCount);
> -      if (Candidate.Conversions[ArgIdx + 1].isBad()) {
> +      if (Candidate.Conversions[ConvIdx].isBad()) {
>          Candidate.Viable = false;
>          Candidate.FailureKind = ovl_fail_bad_conversion;
>          return;
> @@ -6746,7 +6777,7 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>        // (C++ 13.3.2p2): For the purposes of overload resolution, any
>        // argument for which there is no corresponding parameter is
>        // considered to "match the ellipsis" (C+ 13.3.3.1.3).
> -      Candidate.Conversions[ArgIdx + 1].setEllipsis();
> +      Candidate.Conversions[ConvIdx].setEllipsis();
>      }
>    }
>
> @@ -6767,18 +6798,14 @@ Sema::AddMethodCandidate(CXXMethodDecl *
>  /// Add a C++ member function template as a candidate to the candidate
>  /// set, using template argument deduction to produce an appropriate
> member
>  /// function template specialization.
> -void
> -Sema::AddMethodTemplateCandidate(FunctionTemplateDecl *MethodTmpl,
> -                                 DeclAccessPair FoundDecl,
> -                                 CXXRecordDecl *ActingContext,
> -                                 TemplateArgumentListInfo
> *ExplicitTemplateArgs,
> -                                 QualType ObjectType,
> -                                 Expr::Classification
> ObjectClassification,
> -                                 ArrayRef<Expr *> Args,
> -                                 OverloadCandidateSet& CandidateSet,
> -                                 bool SuppressUserConversions,
> -                                 bool PartialOverloading) {
> -  if (!CandidateSet.isNewCandidate(MethodTmpl))
> +void Sema::AddMethodTemplateCandidate(
> +    FunctionTemplateDecl *MethodTmpl, DeclAccessPair FoundDecl,
> +    CXXRecordDecl *ActingContext,
> +    TemplateArgumentListInfo *ExplicitTemplateArgs, QualType ObjectType,
> +    Expr::Classification ObjectClassification, ArrayRef<Expr *> Args,
> +    OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
> +    bool PartialOverloading, OverloadCandidateParamOrder PO) {
> +  if (!CandidateSet.isNewCandidate(MethodTmpl, PO))
>      return;
>
>    // C++ [over.match.funcs]p7:
> @@ -6799,13 +6826,15 @@ Sema::AddMethodTemplateCandidate(Functio
>              return CheckNonDependentConversions(
>                  MethodTmpl, ParamTypes, Args, CandidateSet, Conversions,
>                  SuppressUserConversions, ActingContext, ObjectType,
> -                ObjectClassification);
> +                ObjectClassification, PO);
>            })) {
>      OverloadCandidate &Candidate =
>          CandidateSet.addCandidate(Conversions.size(), Conversions);
>      Candidate.FoundDecl = FoundDecl;
>      Candidate.Function = MethodTmpl->getTemplatedDecl();
>      Candidate.Viable = false;
> +    Candidate.RewriteKind =
> +      CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function,
> PO);
>      Candidate.IsSurrogate = false;
>      Candidate.IgnoreObjectArgument =
>          cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
> @@ -6829,7 +6858,7 @@ Sema::AddMethodTemplateCandidate(Functio
>    AddMethodCandidate(cast<CXXMethodDecl>(Specialization), FoundDecl,
>                       ActingContext, ObjectType, ObjectClassification,
> Args,
>                       CandidateSet, SuppressUserConversions,
> PartialOverloading,
> -                     Conversions);
> +                     Conversions, PO);
>  }
>
>  /// Add a C++ function template specialization as a candidate
> @@ -6839,8 +6868,9 @@ void Sema::AddTemplateOverloadCandidate(
>      FunctionTemplateDecl *FunctionTemplate, DeclAccessPair FoundDecl,
>      TemplateArgumentListInfo *ExplicitTemplateArgs, ArrayRef<Expr *> Args,
>      OverloadCandidateSet &CandidateSet, bool SuppressUserConversions,
> -    bool PartialOverloading, bool AllowExplicit, ADLCallKind
> IsADLCandidate) {
> -  if (!CandidateSet.isNewCandidate(FunctionTemplate))
> +    bool PartialOverloading, bool AllowExplicit, ADLCallKind
> IsADLCandidate,
> +    OverloadCandidateParamOrder PO) {
> +  if (!CandidateSet.isNewCandidate(FunctionTemplate, PO))
>      return;
>
>    // C++ [over.match.funcs]p7:
> @@ -6858,15 +6888,17 @@ void Sema::AddTemplateOverloadCandidate(
>    if (TemplateDeductionResult Result = DeduceTemplateArguments(
>            FunctionTemplate, ExplicitTemplateArgs, Args, Specialization,
> Info,
>            PartialOverloading, [&](ArrayRef<QualType> ParamTypes) {
> -            return CheckNonDependentConversions(FunctionTemplate,
> ParamTypes,
> -                                                Args, CandidateSet,
> Conversions,
> -                                                SuppressUserConversions);
> +            return CheckNonDependentConversions(
> +                FunctionTemplate, ParamTypes, Args, CandidateSet,
> Conversions,
> +                SuppressUserConversions, nullptr, QualType(), {}, PO);
>            })) {
>      OverloadCandidate &Candidate =
>          CandidateSet.addCandidate(Conversions.size(), Conversions);
>      Candidate.FoundDecl = FoundDecl;
>      Candidate.Function = FunctionTemplate->getTemplatedDecl();
>      Candidate.Viable = false;
> +    Candidate.RewriteKind =
> +      CandidateSet.getRewriteInfo().getRewriteKind(Candidate.Function,
> PO);
>      Candidate.IsSurrogate = false;
>      Candidate.IsADLCandidate = IsADLCandidate;
>      // Ignore the object argument if there is one, since we don't have an
> object
> @@ -6891,7 +6923,7 @@ void Sema::AddTemplateOverloadCandidate(
>    AddOverloadCandidate(
>        Specialization, FoundDecl, Args, CandidateSet,
> SuppressUserConversions,
>        PartialOverloading, AllowExplicit,
> -      /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions);
> +      /*AllowExplicitConversions*/ false, IsADLCandidate, Conversions,
> PO);
>  }
>
>  /// Check that implicit conversion sequences can be formed for each
> argument
> @@ -6902,7 +6934,7 @@ bool Sema::CheckNonDependentConversions(
>      ArrayRef<Expr *> Args, OverloadCandidateSet &CandidateSet,
>      ConversionSequenceList &Conversions, bool SuppressUserConversions,
>      CXXRecordDecl *ActingContext, QualType ObjectType,
> -    Expr::Classification ObjectClassification) {
> +    Expr::Classification ObjectClassification,
> OverloadCandidateParamOrder PO) {
>    // FIXME: The cases in which we allow explicit conversions for
> constructor
>    // arguments never consider calling a constructor template. It's not
> clear
>    // that is correct.
> @@ -6925,10 +6957,11 @@ bool Sema::CheckNonDependentConversions(
>    // overload resolution is permitted to sidestep instantiations.
>    if (HasThisConversion && !cast<CXXMethodDecl>(FD)->isStatic() &&
>        !ObjectType.isNull()) {
> -    Conversions[0] = TryObjectArgumentInitialization(
> +    unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed ? 1 :
> 0;
> +    Conversions[ConvIdx] = TryObjectArgumentInitialization(
>          *this, CandidateSet.getLocation(), ObjectType,
> ObjectClassification,
>          Method, ActingContext);
> -    if (Conversions[0].isBad())
> +    if (Conversions[ConvIdx].isBad())
>        return true;
>    }
>
> @@ -6936,14 +6969,17 @@ bool Sema::CheckNonDependentConversions(
>         ++I) {
>      QualType ParamType = ParamTypes[I];
>      if (!ParamType->isDependentType()) {
> -      Conversions[ThisConversions + I]
> +      unsigned ConvIdx = PO == OverloadCandidateParamOrder::Reversed
> +                             ? 0
> +                             : (ThisConversions + I);
> +      Conversions[ConvIdx]
>          = TryCopyInitialization(*this, Args[I], ParamType,
>                                  SuppressUserConversions,
>                                  /*InOverloadResolution=*/true,
>                                  /*AllowObjCWritebackConversion=*/
>                                    getLangOpts().ObjCAutoRefCount,
>                                  AllowExplicit);
> -      if (Conversions[ThisConversions + I].isBad())
> +      if (Conversions[ConvIdx].isBad())
>          return true;
>      }
>    }
> @@ -7331,6 +7367,48 @@ void Sema::AddSurrogateCandidate(CXXConv
>    }
>  }
>
> +/// Add all of the non-member operator function declarations in the given
> +/// function set to the overload candidate set.
> +void Sema::AddNonMemberOperatorCandidates(
> +    const UnresolvedSetImpl &Fns, ArrayRef<Expr *> Args,
> +    OverloadCandidateSet &CandidateSet,
> +    TemplateArgumentListInfo *ExplicitTemplateArgs) {
> +  for (UnresolvedSetIterator F = Fns.begin(), E = Fns.end(); F != E; ++F)
> {
> +    NamedDecl *D = F.getDecl()->getUnderlyingDecl();
> +    ArrayRef<Expr *> FunctionArgs = Args;
> +
> +    FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(D);
> +    FunctionDecl *FD =
> +        FunTmpl ? FunTmpl->getTemplatedDecl() : cast<FunctionDecl>(D);
> +
> +    // Don't consider rewritten functions if we're not rewriting.
> +    if (!CandidateSet.getRewriteInfo().isAcceptableCandidate(FD))
> +      continue;
> +
> +    assert(!isa<CXXMethodDecl>(FD) &&
> +           "unqualified operator lookup found a member function");
> +
> +    if (FunTmpl) {
> +      AddTemplateOverloadCandidate(FunTmpl, F.getPair(),
> ExplicitTemplateArgs,
> +                                   FunctionArgs, CandidateSet);
> +      if (CandidateSet.getRewriteInfo().shouldAddReversed(Context, FD))
> +        AddTemplateOverloadCandidate(
> +            FunTmpl, F.getPair(), ExplicitTemplateArgs,
> +            {FunctionArgs[1], FunctionArgs[0]}, CandidateSet, false,
> false,
> +            true, ADLCallKind::NotADL,
> OverloadCandidateParamOrder::Reversed);
> +    } else {
> +      if (ExplicitTemplateArgs)
> +        continue;
> +      AddOverloadCandidate(FD, F.getPair(), FunctionArgs, CandidateSet);
> +      if (CandidateSet.getRewriteInfo().shouldAddReversed(Context, FD))
> +        AddOverloadCandidate(FD, F.getPair(),
> +                             {FunctionArgs[1], FunctionArgs[0]},
> CandidateSet,
> +                             false, false, true, false,
> ADLCallKind::NotADL,
> +                             None, OverloadCandidateParamOrder::Reversed);
> +    }
> +  }
> +}
> +
>  /// Add overload candidates for overloaded operators that are
>  /// member functions.
>  ///
> @@ -7342,8 +7420,8 @@ void Sema::AddSurrogateCandidate(CXXConv
>  void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
>                                         SourceLocation OpLoc,
>                                         ArrayRef<Expr *> Args,
> -                                       OverloadCandidateSet& CandidateSet,
> -                                       SourceRange OpRange) {
> +                                       OverloadCandidateSet &CandidateSet,
> +                                       OverloadCandidateParamOrder PO) {
>    DeclarationName OpName =
> Context.DeclarationNames.getCXXOperatorName(Op);
>
>    // C++ [over.match.oper]p3:
> @@ -7378,7 +7456,7 @@ void Sema::AddMemberOperatorCandidates(O
>           ++Oper)
>        AddMethodCandidate(Oper.getPair(), Args[0]->getType(),
>                           Args[0]->Classify(Context), Args.slice(1),
> -                         CandidateSet, /*SuppressUserConversion=*/false);
> +                         CandidateSet, /*SuppressUserConversion=*/false,
> PO);
>    }
>  }
>
> @@ -8183,10 +8261,16 @@ public:
>            if (C->Function->isFunctionTemplateSpecialization())
>              continue;
>
> -          QualType FirstParamType =
> -            C->Function->getParamDecl(0)->getType().getUnqualifiedType();
> -          QualType SecondParamType =
> -            C->Function->getParamDecl(1)->getType().getUnqualifiedType();
> +          // 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;
> +          QualType FirstParamType = C->Function->getParamDecl(Reversed ?
> 1 : 0)
> +                                        ->getType()
> +                                        .getUnqualifiedType();
> +          QualType SecondParamType = C->Function->getParamDecl(Reversed ?
> 0 : 1)
> +                                         ->getType()
> +                                         .getUnqualifiedType();
>
>            // Skip if either parameter isn't of enumeral type.
>            if (!FirstParamType->isEnumeralType() ||
> @@ -9240,6 +9324,7 @@ bool clang::isBetterOverloadCandidate(
>    //   A viable function F1 is defined to be a better function than
> another
>    //   viable function F2 if for all arguments i, ICSi(F1) is not a worse
>    //   conversion sequence than ICSi(F2), and then...
> +  bool HasWorseConversion = false;
>    for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
>      switch (CompareImplicitConversionSequences(S, Loc,
>                                                 Cand1.Conversions[ArgIdx],
> @@ -9250,6 +9335,24 @@ bool clang::isBetterOverloadCandidate(
>        break;
>
>      case ImplicitConversionSequence::Worse:
> +      if (Cand1.Function && Cand1.Function == Cand2.Function &&
> +          (Cand2.RewriteKind & CRK_Reversed) != 0) {
> +        // Work around large-scale breakage caused by considering reversed
> +        // forms of operator== in C++20:
> +        //
> +        // When comparing a function against its reversed form, if we
> have a
> +        // better conversion for one argument and a worse conversion for
> the
> +        // other, we prefer the non-reversed form.
> +        //
> +        // This prevents a conversion function from being considered
> ambiguous
> +        // with its own reversed form in various where it's only
> incidentally
> +        // heterogeneous.
> +        //
> +        // We diagnose this as an extension from CreateOverloadedBinOp.
> +        HasWorseConversion = true;
> +        break;
> +      }
> +
>        // Cand1 can't be better than Cand2.
>        return false;
>
> @@ -9263,6 +9366,8 @@ bool clang::isBetterOverloadCandidate(
>    //       ICSj(F2), or, if not that,
>    if (HasBetterConversion)
>      return true;
> +  if (HasWorseConversion)
> +    return false;
>
>    //   -- the context is an initialization by user-defined conversion
>    //      (see 8.5, 13.3.1.5) and the standard conversion sequence
> @@ -9330,8 +9435,10 @@ bool clang::isBetterOverloadCandidate(
>        return BetterTemplate == Cand1.Function->getPrimaryTemplate();
>    }
>
> -  // FIXME: Work around a defect in the C++17 inheriting constructor
> wording.
> -  // A derived-class constructor beats an (inherited) base class
> constructor.
> +  //   -- F1 is a constructor for a class D, F2 is a constructor for a
> base
> +  //      class B of D, and for all arguments the corresponding
> parameters of
> +  //      F1 and F2 have the same type.
> +  // FIXME: Implement the "all parameters have the same type" check.
>    bool Cand1IsInherited =
>
>  dyn_cast_or_null<ConstructorUsingShadowDecl>(Cand1.FoundDecl.getDecl());
>    bool Cand2IsInherited =
> @@ -9349,6 +9456,16 @@ bool clang::isBetterOverloadCandidate(
>      // Inherited from sibling base classes: still ambiguous.
>    }
>
> +  //   -- F2 is a rewritten candidate (12.4.1.2) and F1 is not
> +  //   -- F1 and F2 are rewritten candidates, and F2 is a synthesized
> candidate
> +  //      with reversed order of parameters and F1 is not
> +  //
> +  // We rank reversed + different operator as worse than just reversed,
> but
> +  // that comparison can never happen, because we only consider reversing
> for
> +  // the maximally-rewritten operator (== or <=>).
> +  if (Cand1.RewriteKind != Cand2.RewriteKind)
> +    return Cand1.RewriteKind < Cand2.RewriteKind;
> +
>    // Check C++17 tie-breakers for deduction guides.
>    {
>      auto *Guide1 =
> dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
> @@ -9544,6 +9661,7 @@ namespace {
>  enum OverloadCandidateKind {
>    oc_function,
>    oc_method,
> +  oc_reversed_binary_operator,
>    oc_constructor,
>    oc_implicit_default_constructor,
>    oc_implicit_copy_constructor,
> @@ -9561,6 +9679,7 @@ enum OverloadCandidateSelect {
>
>  static std::pair<OverloadCandidateKind, OverloadCandidateSelect>
>  ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
> +                          OverloadCandidateRewriteKind CRK,
>                            std::string &Description) {
>
>    bool isTemplate = Fn->isTemplateDecl() || Found->isTemplateDecl();
> @@ -9577,6 +9696,9 @@ ClassifyOverloadCandidate(Sema &S, Named
>    }();
>
>    OverloadCandidateKind Kind = [&]() {
> +    if (CRK & CRK_Reversed)
> +      return oc_reversed_binary_operator;
> +
>      if (CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(Fn)) {
>        if (!Ctor->isImplicit()) {
>          if (isa<ConstructorUsingShadowDecl>(Found))
> @@ -9701,6 +9823,7 @@ bool Sema::checkAddressOfFunctionIsAvail
>
>  // Notes the location of an overload candidate.
>  void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
> +                                 OverloadCandidateRewriteKind RewriteKind,
>                                   QualType DestType, bool TakingAddress) {
>    if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))
>      return;
> @@ -9710,7 +9833,7 @@ void Sema::NoteOverloadCandidate(NamedDe
>
>    std::string FnDesc;
>    std::pair<OverloadCandidateKind, OverloadCandidateSelect> KSPair =
> -      ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
> +      ClassifyOverloadCandidate(*this, Found, Fn, RewriteKind, FnDesc);
>    PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
>                           << (unsigned)KSPair.first <<
> (unsigned)KSPair.second
>                           << Fn << FnDesc;
> @@ -9734,11 +9857,11 @@ void Sema::NoteAllOverloadCandidates(Exp
>         I != IEnd; ++I) {
>      if (FunctionTemplateDecl *FunTmpl =
>                  dyn_cast<FunctionTemplateDecl>((*I)->getUnderlyingDecl())
> ) {
> -      NoteOverloadCandidate(*I, FunTmpl->getTemplatedDecl(), DestType,
> +      NoteOverloadCandidate(*I, FunTmpl->getTemplatedDecl(), CRK_None,
> DestType,
>                              TakingAddress);
>      } else if (FunctionDecl *Fun
>                        = dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl())
> ) {
> -      NoteOverloadCandidate(*I, Fun, DestType, TakingAddress);
> +      NoteOverloadCandidate(*I, Fun, CRK_None, DestType, TakingAddress);
>      }
>    }
>  }
> @@ -9788,7 +9911,8 @@ static void DiagnoseBadConversion(Sema &
>
>    std::string FnDesc;
>    std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
> -      ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
> +      ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, Cand->RewriteKind,
> +                                FnDesc);
>
>    Expr *FromExpr = Conv.Bad.FromExpr;
>    QualType FromTy = Conv.Bad.getFromType();
> @@ -10060,7 +10184,7 @@ static void DiagnoseArityMismatch(Sema &
>
>    std::string Description;
>    std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
> -      ClassifyOverloadCandidate(S, Found, Fn, Description);
> +      ClassifyOverloadCandidate(S, Found, Fn, CRK_None, Description);
>
>    if (modeCount == 1 && Fn->getParamDecl(0)->getDeclName())
>      S.Diag(Fn->getLocation(), diag::note_ovl_candidate_arity_one)
> @@ -10357,7 +10481,8 @@ static void DiagnoseBadTarget(Sema &S, O
>
>    std::string FnDesc;
>    std::pair<OverloadCandidateKind, OverloadCandidateSelect> FnKindPair =
> -      ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee, FnDesc);
> +      ClassifyOverloadCandidate(S, Cand->FoundDecl, Callee,
> Cand->RewriteKind,
> +                                FnDesc);
>
>    S.Diag(Callee->getLocation(), diag::note_ovl_candidate_bad_target)
>        << (unsigned)FnKindPair.first << (unsigned)ocs_non_template
> @@ -10475,7 +10600,8 @@ static void NoteFunctionCandidate(Sema &
>      if (Fn->isDeleted()) {
>        std::string FnDesc;
>        std::pair<OverloadCandidateKind, OverloadCandidateSelect>
> FnKindPair =
> -          ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
> +          ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn,
> Cand->RewriteKind,
> +                                    FnDesc);
>
>        S.Diag(Fn->getLocation(), diag::note_ovl_candidate_deleted)
>            << (unsigned)FnKindPair.first << (unsigned)FnKindPair.second <<
> FnDesc
> @@ -10485,7 +10611,7 @@ static void NoteFunctionCandidate(Sema &
>      }
>
>      // We don't really have anything else to say about viable candidates.
> -    S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
> +    S.NoteOverloadCandidate(Cand->FoundDecl, Fn, Cand->RewriteKind);
>      return;
>    }
>
> @@ -10518,7 +10644,7 @@ static void NoteFunctionCandidate(Sema &
>    case ovl_fail_trivial_conversion:
>    case ovl_fail_bad_final_conversion:
>    case ovl_fail_final_conversion_not_exact:
> -    return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
> +    return S.NoteOverloadCandidate(Cand->FoundDecl, Fn,
> Cand->RewriteKind);
>
>    case ovl_fail_bad_conversion: {
>      unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0);
> @@ -10529,7 +10655,7 @@ static void NoteFunctionCandidate(Sema &
>      // FIXME: this currently happens when we're called from SemaInit
>      // when user-conversion overload fails.  Figure out how to handle
>      // those conditions and diagnose them well.
> -    return S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
> +    return S.NoteOverloadCandidate(Cand->FoundDecl, Fn,
> Cand->RewriteKind);
>    }
>
>    case ovl_fail_bad_target:
> @@ -10805,8 +10931,10 @@ struct CompareOverloadCandidatesForDispl
>  /// CompleteNonViableCandidate - Normally, overload resolution only
>  /// computes up to the first bad conversion. Produces the FixIt set if
>  /// possible.
> -static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
> -                                       ArrayRef<Expr *> Args) {
> +static void
> +CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
> +                           ArrayRef<Expr *> Args,
> +                           OverloadCandidateSet::CandidateSetKind CSK) {
>    assert(!Cand->Viable);
>
>    // Don't do anything on failures other than bad conversion.
> @@ -10834,6 +10962,7 @@ static void CompleteNonViableCandidate(S
>    bool SuppressUserConversions = false;
>
>    unsigned ConvIdx = 0;
> +  unsigned ArgIdx = 0;
>    ArrayRef<QualType> ParamTypes;
>
>    if (Cand->IsSurrogate) {
> @@ -10842,15 +10971,18 @@ static void CompleteNonViableCandidate(S
>      if (const PointerType *ConvPtrType = ConvType->getAs<PointerType>())
>        ConvType = ConvPtrType->getPointeeType();
>      ParamTypes = ConvType->castAs<FunctionProtoType>()->getParamTypes();
> -    // Conversion 0 is 'this', which doesn't have a corresponding
> argument.
> +    // Conversion 0 is 'this', which doesn't have a corresponding
> parameter.
>      ConvIdx = 1;
>    } else if (Cand->Function) {
>      ParamTypes =
>
>  Cand->Function->getType()->castAs<FunctionProtoType>()->getParamTypes();
>      if (isa<CXXMethodDecl>(Cand->Function) &&
>          !isa<CXXConstructorDecl>(Cand->Function)) {
> -      // Conversion 0 is 'this', which doesn't have a corresponding
> argument.
> +      // Conversion 0 is 'this', which doesn't have a corresponding
> parameter.
>        ConvIdx = 1;
> +      if (CSK == OverloadCandidateSet::CSK_Operator)
> +        // Argument 0 is 'this', which doesn't have a corresponding
> parameter.
> +        ArgIdx = 1;
>      }
>    } else {
>      // Builtin operator.
> @@ -10859,16 +10991,19 @@ static void CompleteNonViableCandidate(S
>    }
>
>    // Fill in the rest of the conversions.
> -  for (unsigned ArgIdx = 0; ConvIdx != ConvCount; ++ConvIdx, ++ArgIdx) {
> +  bool Reversed = Cand->RewriteKind & CRK_Reversed;
> +  for (unsigned ParamIdx = Reversed ? ParamTypes.size() - 1 : 0;
> +       ConvIdx != ConvCount;
> +       ++ConvIdx, ++ArgIdx, ParamIdx += (Reversed ? -1 : 1)) {
>      if (Cand->Conversions[ConvIdx].isInitialized()) {
>        // We've already checked this conversion.
>      } else if (ArgIdx < ParamTypes.size()) {
> -      if (ParamTypes[ArgIdx]->isDependentType())
> +      if (ParamTypes[ParamIdx]->isDependentType())
>          Cand->Conversions[ConvIdx].setAsIdentityConversion(
>              Args[ArgIdx]->getType());
>        else {
>          Cand->Conversions[ConvIdx] =
> -            TryCopyInitialization(S, Args[ArgIdx], ParamTypes[ArgIdx],
> +            TryCopyInitialization(S, Args[ArgIdx], ParamTypes[ParamIdx],
>                                    SuppressUserConversions,
>                                    /*InOverloadResolution=*/true,
>                                    /*AllowObjCWritebackConversion=*/
> @@ -10896,7 +11031,7 @@ SmallVector<OverloadCandidate *, 32> Ove
>      if (Cand->Viable)
>        Cands.push_back(Cand);
>      else if (OCD == OCD_AllCandidates) {
> -      CompleteNonViableCandidate(S, Cand, Args);
> +      CompleteNonViableCandidate(S, Cand, Args, Kind);
>        if (Cand->Function || Cand->IsSurrogate)
>          Cands.push_back(Cand);
>        // Otherwise, this a non-viable builtin candidate.  We do not, in
> general,
> @@ -11453,7 +11588,7 @@ public:
>          if (FunctionDecl *Fun =
>                  dyn_cast<FunctionDecl>((*I)->getUnderlyingDecl()))
>            if (!functionHasPassObjectSizeParams(Fun))
> -            S.NoteOverloadCandidate(*I, Fun, TargetFunctionType,
> +            S.NoteOverloadCandidate(*I, Fun, CRK_None, TargetFunctionType,
>                                      /*TakingAddress=*/true);
>        FailedCandidates.NoteCandidates(S, OvlExpr->getBeginLoc());
>      }
> @@ -12403,7 +12538,7 @@ Sema::CreateOverloadedUnaryOp(SourceLoca
>    OverloadCandidateSet CandidateSet(OpLoc,
> OverloadCandidateSet::CSK_Operator);
>
>    // Add the candidates from the given function set.
> -  AddFunctionCandidates(Fns, ArgsArray, CandidateSet);
> +  AddNonMemberOperatorCandidates(Fns, ArgsArray, CandidateSet);
>
>    // Add operator candidates that are member functions.
>    AddMemberOperatorCandidates(Op, OpLoc, ArgsArray, CandidateSet);
> @@ -12548,14 +12683,17 @@ Sema::CreateOverloadedUnaryOp(SourceLoca
>  ///
>  /// \param LHS Left-hand argument.
>  /// \param RHS Right-hand argument.
> -ExprResult
> -Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
> -                            BinaryOperatorKind Opc,
> -                            const UnresolvedSetImpl &Fns,
> -                            Expr *LHS, Expr *RHS, bool PerformADL) {
> +ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
> +                                       BinaryOperatorKind Opc,
> +                                       const UnresolvedSetImpl &Fns, Expr
> *LHS,
> +                                       Expr *RHS, bool PerformADL,
> +                                       bool AllowRewrittenCandidates) {
>    Expr *Args[2] = { LHS, RHS };
>    LHS=RHS=nullptr; // Please use only Args instead of LHS/RHS couple
>
> +  if (!getLangOpts().CPlusPlus2a)
> +    AllowRewrittenCandidates = false;
> +
>    OverloadedOperatorKind Op = BinaryOperator::getOverloadedOperator(Opc);
>    DeclarationName OpName =
> Context.DeclarationNames.getCXXOperatorName(Op);
>
> @@ -12613,23 +12751,61 @@ Sema::CreateOverloadedBinOp(SourceLocati
>      return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
>
>    // Build an empty overload set.
> -  OverloadCandidateSet CandidateSet(OpLoc,
> OverloadCandidateSet::CSK_Operator);
> +  OverloadCandidateSet CandidateSet(
> +      OpLoc, OverloadCandidateSet::CSK_Operator,
> +      OverloadCandidateSet::OperatorRewriteInfo(Op,
> AllowRewrittenCandidates));
>
> -  // Add the candidates from the given function set.
> -  AddFunctionCandidates(Fns, Args, CandidateSet);
> +  OverloadedOperatorKind ExtraOp =
> +      AllowRewrittenCandidates ? getRewrittenOverloadedOperator(Op) :
> OO_None;
> +
> +  // Add the candidates from the given function set. This also adds the
> +  // rewritten candidates using these functions if necessary.
> +  AddNonMemberOperatorCandidates(Fns, Args, CandidateSet);
>
>    // Add operator candidates that are member functions.
>    AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);
> +  if (CandidateSet.getRewriteInfo().shouldAddReversed(Op))
> +    AddMemberOperatorCandidates(Op, OpLoc, {Args[1], Args[0]},
> CandidateSet,
> +                                OverloadCandidateParamOrder::Reversed);
> +
> +  // In C++20, also add any rewritten member candidates.
> +  if (ExtraOp) {
> +    AddMemberOperatorCandidates(ExtraOp, OpLoc, Args, CandidateSet);
> +    if (CandidateSet.getRewriteInfo().shouldAddReversed(ExtraOp))
> +      AddMemberOperatorCandidates(ExtraOp, OpLoc, {Args[1], Args[0]},
> +                                  CandidateSet,
> +                                  OverloadCandidateParamOrder::Reversed);
> +  }
>
>    // Add candidates from ADL. Per [over.match.oper]p2, this lookup is not
>    // performed for an assignment operator (nor for operator[] nor
> operator->,
>    // which don't get here).
> -  if (Opc != BO_Assign && PerformADL)
> +  if (Opc != BO_Assign && PerformADL) {
>      AddArgumentDependentLookupCandidates(OpName, OpLoc, Args,
>                                           /*ExplicitTemplateArgs*/ nullptr,
>                                           CandidateSet);
> +    if (ExtraOp) {
> +      DeclarationName ExtraOpName =
> +          Context.DeclarationNames.getCXXOperatorName(ExtraOp);
> +      AddArgumentDependentLookupCandidates(ExtraOpName, OpLoc, Args,
> +                                           /*ExplicitTemplateArgs*/
> nullptr,
> +                                           CandidateSet);
> +    }
> +  }
>
>    // Add builtin operator candidates.
> +  //
> +  // FIXME: We don't add any rewritten candidates here. This is strictly
> +  // incorrect; a builtin candidate could be hidden by a non-viable
> candidate,
> +  // resulting in our selecting a rewritten builtin candidate. For
> example:
> +  //
> +  //   enum class E { e };
> +  //   bool operator!=(E, E) requires false;
> +  //   bool k = E::e != E::e;
> +  //
> +  // ... should select the rewritten builtin candidate 'operator==(E,
> E)'. But
> +  // it seems unreasonable to consider rewritten builtin candidates. A
> core
> +  // issue has been filed proposing to removed this requirement.
>    AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet);
>
>    bool HadMultipleCandidates = (CandidateSet.size() > 1);
> @@ -12641,11 +12817,57 @@ Sema::CreateOverloadedBinOp(SourceLocati
>        // We found a built-in operator or an overloaded operator.
>        FunctionDecl *FnDecl = Best->Function;
>
> +      bool IsReversed = (Best->RewriteKind & CRK_Reversed);
> +      if (IsReversed)
> +        std::swap(Args[0], Args[1]);
> +
>        if (FnDecl) {
>          Expr *Base = nullptr;
>          // We matched an overloaded operator. Build a call to that
>          // operator.
>
> +        OverloadedOperatorKind ChosenOp =
> +            FnDecl->getDeclName().getCXXOverloadedOperator();
> +
> +        // C++2a [over.match.oper]p9:
> +        //   If a rewritten operator== candidate is selected by overload
> +        //   resolution for an operator@, its return type shall be cv
> bool
> +        if (Best->RewriteKind && ChosenOp == OO_EqualEqual &&
> +            !FnDecl->getReturnType()->isBooleanType()) {
> +          Diag(OpLoc, diag::err_ovl_rewrite_equalequal_not_bool)
> +              << FnDecl->getReturnType() <<
> BinaryOperator::getOpcodeStr(Opc)
> +              << Args[0]->getSourceRange() << Args[1]->getSourceRange();
> +          Diag(FnDecl->getLocation(), diag::note_declared_at);
> +          return ExprError();
> +        }
> +
> +        if (AllowRewrittenCandidates && !IsReversed &&
> +            CandidateSet.getRewriteInfo().shouldAddReversed(ChosenOp)) {
> +          // We could have reversed this operator, but didn't. Check if
> the
> +          // reversed form was a viable candidate, and if so, if it had a
> +          // better conversion for either parameter. If so, this call is
> +          // formally ambiguous, and allowing it is an extension.
> +          for (OverloadCandidate &Cand : CandidateSet) {
> +            if (Cand.Viable && Cand.Function == FnDecl &&
> +                Cand.RewriteKind & CRK_Reversed) {
> +              for (unsigned ArgIdx = 0; ArgIdx < 2; ++ArgIdx) {
> +                if (CompareImplicitConversionSequences(
> +                        *this, OpLoc, Cand.Conversions[ArgIdx],
> +                        Best->Conversions[ArgIdx]) ==
> +                    ImplicitConversionSequence::Better) {
> +                  Diag(OpLoc,
> diag::ext_ovl_ambiguous_oper_binary_reversed)
> +                      << BinaryOperator::getOpcodeStr(Opc)
> +                      << Args[0]->getType() << Args[1]->getType()
> +                      << Args[0]->getSourceRange() <<
> Args[1]->getSourceRange();
> +                  Diag(FnDecl->getLocation(),
> +
>  diag::note_ovl_ambiguous_oper_binary_reversed_candidate);
> +                }
> +              }
> +              break;
> +            }
> +          }
> +        }
> +
>          // Convert the arguments.
>          if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
>            // Best->Access is only meaningful for class members.
> @@ -12699,8 +12921,8 @@ Sema::CreateOverloadedBinOp(SourceLocati
>          ResultTy = ResultTy.getNonLValueExprType(Context);
>
>          CXXOperatorCallExpr *TheCall = CXXOperatorCallExpr::Create(
> -            Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc,
> FPFeatures,
> -            Best->IsADLCandidate);
> +            Context, ChosenOp, FnExpr.get(), Args, ResultTy, VK, OpLoc,
> +            FPFeatures, Best->IsADLCandidate);
>
>          if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
>                                  FnDecl))
> @@ -12722,7 +12944,46 @@ Sema::CreateOverloadedBinOp(SourceLocati
>                    isa<CXXMethodDecl>(FnDecl), OpLoc,
> TheCall->getSourceRange(),
>                    VariadicDoesNotApply);
>
> -        return MaybeBindToTemporary(TheCall);
> +        ExprResult R = MaybeBindToTemporary(TheCall);
> +        if (R.isInvalid())
> +          return ExprError();
> +
> +        // For a rewritten candidate, we've already reversed the arguments
> +        // if needed. Perform the rest of the rewrite now.
> +        if ((Best->RewriteKind & CRK_DifferentOperator) ||
> +            (Op == OO_Spaceship && IsReversed)) {
> +          if (Op == OO_ExclaimEqual) {
> +            assert(ChosenOp == OO_EqualEqual && "unexpected operator
> name");
> +            R = CreateBuiltinUnaryOp(OpLoc, UO_LNot, R.get());
> +          } else {
> +            assert(ChosenOp == OO_Spaceship && "unexpected operator
> name");
> +            llvm::APSInt Zero(Context.getTypeSize(Context.IntTy), false);
> +            Expr *ZeroLiteral =
> +                IntegerLiteral::Create(Context, Zero, Context.IntTy,
> OpLoc);
> +
> +            Sema::CodeSynthesisContext Ctx;
> +            Ctx.Kind =
> Sema::CodeSynthesisContext::RewritingOperatorAsSpaceship;
> +            Ctx.Entity = FnDecl;
> +            pushCodeSynthesisContext(Ctx);
> +
> +            R = CreateOverloadedBinOp(
> +                OpLoc, Opc, Fns, IsReversed ? ZeroLiteral : R.get(),
> +                IsReversed ? R.get() : ZeroLiteral, PerformADL,
> +                /*AllowRewrittenCandidates=*/false);
> +
> +            popCodeSynthesisContext();
> +          }
> +          if (R.isInvalid())
> +            return ExprError();
> +        } else {
> +          assert(ChosenOp == Op && "unexpected operator name");
> +        }
> +
> +        // Make a note in the AST if we did any rewriting.
> +        if (Best->RewriteKind != CRK_None)
> +          R = new (Context) CXXRewrittenBinaryOperator(R.get(),
> IsReversed);
> +
> +        return R;
>        } else {
>          // We matched a built-in operator. Convert the arguments, then
>          // break out so that we will build the appropriate built-in
> @@ -12812,10 +13073,12 @@ Sema::CreateOverloadedBinOp(SourceLocati
>          return ExprError();
>        }
>        CandidateSet.NoteCandidates(
> -          PartialDiagnosticAt(OpLoc, PDiag(diag::err_ovl_deleted_oper)
> -                                         <<
> BinaryOperator::getOpcodeStr(Opc)
> -                                         << Args[0]->getSourceRange()
> -                                         << Args[1]->getSourceRange()),
> +          PartialDiagnosticAt(
> +              OpLoc, PDiag(diag::err_ovl_deleted_oper)
> +                         <<
> getOperatorSpelling(Best->Function->getDeclName()
> +
> .getCXXOverloadedOperator())
> +                         << Args[0]->getSourceRange()
> +                         << Args[1]->getSourceRange()),
>            *this, OCD_AllCandidates, Args,
> BinaryOperator::getOpcodeStr(Opc),
>            OpLoc);
>        return ExprError();
> @@ -13692,8 +13955,8 @@ ExprResult Sema::BuildLiteralOperatorCal
>
>    OverloadCandidateSet CandidateSet(UDSuffixLoc,
>                                      OverloadCandidateSet::CSK_Normal);
> -  AddFunctionCandidates(R.asUnresolvedSet(), Args, CandidateSet,
> TemplateArgs,
> -                        /*SuppressUserConversions=*/true);
> +  AddNonMemberOperatorCandidates(R.asUnresolvedSet(), Args, CandidateSet,
> +                                 TemplateArgs);
>
>    bool HadMultipleCandidates = (CandidateSet.size() > 1);
>
>
> Modified: cfe/trunk/lib/Sema/SemaTemplate.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplate.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaTemplate.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaTemplate.cpp Fri Oct 18 17:04:43 2019
> @@ -24,6 +24,7 @@
>  #include "clang/Basic/TargetInfo.h"
>  #include "clang/Sema/DeclSpec.h"
>  #include "clang/Sema/Lookup.h"
> +#include "clang/Sema/Overload.h"
>  #include "clang/Sema/ParsedTemplate.h"
>  #include "clang/Sema/Scope.h"
>  #include "clang/Sema/SemaInternal.h"
> @@ -8488,7 +8489,7 @@ bool Sema::CheckFunctionTemplateSpeciali
>      // candidates at once, to get proper sorting and limiting.
>      for (auto *OldND : Previous) {
>        if (auto *OldFD =
> dyn_cast<FunctionDecl>(OldND->getUnderlyingDecl()))
> -        NoteOverloadCandidate(OldND, OldFD, FD->getType(), false);
> +        NoteOverloadCandidate(OldND, OldFD, CRK_None, FD->getType(),
> false);
>      }
>      FailedCandidates.NoteCandidates(*this, FD->getLocation());
>      return true;
>
> Modified: cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp (original)
> +++ cfe/trunk/lib/Sema/SemaTemplateInstantiate.cpp Fri Oct 18 17:04:43 2019
> @@ -206,6 +206,7 @@ bool Sema::CodeSynthesisContext::isInsta
>    case DefiningSynthesizedFunction:
>    case ExceptionSpecEvaluation:
>    case ConstraintSubstitution:
> +  case RewritingOperatorAsSpaceship:
>      return false;
>
>    // This function should never be called when Kind's value is
> Memoization.
> @@ -682,6 +683,11 @@ void Sema::PrintInstantiationStack() {
>        break;
>      }
>
> +    case CodeSynthesisContext::RewritingOperatorAsSpaceship:
> +      Diags.Report(Active->Entity->getLocation(),
> +                   diag::note_rewriting_operator_as_spaceship);
> +      break;
> +
>      case CodeSynthesisContext::Memoization:
>        break;
>
> @@ -754,6 +760,7 @@ Optional<TemplateDeductionInfo *> Sema::
>
>      case CodeSynthesisContext::DeclaringSpecialMember:
>      case CodeSynthesisContext::DefiningSynthesizedFunction:
> +    case CodeSynthesisContext::RewritingOperatorAsSpaceship:
>        // This happens in a context unrelated to template instantiation, so
>        // there is no SFINAE.
>        return None;
>
> Modified: cfe/trunk/lib/Sema/TreeTransform.h
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/TreeTransform.h?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/lib/Sema/TreeTransform.h (original)
> +++ cfe/trunk/lib/Sema/TreeTransform.h Fri Oct 18 17:04:43 2019
> @@ -2355,13 +2355,13 @@ public:
>
>    /// Build a new rewritten operator expression.
>    ///
> -  /// By default, builds the rewritten operator without performing any
> semantic
> -  /// analysis. Subclasses may override this routine to provide different
> -  /// behavior.
> -  ExprResult RebuildCXXRewrittenBinaryOperator(Expr *SemanticForm,
> -                                             bool IsReversed) {
> -    return new (getSema().Context)
> -        CXXRewrittenBinaryOperator(SemanticForm, IsReversed);
> +  /// By default, performs semantic analysis to build the new expression.
> +  /// Subclasses may override this routine to provide different behavior.
> +  ExprResult RebuildCXXRewrittenBinaryOperator(
> +      SourceLocation OpLoc, BinaryOperatorKind Opcode,
> +      const UnresolvedSetImpl &UnqualLookups, Expr *LHS, Expr *RHS) {
> +    return getSema().CreateOverloadedBinOp(OpLoc, Opcode, UnqualLookups,
> LHS,
> +                                           RHS, /*RequiresADL*/false);
>    }
>
>    /// Build a new conditional operator expression.
> @@ -9783,24 +9783,45 @@ TreeTransform<Derived>::TransformBinaryO
>  template <typename Derived>
>  ExprResult TreeTransform<Derived>::TransformCXXRewrittenBinaryOperator(
>      CXXRewrittenBinaryOperator *E) {
> -  // FIXME: C++ [temp.deduct]p7 "The substitution proceeds in lexical
> order and
> -  // stops when a condition that causes deduction to fail is encountered."
> -  // requires us to substitute into the LHS before the RHS, even in a
> rewrite
> -  // that reversed the operand order.
> -  //
> -  // We can't decompose back to a binary operator here, because that
> would lose
> -  // the unqualified lookup results from the phase 1 name lookup.
> +  CXXRewrittenBinaryOperator::DecomposedForm Decomp =
> E->getDecomposedForm();
>
> -  ExprResult SemanticForm =
> getDerived().TransformExpr(E->getSemanticForm());
> -  if (SemanticForm.isInvalid())
> +  ExprResult LHS =
> getDerived().TransformExpr(const_cast<Expr*>(Decomp.LHS));
> +  if (LHS.isInvalid())
> +    return ExprError();
> +
> +  ExprResult RHS =
> getDerived().TransformExpr(const_cast<Expr*>(Decomp.RHS));
> +  if (RHS.isInvalid())
>      return ExprError();
>
>    if (!getDerived().AlwaysRebuild() &&
> -      SemanticForm.get() == E->getSemanticForm())
> +      LHS.get() == Decomp.LHS &&
> +      RHS.get() == Decomp.RHS)
>      return E;
>
> -  return
> getDerived().RebuildCXXRewrittenBinaryOperator(SemanticForm.get(),
> -                                                      E->isReversed());
> +  // Extract the already-resolved callee declarations so that we can
> restrict
> +  // ourselves to using them as the unqualified lookup results when
> rebuilding.
> +  UnresolvedSet<2> UnqualLookups;
> +  Expr *PossibleBinOps[] = {E->getSemanticForm(),
> +                            const_cast<Expr *>(Decomp.InnerBinOp)};
> +  for (Expr *PossibleBinOp : PossibleBinOps) {
> +    auto *Op =
> dyn_cast<CXXOperatorCallExpr>(PossibleBinOp->IgnoreImplicit());
> +    if (!Op)
> +      continue;
> +    auto *Callee =
> dyn_cast<DeclRefExpr>(Op->getCallee()->IgnoreImplicit());
> +    if (!Callee || isa<CXXMethodDecl>(Callee->getDecl()))
> +      continue;
> +
> +    // Transform the callee in case we built a call to a local extern
> +    // declaration.
> +    NamedDecl *Found = cast_or_null<NamedDecl>(getDerived().TransformDecl(
> +        E->getOperatorLoc(), Callee->getFoundDecl()));
> +    if (!Found)
> +      return ExprError();
> +    UnqualLookups.addDecl(Found);
> +  }
> +
> +  return getDerived().RebuildCXXRewrittenBinaryOperator(
> +      E->getOperatorLoc(), Decomp.Opcode, UnqualLookups, LHS.get(),
> RHS.get());
>  }
>
>  template<typename Derived>
>
> Added:
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp?rev=375306&view=auto
>
> ==============================================================================
> ---
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
> (added)
> +++
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
> Fri Oct 18 17:04:43 2019
> @@ -0,0 +1,172 @@
> +// RUN: %clang_cc1 -std=c++2a -verify %s
> +// RUN: %clang_cc1 -std=c++2a -verify -Wall -DNO_ERRORS %s
> +
> +#ifndef NO_ERRORS
> +namespace bullet3 {
> +  // the built-in candidates include all of the candidate operator
> fnuctions
> +  // [...] that, compared to the given operator
> +
> +  // - do not have the same parameter-type-list as any non-member
> candidate
> +
> +  enum E { e };
> +
> +  // Suppress both builtin operator<=>(E, E) and operator<(E, E).
> +  void operator<=>(E, E); // expected-note {{while rewriting}}
> +  bool cmp = e < e; // expected-error {{invalid operands to binary
> expression ('void' and 'int')}}
> +
> +  // None of the other bullets have anything to test here. In principle we
> +  // need to suppress both builtin operator@(A, B) and operator@(B, A)
> when we
> +  // see a user-declared reversible operator@(A, B), and we do, but
> that's
> +  // untestable because the only built-in reversible candidates are
> +  // operator<=>(E, E) and operator==(E, E) for E an enumeration type, and
> +  // those are both symmetric anyway.
> +}
> +
> +namespace bullet4 {
> +  // The rewritten candidate set is determined as follows:
> +
> +  template<int> struct X {};
> +  X<1> x1;
> +  X<2> x2;
> +
> +  struct Y {
> +    int operator<=>(X<2>) = delete; // #1member
> +    bool operator==(X<2>) = delete; // #2member
> +  };
> +  Y y;
> +
> +  // - For the relational operators, the rewritten candidates include all
> +  //   non-rewritten candidates for the expression x <=> y.
> +  int operator<=>(X<1>, X<2>) = delete; // #1
> +
> +  // expected-note@#1 5{{candidate function has been explicitly deleted}}
> +  // expected-note@#1 5{{candidate function (with reversed parameter
> order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st
> argument}}
> +  bool lt = x1 < x2; // expected-error {{selected deleted operator '<=>'}}
> +  bool le = x1 <= x2; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool gt = x1 > x2; // expected-error {{selected deleted operator '<=>'}}
> +  bool ge = x1 >= x2; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool cmp = x1 <=> x2; // expected-error {{selected deleted operator
> '<=>'}}
> +
> +  // expected-note@#1member 5{{candidate function has been explicitly
> deleted}}
> +  // expected-note@#1 5{{candidate function not viable: no known
> conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}
> +  // expected-note@#1 5{{candidate function (with reversed parameter
> order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st
> argument}}
> +  bool mem_lt = y < x2; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_le = y <= x2; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_gt = y > x2; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_ge = y >= x2; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_cmp = y <=> x2; // expected-error {{selected deleted operator
> '<=>'}}
> +
> +  // - For the relational and three-way comparison operators, the
> rewritten
> +  //   candidates also include a synthesized candidate, with the order of
> the
> +  //   two parameters reversed, for each non-rewritten candidate for the
> +  //   expression y <=> x.
> +
> +  // expected-note@#1 5{{candidate function (with reversed parameter
> order) has been explicitly deleted}}
> +  // expected-note@#1 5{{candidate function not viable: no known
> conversion from 'X<2>' to 'X<1>' for 1st argument}}
> +  bool rlt = x2 < x1; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool rle = x2 <= x1; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool rgt = x2 > x1; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool rge = x2 >= x1; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool rcmp = x2 <=> x1; // expected-error {{selected deleted operator
> '<=>'}}
> +
> +  // expected-note@#1member 5{{candidate function (with reversed
> parameter order) has been explicitly deleted}}
> +  // expected-note@#1 5{{candidate function not viable: no known
> conversion from 'X<2>' to 'X<1>' for 1st argument}}
> +  // expected-note@#1 5{{candidate function (with reversed parameter
> order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd
> argument}}
> +  bool mem_rlt = x2 < y; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_rle = x2 <= y; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_rgt = x2 > y; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_rge = x2 >= y; // expected-error {{selected deleted operator
> '<=>'}}
> +  bool mem_rcmp = x2 <=> y; // expected-error {{selected deleted operator
> '<=>'}}
> +
> +  // For the != operator, the rewritten candidates include all
> non-rewritten
> +  // candidates for the expression x == y
> +  int operator==(X<1>, X<2>) = delete; // #2
> +
> +  // expected-note@#2 2{{candidate function has been explicitly deleted}}
> +  // expected-note@#2 2{{candidate function (with reversed parameter
> order) not viable: no known conversion from 'X<1>' to 'X<2>' for 1st
> argument}}
> +  bool eq = x1 == x2; // expected-error {{selected deleted operator '=='}}
> +  bool ne = x1 != x2; // expected-error {{selected deleted operator '=='}}
> +
> +  // expected-note@#2member 2{{candidate function has been explicitly
> deleted}}
> +  // expected-note@#2 2{{candidate function not viable: no known
> conversion from 'bullet4::Y' to 'X<1>' for 1st argument}}
> +  // expected-note@#2 2{{candidate function (with reversed parameter
> order) not viable: no known conversion from 'bullet4::Y' to 'X<2>' for 1st
> argument}}
> +  bool mem_eq = y == x2; // expected-error {{selected deleted operator
> '=='}}
> +  bool mem_ne = y != x2; // expected-error {{selected deleted operator
> '=='}}
> +
> +  // For the equality operators, the rewritten candidates also include a
> +  // synthesized candidate, with the order of the two parameters
> reversed, for
> +  // each non-rewritten candidate for the expression y == x
> +
> +  // expected-note@#2 2{{candidate function (with reversed parameter
> order) has been explicitly deleted}}
> +  // expected-note@#2 2{{candidate function not viable: no known
> conversion from 'X<2>' to 'X<1>' for 1st argument}}
> +  bool req = x2 == x1; // expected-error {{selected deleted operator
> '=='}}
> +  bool rne = x2 != x1; // expected-error {{selected deleted operator
> '=='}}
> +
> +  // expected-note@#2member 2{{candidate function (with reversed
> parameter order) has been explicitly deleted}}
> +  // expected-note@#2 2{{candidate function not viable: no known
> conversion from 'X<2>' to 'X<1>' for 1st argument}}
> +  // expected-note@#2 2{{candidate function (with reversed parameter
> order) not viable: no known conversion from 'bullet4::Y' to 'X<1>' for 2nd
> argument}}
> +  bool mem_req = x2 == y; // expected-error {{selected deleted operator
> '=='}}
> +  bool mem_rne = x2 != y; // expected-error {{selected deleted operator
> '=='}}
> +
> +  // For all other operators, the rewritten candidate set is empty.
> +  X<3> operator+(X<1>, X<2>) = delete; // expected-note {{no known
> conversion from 'X<2>' to 'X<1>'}}
> +  X<3> reversed_add = x2 + x1; // expected-error {{invalid operands}}
> +}
> +
> +// Various C++17 cases that are known to be broken by the C++20 rules.
> +namespace problem_cases {
> +  // We can have an ambiguity between an operator and its reversed form.
> This
> +  // wasn't intended by the original "consistent comparison" proposal,
> and we
> +  // allow it as extension, picking the non-reversed form.
> +  struct A {
> +    bool operator==(const A&); // expected-note {{ambiguity is between a
> regular call to this operator and a call with the argument order reversed}}
> +  };
> +  bool cmp_non_const = A() == A(); // expected-warning {{ambiguous}}
> +
> +  struct B {
> +    virtual bool operator==(const B&) const;
> +  };
> +  struct D : B {
> +    bool operator==(const B&) const override; // expected-note
> {{operator}}
> +  };
> +  bool cmp_base_derived = D() == D(); // expected-warning {{ambiguous}}
> +
> +  template<typename T> struct CRTPBase {
> +    bool operator==(const T&) const; // expected-note {{operator}}
> +  };
> +  struct CRTP : CRTPBase<CRTP> {};
> +  bool cmp_crtp = CRTP() == CRTP(); // expected-warning {{ambiguous}}
> +
> +  // We can select a non-rewriteable operator== for a != comparison, when
> there
> +  // was a viable operator!= candidate we could have used instead.
> +  //
> +  // Rejecting this seems OK on balance.
> +  using UBool = signed char; // ICU uses this.
> +  struct ICUBase {
> +    virtual UBool operator==(const ICUBase&) const;
> +    UBool operator!=(const ICUBase &arg) const { return !operator==(arg);
> }
> +  };
> +  struct ICUDerived : ICUBase {
> +    UBool operator==(const ICUBase&) const override; // expected-note
> {{declared here}}
> +  };
> +  bool cmp_icu = ICUDerived() != ICUDerived(); // expected-error {{not
> 'bool'}}
> +}
> +
> +#else // NO_ERRORS
> +
> +namespace problem_cases {
> +  // We can select a reversed candidate where we used to select a
> non-reversed
> +  // one, and in the worst case this can dramatically change the meaning
> of the
> +  // program. Make sure we at least warn on the worst cases under -Wall.
> +  struct iterator;
> +  struct const_iterator {
> +    const_iterator(iterator);
> +    bool operator==(const const_iterator&) const;
> +  };
> +  struct iterator {
> +    bool operator==(const const_iterator &o) const { // expected-warning
> {{all paths through this function will call itself}}
> +      return o == *this;
> +    }
> +  };
> +}
> +#endif // NO_ERRORS
>
> Added:
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp?rev=375306&view=auto
>
> ==============================================================================
> ---
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
> (added)
> +++
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
> Fri Oct 18 17:04:43 2019
> @@ -0,0 +1,70 @@
> +// RUN: %clang_cc1 -std=c++2a -verify %s
> +
> +template<typename L, typename R> struct Op { L l; const char *op; R r; };
> +// FIXME: Remove once we implement P1816R0.
> +template<typename L, typename R> Op(L, R) -> Op<L, R>;
> +
> +struct A {};
> +struct B {};
> +constexpr Op<A, B> operator<=>(A a, B b) { return {a, "<=>", b}; }
> +
> +template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V>
> operator<  (Op<T, U> a, V b) { return {a, "<",   b}; }
> +template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V>
> operator<= (Op<T, U> a, V b) { return {a, "<=",  b}; }
> +template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V>
> operator>  (Op<T, U> a, V b) { return {a, ">",   b}; }
> +template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V>
> operator>= (Op<T, U> a, V b) { return {a, ">=",  b}; }
> +template<typename T, typename U, typename V> constexpr Op<Op<T, U>, V>
> operator<=>(Op<T, U> a, V b) { return {a, "<=>", b}; }
> +
> +template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>>
> operator<  (T a, Op<U, V> b) { return {a, "<",   b}; }
> +template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>>
> operator<= (T a, Op<U, V> b) { return {a, "<=",  b}; }
> +template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>>
> operator>  (T a, Op<U, V> b) { return {a, ">",   b}; }
> +template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>>
> operator>= (T a, Op<U, V> b) { return {a, ">=",  b}; }
> +template<typename T, typename U, typename V> constexpr Op<T, Op<U, V>>
> operator<=>(T a, Op<U, V> b) { return {a, "<=>", b}; }
> +
> +constexpr bool same(A, A) { return true; }
> +constexpr bool same(B, B) { return true; }
> +constexpr bool same(int a, int b) { return a == b; }
> +template<typename T, typename U>
> +constexpr bool same(Op<T, U> x, Op<T, U> y) {
> +  return same(x.l, y.l) && __builtin_strcmp(x.op, y.op) == 0 && same(x.r,
> y.r);
> +}
> +
> +// x @ y is interpreted as:
> +void f(A x, B y) {
> +  //   --  (x <=> y) @ 0 if not reversed
> +  static_assert(same(x < y, (x <=> y) < 0));
> +  static_assert(same(x <= y, (x <=> y) <= 0));
> +  static_assert(same(x > y, (x <=> y) > 0));
> +  static_assert(same(x >= y, (x <=> y) >= 0));
> +  static_assert(same(x <=> y, x <=> y)); // (not rewritten)
> +}
> +
> +void g(B x, A y) {
> +  //   --  0 @ (y <=> x) if reversed
> +  static_assert(same(x < y, 0 < (y <=> x)));
> +  static_assert(same(x <= y, 0 <= (y <=> x)));
> +  static_assert(same(x > y, 0 > (y <=> x)));
> +  static_assert(same(x >= y, 0 >= (y <=> x)));
> +  static_assert(same(x <=> y, 0 <=> (y <=> x)));
> +}
> +
> +
> +// We can rewrite into a call involving a builtin operator.
> +struct X { int result; };
> +struct Y {};
> +constexpr int operator<=>(X x, Y) { return x.result; }
> +static_assert(X{-1} < Y{});
> +static_assert(X{0} < Y{}); // expected-error {{failed}}
> +static_assert(X{0} <= Y{});
> +static_assert(X{1} <= Y{}); // expected-error {{failed}}
> +static_assert(X{1} > Y{});
> +static_assert(X{0} > Y{}); // expected-error {{failed}}
> +static_assert(X{0} >= Y{});
> +static_assert(X{-1} >= Y{}); // expected-error {{failed}}
> +static_assert(Y{} < X{1});
> +static_assert(Y{} < X{0}); // expected-error {{failed}}
> +static_assert(Y{} <= X{0});
> +static_assert(Y{} <= X{-1}); // expected-error {{failed}}
> +static_assert(Y{} > X{-1});
> +static_assert(Y{} > X{0}); // expected-error {{failed}}
> +static_assert(Y{} >= X{0});
> +static_assert(Y{} >= X{1}); // expected-error {{failed}}
>
> Added:
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp?rev=375306&view=auto
>
> ==============================================================================
> ---
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
> (added)
> +++
> cfe/trunk/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
> Fri Oct 18 17:04:43 2019
> @@ -0,0 +1,38 @@
> +// RUN: %clang_cc1 -std=c++2a -verify %s
> +
> +// ... return type shall be cv bool ...
> +namespace not_bool {
> +  struct X {} x;
> +  struct Y {} y;
> +  int operator==(X, Y); // expected-note 4{{here}}
> +  bool a = x == y; // ok
> +  bool b = y == x; // expected-error {{return type 'int' of selected
> 'operator==' function for rewritten '==' comparison is not 'bool'}}
> +  bool c = x != y; // expected-error {{return type 'int' of selected
> 'operator==' function for rewritten '!=' comparison is not 'bool'}}
> +  bool d = y != x; // expected-error {{return type 'int' of selected
> 'operator==' function for rewritten '!=' comparison is not 'bool'}}
> +
> +  // cv-qualifiers are OK
> +  const bool operator==(Y, X);
> +  bool e = y != x; // ok
> +
> +  // We don't prefer a function with bool return type over one witn
> non-bool return type.
> +  bool f = x != y; // expected-error {{return type 'int' of selected
> 'operator==' function for rewritten '!=' comparison is not 'bool'}}
> +}
> +
> +struct X { bool equal; };
> +struct Y {};
> +constexpr bool operator==(X x, Y) { return x.equal; }
> +
> +static_assert(X{true} == Y{});
> +static_assert(X{false} == Y{}); // expected-error {{failed}}
> +
> +// x == y -> y == x
> +static_assert(Y{} == X{true});
> +static_assert(Y{} == X{false}); // expected-error {{failed}}
> +
> +// x != y -> !(x == y)
> +static_assert(X{true} != Y{}); // expected-error {{failed}}
> +static_assert(X{false} != Y{});
> +
> +// x != y -> !(y == x)
> +static_assert(Y{} != X{true}); // expected-error {{failed}}
> +static_assert(Y{} != X{false});
>
> Modified: cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp (original)
> +++ cfe/trunk/test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp Fri Oct 18
> 17:04:43 2019
> @@ -1,4 +1,5 @@
>  // RUN: %clang_cc1 -std=c++11 -verify %s
> +// RUN: %clang_cc1 -std=c++2a -verify %s
>
>  struct Q { typedef int type; };
>
> @@ -20,3 +21,36 @@ template<typename T, template<typename T
>  int &c(...);
>  int &c_disabled = c(0);
>  int &c_enabled = c(Q()); // expected-error {{cannot bind to a temporary
> of type 'void'}}
> +
> +// The substitution proceeds in lexical order and stops when a condition
> that
> +// causes deduction to fail is encountered.
> +#if __cplusplus > 201702L
> +namespace reversed_operator_substitution_order {
> +  struct X { X(int); };
> +  struct Y { Y(int); };
> +  struct Cat {};
> +  namespace no_adl {
> +    Cat operator<=>(Y, X);
> +    bool operator<(int, Cat);
> +
> +    template<typename T> struct indirect_sizeof {
> +      static_assert(sizeof(T) != 0);
> +      static const auto value = sizeof(T);
> +    };
> +
> +    // We should substitute into the construction of the X object before
> the
> +    // construction of the Y object, so this is a SFINAE case rather than
> a
> +    // hard error. This requires substitution to proceed in lexical order
> +    // despite the prior rewrite to
> +    //    0 < (Y(...) <=> X(...))
> +    template<typename T> float &f(
> +        decltype(
> +          X(sizeof(T)) < Y(indirect_sizeof<T>::value)
> +        )
> +    );
> +    template<typename T> int &f(...);
> +  }
> +  int &r = no_adl::f<void>(true);
> +  float &s = no_adl::f<int>(true);
> +}
> +#endif
>
> Added: cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp?rev=375306&view=auto
>
> ==============================================================================
> --- cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp (added)
> +++ cfe/trunk/test/CodeGenCXX/mangle-cxx2a.cpp Fri Oct 18 17:04:43 2019
> @@ -0,0 +1,11 @@
> +// RUN: %clang_cc1 -emit-llvm %s -o - -triple=x86_64-linux-gnu -std=c++2a
> | FileCheck %s
> +
> +namespace spaceship {
> +  struct X {};
> +  struct Y {};
> +  int operator<=>(X, Y);
> +
> +  // CHECK-LABEL: define {{.*}}
> @_ZN9spaceship1fIiEEvDTcmltcvNS_1YE_EcvNS_1XE_EcvT__EE
> +  template<typename T> void f(decltype(Y() < X(), T()) x) {}
> +  template void f<int>(int);
> +}
>
> Modified: cfe/trunk/test/PCH/cxx2a-compare.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx2a-compare.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/PCH/cxx2a-compare.cpp (original)
> +++ cfe/trunk/test/PCH/cxx2a-compare.cpp Fri Oct 18 17:04:43 2019
> @@ -15,6 +15,16 @@ inline auto bar(int x) {
>    return (1 <=> x);
>  }
>
> +struct X {
> +  int a;
> +  friend constexpr std::strong_ordering operator<=>(const X &x, const X
> &y) {
> +    return x.a <=> y.a;
> +  }
> +};
> +constexpr auto baz(int x) {
> +  return X{3} < X{x};
> +}
> +
>  #else
>
>  // expected-no-diagnostics
> @@ -25,4 +35,7 @@ auto bar2(int x) {
>    return bar(x);
>  }
>
> +static_assert(!baz(3));
> +static_assert(baz(4));
> +
>  #endif
>
> Modified: cfe/trunk/test/SemaCXX/compare-cxx2a.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/compare-cxx2a.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/compare-cxx2a.cpp (original)
> +++ cfe/trunk/test/SemaCXX/compare-cxx2a.cpp Fri Oct 18 17:04:43 2019
> @@ -298,11 +298,11 @@ void test_enum_enum_compare_no_builtin()
>
>  template <int>
>  struct Tag {};
> -// expected-note at +1 {{candidate}}
> -Tag<0> operator<=>(EnumA, EnumA) {
> +Tag<0> operator<=>(EnumA, EnumA) { // expected-note {{not viable}}
>    return {};
>  }
> -Tag<1> operator<=>(EnumA, EnumB) {
> +// expected-note at +1 {{while rewriting comparison as call to
> 'operator<=>' declared here}}
> +Tag<1> operator<=>(EnumA, EnumB) { // expected-note {{not viable}}
>    return {};
>  }
>
> @@ -311,7 +311,7 @@ void test_enum_ovl_provided() {
>    ASSERT_EXPR_TYPE(r1, Tag<0>);
>    auto r2 = (EnumA::A <=> EnumB::B);
>    ASSERT_EXPR_TYPE(r2, Tag<1>);
> -  (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to
> binary expression ('EnumCompareTests::EnumB' and
> 'EnumCompareTests::EnumA')}}
> +  (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to
> binary expression ('int' and 'Tag<1>')}}
>  }
>
>  void enum_float_test() {
>
> Modified: cfe/trunk/test/SemaCXX/self-comparison.cpp
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/self-comparison.cpp?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/test/SemaCXX/self-comparison.cpp (original)
> +++ cfe/trunk/test/SemaCXX/self-comparison.cpp Fri Oct 18 17:04:43 2019
> @@ -5,7 +5,7 @@ int foo(int x) {
>  }
>
>  struct X {
> -  bool operator==(const X &x);
> +  bool operator==(const X &x) const;
>  };
>
>  struct A {
>
> Modified: cfe/trunk/www/cxx_status.html
> URL:
> http://llvm.org/viewvc/llvm-project/cfe/trunk/www/cxx_status.html?rev=375306&r1=375305&r2=375306&view=diff
>
> ==============================================================================
> --- cfe/trunk/www/cxx_status.html (original)
> +++ cfe/trunk/www/cxx_status.html Fri Oct 18 17:04:43 2019
> @@ -920,23 +920,26 @@ as the draft C++2a standard evolves.
>      <tr>
>        <td rowspan="6">Consistent comparison
> (<tt>operator<=></tt>)</td>
>        <td><a href="http://wg21.link/p0515r3">P0515R3</a></td>
> -      <td rowspan="3" class="partial" align="center">Partial</td>
> +      <td class="partial" align="center">Partial</td>
>      </tr>
>        <tr> <!-- from Jacksonville -->
>          <td><a href="http://wg21.link/p0905r1">P0905R1</a></td>
> +        <td class="svn" align="center">Clang 10</td>
>        </tr>
>        <tr> <!-- from Rapperswil -->
>          <td><a href="http://wg21.link/p1120r0">P1120R0</a></td>
> +        <td rowspan="2" class="partial" align="center">Partial</td>
>        </tr>
>        <tr> <!-- from Kona 2019 -->
>          <td><a href="http://wg21.link/p1185r2">P1185R2</a></td>
> -        <td rowspan="3" class="none" align="center">No</td>
>        </tr>
>        <tr> <!-- from Cologne -->
>          <td><a href="http://wg21.link/p1186r3">P1186R3</a></td>
> +        <td class="none" align="center">No</td>
>        </tr>
>        <tr>
>          <td><a href="http://wg21.link/p1630r1">P1630R1</a></td>
> +        <td class="partial" align="center">Partial</td>
>        </tr>
>      <tr>
>        <td>Access checking on specializations</td>
>
>
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>


-- 
-- 
Peter
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200113/dcf9d827/attachment-0001.html>


More information about the cfe-commits mailing list