EricWF updated this revision to Diff 146090.
EricWF added a comment.

Remove a bunch of nonsense this patch originally had. Specifically remove the 
bits of overload resolution which disambiguated rewritten expressions after the 
fact; Instead attempt to validate that the return type of the three-way 
comparison operand in a rewritten relational or equality can be used with the 
the specified operand (e.g. `std::strong_equality` cannot be used with a 
relational operator).


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/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/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 {};
+// expected-note@+1 2 {{candidate}}
+Tag<0> operator<=>(X, Y) {
+  return {};
+}
+// expected-note@+1 2 {{candidate}}
+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;
+};
+
+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() {
+  // expected-error@+1 {{use of overloaded operator '<' is ambiguous}}
+  (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::CXXRewrittenExprClass:
     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,21 @@
   Code = serialization::EXPR_CXX_FOLD;
 }
 
+void ASTStmtWriter::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) {
+  VisitExpr(E);
+  Record.push_back(E->getRewrittenKind());
+  Record.AddStmt(E->SubExprs[0]);
+  Record.AddStmt(E->SubExprs[1]);
+  switch (E->getRewrittenKind()) {
+  case CXXRewrittenExpr::Comparison: {
+    CXXRewrittenExpr::ComparisonBits Bits = E->ExtraBits.CompareBits;
+    Record.push_back(Bits.IsSynthesized);
+    break;
+  }
+  }
+  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,21 @@
   E->Opcode = (BinaryOperatorKind)Record.readInt();
 }
 
+void ASTStmtReader::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) {
+  VisitExpr(E);
+  E->setRewrittenKind(
+      static_cast<CXXRewrittenExpr::RewrittenKind>(Record.readInt()));
+  E->SubExprs[0] = Record.readSubExpr();
+  E->SubExprs[1] = Record.readSubExpr();
+  switch (E->getRewrittenKind()) {
+  case CXXRewrittenExpr::Comparison: {
+    CXXRewrittenExpr::ComparisonBits &Bits = E->ExtraBits.CompareBits;
+    Bits.IsSynthesized = Record.readInt();
+    break;
+  }
+  }
+}
+
 void ASTStmtReader::VisitOpaqueValueExpr(OpaqueValueExpr *E) {
   VisitExpr(E);
   E->SourceExpr = Record.readSubExpr();
@@ -4074,6 +4089,9 @@
       S = new (Context) CXXFoldExpr(Empty);
       break;
 
+    case EXPR_CXX_REWRITTEN_OPERATOR:
+      S = new (Context) CXXRewrittenExpr(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,14 @@
     return getSema().BuildEmptyCXXFoldExpr(EllipsisLoc, Operator);
   }
 
+  ExprResult
+  RebuildCXXRewrittenExpr(CXXRewrittenExpr::RewrittenKind Kind, Expr *Original,
+                          Expr *Rewritten,
+                          CXXRewrittenExpr::ExtraRewrittenBits ExtraBits) {
+    return new (SemaRef.Context)
+        CXXRewrittenExpr(Kind, Original, Rewritten, ExtraBits);
+  }
+
   /// Build a new atomic operation expression.
   ///
   /// By default, performs semantic analysis to build the new expression.
@@ -11515,6 +11523,68 @@
   return getDerived().TransformExpr(E->GetTemporaryExpr());
 }
 
+static Expr *extractOperand(Expr *E, unsigned Idx) {
+  assert(Idx < 2);
+  if (auto *BO = dyn_cast<BinaryOperator>(E)) {
+    if (Idx == 0)
+      return BO->getLHS();
+    return BO->getRHS();
+  }
+  if (auto *CE = dyn_cast<CallExpr>(E)) {
+    assert(CE->getNumArgs() == 2);
+    return CE->getArg(Idx);
+  }
+  llvm_unreachable("unhandled case");
+}
+static std::pair<Expr *, Expr *>
+extractOriginalOperandsFromRewrittenComparison(Expr *E, bool IsThreeWay,
+                                               bool IsSynthesized) {
+  if (IsThreeWay)
+    return {extractOperand(E, IsSynthesized ? 1 : 0),
+            extractOperand(E, IsSynthesized ? 0 : 1)};
+  return extractOriginalOperandsFromRewrittenComparison(
+      extractOperand(E, IsSynthesized ? 1 : 0), true, IsSynthesized);
+}
+
+template <typename Derived>
+ExprResult
+TreeTransform<Derived>::TransformCXXRewrittenExpr(CXXRewrittenExpr *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;
+
+  Expr *Original;
+  switch (E->getRewrittenKind()) {
+  case CXXRewrittenExpr::Comparison: {
+    BinaryOperator *Op = cast<BinaryOperator>(E->getOriginalExpr());
+
+    // Extract the already transformed operands from the rewritten expression.
+    std::pair<Expr *, Expr *> OrigArgs =
+        extractOriginalOperandsFromRewrittenComparison(
+            Rewritten, Op->getOpcode() == BO_Cmp,
+            E->getRewrittenInfo()->CompareBits.IsSynthesized);
+
+    // Build a dummy node representing the expression as written.
+    Original = new (SemaRef.Context) BinaryOperator(
+        OpaqueValueExpr::Create(SemaRef.Context, OrigArgs.first),
+        OpaqueValueExpr::Create(SemaRef.Context, OrigArgs.second),
+        Op->getOpcode(), Rewritten->getType(), Rewritten->getValueKind(),
+        Rewritten->getObjectKind(), Op->getOperatorLoc(), Op->getFPFeatures());
+    break;
+  }
+  }
+  return getDerived().RebuildCXXRewrittenExpr(
+      E->getRewrittenKind(), Original, Rewritten, *E->getRewrittenInfo());
+}
+
 template<typename Derived>
 ExprResult
 TreeTransform<Derived>::TransformCXXFoldExpr(CXXFoldExpr *E) {
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_Synthesized)
+    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;
@@ -5964,6 +5979,7 @@
   Candidate.Function = Function;
   Candidate.Viable = true;
   Candidate.IsSurrogate = false;
