EricWF updated this revision to Diff 146274.
EricWF marked 13 inline comments as done.
EricWF added a comment.

- Change `CXXRewrittenExpr` to be a non-general wrapper only for rewritten 
comparison operators.
- Start fixing and improving overload resolution diagnostics.


https://reviews.llvm.org/D45680

Files:
  include/clang/AST/ComparisonCategories.h
  include/clang/AST/Expr.h
  include/clang/AST/ExprCXX.h
  include/clang/AST/RecursiveASTVisitor.h
  include/clang/AST/Stmt.h
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Basic/StmtNodes.td
  include/clang/Sema/Overload.h
  include/clang/Sema/Sema.h
  include/clang/Serialization/ASTBitCodes.h
  lib/AST/ComparisonCategories.cpp
  lib/AST/Expr.cpp
  lib/AST/ExprCXX.cpp
  lib/AST/ExprClassification.cpp
  lib/AST/ExprConstant.cpp
  lib/AST/ItaniumMangle.cpp
  lib/AST/StmtPrinter.cpp
  lib/AST/StmtProfile.cpp
  lib/CodeGen/CGExprScalar.cpp
  lib/Sema/SemaExceptionSpec.cpp
  lib/Sema/SemaExpr.cpp
  lib/Sema/SemaOverload.cpp
  lib/Sema/TreeTransform.h
  lib/Serialization/ASTReaderStmt.cpp
  lib/Serialization/ASTWriterStmt.cpp
  lib/StaticAnalyzer/Core/ExprEngine.cpp
  test/CodeGenCXX/cxx2a-compare.cpp
  test/SemaCXX/compare-cxx2a.cpp

Index: test/SemaCXX/compare-cxx2a.cpp
===================================================================
--- test/SemaCXX/compare-cxx2a.cpp
+++ test/SemaCXX/compare-cxx2a.cpp
@@ -292,20 +292,21 @@
 
 template <int>
 struct Tag {};
-// expected-note@+1 {{candidate}}
-Tag<0> operator<=>(EnumA, EnumA) {
-  return {};
+std::strong_ordering operator<=>(EnumA, EnumA) {
+  return std::strong_ordering::equal;
 }
-Tag<1> operator<=>(EnumA, EnumB) {
-  return {};
+// expected-note@+1 {{candidate function}},
+std::strong_ordering operator<=>(EnumA a, EnumB b) {
+  return ((int)a <=> (int)b);
 }
 
 void test_enum_ovl_provided() {
   auto r1 = (EnumA::A <=> EnumA::A);
-  ASSERT_EXPR_TYPE(r1, Tag<0>);
+  ASSERT_EXPR_TYPE(r1, std::strong_ordering);
   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')}}
+  ASSERT_EXPR_TYPE(r2, std::strong_ordering);
+  (void)(EnumB::B <=> EnumA::A); // OK, chooses reverse order synthesized candidate.
+  (void)(EnumB::B <=> EnumC::C); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumC')}}
 }
 
 void enum_float_test() {
@@ -421,3 +422,114 @@
 }
 
 } // namespace ComplexTest
+
+namespace TestRewritting {
+
+struct T {
+  int x;
+  // expected-note@+1 {{candidate}}
+  constexpr std::strong_ordering operator<=>(T y) const {
+    return (x <=> y.x);
+  }
+};
+
+struct U {
+  int x;
+  // FIXME: This diagnostic is wrong-ish.
+  // expected-note@+1 {{candidate function not viable: requires single argument 'y', but 2 arguments were provided}}
+  constexpr std::strong_equality operator<=>(T y) const {
+    if (x == y.x)
+      return std::strong_equality::equal;
+    return std::strong_equality::nonequal;
+  }
+};
+
+struct X { int x; };
+struct Y { int x; };
+template <int>
+struct Tag {};
+
+Tag<0> operator<=>(X, Y) {
+  return {};
+}
+
+constexpr auto operator<=>(Y y, X x) {
+  return y.x <=> x.x;
+}
+
+void foo() {
+  T t{42};
+  T t2{0};
+  U u{101};
+  auto r1 = (t <=> u);
+  ASSERT_EXPR_TYPE(r1, std::strong_equality);
+  auto r2 = (t <=> t2);
+  ASSERT_EXPR_TYPE(r2, std::strong_ordering);
+
+  auto r3 = t == u;
+  ASSERT_EXPR_TYPE(r3, bool);
+
+  (void)(t < u); // expected-error {{invalid operands to binary expression ('TestRewritting::T' and 'TestRewritting::U')}}
+
+  constexpr X x{1};
+  constexpr Y y{2};
+  constexpr auto r4 = (y < x);
+  static_assert(r4 == false);
+  constexpr auto r5 = (x < y);
+  static_assert(r5 == true);
+}
+
+} // namespace TestRewritting
+
+// The implicit object parameter is not considered when performing partial
+// ordering. That makes the reverse synthesized candidates ambiguous with the
+// rewritten candidates if any of them resolve to a member function.
+namespace TestOvlMatchingIgnoresImplicitObject {
+struct U;
+struct T {
+  std::strong_ordering operator<=>(U const &RHS) const;
+};
+struct U {
+  std::strong_ordering operator<=>(T const &RHS) const = delete;
+};
+
+struct V {
+  int x;
+};
+auto operator<=>(V const &LHS, V &&RHS) { // expected-note 4 {{candidate}}
+  return LHS.x <=> RHS.x;
+}
+auto operator<(V const &, V &&) { // expected-note {{candidate}}
+  return std::strong_equality::equal;
+}
+
+void test() {
+  // OK. selects T::operator<=>(U)
+  (void)(T{} < U{});
+  // expected-error@+1 {{use of overloaded operator '<' is ambiguous}}
+  (void)(V{} < V{});
+  // expected-error@+1 {{use of overloaded operator '<=>' is ambiguous}}
+  (void)(V{} <=> V{});
+}
+
+} // namespace TestOvlMatchingIgnoresImplicitObject
+
+namespace TestRewrittenTemplate {
+
+template <class T>
+auto test(T const &LHS, T const &RHS) {
+  // expected-error@+1 {{invalid operands to binary expression ('const TestRewrittenTemplate::None'}}
+  return LHS < RHS;
+}
+struct None {};
+template auto test<None>(None const &, None const &); // expected-note {{requested here}}
+
+struct Relational {};
+bool operator<(Relational, Relational);
+template auto test<Relational>(Relational const &, Relational const &);
+
+struct ThreeWay {};
+std::strong_ordering operator<=>(ThreeWay, ThreeWay);
+template auto test<ThreeWay>(ThreeWay const &, ThreeWay const &);
+
+} // namespace TestRewrittenTemplate
Index: test/CodeGenCXX/cxx2a-compare.cpp
===================================================================
--- test/CodeGenCXX/cxx2a-compare.cpp
+++ test/CodeGenCXX/cxx2a-compare.cpp
@@ -186,3 +186,17 @@
 }
 
 } // namespace ComplexTest
+
+namespace RewrittenTest {
+struct U {
+  int x;
+  std::strong_ordering operator<=>(U const &) const;
+};
+// FIXME(EricWF): Write this test
+auto test(U t1, U t2) {
+  return (t1 < t2);
+}
+
+} // namespace RewrittenTest
+
+
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp
===================================================================
--- lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1289,6 +1289,7 @@
     case Stmt::PackExpansionExprClass:
     case Stmt::SubstNonTypeTemplateParmPackExprClass:
     case Stmt::FunctionParmPackExprClass:
+    case Stmt::CXXRewrittenOperatorExprClass:
     case Stmt::CoroutineBodyStmtClass:
     case Stmt::CoawaitExprClass:
     case Stmt::DependentCoawaitExprClass:
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -1695,6 +1695,13 @@
   Code = serialization::EXPR_CXX_FOLD;
 }
 
+void ASTStmtWriter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+  VisitExpr(E);
+  Record.push_back(E->getRewrittenKind());
+  Record.AddStmt(E->getRewrittenExpr());
+  Code = serialization::EXPR_CXX_REWRITTEN_OPERATOR;
+}
+
 void ASTStmtWriter::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
   VisitExpr(E);
   Record.AddStmt(E->getSourceExpr());
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -1705,6 +1705,13 @@
   E->Opcode = (BinaryOperatorKind)Record.readInt();
 }
 
+void ASTStmtReader::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+  VisitExpr(E);
+  E->setRewrittenKind(
+      static_cast<CXXRewrittenOperatorExpr::RewrittenOpKind>(Record.readInt()));
+  E->Rewritten = Record.readSubExpr();
+}
+
 void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
   VisitExpr(E);
   E->SourceExpr = Record.readSubExpr();
@@ -4074,6 +4081,9 @@
       S = new (Context) CXXFoldExpr(Empty);
       break;
 
+    case EXPR_CXX_REWRITTEN_OPERATOR:
+      S = new (Context) CXXRewrittenOperatorExpr(Empty);
+
     case EXPR_OPAQUE_VALUE:
       S = new (Context) OpaqueValueExpr(Empty);
       break;
Index: lib/Sema/TreeTransform.h
===================================================================
--- lib/Sema/TreeTransform.h
+++ lib/Sema/TreeTransform.h
@@ -3217,6 +3217,11 @@
     return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator);
   }
 
+  ExprResult RebuildCXXRewrittenOperatorExpr(
+      CXXRewrittenOperatorExpr::RewrittenOpKind Kind, Expr *Rewritten) {
+    return new (SemaRef.Context) CXXRewrittenOperatorExpr(Kind, Rewritten);
+  }
+
   /// Build a new atomic operation expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -11516,6 +11521,25 @@
 }
 
 template <typename Derived>
+ExprResult TreeTransform<Derived>::TransformCXXRewrittenOperatorExpr(
+    CXXRewrittenOperatorExpr *E) {
+
+  // FIXME(EricWF): Is there a case where the underlying expression has been
+  // transformed in such a way that we need to re-compute the rewritten
+  // expression? (and not just re-build it).
+  ExprResult RewrittenRes = getDerived().TransformExpr(E->getRewrittenExpr());
+  if (RewrittenRes.isInvalid())
+    return ExprError();
+  Expr *Rewritten = RewrittenRes.get();
+
+  if (Rewritten == E->getRewrittenExpr() && !getDerived().AlwaysRebuild())
+    return E;
+
+  return getDerived().RebuildCXXRewrittenOperatorExpr(E->getRewrittenKind(),
+                                                      Rewritten);
+}
+
+template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
   Expr *Pattern = E->getPattern();
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -831,6 +831,19 @@
   }
 }
 
