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

Peter Collingbourne via cfe-commits cfe-commits at lists.llvm.org
Mon Jan 13 18:03:56 PST 2020


On Mon, Jan 13, 2020 at 5:50 PM Richard Smith <richard at metafoo.co.uk> wrote:

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

Got it, thanks for the information.

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

Yes, I'm only seeing this under -std=c++2a.

Peter

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

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


More information about the cfe-commits mailing list