+  Candidate.RewrittenOpKind = ROC_None;
   Candidate.IgnoreObjectArgument = false;
   Candidate.ExplicitCallArguments = Args.size();
 
@@ -6654,6 +6670,7 @@
     Candidate.Function = MethodTmpl->getTemplatedDecl();
     Candidate.Viable = false;
     Candidate.IsSurrogate = false;
+    Candidate.RewrittenOpKind = ROC_None;
     Candidate.IgnoreObjectArgument =
         cast<CXXMethodDecl>(Candidate.Function)->isStatic() ||
         ObjectType.isNull();
@@ -6718,6 +6735,7 @@
     Candidate.Function = FunctionTemplate->getTemplatedDecl();
     Candidate.Viable = false;
     Candidate.IsSurrogate = false;
+    Candidate.RewrittenOpKind = ROC_None;
     // Ignore the object argument if there is one, since we don't have an object
     // type.
     Candidate.IgnoreObjectArgument =
@@ -6889,6 +6907,7 @@
   Candidate.FoundDecl = FoundDecl;
   Candidate.Function = Conversion;
   Candidate.IsSurrogate = false;
+  Candidate.RewrittenOpKind = ROC_None;
   Candidate.IgnoreObjectArgument = false;
   Candidate.FinalConversion.setAsIdentityConversion();
   Candidate.FinalConversion.setFromType(ConvType);
@@ -7050,6 +7069,7 @@
     Candidate.Viable = false;
     Candidate.FailureKind = ovl_fail_bad_deduction;
     Candidate.IsSurrogate = false;
+    Candidate.RewrittenOpKind = ROC_None;
     Candidate.IgnoreObjectArgument = false;
     Candidate.ExplicitCallArguments = 1;
     Candidate.DeductionFailure = MakeDeductionFailureInfo(Context, Result,
@@ -7090,6 +7110,7 @@
   Candidate.Surrogate = Conversion;
   Candidate.Viable = true;
   Candidate.IsSurrogate = true;
+  Candidate.RewrittenOpKind = ROC_None;
   Candidate.IgnoreObjectArgument = false;
   Candidate.ExplicitCallArguments = Args.size();
 
@@ -7186,7 +7207,7 @@
 void Sema::AddMemberOperatorCandidates(OverloadedOperatorKind Op,
                                        SourceLocation OpLoc,
                                        ArrayRef<Expr *> Args,
-                                       OverloadCandidateSet& CandidateSet,
+                                       OverloadCandidateSet &CandidateSet,
                                        SourceRange OpRange) {
   DeclarationName OpName = Context.DeclarationNames.getCXXOperatorName(Op);
 
@@ -7247,6 +7268,7 @@
   Candidate.FoundDecl = DeclAccessPair::make(nullptr, AS_none);
   Candidate.Function = nullptr;
   Candidate.IsSurrogate = false;
+  Candidate.RewrittenOpKind = ROC_None;
   Candidate.IgnoreObjectArgument = false;
   std::copy(ParamTys, ParamTys + Args.size(), Candidate.BuiltinParamTypes);
 
@@ -8862,20 +8884,109 @@
   }
 }
 
+
+/// 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::RewrittenCandidateContextGuard Guard(CandidateSet);
+
+    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;
+      Ovl.RewrittenOpKind = Kind;
+      if (IsRelationalOrEquality) {
+        if (FunctionDecl *FD = Ovl.Function) {
+          if (FD->getReturnType()->isUndeducedType()) {
+            if (DeduceReturnType(FD, OpLoc)) {
+              Ovl.Viable = false;
+              continue;
+            }
+          }
+          QualType RetTy = FD->getReturnType();
+          if (const ComparisonCategoryInfo *Info =
+                  Context.CompCategories.lookupInfoForType(RetTy)) {
+            if (!Info->isUsableWithOperator(Opc)) {
+              Ovl.Viable = false;
+              continue;
+            }
+          } else {
+            // FIXME(EricWF): Check that the return type can be used with
+            // the specified relational operator
+          }
+        } else {
+          Optional<ComparisonCategoryType> CompType =
+              ComparisonCategories::computeComparisonTypeForBuiltin(
+                  Ovl.BuiltinParamTypes[0], Ovl.BuiltinParamTypes[1]);
+          if (!CompType ||
+              !ComparisonCategoryInfo::isUsableWithOperator(*CompType, Opc)) {
+            Ovl.Viable = false;
+            continue;
+          }
+        }
+      }
+    }
+  };
+
+  // If we have a relational or equality operation, add the rewritten candidates
+  // of the form: (LHS <=> RHS) @ 0
+  if (IsRelationalOrEquality)
+   AddCandidates(InputArgs, ROC_Rewritten);
+
+  // 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_Synthesized);
+}
+
 /// 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,