+const ImplicitConversionSequence &
+OverloadCandidate::getConversion(unsigned ArgIdx) const {
+  return Conversions[getConversionIndexForArgIndex(ArgIdx)];
+}
+
+unsigned OverloadCandidate::getConversionIndexForArgIndex(unsigned Idx) const {
+  if (getRewrittenKind() != ROC_AsReversedThreeWay)
+    return Idx;
+  // FIXME(EricWF): Handle these cases.
+  assert(Idx < 2);
+  return Idx == 0 ? 1 : 0;
+}
+
 void OverloadCandidateSet::destroyCandidates() {
   for (iterator i = begin(), e = end(); i != e; ++i) {
     for (auto &C : i->Conversions)
@@ -5942,13 +5955,15 @@
   //   (possibly cv-qualified) T2", when T2 is an enumeration type, are
   //   candidate functions.
   if (CandidateSet.getKind() == OverloadCandidateSet::CSK_Operator &&
-      !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args))
+      !IsAcceptableNonMemberOperatorCandidate(Context, Function, Args)) {
     return;
+  }
 
   // C++11 [class.copy]p11: [DR1402]
   //   A defaulted move constructor that is defined as deleted is ignored by
   //   overload resolution.
   CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(Function);
+
   if (Constructor && Constructor->isDefaulted() && Constructor->isDeleted() &&
       Constructor->isMoveConstructor())
     return;
@@ -8862,20 +8877,125 @@
   }
 }
 
+
+/// Add the rewritten and synthesized candidates for binary comparison
+/// operators. No additional semantic checking is done to see if the candidate
+/// is well formed.
+void Sema::AddRewrittenOperatorCandidates(OverloadedOperatorKind Op,
+                                          SourceLocation OpLoc,
+                                          ArrayRef<Expr *> InputArgs,
+                                          const UnresolvedSetImpl &Fns,
+                                          OverloadCandidateSet &CandidateSet,
+                                          bool PerformADL) {
+  auto Opc = BinaryOperator::getOverloadedOpcode(Op);
+  bool IsEquality = BinaryOperator::isEqualityOp(Opc);
+  bool IsRelational = BinaryOperator::isRelationalOp(Opc);
+  bool IsRelationalOrEquality = IsEquality || IsRelational;
+  if (!IsRelationalOrEquality && Opc != BO_Cmp)
+    return;
+  assert(InputArgs.size() == 2);
+
+  OverloadedOperatorKind CmpOp = OO_Spaceship;
+  DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(CmpOp);
+
+  // AddCandidates - Add operator<=> candidates for the specified set of args,
+  // and mark all newly generated candidates as having the specified
+  // 'RewrittenOverloadCandidateKind'.
+  auto AddCandidates = [&](ArrayRef<Expr *> Args,
+                           RewrittenOverloadCandidateKind Kind) {
+    OverloadCandidateSet::AddingRewrittenCandidateGuard Guard(CandidateSet,
+                                                              Kind);
+
+    unsigned InitialSize = CandidateSet.size();
+    AddFunctionCandidates(Fns, Args, CandidateSet);
+    AddMemberOperatorCandidates(CmpOp, OpLoc, Args, CandidateSet);
+    if (PerformADL)
+      AddArgumentDependentLookupCandidates(OpName, OpLoc, Args,
+                                           /*ExplicitTemplateArgs*/ nullptr,
+                                           CandidateSet);
+    AddBuiltinOperatorCandidates(CmpOp, OpLoc, Args, CandidateSet);
+
+    for (auto It = std::next(CandidateSet.begin(), InitialSize);
+         It != CandidateSet.end(); ++It) {
+      OverloadCandidate &Ovl = *It;
+      if (!Ovl.getRewrittenKind()) {
+        if (Ovl.Function)
+          Ovl.Function->dumpColor();
+      }
+      assert(Ovl.getRewrittenKind());
+      if (IsRelationalOrEquality) {
+        if (FunctionDecl *FD = Ovl.Function) {
+          if (FD->getReturnType()->isUndeducedType()) {
+            // FIXME(EricWF): Must the return type already be deduced?
+            // Attempt to write a test case to hit this, otherwise remove it.
+            assert(false);
+            if (DeduceReturnType(FD, OpLoc)) {
+              // FIXME(EricWF): Does deduction failure actually make this
+              // non-vialble? probably not?
+              assert(false);
+              Ovl.Viable = false;
+              continue;
+            }
+          }
+          QualType RetTy = FD->getReturnType();
+          assert(!RetTy->isDependentType());
+          if (const ComparisonCategoryInfo *Info =
+                  Context.CompCategories.lookupInfoForType(RetTy)) {
+            if (!Info->isUsableWithOperator(Opc)) {
+              Ovl.Viable = false;
+              Ovl.FailureKind = ovl_rewritten_operand_non_valid_for_operator;
+              continue;
+            }
+          } else {
+            // FIXME(EricWF): Check that the return type can be used with
+            // the specified relational operator
+          }
+        } else {
+          // Attempt to compute the comparison category type for a selecetd
+          // builtin function.
+          Optional<ComparisonCategoryType> CompType =
+              ComparisonCategories::computeComparisonTypeForBuiltin(
+                  Ovl.BuiltinParamTypes[0], Ovl.BuiltinParamTypes[1]);
+          if (!CompType ||
+              !ComparisonCategoryInfo::isUsableWithOperator(*CompType, Opc)) {
+            Ovl.Viable = false;
+            Ovl.FailureKind = ovl_rewritten_operand_non_valid_for_operator;
+            continue;
+          }
+        }
+      }
+    }
+  };
+
+  // If we have a relational or equality operation, add the rewritten candidates
+  // of the form: (LHS <=> RHS) @ 0
+  if (IsRelationalOrEquality)
+    AddCandidates(InputArgs, ROC_AsThreeWay);
+
+  // TODO: We should be able to avoid adding synthesized candidates when LHS and
+  // RHS have the same type, value category, and other relevent properties.
+  // In that case synthesized candidates for <=> should be the same as the
+  // rewritten ones. Note: It's still possible for the result of operator<=> to
+  // be usable only on the left or right side of the expression (0 @ <result>)
+  // or (<result> @ 0).
+
+  // For relational, equality, and three-way comparisons, add the rewritten and
+  // synthesized candidates of the form: 0 @ (RHS <=> LHS)
+  SmallVector<Expr *, 2> ReverseArgs(InputArgs.rbegin(), InputArgs.rend());
+  AddCandidates(ReverseArgs, ROC_AsReversedThreeWay);
+}
+
 /// Add function candidates found via argument-dependent lookup
 /// to the set of overloading candidates.
 ///
 /// This routine performs argument-dependent name lookup based on the
 /// given function name (which may also be an operator name) and adds
 /// all of the overload candidates found by ADL to the overload
 /// candidate set (C++ [basic.lookup.argdep]).
-void
-Sema::AddArgumentDependentLookupCandidates(DeclarationName Name,
-                                           SourceLocation Loc,
-                                           ArrayRef<Expr *> Args,
+void Sema::AddArgumentDependentLookupCandidates(
+    DeclarationName Name, SourceLocation Loc, ArrayRef<Expr *> Args,
     TemplateArgumentListInfo *ExplicitTemplateArgs,
-                                           OverloadCandidateSet& CandidateSet,
-                                           bool PartialOverloading) {
+    OverloadCandidateSet &CandidateSet, bool PartialOverloading) {
   ADLResult Fns;
 
   // FIXME: This approach for uniquing ADL results (and removing
@@ -9008,8 +9128,8 @@
   assert(Cand2.Conversions.size() == NumArgs && "Overload candidate mismatch");
   bool HasBetterConversion = false;
   for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
-    bool Cand1Bad = IsIllFormedConversion(Cand1.Conversions[ArgIdx]);
-    bool Cand2Bad = IsIllFormedConversion(Cand2.Conversions[ArgIdx]);
+    bool Cand1Bad = IsIllFormedConversion(Cand1.getConversion(ArgIdx));
+    bool Cand2Bad = IsIllFormedConversion(Cand2.getConversion(ArgIdx));
     if (Cand1Bad != Cand2Bad) {
       if (Cand1Bad)
         return false;
@@ -9026,8 +9146,8 @@
   //   conversion sequence than ICSi(F2), and then...
   for (unsigned ArgIdx = StartArg; ArgIdx < NumArgs; ++ArgIdx) {
     switch (CompareImplicitConversionSequences(S, Loc,
-                                               Cand1.Conversions[ArgIdx],
-                                               Cand2.Conversions[ArgIdx])) {
+                                               Cand1.getConversion(ArgIdx),
+                                               Cand2.getConversion(ArgIdx))) {
     case ImplicitConversionSequence::Better:
       // Cand1 has a better conversion sequence.
       HasBetterConversion = true;
@@ -9133,6 +9253,55 @@
     // Inherited from sibling base classes: still ambiguous.
   }
 
+  // Check C++2a tie-breakers for rewritten candidates
+  {
+    // --- F2 is a rewritten candidate ([over.match.oper]) and F1 is not.
+    RewrittenOverloadCandidateKind C1Roc = Cand1.getRewrittenKind();
+    RewrittenOverloadCandidateKind C2Roc = Cand2.getRewrittenKind();
+    if (C1Roc || C2Roc) {
+      if (!C1Roc || !C2Roc)
+        return !C1Roc;
+      // --- F1 and F2 are rewritten candidates, and F2 is a synthesized
+      // candidate with reversed order of parameters and F1 is not.
+      if ((C1Roc == ROC_AsReversedThreeWay ||
+           C2Roc == ROC_AsReversedThreeWay) &&
+          C1Roc != C2Roc) {
+        auto GetParamTypes = [&](const OverloadCandidate &Ovl) {
+          SmallVector<QualType, 2> Types;
+          // If the candidate is a method, compute the implicit object type.
+          if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Ovl.Function)) {
+            assert(Ovl.Conversions[0].isStandard());
+            QualType Ty = Ovl.Conversions[0].Standard.getToType(2);
+            assert(!Ty->isReferenceType());
+            const auto *FTP = MD->getType()->getAs<FunctionProtoType>();
+            switch (FTP->getRefQualifier()) {
+            case RQ_LValue:
+            case RQ_None:
+              Types.push_back(S.Context.getLValueReferenceType(Ty));
+              break;
+            case RQ_RValue:
+              Types.push_back(S.Context.getRValueReferenceType(Ty));
+              break;
+            }
+          }
+          for (unsigned I = 0; I < Ovl.getNumParams(); ++I)
+            Types.push_back(Ovl.getParamType(I).getCanonicalType());
+
+          // Reverse the order of the parameter types if this is the
+          // reverse-ordered overload.
+          assert(Types.size() == 2);
+          if (Ovl.getRewrittenKind() == ROC_AsReversedThreeWay)
+            std::swap(Types[0], Types[1]);
+
+          // Return the final list of types.
+          return Types;
+        };
+        if (GetParamTypes(Cand1) == GetParamTypes(Cand2))
+          return C2Roc == ROC_AsReversedThreeWay;
+      }
+    }
+  }
+
   // Check C++17 tie-breakers for deduction guides.
   {
     auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
@@ -9335,12 +9504,17 @@
   oc_implicit_copy_assignment,
   oc_implicit_move_assignment,
   oc_inherited_constructor,
-  oc_inherited_constructor_template
+  oc_inherited_constructor_template,
+  oc_rewritten_comparison_operator,
+  oc_synthesized_comparison_operator,
+  oc_rewritten_comparison_operator_template,
+  oc_synthesized_comparison_operator_template,
 };
 
 static OverloadCandidateKind
 ClassifyOverloadCandidate(Sema &S, NamedDecl *Found, FunctionDecl *Fn,
-                          std::string &Description) {
+                          std::string &Description,
+                          RewrittenOverloadCandidateKind ROC = ROC_None) {
   bool isTemplate = false;
 
   if (FunctionTemplateDecl *FunTmpl = Fn->getPrimaryTemplate()) {
@@ -9385,6 +9559,13 @@
     return oc_method;
   }
 
+  if (ROC == ROC_AsThreeWay)
+    return isTemplate ? oc_rewritten_comparison_operator_template
+                      : oc_rewritten_comparison_operator;
+  if (ROC == ROC_AsReversedThreeWay)
+    return isTemplate ? oc_synthesized_comparison_operator_template
+                      : oc_synthesized_comparison_operator;
+
   return isTemplate ? oc_function_template : oc_function;
 }
 
@@ -9470,22 +9651,30 @@
 
 // Notes the location of an overload candidate.
 void Sema::NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
-                                 QualType DestType, bool TakingAddress) {
+                                 QualType DestType, bool TakingAddress,
+                                 RewrittenOverloadCandidateKind ROC) {
   if (TakingAddress && !checkAddressOfCandidateIsAvailable(*this, Fn))
     return;
   if (Fn->isMultiVersion() && !Fn->getAttr<TargetAttr>()->isDefaultVersion())
     return;
 
   std::string FnDesc;
-  OverloadCandidateKind K = ClassifyOverloadCandidate(*this, Found, Fn, FnDesc);
+  OverloadCandidateKind K =
+      ClassifyOverloadCandidate(*this, Found, Fn, FnDesc, ROC);
   PartialDiagnostic PD = PDiag(diag::note_ovl_candidate)
                              << (unsigned) K << Fn << FnDesc;
 
   HandleFunctionTypeMismatch(PD, Fn->getType(), DestType);
   Diag(Fn->getLocation(), PD);
   MaybeEmitInheritedConstructorNote(*this, Found);
 }
 
