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

Richard Smith via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 13 17:50:08 PST 2020


On Mon, 13 Jan 2020 at 15:12, Peter Collingbourne via cfe-commits <
cfe-commits at lists.llvm.org> wrote:

> 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.
>

Thanks. Yes, this C++20 change is known to break some (hopefully mostly
rare) code patterns. I'd not seen the one above before, but I think that's
moderately likely to stay broken in C++20 onwards; the ambiguity between
non-reversed and reversed != seems real here (note that C++17 wouldn't have
allowed 0 != a, but C++20 does, which is why this became ambiguous). I'll
ask on the committee reflectors just in case.

Hopefully you're only seeing a build break under -std=c++2a? (If not,
that'd be a problem.)


> 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
> _______________________________________________
> cfe-commits mailing list
> cfe-commits at lists.llvm.org
> https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/cfe-commits/attachments/20200113/36b03f64/attachment-0001.html>


More information about the cfe-commits mailing list