-                                 TemplateArgumentListInfo *ExplicitTemplateArgs,
-                                           OverloadCandidateSet& CandidateSet,
-                                           bool PartialOverloading) {
+void Sema::AddArgumentDependentLookupCandidates(
+    DeclarationName Name, SourceLocation Loc, ArrayRef<Expr *> Args,
+    TemplateArgumentListInfo *ExplicitTemplateArgs,
+    OverloadCandidateSet &CandidateSet, bool PartialOverloading) {
   ADLResult Fns;
 
   // FIXME: This approach for uniquing ADL results (and removing
@@ -9008,8 +9119,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 +9137,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 +9244,50 @@
     // 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_Synthesized || C2Roc == ROC_Synthesized) &&
+          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());
+          if (Ovl.getRewrittenKind() == ROC_Synthesized) {
+            SmallVector<QualType, 2> RevTypes(Types.rbegin(), Types.rend());
+            return RevTypes;
+          }
+          return Types;
+        };
+        if (GetParamTypes(Cand1) == GetParamTypes(Cand2))
+          return C2Roc == ROC_Synthesized;
+      }
+    }
+  }
+
   // Check C++17 tie-breakers for deduction guides.
   {
     auto *Guide1 = dyn_cast_or_null<CXXDeductionGuideDecl>(Cand1.Function);
@@ -12228,6 +12383,175 @@
   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_Synthesized;
+  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_Synthesized)
+      std::swap(NewLHS, NewRHS);
+
+    RewrittenRes =
+        S.CreateOverloadedBinOp(OpLoc, Opc, Fns, NewLHS, NewRHS, PerformADL,
+                                /*AllowRewrittenCandidates*/ false);
+    if (RewrittenRes.isInvalid())
+      return ExprError();
+  }
+  Expr *Rewritten = RewrittenRes.get();
+
+  // Create a dummy expression representing the original expression as written.
+  // FIXME(EricWF): This doesn't actually really represent the expression as
+  // written, because it may not result in a to a builtin operator.
+  Expr *Original = new (S.Context)
+      BinaryOperator(OpaqueValueExpr::Create(S.Context, Args[0]),
+                     OpaqueValueExpr::Create(S.Context, Args[1]), Opc,
+                     Rewritten->getType(), Rewritten->getValueKind(),
+                     Rewritten->getObjectKind(), OpLoc, S.FPFeatures);
+
+  CXXRewrittenExpr::ExtraRewrittenBits ExtraBits;
+  ExtraBits.CompareBits.IsSynthesized = IsSynthesized;
+  return new (S.Context) CXXRewrittenExpr(CXXRewrittenExpr::Comparison,
+                                          Original, Rewritten, ExtraBits);
+}
+
 /// Create a binary operation that may resolve to an overloaded
 /// operator.
 ///
@@ -12244,11 +12568,11 @@
 ///
 /// \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
 
@@ -12310,11 +12634,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,191 +12670,98 @@
   // 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>();
-        }
-
-        // 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);
-        }
+  // 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();
+  }
 
-        // 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();
+  // 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
+    //   viable functions, then the operator is assumed to be the
+    //   built-in operator and interpreted according to clause 5.
+    if (Opc == BO_Comma)
+      break;
 
-        ExprResult ArgsRes1 =
-            PerformImplicitConversion(Args[1], Best->BuiltinParamTypes[1],
-                                      Best->Conversions[1], AA_Passing);
-        if (ArgsRes1.isInvalid())
-          return ExprError();
-        Args[1] = ArgsRes1.get();
-        break;
+    // For class as left operand for assignment or compound assignment
+    // 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) {
+      Diag(OpLoc, diag::err_ovl_no_viable_oper)
+          << 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();
       }
-    }
-
-    case OR_No_Viable_Function: {
-      // C++ [over.match.oper]p9:
-      //   If the operator is the operator , [...] and there are no
-      //   viable functions, then the operator is assumed to be the
-      //   built-in operator and interpreted according to clause 5.
-      if (Opc == BO_Comma)
-        break;
-
-      // For class as left operand for assignment or compound assignment
-      // 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) {
-        Diag(OpLoc,  diag::err_ovl_no_viable_oper)
-             << 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();
-        }
-      } else {
-        // This is an erroneous use of an operator which can be overloaded by
-        // a non-member function. Check for non-member operators which were
-        // defined too late to be candidates.
-        if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args))
-          // FIXME: Recover by calling the found function.
-          return ExprError();
+    } else {
+      // This is an erroneous use of an operator which can be overloaded by
+      // a non-member function. Check for non-member operators which were
+      // defined too late to be candidates.
+      if (DiagnoseTwoPhaseOperatorLookup(*this, Op, OpLoc, Args))
+        // FIXME: Recover by calling the found function.
+        return ExprError();
 
-        // No viable function; try to create a built-in operation, which will
-        // produce an error. Then, show the non-viable candidates.
-        Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
-      }
-      assert(Result.isInvalid() &&
-             "C++ binary operator overloading is missing candidates!");
-      if (Result.isInvalid())
-        CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
-                                    BinaryOperator::getOpcodeStr(Opc), OpLoc);
-      return Result;
+      // No viable function; try to create a built-in operation, which will
+      // produce an error. Then, show the non-viable candidates.
+      Result = CreateBuiltinBinOp(OpLoc, Opc, Args[0], Args[1]);
     }