+void Sema::NoteOverloadCandidate(OverloadCandidate *Cand) {
+  assert(Cand->Function && Cand->FoundDecl);
+  NoteOverloadCandidate(Cand->FoundDecl, Cand->Function, QualType(),
+                        /*TakingAddress*/ false, Cand->getRewrittenKind());
+}
+
 // Notes the location of all overload candidates designated through
 // OverloadedExpr
 void Sema::NoteAllOverloadCandidates(Expr *OverloadedExpr, QualType DestType,
@@ -9553,8 +9742,8 @@
   }
 
   std::string FnDesc;
-  OverloadCandidateKind FnKind =
-      ClassifyOverloadCandidate(S, Cand->FoundDecl, Fn, FnDesc);
+  OverloadCandidateKind FnKind = ClassifyOverloadCandidate(
+      S, Cand->FoundDecl, Fn, FnDesc, Cand->getRewrittenKind());
 
   Expr *FromExpr = Conv.Bad.FromExpr;
   QualType FromTy = Conv.Bad.getFromType();
@@ -10164,6 +10353,10 @@
   }
 }
 
+static void DiagnoseFailedRewrittenOperand(Sema &S, OverloadCandidate *Cand) {
+  // FIXME(EricWF): Do something here!
+}
+
 static void DiagnoseFailedEnableIfAttr(Sema &S, OverloadCandidate *Cand) {
   FunctionDecl *Callee = Cand->Function;
   EnableIfAttr *Attr = static_cast<EnableIfAttr*>(Cand->DeductionFailure.Data);
@@ -10213,7 +10406,7 @@
     }
 
     // We don't really have anything else to say about viable candidates.
-    S.NoteOverloadCandidate(Cand->FoundDecl, Fn);
+    S.NoteOverloadCandidate(Cand);
     return;
   }
 
@@ -10236,7 +10429,7 @@
   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);
 
   case ovl_fail_bad_conversion: {
     unsigned I = (Cand->IgnoreObjectArgument ? 1 : 0);
@@ -10247,7 +10440,7 @@
     // 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);
   }
 
   case ovl_fail_bad_target:
@@ -10279,6 +10472,9 @@
   case ovl_non_default_multiversion_function:
     // Do nothing, these should simply be ignored.
     break;
+  case ovl_rewritten_operand_non_valid_for_operator:
+    DiagnoseFailedRewrittenOperand(S, Cand);
+    break;
   }
 }
 
@@ -12228,6 +12424,164 @@
   return CreateBuiltinUnaryOp(OpLoc, Opc, Input);
 }
 
+ExprResult Sema::BuildBinaryOperatorCandidate(SourceLocation OpLoc,
+                                              BinaryOperatorKind Opc,
+                                              const OverloadCandidate &Ovl,
+                                              Expr *LHSE, Expr *RHSE,
+                                              bool HadMultipleCandidates) {
+  Expr *Args[2] = {LHSE, RHSE};
+  OverloadedOperatorKind Op = BinaryOperator::getOverloadedOperator(Opc);
+  // We found a built-in operator or an overloaded operator.
+  FunctionDecl *FnDecl = Ovl.Function;
+
+  if (FnDecl) {
+    Expr *Base = nullptr;
+    // We matched an overloaded operator. Build a call to that
+    // operator.
+
+    // Convert the arguments.
+    if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
+      // Ovl.Access is only meaningful for class members.
+      CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Ovl.FoundDecl);
+
+      ExprResult Arg1 =
+          PerformCopyInitialization(InitializedEntity::InitializeParameter(
+                                        Context, FnDecl->getParamDecl(0)),
+                                    SourceLocation(), Args[1]);
+      if (Arg1.isInvalid())
+        return ExprError();
+
+      ExprResult Arg0 = PerformObjectArgumentInitialization(
+          Args[0], /*Qualifier=*/nullptr, Ovl.FoundDecl, Method);
+      if (Arg0.isInvalid())
+        return ExprError();
+      Base = Args[0] = Arg0.getAs<Expr>();
+      Args[1] = Arg1.getAs<Expr>();
+    } else {
+      // Convert the arguments.
+      ExprResult Arg0 =
+          PerformCopyInitialization(InitializedEntity::InitializeParameter(
+                                        Context, FnDecl->getParamDecl(0)),
+                                    SourceLocation(), Args[0]);
+      if (Arg0.isInvalid())
+        return ExprError();
+
+      ExprResult Arg1 =
+          PerformCopyInitialization(InitializedEntity::InitializeParameter(
+                                        Context, FnDecl->getParamDecl(1)),
+                                    SourceLocation(), Args[1]);
+      if (Arg1.isInvalid())
+        return ExprError();
+      Args[0] = Arg0.getAs<Expr>();
+      Args[1] = Arg1.getAs<Expr>();
+    }
+
+    // Build the actual expression node.
+    ExprResult FnExpr = CreateFunctionRefExpr(
+        *this, FnDecl, Ovl.FoundDecl, Base, HadMultipleCandidates, OpLoc);
+    if (FnExpr.isInvalid())
+      return ExprError();
+
+    // Determine the result type.
+    QualType ResultTy = FnDecl->getReturnType();
+    ExprValueKind VK = Expr::getValueKindForType(ResultTy);
+    ResultTy = ResultTy.getNonLValueExprType(Context);
+
+    CXXOperatorCallExpr *TheCall = new (Context) CXXOperatorCallExpr(
+        Context, Op, FnExpr.get(), Args, ResultTy, VK, OpLoc, FPFeatures);
+
+    if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall, FnDecl))
+      return ExprError();
+
+    ArrayRef<const Expr *> ArgsArray(Args, 2);
+    const Expr *ImplicitThis = nullptr;
+    // Cut off the implicit 'this'.
+    if (isa<CXXMethodDecl>(FnDecl)) {
+      ImplicitThis = ArgsArray[0];
+      ArgsArray = ArgsArray.slice(1);
+    }
+
+    // Check for a self move.
+    if (Op == OO_Equal)
+      DiagnoseSelfMove(Args[0], Args[1], OpLoc);
+
+    checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray,
+              isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
+              Sema::VariadicDoesNotApply);
+
+    return MaybeBindToTemporary(TheCall);
+
+  } else {
+    // We matched a built-in operator. Convert the arguments, then
+    // break out so that we will build the appropriate built-in
+    // operator node.
+    ExprResult ArgsRes0 =
+        PerformImplicitConversion(Args[0], Ovl.BuiltinParamTypes[0],
+                                  Ovl.getConversion(0), Sema::AA_Passing);
+    if (ArgsRes0.isInvalid())
+      return ExprError();
+    Args[0] = ArgsRes0.get();
+
+    ExprResult ArgsRes1 =
+        PerformImplicitConversion(Args[1], Ovl.BuiltinParamTypes[1],
+                                  Ovl.getConversion(1), Sema::AA_Passing);
+    if (ArgsRes1.isInvalid())
+      return ExprError();
+    Args[1] = ArgsRes1.get();
+  }
+  // We matched a built-in operator; build it.
+  return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
+}
+
+static ExprResult BuildRewrittenCandidate(Sema &S, BinaryOperatorKind Opc,
+                                          const OverloadCandidate &Ovl,
+                                          ArrayRef<Expr *> Args,
+                                          const UnresolvedSetImpl &Fns,
+                                          SourceLocation OpLoc,
+                                          bool PerformADL) {
+  Expr *RewrittenArgs[2] = {Args[0], Args[1]};
+  assert(Ovl.getRewrittenKind());
+  bool IsSynthesized = Ovl.getRewrittenKind() == ROC_AsReversedThreeWay;
+  if (IsSynthesized)
+    std::swap(RewrittenArgs[0], RewrittenArgs[1]);
+
+  // Supress diagnostics when building the expressions for the specified
+  // candidate. If evaluation fails the candidate will be marked non-viable
+  // and the best viable candidate re-computed.
+  Sema::TentativeAnalysisScope DiagnosticScopeGuard(S);
+
+  // Build the '(LHS <=> RHS)' operand to the full expression.
+  ExprResult RewrittenRes = S.BuildBinaryOperatorCandidate(
+      OpLoc, BO_Cmp, Ovl, RewrittenArgs[0], RewrittenArgs[1],
+      /*HadMultipleCandidates*/ false);
+  if (RewrittenRes.isInvalid())
+    return ExprError();
+
+  if (Opc != BO_Cmp) {
+    // Now attempt to build the full expression '(LHS <=> RHS) @ 0' using the
+    // evaluated operand and the literal 0.
+    llvm::APInt I =
+        llvm::APInt::getNullValue(S.Context.getIntWidth(S.Context.IntTy));
+    Expr *Zero =
+        IntegerLiteral::Create(S.Context, I, S.Context.IntTy, SourceLocation());
+
+    Expr *NewLHS = RewrittenRes.get();
+    Expr *NewRHS = Zero;
+    if (Ovl.getRewrittenKind() == ROC_AsReversedThreeWay)
+      std::swap(NewLHS, NewRHS);
+
+    RewrittenRes =
+        S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL,
+                                /*AllowRewrittenCandidates*/ false);
+    if (RewrittenRes.isInvalid())
+      return ExprError();
+  }
+  Expr *Rewritten = RewrittenRes.get();
+
+  return new (S.Context)
+      CXXRewrittenOperatorExpr(Ovl.getRewrittenKind(), Rewritten);
+}
+
 /// Create a binary operation that may resolve to an overloaded
 /// operator.
 ///
