]> granicus.if.org Git - clang/commitdiff
[c++20] Add rewriting from comparison operators to <=> / ==.
authorRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 19 Oct 2019 00:04:43 +0000 (00:04 +0000)
committerRichard Smith <richard-llvm@metafoo.co.uk>
Sat, 19 Oct 2019 00:04:43 +0000 (00:04 +0000)
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.

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@375306 91177308-0d34-0410-b5e6-96231b3b80d8

21 files changed:
include/clang/AST/ExprCXX.h
include/clang/Basic/DiagnosticSemaKinds.td
include/clang/Basic/OperatorKinds.h
include/clang/Sema/Overload.h
include/clang/Sema/Sema.h
lib/AST/ExprCXX.cpp
lib/Frontend/FrontendActions.cpp
lib/Sema/SemaExpr.cpp
lib/Sema/SemaOverload.cpp
lib/Sema/SemaTemplate.cpp
lib/Sema/SemaTemplateInstantiate.cpp
lib/Sema/TreeTransform.h
test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp [new file with mode: 0644]
test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp [new file with mode: 0644]
test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp [new file with mode: 0644]
test/CXX/temp/temp.fct.spec/temp.deduct/p7.cpp
test/CodeGenCXX/mangle-cxx2a.cpp [new file with mode: 0644]
test/PCH/cxx2a-compare.cpp
test/SemaCXX/compare-cxx2a.cpp
test/SemaCXX/self-comparison.cpp
www/cxx_status.html

index 2b3085183c1bc1f71d478491bf6a8997d99f2857..7b7ca9bf8f0f07eca26c0379ead5ac30dd3307f6 100644 (file)
@@ -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
index 755fac842d82dce945d5f0b78051b94f22219703..e41cfe2fe7310f16eed708f85598b040b74ddbed 100644 (file)
@@ -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 : Error<
    "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<
index 9757acaa53009bf50625915e6fc59f60858644fc..d661891445115a3b35df99798a4539fe0c81f3c3 100644 (file)
@@ -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
index 144e6d927cdba476bd09ac38b290995e8789b568..a97a7181f7d5fa4fca6be7b64034d4ce48f8eafb 100644 (file)
@@ -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.
index d3481af50a53fdb73c8056e9a0fb2310084cf660..1e3d6b93d04802db1923871f6457faf1f603acc6 100644 (file)
@@ -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,
-                                   OverloadCandidateSetCandidateSet,
-                                   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
index 0b0d3c6880a733a01fd554f90f70ed8d8d7b222a..904928bdf2861e2ae85e8c7d334d9bcf7d7263b2 100644 (file)
@@ -79,7 +79,7 @@ CXXRewrittenBinaryOperator::getDecomposedForm() const {
     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::getDecomposedForm() const {
     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();
index fea5826179ac187c8be60ae610bf96a590270fb1..4d47c768ad3c2532786e1f9e2ee2bdc42b81552a 100644 (file)
@@ -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:
index 333983a430df6dadc87516e7773f4984e87759f7..2734b4d076013a33fdb6d5e1ab49a2799659f63f 100644 (file)
@@ -13310,6 +13310,13 @@ static ExprResult BuildOverloadedBinOp(Sema &S, Scope *Sc, SourceLocation OpLoc,
     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);
index 80bd3562bc7fda9ea664426ca7c9e0ee251cc358..6793079ff0ab5d8c471c1ac153bda80eaef9c7d7 100644 (file)
@@ -848,6 +848,25 @@ llvm::Optional<unsigned> DeductionFailureInfo::getCallArgIndex() {
   }
 }
 
+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 UnresolvedSetImpl &Fns,
         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 UnresolvedSetImpl &Fns,
 
 /// 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(DeclAccessPair FoundDecl,
     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 *Method, DeclAccessPair FoundDecl,
                          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 *Method, DeclAccessPair FoundDecl,
       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 *Method, DeclAccessPair FoundDecl,
     // 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 *Method, DeclAccessPair FoundDecl,
   // 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 *Method, DeclAccessPair FoundDecl,
       // (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 *Method, DeclAccessPair FoundDecl,
       // (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 *Method, DeclAccessPair FoundDecl,
 /// 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(FunctionTemplateDecl *MethodTmpl,
             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(FunctionTemplateDecl *MethodTmpl,
   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(CXXConversionDecl *Conversion,
   }
 }
 
+/// 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(CXXConversionDecl *Conversion,
 void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
                                        SourceLocation OpLoc,
                                        ArrayRef<Expr *> Args,
-                                       OverloadCandidateSetCandidateSet,
-                                       SourceRange OpRange) {
+                                       OverloadCandidateSet &CandidateSet,
+                                       OverloadCandidateParamOrder PO) {
   DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);
 
   // C++ [over.match.oper]p3:
@@ -7378,7 +7456,7 @@ void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
          ++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, NamedDecl *Found, FunctionDecl *Fn,
   }();
 
   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::checkAddressOfFunctionIsAvailable(const FunctionDecl *Function,
 
 // 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(NamedDecl *Found, FunctionDecl *Fn,
 
   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(Expr *OverloadedExpr, QualType DestType,
        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 &S, OverloadCandidate *Cand,
 
   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 &S, NamedDecl *Found, Decl *D,
 
   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, OverloadCandidate *Cand) {
 
   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 &S, OverloadCandidate *Cand,
     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 &S, OverloadCandidate *Cand,
     }
 
     // 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 &S, OverloadCandidate *Cand,
   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 &S, OverloadCandidate *Cand,
     // 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 CompareOverloadCandidatesForDisplay {
 /// 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(Sema &S, OverloadCandidate *Cand,
   bool SuppressUserConversions = false;
 
   unsigned ConvIdx = 0;
+  unsigned ArgIdx = 0;
   ArrayRef<QualType> ParamTypes;
 
   if (Cand->IsSurrogate) {
@@ -10842,15 +10971,18 @@ static void CompleteNonViableCandidate(Sema &S, OverloadCandidate *Cand,
     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(Sema &S, OverloadCandidate *Cand,
   }
 
   // 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> OverloadCandidateSet::CompleteCandidates(
     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(SourceLocation OpLoc, UnaryOperatorKind Opc,
   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(SourceLocation OpLoc, UnaryOperatorKind Opc,
 ///
 /// \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(SourceLocation OpLoc,
     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(SourceLocation OpLoc,
       // 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(SourceLocation OpLoc,
         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(SourceLocation OpLoc,
                   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(SourceLocation OpLoc,
         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::BuildLiteralOperatorCall(LookupResult &R,
 
   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);
 
index 09cc525837c0dff7e115aa76a2a0f1deb8136914..3f2d38630c3630eeef2bd409b25e28ddcde4731d 100644 (file)
@@ -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::CheckFunctionTemplateSpecialization(
     // 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;
index 42411c9c33f1614e7a8513abed9b9fe9f6bbd82f..0daa33cfbef553d5bdb772be5d3810b52f95b8dc 100644 (file)
@@ -206,6 +206,7 @@ bool Sema::CodeSynthesisContext::isInstantiationRecord() const {
   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::isSFINAEContext() const {
 
     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;
index 461cd909c2046500a25ef4ec19e67b36e7e165d8..89a7b8cc845e659d5715d67e0ba8cb288bcf3161 100644 (file)
@@ -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>::TransformBinaryOperator(BinaryOperator *E) {
 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>
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p3-2a.cpp
new file mode 100644 (file)
index 0000000..3be8dc7
--- /dev/null
@@ -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
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p8-2a.cpp
new file mode 100644 (file)
index 0000000..da0f576
--- /dev/null
@@ -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}}
diff --git a/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp b/test/CXX/over/over.match/over.match.funcs/over.match.oper/p9-2a.cpp
new file mode 100644 (file)
index 0000000..fce4681
--- /dev/null
@@ -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});
index bc074ba25e8ef9e1b4a7555c47d38e4da8307a19..f0ab7b8ea7612fa3fd0ef5ff70db532bfe251541 100644 (file)
@@ -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::type...> class ...X> void c(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
diff --git a/test/CodeGenCXX/mangle-cxx2a.cpp b/test/CodeGenCXX/mangle-cxx2a.cpp
new file mode 100644 (file)
index 0000000..3e46899
--- /dev/null
@@ -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);
+}
index 9ad368d88fd9f04cce41b7e239d1fad52b67e740..019544d28c18471914300465ba8295144b19a5c3 100644 (file)
@@ -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
index b6e7fc806114bdf32c8c1ad00c3f2ae7acb1dfa8..28854a77fb24a9a57c9b4199502de2ea16babef4 100644 (file)
@@ -298,11 +298,11 @@ void test_enum_enum_compare_no_builtin() {
 
 template <int>
 struct Tag {};
-// expected-note@+1 {{candidate}}
-Tag<0> operator<=>(EnumA, EnumA) {
+Tag<0> operator<=>(EnumA, EnumA) { // expected-note {{not viable}}
   return {};
 }
-Tag<1> operator<=>(EnumA, EnumB) {
+// expected-note@+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() {
index e20706a8b6cc9534599cd515c015eaa5697500da..95b830c910f5454cc20c8d3ef4ada231d6397a54 100644 (file)
@@ -5,7 +5,7 @@ int foo(int x) {
 }
 
 struct X {
-  bool operator==(const X &x);
+  bool operator==(const X &x) const;
 };
 
 struct A {
index 322c49a6147145a1774725b0d6a78e2138d258ee..70fab7ab3405abb8447bb1a08cae0530b603c3e8 100755 (executable)
@@ -920,23 +920,26 @@ as the draft C++2a standard evolves.
     <tr>
       <td rowspan="6">Consistent comparison (<tt>operator&lt;=&gt;</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>