-
-    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();
-      CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args,
+    assert(Result.isInvalid() &&
+           "C++ binary operator overloading is missing candidates!");
+    if (Result.isInvalid())
+      CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
                                   BinaryOperator::getOpcodeStr(Opc), OpLoc);
-      return ExprError();
+    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();
+    CandidateSet.NoteCandidates(*this, OCD_ViableCandidates, Args,
+                                BinaryOperator::getOpcodeStr(Opc), OpLoc);
+    return ExprError();
 
-    case OR_Deleted:
-      if (isImplicitlyDeleted(Best->Function)) {
-        CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
-        Diag(OpLoc, diag::err_ovl_deleted_special_oper)
+  case OR_Deleted:
+    if (isImplicitlyDeleted(Best->Function)) {
+      CXXMethodDecl *Method = cast<CXXMethodDecl>(Best->Function);
+      Diag(OpLoc, diag::err_ovl_deleted_special_oper)
           << Context.getRecordType(Method->getParent())
           << getSpecialMember(Method);
 
-        // The user probably meant to call this special member. Just
-        // explain why it's deleted.
-        NoteDeletedFunction(Method);
-        return ExprError();
-      } else {
-        Diag(OpLoc, diag::err_ovl_deleted_oper)
-          << Best->Function->isDeleted()
-          << BinaryOperator::getOpcodeStr(Opc)
+      // The user probably meant to call this special member. Just
+      // explain why it's deleted.
+      NoteDeletedFunction(Method);
+      return ExprError();
+    } else {
+      Diag(OpLoc, diag::err_ovl_deleted_oper)
+          << Best->Function->isDeleted() << BinaryOperator::getOpcodeStr(Opc)
           << getDeletedOrUnavailableSuffix(Best->Function)
           << Args[0]->getSourceRange() << Args[1]->getSourceRange();
-      }
-      CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
-                                  BinaryOperator::getOpcodeStr(Opc), OpLoc);
-      return ExprError();
+    }
+    CandidateSet.NoteCandidates(*this, OCD_AllCandidates, Args,
+                                BinaryOperator::getOpcodeStr(Opc), OpLoc);
+    return ExprError();
   }
 
   // We matched a built-in operator; build it.
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");
-
-  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");
-  }();
+  Optional<ComparisonCategoryType> TypeKind =
+      ComparisonCategories::computeComparisonTypeForBuiltin(Type);
+  assert(TypeKind && "composite type for <=> has not been set");
 
-  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.
-    return InvalidOperands(Loc, LHS, RHS);
+    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::CXXRewrittenExprClass:
+    return canThrow(cast<CXXRewrittenExpr>(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 *VisitCXXRewrittenExpr(const CXXRewrittenExpr *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,11 @@
   ID.AddInteger(S->getOperator());
 }
 
+void StmtProfiler::VisitCXXRewrittenExpr(const CXXRewrittenExpr *S) {
+  VisitExpr(S);
+  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,13 @@
   OS << ")";
 }
 
+void StmtPrinter::VisitCXXRewrittenExpr(CXXRewrittenExpr *E) {
+  // FIXME(EricWF): Are there ever cases where we want to display the rewritten
+  // code? For example when producing diagnostics on implicitly generated
+  // expressions?
+  Visit(E->getOriginalExpr());
+}
+
 // 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,10 @@
     mangleExpression(cast<CXXStdInitializerListExpr>(E)->getSubExpr(), Arity);
     break;
 
+  case Expr::CXXRewrittenExprClass:
+    mangleExpression(cast<CXXRewrittenExpr>(E)->getOriginalExpr(), Arity);
+    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 VisitCXXRewrittenExpr(const CXXRewrittenExpr *E) {
+    return StmtVisitorTy::Visit(E->getRewrittenExpr());
+  }
 };
 
 } // namespace
@@ -10859,6 +10863,8 @@
   case Expr::ChooseExprClass: {
     return CheckICE(cast<ChooseExpr>(E)->getChosenSubExpr(), Ctx);
   }
+  case Expr::CXXRewrittenExprClass:
+    return CheckICE(cast<CXXRewrittenExpr>(E)->getRewrittenExpr(), Ctx);
   }
 
   llvm_unreachable("Invalid StmtClass!");
Index: lib/AST/ExprClassification.cpp
===================================================================
--- lib/AST/ExprClassification.cpp
+++ lib/AST/ExprClassification.cpp
@@ -287,7 +287,8 @@
     if (cast<GenericSelectionExpr>(E)->isResultDependent())
       return Cl::CL_PRValue;
     return ClassifyInternal(Ctx,cast<GenericSelectionExpr>(E)->getResultExpr());