@@ -12244,11 +12598,11 @@
 ///
 /// \param LHS Left-hand argument.
 /// \param RHS Right-hand argument.
-ExprResult
-Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
+ExprResult Sema::CreateOverloadedBinOp(SourceLocation OpLoc,
                                        BinaryOperatorKind Opc,
-                            const UnresolvedSetImpl &Fns,
-                            Expr *LHS, Expr *RHS, bool PerformADL) {
+                                       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
 
@@ -12310,11 +12664,27 @@
   if (Opc == BO_PtrMemD)
     return CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
 
+  UnresolvedSet<6> OrigFuncs;
+  UnresolvedSet<6> ThreeWayFuncs;
+  for (NamedDecl *D : Fns) {
+    FunctionDecl *FD = D->getAsFunction();
+    if (FD) {
+      assert(FD->isOverloadedOperator());
+      if (FD->getOverloadedOperator() == OO_Spaceship) {
+        ThreeWayFuncs.addDecl(D);
+        if (Op == OO_Spaceship)
+          OrigFuncs.addDecl(D);
+      } else
+        OrigFuncs.addDecl(D);
+    } else
+      OrigFuncs.addDecl(D);
+  }
+
   // Build an empty overload set.
   OverloadCandidateSet CandidateSet(OpLoc, OverloadCandidateSet::CSK_Operator);
 
   // Add the candidates from the given function set.
-  AddFunctionCandidates(Fns, Args, CandidateSet);
+  AddFunctionCandidates(OrigFuncs, Args, CandidateSet);
 
   // Add operator candidates that are member functions.
   AddMemberOperatorCandidates(Op, OpLoc, Args, CandidateSet);
@@ -12330,119 +12700,28 @@
   // Add builtin operator candidates.
   AddBuiltinOperatorCandidates(Op, OpLoc, Args, CandidateSet);
 
-  bool HadMultipleCandidates = (CandidateSet.size() > 1);
-
-  // Perform overload resolution.
-  OverloadCandidateSet::iterator Best;
-  switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) {
-    case OR_Success: {
-      // We found a built-in operator or an overloaded operator.
-      FunctionDecl *FnDecl = Best->Function;
-
-      if (FnDecl) {
-        Expr *Base = nullptr;
-        // We matched an overloaded operator. Build a call to that
-        // operator.
-
-        // Convert the arguments.
-        if (CXXMethodDecl *Method = dyn_cast<CXXMethodDecl>(FnDecl)) {
-          // Best->Access is only meaningful for class members.
-          CheckMemberOperatorAccess(OpLoc, Args[0], Args[1], Best->FoundDecl);
-
-          ExprResult Arg1 =
-            PerformCopyInitialization(
-              InitializedEntity::InitializeParameter(Context,
-                                                     FnDecl->getParamDecl(0)),
-              SourceLocation(), Args[1]);
-          if (Arg1.isInvalid())
-            return ExprError();
-
-          ExprResult Arg0 =
-            PerformObjectArgumentInitialization(Args[0], /*Qualifier=*/nullptr,
-                                                Best->FoundDecl, Method);
-          if (Arg0.isInvalid())
-            return ExprError();
-          Base = Args[0] = Arg0.getAs<Expr>();
-          Args[1] = RHS = Arg1.getAs<Expr>();
-        } else {
-          // Convert the arguments.
-          ExprResult Arg0 = PerformCopyInitialization(
-            InitializedEntity::InitializeParameter(Context,
-                                                   FnDecl->getParamDecl(0)),
-            SourceLocation(), Args[0]);
-          if (Arg0.isInvalid())
-            return ExprError();
-
-          ExprResult Arg1 =
-            PerformCopyInitialization(
-              InitializedEntity::InitializeParameter(Context,
-                                                     FnDecl->getParamDecl(1)),
-              SourceLocation(), Args[1]);
-          if (Arg1.isInvalid())
-            return ExprError();
-          Args[0] = LHS = Arg0.getAs<Expr>();
-          Args[1] = RHS = Arg1.getAs<Expr>();
+  // C++2a Add rewritten and synthesized operator candidates.
+  bool HasRewrittenCandidates = false;
+  if (getLangOpts().CPlusPlus2a && AllowRewrittenCandidates &&
+      BinaryOperator::isComparisonOp(Opc)) {
+    unsigned BeforeRewrittenSize = CandidateSet.size();
+    AddRewrittenOperatorCandidates(Op, OpLoc, Args, ThreeWayFuncs, CandidateSet,
+                                   PerformADL);
+    HasRewrittenCandidates = BeforeRewrittenSize != CandidateSet.size();
   }
 
-        // Build the actual expression node.
-        ExprResult FnExpr = CreateFunctionRefExpr(*this, FnDecl,
-                                                  Best->FoundDecl, Base,
-                                                  HadMultipleCandidates, OpLoc);
-        if (FnExpr.isInvalid())
-          return ExprError();
-
-        // Determine the result type.
-        QualType ResultTy = FnDecl->getReturnType();
-        ExprValueKind VK = Expr::getValueKindForType(ResultTy);
-        ResultTy = ResultTy.getNonLValueExprType(Context);
-
-        CXXOperatorCallExpr *TheCall =
-          new (Context) CXXOperatorCallExpr(Context, Op, FnExpr.get(),
-                                            Args, ResultTy, VK, OpLoc,
-                                            FPFeatures);
-
-        if (CheckCallReturnType(FnDecl->getReturnType(), OpLoc, TheCall,
-                                FnDecl))
-          return ExprError();
-
-        ArrayRef<const Expr *> ArgsArray(Args, 2);
-        const Expr *ImplicitThis = nullptr;
-        // Cut off the implicit 'this'.
-        if (isa<CXXMethodDecl>(FnDecl)) {
-          ImplicitThis = ArgsArray[0];
-          ArgsArray = ArgsArray.slice(1);
-        }
-
-        // Check for a self move.
-        if (Op == OO_Equal)
-          DiagnoseSelfMove(Args[0], Args[1], OpLoc);
-
-        checkCall(FnDecl, nullptr, ImplicitThis, ArgsArray,
-                  isa<CXXMethodDecl>(FnDecl), OpLoc, TheCall->getSourceRange(),
-                  VariadicDoesNotApply);
-
-        return MaybeBindToTemporary(TheCall);
-      } else {
-        // We matched a built-in operator. Convert the arguments, then
-        // break out so that we will build the appropriate built-in
-        // operator node.
-        ExprResult ArgsRes0 =
-            PerformImplicitConversion(Args[0], Best->BuiltinParamTypes[0],
-                                      Best->Conversions[0], AA_Passing);
-        if (ArgsRes0.isInvalid())
-          return ExprError();
-        Args[0] = ArgsRes0.get();
 
-        ExprResult ArgsRes1 =
-            PerformImplicitConversion(Args[1], Best->BuiltinParamTypes[1],
-                                      Best->Conversions[1], AA_Passing);
-        if (ArgsRes1.isInvalid())
-          return ExprError();
-        Args[1] = ArgsRes1.get();
-        break;
-      }
-    }
 
+  // Perform final overload resolution.
+  bool HadMultipleCandidates = (CandidateSet.size() > 1);
+  OverloadCandidateSet::iterator Best;
+  switch (CandidateSet.BestViableFunction(*this, OpLoc, Best)) {
+  case OR_Success:
+    if (Best->getRewrittenKind())
+      return BuildRewrittenCandidate(*this, Opc, *Best, Args, Fns, OpLoc,
+                                     PerformADL);
+    return BuildBinaryOperatorCandidate(OpLoc, Opc, *Best, Args[0], Args[1],
+                                        HadMultipleCandidates);
   case OR_No_Viable_Function: {
     // C++ [over.match.oper]p9:
     //   If the operator is the operator , [...] and there are no
@@ -12455,15 +12734,15 @@
     // operator do not fall through to handling in built-in, but report that
     // no overloaded assignment operator found
     ExprResult Result = ExprError();
-      if (Args[0]->getType()->isRecordType() &&
-          Opc >= BO_Assign && Opc <= BO_OrAssign) {
+    if (Args[0]->getType()->isRecordType() && Opc >= BO_Assign &&
+        Opc <= BO_OrAssign) {
       Diag(OpLoc, diag::err_ovl_no_viable_oper)
-             << BinaryOperator::getOpcodeStr(Opc)
-             << Args[0]->getSourceRange() << Args[1]->getSourceRange();
+          << BinaryOperator::getOpcodeStr(Opc) << Args[0]->getSourceRange()
+          << Args[1]->getSourceRange();
       if (Args[0]->getType()->isIncompleteType()) {
         Diag(OpLoc, diag::note_assign_lhs_incomplete)
-            << Args[0]->getType()
-            << Args[0]->getSourceRange() << Args[1]->getSourceRange();
+            << Args[0]->getType() << Args[0]->getSourceRange()
+            << Args[1]->getSourceRange();
       }
     } else {
       // This is an erroneous use of an operator which can be overloaded by
@@ -12484,12 +12763,11 @@
                                   BinaryOperator::getOpcodeStr(Opc), OpLoc);
     return Result;
   }
-
   case OR_Ambiguous:
     Diag(OpLoc, diag::err_ovl_ambiguous_oper_binary)
-          << BinaryOperator::getOpcodeStr(Opc)
-          << Args[0]->getType() << Args[1]->getType()
-          << Args[0]->getSourceRange() << Args[1]->getSourceRange();
+        << BinaryOperator::getOpcodeStr(Opc) << Args[0]->getType()
+        << Args[1]->getType() << Args[0]->getSourceRange()
+        << Args[1]->getSourceRange();
     CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args,
                                 BinaryOperator::getOpcodeStr(Opc), OpLoc);
     return ExprError();
@@ -12507,8 +12785,7 @@
       return ExprError();
     } else {
       Diag(OpLoc, diag::err_ovl_deleted_oper)
-          << Best->Function->isDeleted()
-          << BinaryOperator::getOpcodeStr(Opc)
+          << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc)
           << getDeletedOrUnavailableSuffix(Best->Function)
           << Args[0]->getSourceRange() << Args[1]->getSourceRange();
     }
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -9797,8 +9797,6 @@
                                                          ExprResult &LHS,
                                                          ExprResult &RHS,
                                                          SourceLocation Loc) {
-  using CCT = ComparisonCategoryType;
-
   QualType LHSType = LHS.get()->getType();
   QualType RHSType = RHS.get()->getType();
   // Dig out the original argument type and expression before implicit casts
@@ -9865,22 +9863,11 @@
   if (HasNarrowing)
     return QualType();
 
-  assert(!Type.isNull() && "composite type for <=> has not been set");
+  Optional<ComparisonCategoryType> TypeKind =
+      ComparisonCategories::computeComparisonTypeForBuiltin(Type);
+  assert(TypeKind && "composite type for <=> has not been set");
 
-  auto TypeKind = [&]() {
-    if (const ComplexType *CT = Type->getAs<ComplexType>()) {
-      if (CT->getElementType()->hasFloatingRepresentation())
-        return CCT::WeakEquality;
-      return CCT::StrongEquality;
-    }
-    if (Type->isIntegralOrEnumerationType())
-      return CCT::StrongOrdering;
-    if (Type->hasFloatingRepresentation())
-      return CCT::PartialOrdering;
-    llvm_unreachable("other types are unimplemented");
-  }();
-
-  return S.CheckComparisonCategoryType(TypeKind, Loc);
+  return S.CheckComparisonCategoryType(TypeKind.getValue(), Loc);
 }
 
 static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