-
+  case Expr::CXXRewrittenExprClass:
+    return ClassifyInternal(Ctx, cast<CXXRewrittenExpr>(E)->getRewrittenExpr());
   case Expr::BinaryOperatorClass:
   case Expr::CompoundAssignOperatorClass:
     // C doesn't have any binary expressions that are lvalues.
Index: lib/AST/Expr.cpp
===================================================================
--- lib/AST/Expr.cpp
+++ lib/AST/Expr.cpp
@@ -3188,6 +3188,11 @@
     return false;
   }
 
+  case CXXRewrittenExprClass: {
+    const auto *RO = cast<CXXRewrittenExpr>(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,127 @@
     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;
+}
+
+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;
+  // Count the number of times each comparison category type occurs in the
+  // specified type list. 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. If any type is not a comparison category, return
+  // nullptr.
+  for (auto TyKind : Types) {
+    // --- If any T is not a comparison category type, U is void.
+    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;
+
+  // FIXME(EricWF): What if we don't find std::strong_ordering
+  // --- 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
@@ -1666,7 +1666,7 @@
       EXPR_OBJC_BOXED_EXPRESSION,
       EXPR_OBJC_ARRAY_LITERAL,
       EXPR_OBJC_DICTIONARY_LITERAL,
-   
+
       /// An ObjCEncodeExpr record.
       EXPR_OBJC_ENCODE,
 
@@ -1725,7 +1725,7 @@
       EXPR_OBJC_AVAILABILITY_CHECK,
 
       // C++
-      
+
       /// A CXXCatchStmt record.
       STMT_CXX_CATCH,
 
@@ -1774,59 +1774,60 @@
       /// A CXXBoolLiteralExpr record.
       EXPR_CXX_BOOL_LITERAL,
 
-      EXPR_CXX_NULL_PTR_LITERAL,  // CXXNullPtrLiteralExpr
-      EXPR_CXX_TYPEID_EXPR,       // CXXTypeidExpr (of expr).
-      EXPR_CXX_TYPEID_TYPE,       // CXXTypeidExpr (of type).
-      EXPR_CXX_THIS,              // CXXThisExpr
-      EXPR_CXX_THROW,             // CXXThrowExpr
-      EXPR_CXX_DEFAULT_ARG,       // CXXDefaultArgExpr
-      EXPR_CXX_DEFAULT_INIT,      // CXXDefaultInitExpr
-      EXPR_CXX_BIND_TEMPORARY,    // CXXBindTemporaryExpr
+      EXPR_CXX_NULL_PTR_LITERAL, // CXXNullPtrLiteralExpr
+      EXPR_CXX_TYPEID_EXPR,      // CXXTypeidExpr (of expr).
+      EXPR_CXX_TYPEID_TYPE,      // CXXTypeidExpr (of type).
+      EXPR_CXX_THIS,             // CXXThisExpr
+      EXPR_CXX_THROW,            // CXXThrowExpr
+      EXPR_CXX_DEFAULT_ARG,      // CXXDefaultArgExpr
+      EXPR_CXX_DEFAULT_INIT,     // CXXDefaultInitExpr
+      EXPR_CXX_BIND_TEMPORARY,   // CXXBindTemporaryExpr
 
       EXPR_CXX_SCALAR_VALUE_INIT, // CXXScalarValueInitExpr
       EXPR_CXX_NEW,               // CXXNewExpr
       EXPR_CXX_DELETE,            // CXXDeleteExpr
       EXPR_CXX_PSEUDO_DESTRUCTOR, // CXXPseudoDestructorExpr
-      
-      EXPR_EXPR_WITH_CLEANUPS,    // ExprWithCleanups
-      
+
+      EXPR_EXPR_WITH_CLEANUPS, // ExprWithCleanups
+
       EXPR_CXX_DEPENDENT_SCOPE_MEMBER,   // CXXDependentScopeMemberExpr
       EXPR_CXX_DEPENDENT_SCOPE_DECL_REF, // DependentScopeDeclRefExpr
       EXPR_CXX_UNRESOLVED_CONSTRUCT,     // CXXUnresolvedConstructExpr
       EXPR_CXX_UNRESOLVED_MEMBER,        // UnresolvedMemberExpr
       EXPR_CXX_UNRESOLVED_LOOKUP,        // UnresolvedLookupExpr
 
-      EXPR_CXX_EXPRESSION_TRAIT,  // ExpressionTraitExpr
-      EXPR_CXX_NOEXCEPT,          // CXXNoexceptExpr
+      EXPR_CXX_EXPRESSION_TRAIT, // ExpressionTraitExpr
+      EXPR_CXX_NOEXCEPT,         // CXXNoexceptExpr
 
-      EXPR_OPAQUE_VALUE,          // OpaqueValueExpr
-      EXPR_BINARY_CONDITIONAL_OPERATOR,  // BinaryConditionalOperator
-      EXPR_TYPE_TRAIT,            // TypeTraitExpr
-      EXPR_ARRAY_TYPE_TRAIT,      // ArrayTypeTraitIntExpr
-      
-      EXPR_PACK_EXPANSION,        // PackExpansionExpr
-      EXPR_SIZEOF_PACK,           // SizeOfPackExpr
-      EXPR_SUBST_NON_TYPE_TEMPLATE_PARM, // SubstNonTypeTemplateParmExpr
-      EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK,// SubstNonTypeTemplateParmPackExpr
-      EXPR_FUNCTION_PARM_PACK,    // FunctionParmPackExpr
-      EXPR_MATERIALIZE_TEMPORARY, // MaterializeTemporaryExpr
-      EXPR_CXX_FOLD,              // CXXFoldExpr
+      EXPR_OPAQUE_VALUE,                // OpaqueValueExpr
+      EXPR_BINARY_CONDITIONAL_OPERATOR, // BinaryConditionalOperator
+      EXPR_TYPE_TRAIT,                  // TypeTraitExpr
+      EXPR_ARRAY_TYPE_TRAIT,            // ArrayTypeTraitIntExpr
+
+      EXPR_PACK_EXPANSION,                    // PackExpansionExpr
+      EXPR_SIZEOF_PACK,                       // SizeOfPackExpr
+      EXPR_SUBST_NON_TYPE_TEMPLATE_PARM,      // SubstNonTypeTemplateParmExpr
+      EXPR_SUBST_NON_TYPE_TEMPLATE_PARM_PACK, // SubstNonTypeTemplateParmPackExpr
+      EXPR_FUNCTION_PARM_PACK,                // FunctionParmPackExpr
+      EXPR_MATERIALIZE_TEMPORARY,             // MaterializeTemporaryExpr
+      EXPR_CXX_FOLD,                          // CXXFoldExpr
+      EXPR_CXX_REWRITTEN_OPERATOR,            // CXXRewrittenExpr
 
       // CUDA
-      EXPR_CUDA_KERNEL_CALL,       // CUDAKernelCallExpr      
+      EXPR_CUDA_KERNEL_CALL, // CUDAKernelCallExpr
 
       // OpenCL
-      EXPR_ASTYPE,                 // AsTypeExpr
+      EXPR_ASTYPE, // AsTypeExpr
 
       // Microsoft
-      EXPR_CXX_PROPERTY_REF_EXPR, // MSPropertyRefExpr
+      EXPR_CXX_PROPERTY_REF_EXPR,       // MSPropertyRefExpr
       EXPR_CXX_PROPERTY_SUBSCRIPT_EXPR, // MSPropertySubscriptExpr
-      EXPR_CXX_UUIDOF_EXPR,       // CXXUuidofExpr (of expr).
-      EXPR_CXX_UUIDOF_TYPE,       // CXXUuidofExpr (of type).
-      STMT_SEH_LEAVE,             // SEHLeaveStmt
-      STMT_SEH_EXCEPT,            // SEHExceptStmt
-      STMT_SEH_FINALLY,           // SEHFinallyStmt
-      STMT_SEH_TRY,               // SEHTryStmt
+      EXPR_CXX_UUIDOF_EXPR,             // CXXUuidofExpr (of expr).
+      EXPR_CXX_UUIDOF_TYPE,             // CXXUuidofExpr (of type).
+      STMT_SEH_LEAVE,                   // SEHLeaveStmt
+      STMT_SEH_EXCEPT,                  // SEHExceptStmt
+      STMT_SEH_FINALLY,                 // SEHFinallyStmt
+      STMT_SEH_TRY,                     // SEHTryStmt
 
       // OpenMP directives
       STMT_OMP_PARALLEL_DIRECTIVE,
@@ -1879,10 +1880,10 @@
       EXPR_OMP_ARRAY_SECTION,
 
       // ARC
-      EXPR_OBJC_BRIDGED_CAST,     // ObjCBridgedCastExpr
+      EXPR_OBJC_BRIDGED_CAST, // ObjCBridgedCastExpr
 
-      STMT_MS_DEPENDENT_EXISTS,   // MSDependentExistsStmt
-      EXPR_LAMBDA,                // LambdaExpr
+      STMT_MS_DEPENDENT_EXISTS, // MSDependentExistsStmt
+      EXPR_LAMBDA,              // LambdaExpr
       STMT_COROUTINE_BODY,
       STMT_CORETURN,
       EXPR_COAWAIT,
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -2784,7 +2784,12 @@
                                 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(),
@@ -2920,16 +2925,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
@@ -62,7 +62,7 @@
     /// Succeeded, but refers to a deleted function.
     OR_Deleted
   };
-  
+
   enum OverloadCandidateDisplayKind {
     /// Requests that all candidates be shown.  Viable candidates will
     /// be printed first.
@@ -72,6 +72,17 @@
     OCD_ViableCandidates
   };
 
+  /// 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_Rewritten,
+    /// Both rewritten and synthesized.
+    ROC_Synthesized
+  };
+
   /// 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
@@ -107,7 +118,7 @@
     /// Integral conversions (C++ [conv.integral])
     ICK_Integral_Conversion,
 
-    /// Floating point conversions (C++ [conv.double] 
+    /// Floating point conversions (C++ [conv.double]
     ICK_Floating_Conversion,
 
     /// Complex conversions (C99 6.3.1.6)
@@ -252,7 +263,7 @@
     /// Whether the qualification conversion involves a change in the
     /// Objective-C lifetime (for automatic reference counting).
     unsigned QualificationIncludesObjCLifetime : 1;
-    
+
     /// IncompatibleObjC - Whether this is an Objective-C conversion
     /// that we should warn about (if we actually use it).
     unsigned IncompatibleObjC : 1;
@@ -282,7 +293,7 @@
     /// Whether this binds a reference to an object with a different
     /// Objective-C lifetime qualifier.
     unsigned ObjCLifetimeConversionBinding : 1;
-    
+
     /// FromType - The type that this conversion is converting
     /// from. This is an opaque pointer that can be translated into a
     /// QualType.
@@ -303,13 +314,13 @@
 
     void setFromType(QualType T) { FromTypePtr = T.getAsOpaquePtr(); }
 
-    void setToType(unsigned Idx, QualType T) { 
+    void setToType(unsigned Idx, QualType T) {
       assert(Idx < 3 && "To type index is out of range");
-      ToTypePtrs[Idx] = T.getAsOpaquePtr(); 
+      ToTypePtrs[Idx] = T.getAsOpaquePtr();
     }
 
     void setAllToTypes(QualType T) {
-      ToTypePtrs[0] = T.getAsOpaquePtr(); 
+      ToTypePtrs[0] = T.getAsOpaquePtr();
       ToTypePtrs[1] = ToTypePtrs[0];
       ToTypePtrs[2] = ToTypePtrs[0];
     }
@@ -324,11 +335,11 @@
     }
 
     void setAsIdentityConversion();