@@ -9975,32 +9962,12 @@
     QualType CompositeTy = LHS.get()->getType();
     assert(!CompositeTy->isReferenceType());
 
-    auto buildResultTy = [&](ComparisonCategoryType Kind) {
-      return CheckComparisonCategoryType(Kind, Loc);
-    };
-
-    // C++2a [expr.spaceship]p7: If the composite pointer type is a function
-    // pointer type, a pointer-to-member type, or std::nullptr_t, the
-    // result is of type std::strong_equality
-    if (CompositeTy->isFunctionPointerType() ||
-        CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType())
-      // FIXME: consider making the function pointer case produce
-      // strong_ordering not strong_equality, per P0946R0-Jax18 discussion
-      // and direction polls
-      return buildResultTy(ComparisonCategoryType::StrongEquality);
-
-    // C++2a [expr.spaceship]p8: If the composite pointer type is an object
-    // pointer type, p <=> q is of type std::strong_ordering.
-    if (CompositeTy->isPointerType()) {
-      // P0946R0: Comparisons between a null pointer constant and an object
-      // pointer result in std::strong_equality
-      if (LHSIsNull != RHSIsNull)
-        return buildResultTy(ComparisonCategoryType::StrongEquality);
-      return buildResultTy(ComparisonCategoryType::StrongOrdering);
-    }
-    // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed.
-    // TODO: Extend support for operator<=> to ObjC types.
+    Optional<ComparisonCategoryType> TypeKind =
+        ComparisonCategories::computeComparisonTypeForBuiltin(
+            CompositeTy, LHSIsNull != RHSIsNull);
+    if (!TypeKind)
       return InvalidOperands(Loc, LHS, RHS);
+    return CheckComparisonCategoryType(TypeKind.getValue(), Loc);
   };
 
 
@@ -12370,7 +12337,12 @@
   if (Sc && OverOp != OO_None && OverOp != OO_Equal)
     S.LookupOverloadedOperatorName(OverOp, Sc, LHS->getType(),
                                    RHS->getType(), Functions);
-
+  if (S.getLangOpts().CPlusPlus2a) {
+    if (Sc && Opc != BO_Cmp && BinaryOperator::isRelationalOp(Opc)) {
+      S.LookupOverloadedOperatorName(OO_Spaceship, Sc, LHS->getType(),
+                                     RHS->getType(), Functions);
+    }
+  }
   // Build the (potentially-overloaded, potentially-dependent)
   // binary operation.
   return S.CreateOverloadedBinOp(OpLoc, Opc, Functions, LHS, RHS);
Index: lib/Sema/SemaExceptionSpec.cpp
===================================================================
--- lib/Sema/SemaExceptionSpec.cpp
+++ lib/Sema/SemaExceptionSpec.cpp
@@ -1174,6 +1174,9 @@
   case Expr::VAArgExprClass:
     return canSubExprsThrow(*this, E);
 
+  case Expr::CXXRewrittenOperatorExprClass:
+    return canThrow(cast<CXXRewrittenOperatorExpr>(E)->getRewrittenExpr());
+
     // Some might be dependent for other reasons.
   case Expr::ArraySubscriptExprClass:
   case Expr::OMPArraySectionExprClass:
Index: lib/CodeGen/CGExprScalar.cpp
===================================================================
--- lib/CodeGen/CGExprScalar.cpp
+++ lib/CodeGen/CGExprScalar.cpp
@@ -541,6 +541,10 @@
     return EmitScalarPrePostIncDec(E, LV, true, true);
   }
 
+  Value *VisitCXXRewrittenOperatorExpr(const CXXRewrittenOperatorExpr *E) {
+    return Visit(E->getRewrittenExpr());
+  }
+
   llvm::Value *EmitIncDecConsiderOverflowBehavior(const UnaryOperator *E,
                                                   llvm::Value *InVal,
                                                   bool IsInc);
Index: lib/AST/StmtProfile.cpp
===================================================================
--- lib/AST/StmtProfile.cpp
+++ lib/AST/StmtProfile.cpp
@@ -1816,6 +1816,13 @@
   ID.AddInteger(S->getOperator());
 }
 
+void StmtProfiler::VisitCXXRewrittenOperatorExpr(
+    const CXXRewrittenOperatorExpr *S) {
+  VisitExpr(S);
+  ID.AddInteger(S->getRewrittenKind());
+  VisitExpr(S->getRewrittenExpr());
+}
+
 void StmtProfiler::VisitCoroutineBodyStmt(const CoroutineBodyStmt *S) {
   VisitStmt(S);
 }
Index: lib/AST/StmtPrinter.cpp
===================================================================
--- lib/AST/StmtPrinter.cpp
+++ lib/AST/StmtPrinter.cpp
@@ -2588,6 +2588,15 @@
   OS << ")";
 }
 
+void StmtPrinter::VisitCXXRewrittenOperatorExpr(CXXRewrittenOperatorExpr *E) {
+  // FIXME(EricWF): Are there ever cases where we want to display the rewritten
+  // code? For example when producing diagnostics on implicitly generated
+  // expressions?
+  PrintExpr(E->getOriginalLHS());
+  OS << ' ' << BinaryOperator::getOpcodeStr(E->getOriginalOpcode()) << ' ';
+  PrintExpr(E->getOriginalRHS());
+}
+
 // C++ Coroutines TS
 
 void StmtPrinter::VisitCoroutineBodyStmt(CoroutineBodyStmt *S) {
Index: lib/AST/ItaniumMangle.cpp
===================================================================
--- lib/AST/ItaniumMangle.cpp
+++ lib/AST/ItaniumMangle.cpp
@@ -3516,9 +3516,13 @@
   }
 
   // These are used for internal purposes and cannot be meaningfully mangled.
-  case Expr::OpaqueValueExprClass:
-    llvm_unreachable("cannot mangle opaque value; mangling wrong thing?");
-
+  case Expr::OpaqueValueExprClass: {
+    const OpaqueValueExpr *OVE = cast<OpaqueValueExpr>(E);
+    assert(OVE->getSourceExpr() && "cannot mangle opaque value without a "
+                                   "source expression; mangling wrong thing?");
+    mangleExpression(OVE->getSourceExpr());
+    break;
+  }
   case Expr::InitListExprClass: {
     Out << "il";
     mangleInitListElements(cast<InitListExpr>(E));
@@ -3559,6 +3563,17 @@
     mangleExpression(cast<CXXStdInitializerListExpr>(E)->getSubExpr(), Arity);
     break;
 
+  case Expr::CXXRewrittenOperatorExprClass: {
+    const CXXRewrittenOperatorExpr *RE = cast<CXXRewrittenOperatorExpr>(E);
+    const unsigned NumArgs = 2;
+    auto Operator =
+        BinaryOperator::getOverloadedOperator(RE->getOriginalOpcode());
+    mangleOperatorName(Operator, NumArgs);
+    mangleExpression(RE->getOriginalLHS());
+    mangleExpression(RE->getOriginalRHS());
+    break;
+  }
+
   case Expr::SubstNonTypeTemplateParmExprClass:
     mangleExpression(cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(),
                      Arity);
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -5059,6 +5059,10 @@
       return;
     VisitIgnoredValue(E);
   }
+
+  bool VisitCXXRewrittenOperatorExpr(const CXXRewrittenOperatorExpr *E) {
+    return StmtVisitorTy::Visit(E->getRewrittenExpr());
+  }
 };
 
 } // namespace
@@ -10859,6 +10863,8 @@
   case Expr::ChooseExprClass: {
     return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(), Ctx);
   }
+  case Expr::CXXRewrittenOperatorExprClass:
+    return CheckICE(cast<CXXRewrittenOperatorExpr>(E)->getRewrittenExpr(), Ctx);
   }
 
   llvm_unreachable("Invalid StmtClass!");
Index: lib/AST/ExprClassification.cpp
===================================================================
--- lib/AST/ExprClassification.cpp
+++ lib/AST/ExprClassification.cpp
@@ -287,7 +287,9 @@
     if (cast<GenericSelectionExpr>(E)->isResultDependent())
       return Cl::CL_PRValue;
     return ClassifyInternal(Ctx,cast<GenericSelectionExpr>(E)->getResultExpr());
-
+  case Expr::CXXRewrittenOperatorExprClass:
+    return ClassifyInternal(
+        Ctx, cast<CXXRewrittenOperatorExpr>(E)->getRewrittenExpr());
   case Expr::BinaryOperatorClass:
   case Expr::CompoundAssignOperatorClass:
     // C doesn't have any binary expressions that are lvalues.
Index: lib/AST/ExprCXX.cpp
===================================================================
--- lib/AST/ExprCXX.cpp
+++ lib/AST/ExprCXX.cpp
@@ -1433,3 +1433,100 @@
 }
 
 void ArrayTypeTraitExpr::anchor() {}