-    
+
     bool isIdentityConversion() const {
       return Second == ICK_Identity && Third == ICK_Identity;
     }
-    
+
     ImplicitConversionRank getRank() const;
     NarrowingKind
     getNarrowingKind(ASTContext &Context, const Expr *Converted,
@@ -562,16 +573,16 @@
       new (this) ImplicitConversionSequence(Other);
       return *this;
     }
-    
+
     ~ImplicitConversionSequence() {
       destruct();
     }
 
     Kind getKind() const {
       assert(isInitialized() && "querying uninitialized conversion");
       return Kind(ConversionKind);
     }
-    
+
     /// Return a ranking of the implicit conversion sequence
     /// kind, where smaller ranks represent better conversion
     /// sequences.
@@ -581,11 +592,11 @@
     /// per C++ [over.best.ics]p10.
     unsigned getKindRank() const {
       switch (getKind()) {
-      case StandardConversion: 
+      case StandardConversion:
         return 0;
 
       case UserDefinedConversion:
-      case AmbiguousConversion: 
+      case AmbiguousConversion:
         return 1;
 
       case EllipsisConversion:
@@ -755,21 +766,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.
@@ -781,7 +796,7 @@
 
     union {
       DeductionFailureInfo DeductionFailure;
-      
+
       /// FinalConversion - For a conversion function (where Function is
       /// a CXXConversionDecl), the standard conversion that occurs
       /// after the call to the overload candidate to convert the result
@@ -812,6 +827,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 +856,10 @@
         return Function->getNumParams();
       return ExplicitCallArguments;
     }
+
+    RewrittenOverloadCandidateKind getRewrittenKind() const {
+      return static_cast<RewrittenOverloadCandidateKind>(RewrittenOpKind);
+    }
   };
 
   /// OverloadCandidateSet - A set of overload candidates, used in C++
@@ -853,8 +890,10 @@
 
   private:
     SmallVector<OverloadCandidate, 16> Candidates;
-    llvm::SmallPtrSet<Decl *, 16> Functions;
+    using DeclSet = llvm::SmallPtrSet<Decl *, 16>;
+    DeclSet Functions;
 
+  private:
     // Allocator for ConversionSequenceLists. We store the first few of these
     // inline to avoid allocation for small sets.
     llvm::BumpPtrAllocator SlabAllocator;
@@ -896,6 +935,31 @@
     void destroyCandidates();
 
   public:
+    /// \brief RewrittenCandidateContextGuard - Enter a context suitable for
+    /// adding rewritten overload candidates. Rewritten candidates can
+    /// re-consider previously seen functions, so save and clear the list of
+    /// considered functions, and restore it when the rewritten context is
+    /// exited.
+    struct RewrittenCandidateContextGuard {
+      RewrittenCandidateContextGuard(OverloadCandidateSet &CS)
+          : CandidateSet(CS) {
+        assert(CS.Kind == CSK_Operator &&
+               "rewritten expressions can only occur for operators");
+        OldFunctions = std::move(CandidateSet.Functions);
+      }
+
+      ~RewrittenCandidateContextGuard() {
+        CandidateSet.Functions.insert(OldFunctions.begin(), OldFunctions.end());
+      }
+
+    private:
+      OverloadCandidateSet &CandidateSet;
+      DeclSet OldFunctions;
+    };
+
+    friend struct RewrittenCandidateContextGuard;
+
+  public:
     OverloadCandidateSet(SourceLocation Loc, CandidateSetKind CSK)
         : Loc(Loc), Kind(CSK) {}
     OverloadCandidateSet(const OverloadCandidateSet &) = delete;
@@ -953,7 +1017,7 @@
 
     /// Find the best viable function on this overload set, if it exists.
     OverloadingResult BestViableFunction(Sema &S, SourceLocation Loc,
-                                         OverloadCandidateSet::iterator& Best);
+                                         OverloadCandidateSet::iterator &Best);
 
     void NoteCandidates(Sema &S,
                         OverloadCandidateDisplayKind OCD,
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 CXXRewrittenExpr : DStmt<Expr>;
 
 // C++ Coroutines TS expressions
 def CoroutineSuspendExpr : DStmt<Expr, 1>;
Index: include/clang/AST/Stmt.h
===================================================================
--- include/clang/AST/Stmt.h
+++ include/clang/AST/Stmt.h
@@ -246,6 +246,13 @@
     /// bit is set to true.
     unsigned IsUnique : 1;
   };