+
+namespace {
+struct ArgumentExtractor {
+  Expr *Rewritten;
+  bool IsReverseOrder;
+  bool IsThreeWay;
+
+  ArgumentExtractor(const CXXRewrittenOperatorExpr *E)
+      : Rewritten(E->getRewrittenExpr()) {
+    IsReverseOrder = E->getRewrittenKind() == ROC_AsReversedThreeWay;
+    IsThreeWay = getOpcode(Rewritten) == BO_Cmp;
+  }
+
+  static CXXRewrittenOperatorExpr::Opcode getOpcode(Expr *E) {
+    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+      return BO->getOpcode();
+    if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+      return BinaryOperator::getOverloadedOpcode(CE->getOperator());
+    llvm_unreachable("unhandled case");
+  }
+
+  CXXRewrittenOperatorExpr::Opcode getOriginalOpcode() const {
+    if (IsThreeWay)
+      return BO_Cmp;
+    if (IsReverseOrder)
+      return getOpcode(getRHS(Rewritten));
+    return getOpcode(Rewritten);
+  }
+
+  static Expr *getLHS(Expr *E) {
+    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+      return BO->getLHS();
+    if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+      return CE->getArg(0);
+    llvm_unreachable("unhandled case");
+  }
+
+  static Expr *getRHS(Expr *E) {
+    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+      return BO->getRHS();
+    if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+      return CE->getArg(1);
+    llvm_unreachable("unhandled case");
+  }
+
+  Expr *getOriginalLHS() const {
+    if (IsThreeWay) {
+      if (IsReverseOrder)
+        return getRHS(Rewritten);
+      return getLHS(Rewritten);
+    }
+    if (IsReverseOrder)
+      return getRHS(getRHS(Rewritten));
+    return getLHS(getLHS(Rewritten));
+  }
+
+  Expr *getOriginalRHS() const {
+    if (IsThreeWay) {
+      if (IsReverseOrder)
+        return getLHS(Rewritten);
+      return getRHS(Rewritten);
+    }
+    if (IsReverseOrder)
+      return getRHS(getLHS(Rewritten));
+    return getLHS(getRHS(Rewritten));
+  }
+
+  static SourceLocation getOperatorLoc(Expr *E) {
+    if (BinaryOperator *BO = dyn_cast<BinaryOperator>(E))
+      return BO->getOperatorLoc();
+    if (auto *CE = dyn_cast<CXXOperatorCallExpr>(E))
+      return CE->getOperatorLoc();
+    llvm_unreachable("unhandled case");
+  }
+};
+} // namespace
+
+SourceLocation CXXRewrittenOperatorExpr::getOperatorLoc() const {
+  return ArgumentExtractor::getOperatorLoc(this->getRewrittenExpr());
+}
+
+CXXRewrittenOperatorExpr::Opcode CXXRewrittenOperatorExpr::getOpcode() const {
+  return ArgumentExtractor::getOpcode(getRewrittenExpr());
+}
+
+CXXRewrittenOperatorExpr::Opcode
+CXXRewrittenOperatorExpr::getOriginalOpcode() const {
+  return ArgumentExtractor{this}.getOriginalOpcode();
+}
+
+Expr *CXXRewrittenOperatorExpr::getOriginalLHS() const {
+  return ArgumentExtractor{this}.getOriginalLHS();
+}
+
+Expr *CXXRewrittenOperatorExpr::getOriginalRHS() const {
+  return ArgumentExtractor{this}.getOriginalRHS();
+}
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -3188,6 +3188,11 @@
     return false;
   }
 
+  case CXXRewrittenOperatorExprClass: {
+    const auto *RO = cast<CXXRewrittenOperatorExpr>(this);
+    return RO->getRewrittenExpr()->HasSideEffects(Ctx, IncludePossibleEffects);
+  }
+
   case PseudoObjectExprClass: {
     // Only look for side-effects in the semantic form, and look past
     // OpaqueValueExpr bindings in that form.
@@ -3898,6 +3903,11 @@
   }
 }
 
+OpaqueValueExpr *OpaqueValueExpr::Create(const ASTContext &Ctx, Expr *E) {
+  return new (Ctx) OpaqueValueExpr(E->getExprLoc(), E->getType(),
+                                   E->getValueKind(), E->getObjectKind(), E);
+}
+
 const OpaqueValueExpr *OpaqueValueExpr::findInCopyConstruct(const Expr *e) {
   if (const ExprWithCleanups *ewc = dyn_cast<ExprWithCleanups>(e))
     e = ewc->getSubExpr();
Index: lib/AST/ComparisonCategories.cpp
===================================================================
--- lib/AST/ComparisonCategories.cpp
+++ lib/AST/ComparisonCategories.cpp
@@ -209,3 +209,132 @@
     Values.push_back(CCR::Unordered);
   return Values;
 }
+
+Optional<ComparisonCategoryType>
+ComparisonCategories::computeComparisonTypeForBuiltin(QualType Ty,
+                                                      bool IsMixedNullCompare) {
+  using CCT = ComparisonCategoryType;
+  if (const ComplexType *CT = Ty->getAs<ComplexType>()) {
+    if (CT->getElementType()->hasFloatingRepresentation())
+      return CCT::WeakEquality;
+    return CCT::StrongEquality;
+  }
+  if (Ty->isIntegralOrEnumerationType())
+    return CCT::StrongOrdering;
+  if (Ty->hasFloatingRepresentation())
+    return CCT::PartialOrdering;
+  // C++2a [expr.spaceship]p7: If the composite pointer type is a function
+  // pointer type, a pointer-to-member type, or std::nullptr_t, the
+  // result is of type std::strong_equality
+  if (Ty->isFunctionPointerType() || Ty->isMemberPointerType() ||
+      Ty->isNullPtrType())
+    // FIXME: consider making the function pointer case produce
+    // strong_ordering not strong_equality, per P0946R0-Jax18 discussion
+    // and direction polls
+    return CCT::StrongEquality;
+  // C++2a [expr.spaceship]p8: If the composite pointer type is an object
+  // pointer type, p <=> q is of type std::strong_ordering.
+  if (Ty->isPointerType()) {
+    // P0946R0: Comparisons between a null pointer constant and an object
+    // pointer result in std::strong_equality
+    if (IsMixedNullCompare)
+      return ComparisonCategoryType::StrongEquality;
+    return CCT::StrongOrdering;
+  }
+  return None;
+}
+
+// FIXME(EricWF): The rules to deduce the composite argument type are actually
+// quite complicated. However the rules to compute the comparison type for a
+// single builtin argument type are simple, and so are the rules to compute the
+// common comparison type. So we do that to determine the "likely" comparison
+// category type for the specified set of parameter types for a builtin
+// function.
+//
+// This information is used during overload resolution to determine if the
+// rewritten expression '(LHS <=> RHS) @ 0' (or the reversed candidate) is
+// valid for a given '@'. For example `(MemPtr <=> MemPtr) < 0` is ill-formed
+// because  `std::strong_equality` cannot be used in a relational operator.
+Optional<ComparisonCategoryType>
+ComparisonCategories::computeComparisonTypeForBuiltin(QualType LHSTy,
+                                                      QualType RHSTy) {
+  QualType Args[2] = {LHSTy, RHSTy};
+  SmallVector<ComparisonCategoryType, 8> TypeKinds;
+  for (auto QT : Args) {
+    Optional<ComparisonCategoryType> CompType =
+        computeComparisonTypeForBuiltin(QT);
+    if (!CompType)
+      return None;
+    TypeKinds.push_back(*CompType);
+  }
+  return computeCommonComparisonType(TypeKinds);
+}
+
+bool ComparisonCategoryInfo::isUsableWithOperator(
+        ComparisonCategoryType CompKind, BinaryOperatorKind Opcode) {
+  assert(BinaryOperator::isComparisonOp(Opcode));
+  if (BinaryOperator::isRelationalOp(Opcode))
+    return isOrdered(CompKind);
+  // We either have an equality or three-way opcode. These are all OK for
+  // any comparison category type.
+  return true;
+}
+
+/// C++2a [class.spaceship]p4 - compute the common category type.
+const ComparisonCategoryInfo *ComparisonCategories::computeCommonComparisonType(
+    ArrayRef<QualType> Types) const {
+  SmallVector<ComparisonCategoryType, 8> Kinds;
+  // If any type is not a comparison category, return nullptr.
+  for (auto Ty : Types) {
+    const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
+    // --- If any T is not a comparison category type, U is void.
+    if (!Info)
+      return nullptr;
+    Kinds.push_back(Info->Kind);
+  }
+  Optional<ComparisonCategoryType> CommonType =
+      computeCommonComparisonType(Kinds);
+  if (!CommonType)
+    return nullptr;
+  return lookupInfo(*CommonType);
+}
+
+Optional<ComparisonCategoryType>
+ComparisonCategories::computeCommonComparisonType(
+    ArrayRef<ComparisonCategoryType> Types) {
+  using CCT = ComparisonCategoryType;
+  std::array<unsigned, static_cast<unsigned>(CCT::Last) + 1> Seen = {};
+  auto Count = [&](CCT T) { return Seen[static_cast<unsigned>(T)]; };
+
+  // Count the number of times each comparison category type occurs in the
+  // specified type list.
+  for (auto TyKind : Types)
+    Seen[static_cast<unsigned>(TyKind)]++;
+
+  // --- Otherwise, if at least one Ti is std::weak_equality, or at least one
+  // Ti is std::strong_equality and at least one Tj is
+  // std::partial_ordering or std::weak_ordering, U is
+  // std::weak_equality.
+  if (Count(CCT::WeakEquality) ||
+      (Count(CCT::StrongEquality) &&
+       (Count(CCT::PartialOrdering) || Count(CCT::WeakOrdering))))
+    return CCT::WeakEquality;
+
+  // --- Otherwise, if at least one Ti is std::strong_equality, U is
+  // std::strong_equality
+  if (Count(CCT::StrongEquality))
+    return CCT::StrongEquality;
+
+  // --- Otherwise, if at least one Ti is std::partial_ordering, U is
+  // std::partial_ordering.
+  if (Count(CCT::PartialOrdering))
+    return CCT::PartialOrdering;
+
+  // --- Otherwise, if at least one Ti is std::weak_ordering, U is
+  // std::weak_ordering.
+  if (Count(CCT::WeakOrdering))
+    return CCT::WeakOrdering;
+
+  // --- Otherwise, U is std::strong_ordering.
+  return CCT::StrongOrdering;
+}
Index: include/clang/Serialization/ASTBitCodes.h
===================================================================
--- include/clang/Serialization/ASTBitCodes.h
+++ include/clang/Serialization/ASTBitCodes.h
@@ -1811,6 +1811,7 @@
       EXPR_FUNCTION_PARM_PACK,                // FunctionParmPackExpr
       EXPR_MATERIALIZE_TEMPORARY,             // MaterializeTemporaryExpr
       EXPR_CXX_FOLD,                          // CXXFoldExpr
+      EXPR_CXX_REWRITTEN_OPERATOR,            // CXXRewrittenOperatorExpr
 
       // CUDA
       EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -43,6 +43,7 @@
 #include "clang/Sema/ExternalSemaSource.h"
 #include "clang/Sema/IdentifierResolver.h"
 #include "clang/Sema/ObjCMethodList.h"
+#include "clang/Sema/Overload.h"
 #include "clang/Sema/Ownership.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/TypoCorrection.h"
@@ -2784,11 +2785,18 @@
                                 TemplateArgumentListInfo *ExplicitTemplateArgs,
                                             OverloadCandidateSet& CandidateSet,
                                             bool PartialOverloading = false);
-
+  void AddRewrittenOperatorCandidates(OverloadedOperatorKind Op,
+                                      SourceLocation OpLoc,
+                                      ArrayRef<Expr *> InputArgs,
+                                      const UnresolvedSetImpl &Fns,
+                                      OverloadCandidateSet &CandidateSet,
+                                      bool PerformADL);
   // Emit as a 'note' the specific overload candidate
   void NoteOverloadCandidate(NamedDecl *Found, FunctionDecl *Fn,
                              QualType DestType = QualType(),
-                             bool TakingAddress = false);
+                             bool TakingAddress = false,
+                             RewrittenOverloadCandidateKind ROC = ROC_None);
+  void NoteOverloadCandidate(OverloadCandidate *Ovl);
 
   // Emit as a series of 'note's all template and non-templates identified by
   // the expression Expr
@@ -2920,16 +2928,21 @@
                                      const UnresolvedSetImpl &Fns,
                                      Expr *input, bool RequiresADL = true);
 
-  ExprResult CreateOverloadedBinOp(SourceLocation OpLoc,
-                                   BinaryOperatorKind Opc,
-                                   const UnresolvedSetImpl &Fns,
-                                   Expr *LHS, Expr *RHS,
-                                   bool RequiresADL = true);
+  ExprResult CreateOverloadedBinOp(SourceLocation OpLoc, BinaryOperatorKind Opc,
+                                   const UnresolvedSetImpl &Fns, Expr *LHS,
+                                   Expr *RHS, bool RequiresADL = true,
+                                   bool AllowRewrittenCandidates = true);
 
   ExprResult CreateOverloadedArraySubscriptExpr(SourceLocation LLoc,
                                                 SourceLocation RLoc,
                                                 Expr *Base,Expr *Idx);
 
+  ExprResult BuildBinaryOperatorCandidate(SourceLocation OpLoc,
+                                          BinaryOperatorKind Opc,
+                                          const OverloadCandidate &Ovl,
+                                          Expr *LHS, Expr *RHS,
+                                          bool HadMultipleCandidates);
+
   ExprResult
   BuildCallToMemberFunction(Scope *S, Expr *MemExpr,
                             SourceLocation LParenLoc,
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -719,6 +719,10 @@
     /// This candidate was not viable because it is a non-default multiversioned
     /// function.
     ovl_non_default_multiversion_function,
+
+    // Thes candidate was not viable because the return type of the rewritten
+    // expression was not a valid operand to the original binary operator.
+    ovl_rewritten_operand_non_valid_for_operator
   };
 
   /// A list of implicit conversion sequences for the arguments of an
@@ -755,21 +759,25 @@
     ConversionFixItGenerator Fix;
 
     /// Viable - True to indicate that this overload candidate is viable.
-    bool Viable;
+    bool Viable : 1;
 
     /// IsSurrogate - True to indicate that this candidate is a
     /// surrogate for a conversion to a function pointer or reference
     /// (C++ [over.call.object]).
-    bool IsSurrogate;
+    bool IsSurrogate : 1;
 
     /// IgnoreObjectArgument - True to indicate that the first
     /// argument's conversion, which for this function represents the
     /// implicit object argument, should be ignored. This will be true
     /// when the candidate is a static member function (where the
     /// implicit object argument is just a placeholder) or a
     /// non-static member function when the call doesn't have an
     /// object argument.
-    bool IgnoreObjectArgument;
+    bool IgnoreObjectArgument : 1;
+
+    /// RewrittenKind - For rewritten operator candidates, the kind of rewritten
+    /// candidate it is: rewritten or synthesized.
+    unsigned char RewrittenOpKind : 2;
 
     /// FailureKind - The reason why this candidate is not viable.
     /// Actually an OverloadFailureKind.
@@ -812,6 +820,24 @@
       return CanFix;
     }
 
+    /// \brief Return the index of the conversion corresponding to the specified
+    /// argument index. If this is not a synthesized candidate, 'Idx' is
+    /// returned. Otherwise the index corresponding to the reversed parameter
+    /// is returned.
+    unsigned getConversionIndexForArgIndex(unsigned Idx) const;
+
+    /// \brief Return the conversion sequence for the specified argument index.
+    /// If this is a synthesized candidate, the argument index is reversed.
+    const ImplicitConversionSequence &getConversion(unsigned ArgIdx) const;
+
+    /// \brief Returns the parameter type corresponding to the specified index.
+    /// (The index is not reversed for synthesized candidates).
+    QualType getParamType(unsigned Idx) const {
+      if (Function)
+        return Function->getParamDecl(Idx)->getType();
+      return BuiltinParamTypes[Idx];
+    }
+
     unsigned getNumParams() const {
       if (IsSurrogate) {
         auto STy = Surrogate->getConversionType();
@@ -823,6 +849,10 @@
         return Function->getNumParams();
       return ExplicitCallArguments;
     }
+
+    RewrittenOverloadCandidateKind getRewrittenKind() const {
+      return static_cast<RewrittenOverloadCandidateKind>(RewrittenOpKind);
+    }
   };
 
   /// OverloadCandidateSet - A set of overload candidates, used in C++
@@ -853,15 +883,22 @@
 
   private:
     SmallVector<OverloadCandidate, 16> Candidates;
-    llvm::SmallPtrSet<Decl *, 16> Functions;
 
+    using DeclKindPair =
+        llvm::PointerIntPair<Decl *, 2, RewrittenOverloadCandidateKind>;
+    using DeclKindSet = llvm::SmallSet<DeclKindPair, 4>;
+    DeclKindSet Functions;
+
+  private:
     // Allocator for ConversionSequenceLists. We store the first few of these
     // inline to avoid allocation for small sets.
     llvm::BumpPtrAllocator SlabAllocator;
 
     SourceLocation Loc;
     CandidateSetKind Kind;
 
+    RewrittenOverloadCandidateKind AddingOverloadKind;
+
     constexpr static unsigned NumInlineBytes =
         24 * sizeof(ImplicitConversionSequence);
     unsigned NumInlineBytesUsed = 0;
@@ -896,19 +933,40 @@
     void destroyCandidates();
 
   public:
+    friend struct AddingRewrittenCandidateGuard;
+    struct AddingRewrittenCandidateGuard {
+      AddingRewrittenCandidateGuard(
+          OverloadCandidateSet &Candidates,
+          RewrittenOverloadCandidateKind NewOverloadKind)
+          : Candidates(Candidates) {
+        OldRewrittenKind = Candidates.AddingOverloadKind;
+        Candidates.AddingOverloadKind = NewOverloadKind;
+      }
+      ~AddingRewrittenCandidateGuard() {
+        Candidates.AddingOverloadKind = OldRewrittenKind;
+      }
+      OverloadCandidateSet &Candidates;
+      RewrittenOverloadCandidateKind OldRewrittenKind;
+    };
+
+  public:
     OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
-        : Loc(Loc), Kind(CSK) {}
+        : Loc(Loc), Kind(CSK), AddingOverloadKind(ROC_None) {}
     OverloadCandidateSet(const OverloadCandidateSet &) = delete;
     OverloadCandidateSet &operator=(const OverloadCandidateSet &) = delete;
     ~OverloadCandidateSet() { destroyCandidates(); }
 
     SourceLocation getLocation() const { return Loc; }
     CandidateSetKind getKind() const { return Kind; }
+    RewrittenOverloadCandidateKind getRewrittenKind() const {
+      return AddingOverloadKind;
+    }
 
     /// Determine when this overload candidate will be new to the
     /// overload set.
     bool isNewCandidate(Decl *F) {
-      return Functions.insert(F->getCanonicalDecl()).second;
+      DeclKindPair P(F->getCanonicalDecl(), AddingOverloadKind);
+      return Functions.insert(P).second;
     }
 
     /// Clear out all of the candidates.
@@ -948,6 +1006,7 @@
       C.Conversions = Conversions.empty()
                           ? allocateConversionSequences(NumConversions)
                           : Conversions;
+      C.RewrittenOpKind = AddingOverloadKind;
       return C;
     }
 
Index: include/clang/Basic/StmtNodes.td
===================================================================
--- include/clang/Basic/StmtNodes.td
+++ include/clang/Basic/StmtNodes.td
@@ -146,6 +146,7 @@
 def MaterializeTemporaryExpr : DStmt<Expr>;
 def LambdaExpr : DStmt<Expr>;
 def CXXFoldExpr : DStmt<Expr>;
+def CXXRewrittenOperatorExpr : DStmt<Expr>;
 
 // C++ Coroutines TS expressions
 def CoroutineSuspendExpr : DStmt<Expr, 1>;
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -3534,7 +3534,11 @@
     "is the implicit copy assignment operator|"
     "is the implicit move assignment operator|"
     "inherited constructor|"
-    "inherited constructor }0%2"
+    "inherited constructor |"
+    "rewritten comparison operator|"
+    "synthesized comparison operator|"
+    "rewritten comparison operator |"
+    "synthesized comparison operator }0%2"
     "%select{| has different class%diff{ (expected $ but has $)|}4,5"
     "| has different number of parameters (expected %4 but has %5)"
     "| has type mismatch at %ordinal4 parameter"
@@ -3622,7 +3626,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor}0 %select{|template }1"
+    "inherited constructor|"
+    "rewritten comparison operator|sythesized comparison operator|"
+    "rewritten comparison operator|synthesized comparison operator}0 %select{|template }1"
     "not viable: requires%select{ at least| at most|}2 %3 argument%s3, but %4 "
     "%plural{1:was|:were}4 provided">;
 
@@ -3634,7 +3640,10 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor}0 %select{|template }1not viable: "
+    "inherited constructor|"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator|sythesized comparison operator}0 "
+    "%select{|template }1not viable: "
     "%select{requires at least|allows at most single|requires single}2 "
     "argument %3, but %plural{0:no|:%4}4 arguments were provided">;
 
@@ -3647,7 +3656,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 has been "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 has been "
     "%select{explicitly made unavailable|explicitly deleted|"
     "implicitly deleted}2">;
 
@@ -3665,7 +3676,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 "
     "not viable: cannot convert argument of incomplete type "
     "%diff{$ to $|to parameter type}2,3 for "
     "%select{%ordinal5 argument|object argument}4"
@@ -3682,7 +3695,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 "
     "not viable: cannot convert initializer list argument to %3">;
 def note_ovl_candidate_bad_overload : Note<"candidate "
     "%select{function|function|constructor|"
@@ -3693,7 +3708,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1"
+    "inherited constructor "
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1"
     " not viable: no overload of %3 matching %2 for %ordinal4 argument">;
 def note_ovl_candidate_bad_conv : Note<"candidate "
     "%select{function|function|constructor|"
@@ -3704,7 +3721,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1"
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1"
     " not viable: no known conversion "
     "%diff{from $ to $|from argument type to parameter type}2,3 for "
     "%select{%ordinal5 argument|object argument}4"
@@ -3721,7 +3740,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1"
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1"
     " not viable: cannot implicitly convert argument "
     "%diff{of type $ to $|type to parameter type}2,3 for "
     "%select{%ordinal5 argument|object argument}4 under ARC">;
@@ -3734,7 +3755,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1"
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1"
     " not viable: expects an l-value for "
     "%select{%ordinal3 argument|object argument}2">;
 def note_ovl_candidate_bad_addrspace : Note<"candidate "
@@ -3746,8 +3769,10 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 not viable: "
-    "%select{%ordinal6|'this'}5 argument (%2) is in "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1"
+    " not viable: %select{%ordinal6|'this'}5 argument (%2) is in "
     "address space %3, but parameter must be in address space %4">;
 def note_ovl_candidate_bad_gc : Note<"candidate "
     "%select{function|function|constructor|"
@@ -3758,7 +3783,8 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 not viable: "
+    "inherited constructor }0%1"
+    " not viable: "
     "%select{%ordinal6|'this'}5 argument (%2) has %select{no|__weak|__strong}3 "
     "ownership, but parameter has %select{no|__weak|__strong}4 ownership">;
 def note_ovl_candidate_bad_ownership : Note<"candidate "