+  class CXXRewrittenExprBitfields {
+    friend class CXXRewrittenExpr;
+
+    unsigned : NumExprBits;
+
+    unsigned Kind : 1;
+  };
 
   class ObjCIndirectCopyRestoreExprBitfields {
     friend class ObjCIndirectCopyRestoreExpr;
@@ -309,6 +316,7 @@
     InitListExprBitfields InitListExprBits;
     TypeTraitExprBitfields TypeTraitExprBits;
     CoawaitExprBitfields CoawaitBits;
+    CXXRewrittenExprBitfields CXXRewrittenBits;
   };
 
 public:
Index: include/clang/AST/RecursiveASTVisitor.h
===================================================================
--- include/clang/AST/RecursiveASTVisitor.h
+++ include/clang/AST/RecursiveASTVisitor.h
@@ -2549,6 +2549,14 @@
 DEF_TRAVERSE_STMT(CXXFoldExpr, {})
 DEF_TRAVERSE_STMT(AtomicExpr, {})
 
+DEF_TRAVERSE_STMT(CXXRewrittenExpr, {
+  TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getOriginalExpr());
+  if (!getDerived().shouldVisitImplicitCode()) {
+    TRY_TO_TRAVERSE_OR_ENQUEUE_STMT(S->getRewrittenExpr());
+  }
+  ShouldVisitChildren = false;
+})
+
 // For coroutines expressions, traverse either the operand
 // as written or the implied calls, depending on what the
 // derived class requests.
Index: include/clang/AST/ExprCXX.h
===================================================================
--- include/clang/AST/ExprCXX.h
+++ include/clang/AST/ExprCXX.h
@@ -87,6 +87,8 @@
   SourceRange getSourceRangeImpl() const LLVM_READONLY;
 
 public:
+  friend class ASTReader;
+  friend class ASTWriter;
   friend class ASTStmtReader;
   friend class ASTStmtWriter;
 
@@ -4207,6 +4209,73 @@
   child_range children() { return child_range(SubExprs, SubExprs + 2); }
 };
 
+
+class CXXRewrittenExpr : public Expr {
+
+public:
+  enum RewrittenKind { Comparison };
+
+  struct ComparisonBits {
+    /// Whether this rewritten comparison expression has reverse-order
+    /// parameters.
+    unsigned IsSynthesized : 1;
+  };
+
+  union ExtraRewrittenBits {
+    ComparisonBits CompareBits;
+  };
+
+private:
+  friend class ASTReader;
+  friend class ASTStmtReader;
+  friend class ASTStmtWriter;
+
+  Stmt *SubExprs[2];
+  ExtraRewrittenBits ExtraBits;
+
+  CXXRewrittenExpr(EmptyShell Empty) : Expr(CXXRewrittenExprClass, Empty) {}
+
+public:
+  CXXRewrittenExpr(RewrittenKind Kind, Expr *Original, Expr *Rewritten,
+                   ExtraRewrittenBits ExtraBits)
+      : Expr(CXXRewrittenExprClass, Rewritten->getType(),
+             Rewritten->getValueKind(), Rewritten->getObjectKind(),
+             /*Dependent*/ Rewritten->isTypeDependent(),
+             Rewritten->isValueDependent(),
+             Original->isInstantiationDependent(),
+             Rewritten->containsUnexpandedParameterPack()),
+        ExtraBits(ExtraBits) {
+    SubExprs[0] = Original;
+    SubExprs[1] = Rewritten;
+  }
+
+  RewrittenKind getRewrittenKind() const {
+    return static_cast<RewrittenKind>(CXXRewrittenBits.Kind);
+  }
+  void setRewrittenKind(RewrittenKind Kind) { CXXRewrittenBits.Kind = Kind; }
+
+  ExtraRewrittenBits *getRewrittenInfo() { return &ExtraBits; }
+  const ExtraRewrittenBits *getRewrittenInfo() const { return &ExtraBits; }
+
+  Expr *getOriginalExpr() const { return static_cast<Expr *>(SubExprs[0]); }
+  Expr *getRewrittenExpr() const {
+    return static_cast<Expr *>(SubExprs[1]);
+  }
+
+  SourceLocation getLocStart() const {
+    return getOriginalExpr()->getLocStart();
+  }
+  SourceLocation getLocEnd() const { return getOriginalExpr()->getLocEnd(); }
+  SourceLocation getExprLoc() const { return getOriginalExpr()->getExprLoc(); }
+
+  child_range children() { return child_range(SubExprs, SubExprs + 2); }
+
+  static bool classof(const Stmt *T) {
+    return T->getStmtClass() == CXXRewrittenExprClass;
+  }
+};
+
+
 /// 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"
@@ -127,28 +128,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 +204,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 +246,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 +261,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