@@ -3770,7 +3796,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 not viable: "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
     "%select{%ordinal6|'this'}5 argument (%2) has "
     "%select{no|__unsafe_unretained|__strong|__weak|__autoreleasing}3 ownership,"
     " but parameter has %select{no|__unsafe_unretained|__strong|__weak|"
@@ -3791,7 +3819,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 not viable: "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
     "%ordinal4 argument (%2) would lose "
     "%select{const|restrict|const and restrict|volatile|const and volatile|"
     "volatile and restrict|const, volatile, and restrict}3 qualifier"
@@ -3805,7 +3835,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 not viable: "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
     "%ordinal4 argument (%2) would lose __unaligned qualifier">;
 def note_ovl_candidate_bad_base_to_derived_conv : Note<"candidate "
     "%select{function|function|constructor|"
@@ -3816,7 +3848,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor }0%1 not viable: "
+    "inherited constructor |"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator |synthesized comparison operator }0%1 not viable: "
     "cannot %select{convert from|convert from|bind}2 "
     "%select{base class pointer|superclass|base class object of type}2 %3 to "
     "%select{derived class pointer|subclass|derived class reference}2 %4 for "
@@ -3830,7 +3864,9 @@
     "function (the implicit copy assignment operator)|"
     "function (the implicit move assignment operator)|"
     "inherited constructor|"
-    "inherited constructor}0 not viable: "
+    "inherited constructor|"
+    "rewritten comparison operator|synthesized comparison operator|"
+    "rewritten comparison operator|synthesized comparison operator}0 not viable: "
     "call to "
     "%select{__device__|__global__|__host__|__host__ __device__|invalid}1 function from"
     " %select{__device__|__global__|__host__|__host__ __device__|invalid}2 function">;
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -247,6 +247,14 @@
     unsigned IsUnique : 1;
   };
 
+  class CXXRewrittenOperatorExprBitfields {
+    friend class CXXRewrittenOperatorExpr;
+
+    unsigned : NumExprBits;
+
+    unsigned Kind : 2;
+  };
+
   class ObjCIndirectCopyRestoreExprBitfields {
     friend class ObjCIndirectCopyRestoreExpr;
 
@@ -309,6 +317,7 @@
     InitListExprBitfields InitListExprBits;
     TypeTraitExprBitfields TypeTraitExprBits;
     CoawaitExprBitfields CoawaitBits;
+    CXXRewrittenOperatorExprBitfields CXXRewrittenOperatorBits;
   };
 
 public:
Index: include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -2548,6 +2548,7 @@
 DEF_TRAVERSE_STMT(MaterializeTemporaryExpr, {})
 DEF_TRAVERSE_STMT(CXXFoldExpr, {})
 DEF_TRAVERSE_STMT(AtomicExpr, {})
+DEF_TRAVERSE_STMT(CXXRewrittenOperatorExpr, {})
 
 // For coroutines expressions, traverse either the operand
 // as written or the implied calls, depending on what the
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -15,6 +15,7 @@
 #ifndef LLVM_CLANG_AST_EXPRCXX_H
 #define LLVM_CLANG_AST_EXPRCXX_H
 
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclCXX.h"
@@ -87,6 +88,8 @@
   SourceRange getSourceRangeImpl() const LLVM_READONLY;
 
 public:
+  friend class ASTReader;
+  friend class ASTWriter;
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
 
@@ -4207,6 +4210,65 @@
   child_range children() { return child_range(SubExprs, SubExprs + 2); }
 };
 
+// FIXME(EricWF): Document this
+class CXXRewrittenOperatorExpr : public Expr {
+public:
+  using RewrittenOpKind = RewrittenOverloadCandidateKind;
+  typedef BinaryOperatorKind Opcode;
+
+private:
+  friend class ASTReader;
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+
+  Stmt *Rewritten;
+
+  CXXRewrittenOperatorExpr(EmptyShell Empty)
+      : Expr(CXXRewrittenOperatorExprClass, Empty) {}
+
+public:
+  CXXRewrittenOperatorExpr(RewrittenOpKind Kind, Expr *Rewritten)
+      : Expr(CXXRewrittenOperatorExprClass, Rewritten->getType(),
+             Rewritten->getValueKind(), Rewritten->getObjectKind(),
+             /*Dependent*/ Rewritten->isTypeDependent(),
+             Rewritten->isValueDependent(),
+             Rewritten->isInstantiationDependent(),
+             Rewritten->containsUnexpandedParameterPack()),
+        Rewritten(Rewritten) {
+    CXXRewrittenOperatorBits.Kind = Kind;
+  }
+
+  RewrittenOpKind getRewrittenKind() const {
+    return static_cast<RewrittenOpKind>(CXXRewrittenOperatorBits.Kind);
+  }
+  void setRewrittenKind(RewrittenOpKind Kind) {
+    CXXRewrittenOperatorBits.Kind = Kind;
+  }
+
+  Expr *getRewrittenExpr() const { return static_cast<Expr *>(Rewritten); }
+  Opcode getOpcode() const;
+
+  bool isRewrittenAsReversed() const {
+    return getRewrittenKind() == ROC_AsReversedThreeWay;
+  }
+  bool isRewrittenAsThreeWay() const { return getOriginalOpcode() != BO_Cmp; }
+
+  SourceLocation getLocStart() const { return Rewritten->getLocStart(); }
+  SourceLocation getLocEnd() const { return Rewritten->getLocEnd(); }
+  SourceLocation getExprLoc() const { return getRewrittenExpr()->getExprLoc(); }
+  SourceLocation getOperatorLoc() const;
+
+  Opcode getOriginalOpcode() const;
+  Expr *getOriginalLHS() const;
+  Expr *getOriginalRHS() const;
+
+  child_range children() { return child_range(&Rewritten, &Rewritten + 1); }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXRewrittenOperatorExprClass;
+  }
+};
+
 /// Represents an expression that might suspend coroutine execution;
 /// either a co_await or co_yield expression.
 ///
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h
+++ include/clang/AST/Expr.h
@@ -886,6 +886,9 @@
     setIsUnique(false);
   }
 
+  /// Create an OpaqueValueExpr representing the specified source expression
+  static OpaqueValueExpr *Create(const ASTContext &Ctx, Expr *Source);
+
   /// Given an expression which invokes a copy constructor --- i.e.  a
   /// CXXConstructExpr, possibly wrapped in an ExprWithCleanups ---
   /// find the OpaqueValueExpr that's the source of the construction.
Index: include/clang/AST/ComparisonCategories.h
===================================================================
--- include/clang/AST/ComparisonCategories.h
+++ include/clang/AST/ComparisonCategories.h
@@ -15,6 +15,7 @@
 #ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H
 #define LLVM_CLANG_AST_COMPARISONCATEGORIES_H
 
+#include "clang/AST/OperationKinds.h"
 #include "clang/Basic/LLVM.h"
 #include "llvm/ADT/APSInt.h"
 #include "llvm/ADT/DenseMap.h"
@@ -65,6 +66,17 @@
   Last = Unordered
 };
 
+/// OperatorOverloadCandidateKind - The kind of the operator candidate in
+/// accordance with [over.match.oper].
+enum RewrittenOverloadCandidateKind : unsigned char {
+  /// Not a rewritten candidate.
+  ROC_None,
+  /// Rewritten but not synthesized.
+  ROC_AsThreeWay,
+  /// Both rewritten and synthesized.
+  ROC_AsReversedThreeWay
+};
+
 class ComparisonCategoryInfo {
   friend class ComparisonCategories;
   friend class Sema;
@@ -127,28 +139,42 @@
   }
 
   /// True iff the comparison category is an equality comparison.
-  bool isEquality() const { return !isOrdered(); }
+  bool isEquality() const { return isEquality(Kind); }
+  static bool isEquality(ComparisonCategoryType Kind) {
+    return !isOrdered(Kind);
+  }
 
   /// True iff the comparison category is a relational comparison.
-  bool isOrdered() const {
+  bool isOrdered() const { return isOrdered(Kind); }
+  static bool isOrdered(ComparisonCategoryType Kind) {
     using CCK = ComparisonCategoryType;
     return Kind == CCK::PartialOrdering || Kind == CCK::WeakOrdering ||
            Kind == CCK::StrongOrdering;
   }
 
   /// True iff the comparison is "strong". i.e. it checks equality and
   /// not equivalence.
-  bool isStrong() const {
+  bool isStrong() const { return isStrong(Kind); }
+  static bool isStrong(ComparisonCategoryType Kind) {
     using CCK = ComparisonCategoryType;
     return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering;
   }
 
   /// True iff the comparison is not totally ordered.
-  bool isPartial() const {
+  bool isPartial() const { return isPartial(Kind); }
+  static bool isPartial(ComparisonCategoryType Kind) {
     using CCK = ComparisonCategoryType;
     return Kind == CCK::PartialOrdering;
   }
 
+  /// Return whether the specified comparison category type can be used as
+  /// an operand to the specified relational binary operator.
+  static bool isUsableWithOperator(ComparisonCategoryType CompKind,
+                                   BinaryOperatorKind Opcode);
+  bool isUsableWithOperator(BinaryOperatorKind Opc) const {
+    return isUsableWithOperator(Kind, Opc);
+  }
+
   /// Converts the specified result kind into the the correct result kind
   /// for this category. Specifically it lowers strong equality results to
   /// weak equivalence if needed.
@@ -189,6 +215,28 @@
   static StringRef getCategoryString(ComparisonCategoryType Kind);
   static StringRef getResultString(ComparisonCategoryResult Kind);
 
+  /// Return the comparison category information for the
+  /// "common comparison type" for a specified list of types. If there is no
+  /// such common comparison type, or if any of the specified types are not
+  /// comparison category types, null is returned.
+  const ComparisonCategoryInfo *
+  computeCommonComparisonType(ArrayRef<QualType> Types) const;
+  static Optional<ComparisonCategoryType>
+  computeCommonComparisonType(ArrayRef<ComparisonCategoryType> Types);
+
+  /// Return the comparison category type which would be returned
+  /// for a builtin comparison operator taking the specified type, or None if no
+  /// such type exists.
+  ///
+  /// \param Ty The composite comparison type
+  /// \param IsMixedNullCompare True if exactly one of the operands is a null
+  /// pointer constant.
+  static Optional<ComparisonCategoryType>
+  computeComparisonTypeForBuiltin(QualType Ty, bool IsMixedNullCompare = false);
+
+  static Optional<ComparisonCategoryType>
+  computeComparisonTypeForBuiltin(QualType LHSTy, QualType RHSTy);
+
   /// Return the list of results which are valid for the specified
   /// comparison category type.
   static std::vector<ComparisonCategoryResult>
@@ -209,6 +257,7 @@
   /// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is
   /// possible.
   const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
+  const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
 
 public:
   /// Return the cached comparison category information for the
@@ -223,9 +272,6 @@
   }
 
 private:
-  const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
-
-private:
   friend class ASTContext;
 
   explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to