Index: include/clang/AST/ASTContext.h
===================================================================
--- include/clang/AST/ASTContext.h
+++ include/clang/AST/ASTContext.h
@@ -18,6 +18,7 @@
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/CanonicalType.h"
 #include "clang/AST/CommentCommandTraits.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclarationName.h"
@@ -1973,6 +1974,13 @@
   QualType GetBuiltinType(unsigned ID, GetBuiltinTypeError &Error,
                           unsigned *IntegerConstantArgs = nullptr) const;
 
+  /// \brief Types and expressions required to build C++2a three-way comparisons
+  ///   using operator<=>, including the values return by builtin <=> operators.
+  ///
+  /// This object needs to be initialized by Sema the first time it checks
+  /// a three-way comparison.
+  ComparisonCategories CompCategories;
+
 private:
   CanQualType getFromTargetType(unsigned Type) const;
   TypeInfo getTypeInfoImpl(const Type *T) const;
Index: include/clang/AST/ComparisonCategories.h
===================================================================
--- /dev/null
+++ include/clang/AST/ComparisonCategories.h
@@ -0,0 +1,199 @@
+//===- ComparisonCategories.h - Three Way Comparison Data -------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the Comparison Category enum and data types, which
+//  store the types and expressions needed to support operator<=>
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H
+#define LLVM_CLANG_AST_COMPARISONCATEGORIES_H
+
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/Optional.h"
+#include "clang/Basic/LLVM.h"
+#include <array>
+#include <cassert>
+#include <type_traits>
+
+namespace llvm {
+  class StringRef;
+}
+
+namespace clang {
+
+class DeclRefExpr;
+class RecordDecl;
+class QualType;
+
+/// \brief An enumeration representing the different comparison categories.
+///   The values map to the category types defined in the standard library.
+///   i.e. 'std::weak_equality'.
+enum class ComparisonCategoryKind : unsigned char {
+  WeakEquality,
+  StrongEquality,
+  PartialOrdering,
+  WeakOrdering,
+  StrongOrdering,
+  Last = StrongOrdering,
+  First = WeakEquality
+};
+
+/// \brief An enumeration representing the possible results of a three-way
+///   comparison. These values map onto instances of comparison category types
+///   defined in the standard library. i.e. 'std::strong_ordering::less'.
+enum class ComparisonCategoryResult : unsigned char {
+  Invalid, // Internal implementation detail
+  Equal,
+  Equivalent,
+  Nonequivalent,
+  Nonequal,
+  Less,
+  Greater,
+  Unordered,
+};
+
+struct ComparisonCategoryInfo {
+  /// \brief The declaration for the comparison category type from the
+  /// standard library.
+  RecordDecl *CCDecl = nullptr;
+
+  /// \brief A map containing the comparison category values built from the
+  /// standard library. The key is a value of ComparisonCategoryValue.
+  llvm::DenseMap<char, DeclRefExpr *> Objects;
+
+  /// \brief The Kind of the comparison category type
+  ComparisonCategoryKind Kind;
+
+public:
+  /// \brief Return an expression referencing the member of the specified
+  ///   comparison category. For example 'std::strong_equality::equal'
+  const DeclRefExpr *getResultValue(ComparisonCategoryResult ValueKind) const {
+    const DeclRefExpr *DR = getResultValueUnsafe(ValueKind);
+    assert(DR &&
+           "comparison category does not contain the specified result kind");
+    return DR;
+  }
+
+  const DeclRefExpr *
+  getResultValueUnsafe(ComparisonCategoryResult ValueKind) const {
+    assert(ValueKind != ComparisonCategoryResult::Invalid &&
+           "invalid value specified");
+    char Key = static_cast<char>(ValueKind);
+    return Objects.lookup(Key);
+  }
+
+  /// \brief True iff the comparison category is an equality comparison.
+  bool isEquality() const { return !isOrdered(); }
+
+  /// \brief True iff the comparison category is a relational comparison.
+  bool isOrdered() const {
+    using CCK = ComparisonCategoryKind;
+    return Kind == CCK::PartialOrdering || Kind == CCK::WeakOrdering ||
+           Kind == CCK::StrongOrdering;
+  }
+
+  /// \brief True iff the comparison is "strong". i.e. it checks equality and
+  ///    not equivalence.
+  bool isStrong() const {
+    using CCK = ComparisonCategoryKind;
+    return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering;
+  }
+
+  /// \brief True iff the comparison is not totally ordered.
+  bool isPartial() const {
+    using CCK = ComparisonCategoryKind;
+    return Kind == CCK::PartialOrdering;
+  }
+
+  /// \brief 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.
+  ComparisonCategoryResult makeWeakResult(ComparisonCategoryResult Res) const {
+    using CCR = ComparisonCategoryResult;
+    if (!isStrong()) {
+      if (Res == CCR::Equal)
+        return CCR::Equivalent;
+      if (Res == CCR::Nonequal)
+        return CCR::Nonequivalent;
+    }
+    return Res;
+  }
+
+  const DeclRefExpr *getEqualOrEquiv() const {
+    return getResultValue(makeWeakResult(ComparisonCategoryResult::Equal));
+  }
+  const DeclRefExpr *getNonequalOrNonequiv() const {
+    return getResultValue(makeWeakResult(ComparisonCategoryResult::Nonequal));
+  }
+  const DeclRefExpr *getLess() const {
+    assert(isOrdered());
+    return getResultValue(ComparisonCategoryResult::Less);
+  }
+  const DeclRefExpr *getGreater() const {
+    assert(isOrdered());
+    return getResultValue(ComparisonCategoryResult::Greater);
+  }
+  const DeclRefExpr *getUnordered() const {
+    assert(isPartial());
+    return getResultValue(ComparisonCategoryResult::Unordered);
+  }
+};
+
+struct ComparisonCategories {
+  static StringRef getCategoryString(ComparisonCategoryKind Kind);
+  static StringRef getResultString(ComparisonCategoryResult Kind);
+
+  /// \brief Return the comparison category information for the category
+  ///   specified by 'Kind'.
+  const ComparisonCategoryInfo &getInfo(ComparisonCategoryKind Kind) const {
+    assert(HasData && "comparison category information not yet built");
+    return Data[static_cast<unsigned>(Kind)];
+  }
+
+  /// \brief Return the comparison category decl for the category
+  ///   specified by 'Kind'.
+  const RecordDecl *getDecl(ComparisonCategoryKind Kind) const {
+    return getInfo(Kind).CCDecl;
+  }
+
+  /// \brief Return the comparison category information as specified by
+  ///   `getCategoryForType(Ty)`.
+  const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
+
+  /// \brief Return the comparison category kind corresponding to the specified
+  ///   type. 'Ty' is expected to refer to the type of one of the comparison
+  ///   category decls; if it doesn't no value is returned.
+  Optional<ComparisonCategoryKind> getCategoryForType(QualType Ty) const;
+
+  /// \brief returns true if the comparison category data has already been
+  /// built, and false otherwise.
+  bool hasData() const { return HasData; }
+
+public:
+  // implementation details which should only be used by the function creating
+  // and setting the data.
+  using InfoList =
+      std::array<ComparisonCategoryInfo,
+                 static_cast<unsigned>(ComparisonCategoryKind::Last) + 1>;
+
+  void setData(InfoList &&NewData) {
+    assert(!HasData && "comparison categories already built");
+    Data = std::move(NewData);
+    HasData = true;
+  }
+
+private:
+  bool HasData = false;
+  InfoList Data;
+};
+
+} // namespace clang
+
+#endif
Index: include/clang/AST/Expr.h
===================================================================
--- include/clang/AST/Expr.h
+++ include/clang/AST/Expr.h
@@ -3014,15 +3014,19 @@
   // This is only meaningful for operations on floating point types and 0
   // otherwise.
   unsigned FPFeatures : 2;
+
+  // When Opc is BO_Cmp, this is set only when the comparison category is an
+  // ordered comparison. Otherwise the comparison is an equality comparison.
+  unsigned IsCmpOrdered : 1;
+
   SourceLocation OpLoc;
 
   enum { LHS, RHS, END_EXPR };
   Stmt* SubExprs[END_EXPR];
 public:
-
   BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy,
-                 ExprValueKind VK, ExprObjectKind OK,
-                 SourceLocation opLoc, FPOptions FPFeatures)
+                 ExprValueKind VK, ExprObjectKind OK, SourceLocation OpLoc,
+                 FPOptions FPFeatures)
       : Expr(BinaryOperatorClass, ResTy, VK, OK,
              lhs->isTypeDependent() || rhs->isTypeDependent(),
              lhs->isValueDependent() || rhs->isValueDependent(),
@@ -3030,7 +3034,8 @@
               rhs->isInstantiationDependent()),
              (lhs->containsUnexpandedParameterPack() ||
               rhs->containsUnexpandedParameterPack())),
-      Opc(opc), FPFeatures(FPFeatures.getInt()), OpLoc(opLoc) {
+        Opc(opc), FPFeatures(FPFeatures.getInt()), IsCmpOrdered(false),
+        OpLoc(OpLoc) {
     SubExprs[LHS] = lhs;
     SubExprs[RHS] = rhs;
     assert(!isCompoundAssignmentOp() &&
@@ -3089,10 +3094,16 @@
   bool isBitwiseOp() const { return isBitwiseOp(getOpcode()); }
 
   static bool isRelationalOp(Opcode Opc) { return Opc >= BO_LT && Opc<=BO_GE; }
-  bool isRelationalOp() const { return isRelationalOp(getOpcode()); }
+  bool isRelationalOp() const {
+    return isRelationalOp(getOpcode()) ||
+           (getOpcode() == BO_Cmp && IsCmpOrdered);
+  }
 
   static bool isEqualityOp(Opcode Opc) { return Opc == BO_EQ || Opc == BO_NE; }
-  bool isEqualityOp() const { return isEqualityOp(getOpcode()); }
+  bool isEqualityOp() const {
+    return isEqualityOp(getOpcode()) ||
+           (getOpcode() == BO_Cmp && !IsCmpOrdered);
+  }
 
   static bool isComparisonOp(Opcode Opc) { return Opc >= BO_Cmp && Opc<=BO_NE; }
   bool isComparisonOp() const { return isComparisonOp(getOpcode()); }
@@ -3153,6 +3164,9 @@
     return isShiftAssignOp(getOpcode());
   }
 
+  void setIsCmpOrdered(bool Val) { IsCmpOrdered = Val; }
+  bool getIsCmpOrdered() const { return IsCmpOrdered; }
+
   // Return true if a binary operator using the specified opcode and operands
   // would match the 'p = (i8*)nullptr + n' idiom for casting a pointer-sized
   // integer to a pointer.
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -9372,4 +9372,23 @@
 def err_multiversion_not_supported : Error<
  "function multiversioning is not supported on the current target">;
 
+// spaceship operator diagnostics
+def err_implied_comparison_category_type_not_found : Error<
+  "%0 type was not found; include <compare> before defining "
+  "or using 'operator<=>'">;
+def err_spaceship_argument_narrowing : Error<
+  "argument to 'operator<=>' "
+  "%select{cannot be narrowed from type %1 to %2|"
+  "evaluates to %1, which cannot be narrowed to type %2}0">;
+def err_spaceship_comparison_of_void_ptr : Error<
+  "three-way comparison with void pointer %select{operand type|operand types}0 "
+  "(%1 and %2)">;
+def err_spaceship_comparison_of_invalid_comp_type : Error<
+  "three-way comparison of operands has composite type %0 (%1 and %2)">;
+def err_std_compare_type_missing_member : Error<
+  "%0 missing a member named '%1'">;
+def err_std_compare_type_invalid_member : Error<
+  "member '%1' of %0 is ill-formed">;
+def note_spaceship_operand_not_cce : Note<
+  "argument is not a constant expression">;
 } // end of sema component.
Index: include/clang/Sema/Overload.h
===================================================================
--- include/clang/Sema/Overload.h
+++ include/clang/Sema/Overload.h
@@ -330,9 +330,10 @@
     }
     
     ImplicitConversionRank getRank() const;
-    NarrowingKind getNarrowingKind(ASTContext &Context, const Expr *Converted,
-                                   APValue &ConstantValue,
-                                   QualType &ConstantType) const;
+    NarrowingKind
+    getNarrowingKind(ASTContext &Context, const Expr *Converted,
+                     APValue &ConstantValue, QualType &ConstantType,
+                     bool IgnoreFloatToIntegralConversion = false) const;
     bool isPointerConversionToBool() const;
     bool isPointerConversionToVoidPointer(ASTContext& Context) const;
     void dump() const;
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -4541,6 +4541,14 @@
   CXXRecordDecl *getStdBadAlloc() const;
   EnumDecl *getStdAlignValT() const;
 
+  /// \brief Lookup the comparison category types in the standard library, and
+  /// build DeclRefExprs to values returned by the operator<=> builtins. The
+  /// results are cached in ASTContext so they are accessible outside of Sema.
+  /// An error is emitted if the types are not found or another error occurs.
+  ///
+  /// \return true if an error occurred. False otherwise.
+  bool BuildComparisonCategoryData(SourceLocation Loc);
+
   /// \brief Tests whether Ty is an instance of std::initializer_list and, if
   /// it is and Element is not NULL, assigns the element type to Element.
   bool isStdInitializerList(QualType Ty, QualType *Element);
Index: lib/AST/CMakeLists.txt
===================================================================
--- lib/AST/CMakeLists.txt
+++ lib/AST/CMakeLists.txt
@@ -20,6 +20,7 @@
   CommentLexer.cpp
   CommentParser.cpp
   CommentSema.cpp
+  ComparisonCategories.cpp
   DataCollection.cpp
   Decl.cpp
   DeclarationName.cpp
Index: lib/AST/ComparisonCategories.cpp
===================================================================
--- /dev/null
+++ lib/AST/ComparisonCategories.cpp
@@ -0,0 +1,82 @@
+//===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+//  This file defines the Comparison Category enum and data types, which
+//  store the types and expressions needed to support operator<=>
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ComparisonCategories.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Type.h"
+
+using namespace clang;
+
+Optional<ComparisonCategoryKind>
+ComparisonCategories::getCategoryForType(QualType Ty) const {
+  assert(hasData() && "comparison category data not built");
+  assert(!Ty.isNull() && "type must be non-null");
+  if (const auto *RD = Ty->getAsCXXRecordDecl()) {
+    const auto *CanonRD = RD->getCanonicalDecl();
+    for (auto &Info : Data) {
+      if (CanonRD == Info.CCDecl->getCanonicalDecl())
+        return Info.Kind;
+    }
+  }
+  return None;
+}
+
+const ComparisonCategoryInfo &
+ComparisonCategories::getInfoForType(QualType Ty) const {
+  Optional<ComparisonCategoryKind> OptKind = getCategoryForType(Ty);
+  assert(OptKind &&
+         "return type for operator<=> is not a comparison category type");
+  return getInfo(OptKind.getValue());
+}
+
+StringRef ComparisonCategories::getCategoryString(ComparisonCategoryKind Kind) {
+  using CCKT = ComparisonCategoryKind;
+  switch (Kind) {
+  case CCKT::WeakEquality:
+    return "weak_equality";
+  case CCKT::StrongEquality:
+    return "strong_equality";
+  case CCKT::PartialOrdering:
+    return "partial_ordering";
+  case CCKT::WeakOrdering:
+    return "weak_ordering";
+  case CCKT::StrongOrdering:
+    return "strong_ordering";
+  }
+  llvm_unreachable("unhandled cases in switch");
+}
+
+StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
+  using CCVT = ComparisonCategoryResult;
+  switch (Kind) {
+  case CCVT::Equal:
+    return "equal";
+  case CCVT::Nonequal:
+    return "nonequal";
+  case CCVT::Equivalent:
+    return "equivalent";
+  case CCVT::Nonequivalent:
+    return "nonequivalent";
+  case CCVT::Less:
+    return "less";
+  case CCVT::Greater:
+    return "greater";
+  case CCVT::Unordered:
+    return "unordered";
+  case CCVT::Invalid:
+    return "invalid";
+  }
+  llvm_unreachable("unhandled case in switch");
+}
Index: lib/AST/ExprConstant.cpp
===================================================================
--- lib/AST/ExprConstant.cpp
+++ lib/AST/ExprConstant.cpp
@@ -2242,6 +2242,8 @@
   case BO_GE: Result = LHS >= RHS; return true;
   case BO_EQ: Result = LHS == RHS; return true;
   case BO_NE: Result = LHS != RHS; return true;
+  case BO_Cmp:
+    llvm_unreachable("BO_Cmp should be handled elsewhere");
   }
 }
 
@@ -5059,7 +5061,7 @@
   }
 };
 
-}
+} // namespace
 
 //===----------------------------------------------------------------------===//
 // Common base class for lvalue and temporary evaluation.
@@ -6232,6 +6234,33 @@
     bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E);
     bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
     bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
+
+    bool VisitBinCmp(const BinaryOperator *E) {
+      using CCR = ComparisonCategoryResult;
+      const ComparisonCategoryInfo &CmpInfo =
+          Info.Ctx.CompCategories.getInfoForType(E->getType());
+
+      // Build a new version of the binary operator which returns an integer
+      // representing the ComparisonCategoryResult. Then defer to
+      // IntExprEvaluator to evaluate it.
+      BinaryOperator NewOp(E->getLHS(), E->getRHS(), BO_Cmp, Info.Ctx.IntTy,
+                           VK_RValue, OK_Ordinary, E->getOperatorLoc(),
+                           E->getFPFeatures());
+      NewOp.setIsCmpOrdered(E->getIsCmpOrdered());
+
+      APSInt Result;
+      if (!EvaluateInteger(&NewOp, Result, Info))
+        return false;
+      CCR CmpRes = static_cast<CCR>(Result.getExtValue());
+
+      const DeclRefExpr *Value =
+          CmpInfo.getResultValue(CmpInfo.makeWeakResult(CmpRes));
+
+      APValue Res;
+      if (!EvaluateAsRValue(Info, Value, Res))
+        return false;
+      return Success(Res, E);
+    }
   };
 }
 
@@ -7071,12 +7100,11 @@
 //===----------------------------------------------------------------------===//
 
 namespace {
-class IntExprEvaluator
-  : public ExprEvaluatorBase<IntExprEvaluator> {
+class IntExprEvaluator : public ExprEvaluatorBase<IntExprEvaluator> {
   APValue &Result;
 public:
   IntExprEvaluator(EvalInfo &info, APValue &result)
-    : ExprEvaluatorBaseTy(info), Result(result) {}
+      : ExprEvaluatorBase(info), Result(result) {}
 
   bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) {
     assert(E->getType()->isIntegralOrEnumerationType() &&
@@ -7116,6 +7144,10 @@
     return Success(Value, E, Result);
   }
 
+  bool Success(ComparisonCategoryResult Res, const Expr *E) {
+    return Success(static_cast<uint64_t>(Res), E);
+  }
+
   bool Success(CharUnits Size, const Expr *E) {
     return Success(Size.getQuantity(), E);
   }
@@ -8215,12 +8247,11 @@
   /// We handle binary operators that are comma, logical, or that have operands
   /// with integral or enumeration type.
   static bool shouldEnqueue(const BinaryOperator *E) {
-    return E->getOpcode() == BO_Comma ||
-           E->isLogicalOp() ||
-           (E->isRValue() &&
-            E->getType()->isIntegralOrEnumerationType() &&
+    return E->getOpcode() != BO_Cmp && // TODO Make this work with <=>
+           (E->getOpcode() == BO_Comma || E->isLogicalOp() ||
+            (E->isRValue() && E->getType()->isIntegralOrEnumerationType() &&
              E->getLHS()->getType()->isIntegralOrEnumerationType() &&
-            E->getRHS()->getType()->isIntegralOrEnumerationType());
+             E->getRHS()->getType()->isIntegralOrEnumerationType()));
   }
 
   bool Traverse(const BinaryOperator *E) {
@@ -8498,6 +8529,9 @@
 }
 
 bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
+  using CCR = ComparisonCategoryResult;
+  bool IsSpaceship = E->getOpcode() == BO_Cmp;
+  auto &Info = this->Info;
   // We don't call noteFailure immediately because the assignment happens after
   // we evaluate LHS and RHS.
   if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
@@ -8600,10 +8634,26 @@
       return Success(CR == APFloat::cmpGreaterThan
                      || CR == APFloat::cmpLessThan
                      || CR == APFloat::cmpUnordered, E);
+    case BO_Cmp: {
+      auto GetCmpRes = [&]() {
+        switch (CR) {
+        case APFloat::cmpEqual:
+          return CCR::Equal;
+        case APFloat::cmpUnordered:
+          return CCR::Unordered;
+        case APFloat::cmpLessThan:
+          return CCR::Less;
+        case APFloat::cmpGreaterThan:
+          return CCR::Greater;
+        }
+      };
+      return Success(GetCmpRes(), E);
+    }
     }
   }
 
   if (LHSTy->isPointerType() && RHSTy->isPointerType()) {
+    assert(!LHSTy->isMemberPointerType());
     if (E->getOpcode() == BO_Sub || E->isComparisonOp()) {
       LValue LHSValue, RHSValue;
 
@@ -8668,6 +8718,8 @@
         if ((RHSValue.Base && isZeroSized(LHSValue)) ||
             (LHSValue.Base && isZeroSized(RHSValue)))
           return Error(E);
+        if (E->getOpcode() == BO_Cmp)
+          return Success(CCR::Nonequal, E);
         // Pointers with different bases cannot represent the same object.
         return Success(E->getOpcode() == BO_NE, E);
       }
@@ -8811,6 +8863,14 @@
       case BO_GE: return Success(CompareLHS >= CompareRHS, E);
       case BO_EQ: return Success(CompareLHS == CompareRHS, E);
       case BO_NE: return Success(CompareLHS != CompareRHS, E);
+      case BO_Cmp: {
+        if (CompareLHS < CompareRHS)
+          return Success(CCR::Less, E);
+        if (CompareLHS > CompareRHS)
+          return Success(CCR::Greater, E);
+        return Success(CCR::Equal, E);
+
+      }
       }
     }
   }
@@ -8833,6 +8893,8 @@
     //   null, they compare unequal.
     if (!LHSValue.getDecl() || !RHSValue.getDecl()) {
       bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl();
+      if (E->getOpcode() == BO_Cmp)
+        return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
       return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E);
     }
 
@@ -8850,6 +8912,9 @@
     //   they were dereferenced with a hypothetical object of the associated
     //   class type.
     bool Equal = LHSValue == RHSValue;
+    if (E->getOpcode() == BO_Cmp)
+      return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
+
     return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E);
   }
 
@@ -8860,14 +8925,37 @@
     // are compared, the result is true of the operator is <=, >= or ==, and
     // false otherwise.
     BinaryOperator::Opcode Opcode = E->getOpcode();
+    if (Opcode == BO_Cmp)
+      return Success(CCR::Equal, E);
     return Success(Opcode == BO_EQ || Opcode == BO_LE || Opcode == BO_GE, E);
   }
 
+  if (IsSpaceship && LHSTy->isIntegralOrEnumerationType() &&
+      RHSTy->isIntegralOrEnumerationType()) {
+    APSInt LHS, RHS;
+    bool LHSOK = EvaluateInteger(E->getLHS(), LHS, Info);
+    if (!LHSOK && !Info.noteFailure())
+      return false;
+
+    if (!EvaluateInteger(E->getRHS(), RHS, Info) || !LHSOK)
+      return false;
+
+    if (LHS < RHS)
+      return Success(CCR::Less, E);
+    if (LHS > RHS)
+      return Success(CCR::Greater, E);
+    return Success(CCR::Equal, E);
+  }
+
   assert((!LHSTy->isIntegralOrEnumerationType() ||
           !RHSTy->isIntegralOrEnumerationType()) &&
          "DataRecursiveIntBinOpEvaluator should have handled integral types");
+  assert(!IsSpaceship && "case not handled for operator<=>");
   // We can't continue from here for non-integral types.
   return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
+
+
+
 }
 
 /// VisitUnaryExprOrTypeTraitExpr - Evaluate a sizeof, alignof or vec_step with
@@ -10609,7 +10697,6 @@
     case BO_AndAssign:
     case BO_XorAssign:
     case BO_OrAssign:
-    case BO_Cmp: // FIXME: Re-enable once we can evaluate this.
       // C99 6.6/3 allows assignments within unevaluated subexpressions of
       // constant expressions, but they can never be ICEs because an ICE cannot
       // contain an lvalue operand.
@@ -10631,7 +10718,8 @@
     case BO_And:
     case BO_Xor:
     case BO_Or:
-    case BO_Comma: {
+    case BO_Comma:
+    case BO_Cmp: {
       ICEDiag LHSResult = CheckICE(Exp->getLHS(), Ctx);
       ICEDiag RHSResult = CheckICE(Exp->getRHS(), Ctx);
       if (Exp->getOpcode() == BO_Div ||
@@ -10660,6 +10748,16 @@
           return ICEDiag(IK_NotICE, E->getLocStart());
         }
       }
+      if (Exp->getOpcode() == BO_Cmp) {
+        // Check that all of the references to the result objects are ICE.
+        const ComparisonCategoryInfo &CmpInfo =
+            Ctx.CompCategories.getInfoForType(Exp->getType());
+        ICEDiag RetDiag(IK_ICE, E->getLocStart());
+        for (const auto &KV : CmpInfo.Objects)
+          RetDiag = Worst(RetDiag, CheckICE(KV.second, Ctx));
+
+        return Worst(Worst(LHSResult, RHSResult), RetDiag);
+      }
       return Worst(LHSResult, RHSResult);
     }
     case BO_LAnd:
Index: lib/CodeGen/CGExprAgg.cpp
===================================================================
--- lib/CodeGen/CGExprAgg.cpp
+++ lib/CodeGen/CGExprAgg.cpp
@@ -11,19 +11,21 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "CodeGenFunction.h"
+#include "CGCXXABI.h"
 #include "CGObjCRuntime.h"
+#include "CodeGenFunction.h"
 #include "CodeGenModule.h"
 #include "ConstantEmitter.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclCXX.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/StmtVisitor.h"
+#include "llvm/ADT/SmallVector.h"
 #include "llvm/IR/Constants.h"
 #include "llvm/IR/Function.h"
 #include "llvm/IR/GlobalVariable.h"
-#include "llvm/IR/Intrinsics.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Intrinsics.h"
 using namespace clang;
 using namespace CodeGen;
 
@@ -145,6 +147,7 @@
   void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *BO);
   void VisitBinAssign(const BinaryOperator *E);
   void VisitBinComma(const BinaryOperator *E);
+  void VisitBinCmp(const BinaryOperator *E);
 
   void VisitObjCMessageExpr(ObjCMessageExpr *E);
   void VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) {
@@ -196,6 +199,7 @@
     RValue Res = CGF.EmitAtomicExpr(E);
     EmitFinalDestCopy(E->getType(), Res);
   }
+
 };
 }  // end anonymous namespace.
 
@@ -879,10 +883,125 @@
   CGF.EmitCompoundStmt(*E->getSubStmt(), true, Dest);
 }
 
+enum CompareKind {
+  CK_Less,
+  CK_Greater,
+  CK_Equal,
+};
+
+static llvm::Value *EmitCompare(CGBuilderTy &Builder, CodeGenFunction &CGF,
+                                const BinaryOperator *E, llvm::Value *LHS,
+                                llvm::Value *RHS, CompareKind Kind) {
+  QualType ArgTy = E->getLHS()->getType();
+  if (const auto *MPT = ArgTy->getAs<MemberPointerType>()) {
+    assert(Kind == CK_Equal &&
+           "member pointers may only be compared for equality");
+    return CGF.CGM.getCXXABI().EmitMemberPointerComparison(
+        CGF, LHS, RHS, MPT, /*IsInequality*/ false);
+  }
+
+  // Compute the comparison instructions for the specified comparison kind.
+  struct CmpInstInfo {
+    const char *Name;
+    llvm::CmpInst::Predicate FCmp;
+    llvm::CmpInst::Predicate SCmp;
+    llvm::CmpInst::Predicate UCmp;
+  };
+  CmpInstInfo InstInfo = [&]() -> CmpInstInfo {
+    using FI = llvm::FCmpInst;
+    using II = llvm::ICmpInst;
+    switch (Kind) {
+    case CK_Less:
+      return {"cmp.lt", FI::FCMP_OLT, II::ICMP_SLT, II::ICMP_ULT};
+    case CK_Greater:
+      return {"cmp.gt", FI::FCMP_OGT, II::ICMP_SGT, II::ICMP_UGT};
+    case CK_Equal:
+      return {"cmp.eq", FI::FCMP_OEQ, II::ICMP_EQ, II::ICMP_EQ};
+    }
+  }();
+
+  if (ArgTy->isRealFloatingType()) {
+    return Builder.CreateFCmp(InstInfo.FCmp, LHS, RHS, InstInfo.Name);
+  } else if (ArgTy->isIntegralOrEnumerationType() || ArgTy->isPointerType()) {
+    auto Inst =
+        ArgTy->hasSignedIntegerRepresentation() ? InstInfo.SCmp : InstInfo.UCmp;
+    return Builder.CreateICmp(Inst, LHS, RHS, InstInfo.Name);
+  } else if (ArgTy->isAnyComplexType()) {
+    llvm_unreachable("support for complex types is unimplemented");
+  } else if (ArgTy->isVectorType()) {
+    llvm_unreachable("support for vector types is unimplemented");
+  } else {
+    llvm_unreachable("unexpected argument type when building operator<=>");
+  }
+}
+
+void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) {
+  using llvm::BasicBlock;
+  using llvm::PHINode;
+  using llvm::Value;
+  assert(CGF.getContext().hasSameType(E->getLHS()->getType(),
+                                      E->getRHS()->getType()));
+  const ComparisonCategoryInfo &CmpInfo =
+      CGF.getContext().CompCategories.getInfoForType(E->getType());
+
+  QualType ArgTy = E->getLHS()->getType();
+  Value *LHS = nullptr, *RHS = nullptr;
+  switch (CGF.getEvaluationKind(ArgTy)) {
+  case TEK_Scalar:
+    LHS = CGF.EmitScalarExpr(E->getLHS());
+    RHS = CGF.EmitScalarExpr(E->getRHS());
+    break;
+  case TEK_Aggregate:
+    // FIXME: Is this ever used?
+    LHS = CGF.EmitAnyExpr(E->getLHS()).getAggregatePointer();
+    RHS = CGF.EmitAnyExpr(E->getRHS()).getAggregatePointer();
+    break;
+  case TEK_Complex:
+    llvm_unreachable("support for complex types is unimplemented");
+  }
+  assert(LHS && RHS);
+
+  auto EmitCmpRes = [&](const DeclRefExpr *DRE) {
+    return CGF.EmitLValue(DRE).getPointer();
+  };
+  auto EmitCmp = [&](CompareKind K) {
+    return EmitCompare(Builder, CGF, E, LHS, RHS, K);
+  };
+  Value *Select = nullptr;
+  if (CmpInfo.isEquality()) {
+    Select = Builder.CreateSelect(
+        EmitCmp(CK_Equal), EmitCmpRes(CmpInfo.getEqualOrEquiv()),
+        EmitCmpRes(CmpInfo.getNonequalOrNonequiv()), "sel.eq");
+  } else if (!CmpInfo.isPartial()) {
+    Value *SelectOne =
+        Builder.CreateSelect(EmitCmp(CK_Less), EmitCmpRes(CmpInfo.getLess()),
+                             EmitCmpRes(CmpInfo.getGreater()), "sel.lt");
+    Select = Builder.CreateSelect(EmitCmp(CK_Equal),
+                                  EmitCmpRes(CmpInfo.getEqualOrEquiv()),
+                                  SelectOne, "sel.eq");
+  } else {
+    Value *SelectEq = Builder.CreateSelect(
+        EmitCmp(CK_Equal), EmitCmpRes(CmpInfo.getEqualOrEquiv()),
+        EmitCmpRes(CmpInfo.getUnordered()), "sel.eq");
+
+    Value *SelectGT = Builder.CreateSelect(EmitCmp(CK_Greater),
+                                           EmitCmpRes(CmpInfo.getGreater()),
+                                           SelectEq, "sel.gt");
+    Select = Builder.CreateSelect(
+        EmitCmp(CK_Less), EmitCmpRes(CmpInfo.getLess()), SelectGT, "sel.lt");
+  }
+  assert(Select != nullptr);
+  return EmitFinalDestCopy(
+      E->getType(), CGF.MakeNaturalAlignAddrLValue(Select, E->getType()));
+}
+
 void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) {
-  if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI)
+
+  if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) {
     VisitPointerToDataMemberBinaryOperator(E);
-  else
+    return;
+  }
+
   CGF.ErrorUnsupported(E, "aggregate binary expression");
 }
 
Index: lib/Sema/SemaDeclCXX.cpp
===================================================================
--- lib/Sema/SemaDeclCXX.cpp
+++ lib/Sema/SemaDeclCXX.cpp
@@ -17,6 +17,7 @@
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/CXXInheritance.h"
 #include "clang/AST/CharUnits.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/EvaluatedExprVisitor.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecordLayout.h"
@@ -8884,6 +8885,96 @@
   return StdExperimentalNamespaceCache;
 }
 
+bool Sema::BuildComparisonCategoryData(SourceLocation Loc) {
+  using CCKT = ComparisonCategoryKind;
+  using CCVT = ComparisonCategoryResult;
+  assert(getLangOpts().CPlusPlus &&
+         "Looking for comparison category type outside of C++.");
+
+  // Check if we've already successfully built the comparison category data.
+  if (Context.CompCategories.hasData())
+    return false;
+
+  ComparisonCategories::InfoList NewData;
+  // Otherwise, look up each of the category types and build their required
+  // data.
+  for (unsigned I = static_cast<unsigned>(CCKT::First),
+                End = static_cast<unsigned>(CCKT::Last);
+       I <= End; ++I) {
+    CCKT CCK = static_cast<CCKT>(I);
+
+    StringRef Name = ComparisonCategories::getCategoryString(CCK);
+
+    // Build the initial category information
+    ComparisonCategoryInfo Info;
+    Info.Kind = CCK;
+
+    // Lookup the record for the category type
+    if (auto Std = getStdNamespace()) {
+      LookupResult Result(*this, &PP.getIdentifierTable().get(Name),
+                          SourceLocation(), Sema::LookupTagName);
+      if (LookupQualifiedName(Result, Std))
+        Info.CCDecl = Result.getAsSingle<RecordDecl>();
+      Result.suppressDiagnostics();
+    }
+    if (!Info.CCDecl) {
+      Diag(Loc, diag::err_implied_comparison_category_type_not_found) << Name;
+      return true;
+    }
+
+    // Calculate the list of values belonging to this comparison category type.
+    SmallVector<CCVT, 6> Values;
+    Values.push_back(CCVT::Equivalent);
+    if (Info.isStrong())
+      Values.push_back(CCVT::Equal);
+    if (Info.isOrdered()) {
+      Values.push_back(CCVT::Less);
+      Values.push_back(CCVT::Greater);
+    } else {
+      Values.push_back(CCVT::Nonequivalent);
+      if (Info.isStrong())
+        Values.push_back(CCVT::Nonequal);
+    }
+    if (Info.isPartial())
+      Values.push_back(CCVT::Unordered);
+
+    // Build each of the require values and store them in Info.
+    for (CCVT CCV : Values) {
+      StringRef ValueName = ComparisonCategories::getResultString(CCV);
+      QualType Ty(Info.CCDecl->getTypeForDecl(), 0);
+      DeclContext *LookupCtx = computeDeclContext(Ty);
+      LookupResult Found(*this, &PP.getIdentifierTable().get(ValueName), Loc,
+                         Sema::LookupOrdinaryName);
+      if (!LookupQualifiedName(Found, LookupCtx)) {
+        Diag(Loc, diag::err_std_compare_type_missing_member)
+            << Info.CCDecl << ValueName;
+        return true;
+      }
+      auto *VD = Found.getAsSingle<VarDecl>();
+      if (!VD || !VD->isStaticDataMember()) {
+        // FIXME: Handle more ways the lookup can result in a invalid value.
+        Diag(Loc, diag::err_std_compare_type_invalid_member)
+            << Info.CCDecl << ValueName;
+        return true;
+      }
+
+      ExprResult Res =
+          BuildDeclRefExpr(VD, VD->getType(), VK_LValue, SourceLocation());
+      if (Res.isInvalid())
+        return true;
+
+      Info.Objects.try_emplace((char)CCV, cast<DeclRefExpr>(Res.get()));
+    }
+
+    // Success. Set the value in ASTContext.
+    NewData[I] = std::move(Info);
+  }
+
+  // All of the objects and values have been built successfully.
+  Context.CompCategories.setData(std::move(NewData));
+  return false;
+}
+
 /// \brief Retrieve the special "std" namespace, which may require us to
 /// implicitly define the namespace.
 NamespaceDecl *Sema::getOrCreateStdNamespace() {
@@ -11440,11 +11531,10 @@
   // Create the comparison against the array bound.
   llvm::APInt Upper
     = ArrayTy->getSize().zextOrTrunc(S.Context.getTypeSize(SizeType));
-  Expr *Comparison
-    = new (S.Context) BinaryOperator(IterationVarRefRVal.build(S, Loc),
-                     IntegerLiteral::Create(S.Context, Upper, SizeType, Loc),
-                                     BO_NE, S.Context.BoolTy,
-                                     VK_RValue, OK_Ordinary, Loc, FPOptions());
+  Expr *Comparison = new (S.Context) BinaryOperator(
+      IterationVarRefRVal.build(S, Loc),
+      IntegerLiteral::Create(S.Context, Upper, SizeType, Loc), BO_NE,
+      S.Context.BoolTy, VK_RValue, OK_Ordinary, Loc, FPOptions());
 
   // Create the pre-increment of the iteration variable. We can determine
   // whether the increment will overflow based on the value of the array
Index: lib/Sema/SemaExpr.cpp
===================================================================
--- lib/Sema/SemaExpr.cpp
+++ lib/Sema/SemaExpr.cpp
@@ -37,6 +37,7 @@
 #include "clang/Sema/Designator.h"
 #include "clang/Sema/Initialization.h"
 #include "clang/Sema/Lookup.h"
+#include "clang/Sema/Overload.h"
 #include "clang/Sema/ParsedTemplate.h"
 #include "clang/Sema/Scope.h"
 #include "clang/Sema/ScopeInfo.h"
@@ -9703,10 +9704,161 @@
   }
 }
 
+static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) {
+  switch (CK) {
+  default:
+    llvm_unreachable("unhandled cast kind");
+  case CK_LValueToRValue:
+    return ICK_Lvalue_To_Rvalue;
+  case CK_ArrayToPointerDecay:
+    return ICK_Array_To_Pointer;
+  case CK_FunctionToPointerDecay:
+    return ICK_Function_To_Pointer;
+  case CK_IntegralCast:
+    return ICK_Integral_Conversion;
+  case CK_FloatingCast:
+    return ICK_Floating_Conversion;
+  case CK_IntegralToFloating:
+  case CK_FloatingToIntegral:
+    return ICK_Floating_Integral;
+  }
+}
+
+static bool checkNarrowingConversion(Sema &S, QualType ToType, Expr *E,
+                                     QualType FromType, SourceLocation Loc) {
+  // Check for a narrowing implicit conversion.
+  StandardConversionSequence SCS;
+  SCS.setToType(0, FromType);
+  SCS.setToType(1, ToType);
+  if (const auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+    auto CastK = ICE->getCastKind();
+    SCS.Second = castKindToImplicitConversionKind(CastK);
+  }
+  APValue PreNarrowingValue;
+  QualType PreNarrowingType;
+  switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue,
+                               PreNarrowingType,
+                               /*IgnoreFloatToIntegralConversion*/ true)) {
+  case NK_Dependent_Narrowing:
+    // Implicit conversion to a narrower type, but the expression is
+    // value-dependent so we can't tell whether it's actually narrowing.
+  case NK_Not_Narrowing:
+    return false;
+
+  case NK_Constant_Narrowing:
+    S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing)
+        << /*Constant*/ 1
+        << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType;
+    return true;
+
+  case NK_Variable_Narrowing:
+    // Implicit conversion to a narrower type, and the value is not a constant
+    // expression. We'll diagnose this in a moment.
+  case NK_Type_Narrowing:
+    break;
+  }
+
+  if (E->isValueDependent())
+    return false;
+
+  // Check the expression is a constant expression.
+  SmallVector<PartialDiagnosticAt, 8> Notes;
+  Expr::EvalResult Eval;
+  Eval.Diag = &Notes;
+
+  if (ToType->isReferenceType() ? !E->EvaluateAsLValue(Eval, S.Context)
+                                : !E->EvaluateAsRValue(Eval, S.Context)) {
+    // The expression can't be folded, so we can't keep it at this position in
+    // the AST.
+  } else if (Notes.empty()) {
+    // It's a constant expression.
+    return false;
+  }
+  S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing)
+        << /*Constant*/ 0 << FromType << ToType;
+  // It's not a constant expression. Produce an appropriate diagnostic.
+  if (Notes.size() >= 1 &&
+      Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr)
+    S.Diag(Notes[0].first, diag::note_spaceship_operand_not_cce);
+  else {
+    S.Diag(E->getLocStart(), diag::note_spaceship_operand_not_cce);
+    // FIXME: Re-enable these notes?
+    // for (unsigned I = 0; I < Notes.size(); ++I)
+    //  S.Diag(Notes[I].first, Notes[I].second);
+  }
+  return true;
+}
+
+static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
+                                                         ExprResult &LHS,
+                                                         ExprResult &RHS,
+                                                         SourceLocation Loc) {
+  if (S.BuildComparisonCategoryData(Loc))
+    return QualType();
+
+  QualType LHSType = LHS.get()->getType();
+  QualType RHSType = RHS.get()->getType();
+
+  // C++2a [expr.spaceship]p3
+  if (int Count = (LHSType->isBooleanType() + RHSType->isBooleanType())) {
+    // TODO: The spec says that if one but not both of the operands is 'bool'
+    // the program is ill-formed. However, what about bool non-narrowing cases?
+    if (Count != 2) {
+      S.InvalidOperands(Loc, LHS, RHS);
+      return QualType();
+    }
+  }
+
+  QualType Type;
+  if (LHSType->isEnumeralType() || RHSType->isEnumeralType()) {
+    // C++2a [expr.spaceship]p5
+    if (!S.Context.hasSameUnqualifiedType(LHSType, RHSType)) {
+      S.InvalidOperands(Loc, LHS, RHS);
+      return QualType();
+    }
+    Type = LHSType->getAs<EnumType>()->getDecl()->getIntegerType();
+    assert(Type->isArithmeticType());
+
+    LHS = S.ImpCastExprToType(LHS.get(), Type, CK_BitCast);
+    RHS = S.ImpCastExprToType(RHS.get(), Type, CK_BitCast);
+  } else {
+    // C++2a [expr.spaceship]p4
+    Type = S.UsualArithmeticConversions(LHS, RHS);
+    if (LHS.isInvalid() || RHS.isInvalid())
+      return QualType();
+    if (Type.isNull())
+      return S.InvalidOperands(Loc, LHS, RHS);
+    assert(Type->isArithmeticType());
+
+    bool HasNarrowing = checkNarrowingConversion(S, Type, LHS.get(), LHSType,
+                                                 LHS.get()->getLocStart());
+    HasNarrowing |= checkNarrowingConversion(S, Type, RHS.get(), RHSType,
+                                             RHS.get()->getLocStart());
+    if (HasNarrowing)
+      return QualType();
+  }
+  assert(!Type.isNull() && "composite type for <=> has not been set");
+
+  auto TypeKind = [&]() {
+    using CCK = ComparisonCategoryKind;
+    if (Type->isIntegralOrEnumerationType())
+      return CCK::StrongOrdering;
+    if (Type->hasFloatingRepresentation())
+      return CCK::PartialOrdering;
+    llvm_unreachable("other types are unimplemented");
+  }();
+
+  return QualType(S.Context.CompCategories.getDecl(TypeKind)->getTypeForDecl(),
+                  0);
+}
+
 static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
                                                  ExprResult &RHS,
                                                  SourceLocation Loc,
                                                  BinaryOperatorKind Opc) {
+  if (Opc == BO_Cmp)
+    return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc);
+
   // C99 6.5.8p3 / C99 6.5.9p4
   QualType Type = S.UsualArithmeticConversions(LHS, RHS);
   if (LHS.isInvalid() || RHS.isInvalid())
@@ -9733,7 +9885,6 @@
     S.CheckFloatComparison(Loc, LHS.get(), RHS.get());
 
   // The result of comparisons is 'bool' in C++, 'int' in C.
-  // FIXME: For BO_Cmp, return the relevant comparison category type.
   return S.Context.getLogicalOperationType();
 }
 
@@ -9741,6 +9892,7 @@
 QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
                                     SourceLocation Loc, BinaryOperatorKind Opc,
                                     bool IsRelational) {
+  bool IsSpaceship = Opc == BO_Cmp;
   // Comparisons expect an rvalue, so convert to rvalue before any
   // type-related checks.
   LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
@@ -9766,7 +9918,46 @@
       (RHSType->isArithmeticType() || RHSType->isEnumeralType()))
     return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc);
 
-  QualType ResultTy = Context.getLogicalOperationType();
+  auto computeResultTy = [&]() {
+    if (!IsSpaceship)
+      return Context.getLogicalOperationType();
+    assert(getLangOpts().CPlusPlus);
+    assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType()));
+
+    if (BuildComparisonCategoryData(Loc))
+      return QualType();
+
+    QualType CompositeTy = LHS.get()->getType();
+    assert(!CompositeTy->isReferenceType());
+    const RecordDecl *CompDecl = nullptr;
+    if (CompositeTy->isVoidPointerType()) {
+      if (!isSFINAEContext()) {
+        Diag(Loc, diag::err_spaceship_comparison_of_void_ptr)
+            << (LHSType->isVoidPointerType() + RHSType->isVoidPointerType() - 1)
+            << LHSType << RHSType;
+      }
+      return QualType();
+    }
+    if (CompositeTy->isFunctionPointerType() ||
+        CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType())
+      CompDecl = Context.CompCategories.getDecl(
+          ComparisonCategoryKind::StrongEquality);
+    else if (CompositeTy->isPointerType()) {
+      auto PointeeTy = CompositeTy->getPointeeType();
+      if (!PointeeTy->isObjectType()) {
+        // TODO: Can this case actually occur?
+        Diag(Loc, diag::err_spaceship_comparison_of_invalid_comp_type)
+            << CompositeTy << LHSType << RHSType;
+        return QualType();
+      }
+      CompDecl = Context.CompCategories.getDecl(
+          ComparisonCategoryKind::StrongOrdering);
+    } else
+      llvm_unreachable("unhandled three-way comparison composite type");
+    if (!CompDecl)
+      return QualType();
+    return QualType(CompDecl->getTypeForDecl(), 0);
+  };
 
   const Expr::NullPointerConstantKind LHSNullKind =
       LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull);
@@ -9807,12 +9998,15 @@
         return QualType();
 
       RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
-      return ResultTy;
+      return computeResultTy();
     }
 
     // C++ [expr.eq]p2:
     //   If at least one operand is a pointer [...] bring them to their
     //   composite pointer type.
+    // C++ [expr.spaceship]p6
+    //  If at least one of the operands is of pointer type, [...] bring them
+    //  to their composite pointer type.
     // C++ [expr.rel]p2:
     //   If both operands are pointers, [...] bring them to their composite
     //   pointer type.
@@ -9823,8 +10017,7 @@
            RHSType->isObjCObjectPointerType()))) {
       if (convertPointersToCompositeType(*this, Loc, LHS, RHS))
         return QualType();
-      else
-        return ResultTy;
+      return computeResultTy();
     }
   } else if (LHSType->isPointerType() &&
              RHSType->isPointerType()) { // C99 6.5.8p2
@@ -9839,7 +10032,7 @@
     if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(),
                                    RCanPointeeTy.getUnqualifiedType())) {
       // Valid unless a relational comparison of function pointers
-      if (IsRelational && LCanPointeeTy->isFunctionType()) {
+      if ((IsRelational && !IsSpaceship) && LCanPointeeTy->isFunctionType()) {
         Diag(Loc, diag::ext_typecheck_ordered_comparison_of_function_pointers)
           << LHSType << RHSType << LHS.get()->getSourceRange()
           << RHS.get()->getSourceRange();
@@ -9875,7 +10068,7 @@
       else
         RHS = ImpCastExprToType(RHS.get(), LHSType, Kind);
     }
-    return ResultTy;
+    return computeResultTy();
   }
 
   if (getLangOpts().CPlusPlus) {
@@ -9885,11 +10078,11 @@
     if (!IsRelational && LHSIsNull && RHSIsNull) {
       if (LHSType->isNullPtrType()) {
         RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
-        return ResultTy;
+        return computeResultTy();
       }
       if (RHSType->isNullPtrType()) {
         LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
-        return ResultTy;
+        return computeResultTy();
       }
     }
 
@@ -9898,12 +10091,12 @@
     if (!IsRelational && RHSType->isNullPtrType() &&
         (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) {
       RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
-      return ResultTy;
+      return computeResultTy();
     }
     if (!IsRelational && LHSType->isNullPtrType() &&
         (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) {
       LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
-      return ResultTy;
+      return computeResultTy();
     }
 
     if (IsRelational &&
@@ -9926,7 +10119,7 @@
             RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
           else
             LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
-          return ResultTy;
+          return computeResultTy();
         }
       }
     }
@@ -9934,12 +10127,12 @@
     // C++ [expr.eq]p2:
     //   If at least one operand is a pointer to member, [...] bring them to
     //   their composite pointer type.
-    if (!IsRelational &&
+    if ((!IsRelational || IsSpaceship) &&
         (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) {
       if (convertPointersToCompositeType(*this, Loc, LHS, RHS))
         return QualType();
       else
-        return ResultTy;
+        return computeResultTy();
     }
   }
 
@@ -9956,7 +10149,7 @@
         << RHS.get()->getSourceRange();
     }
     RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
-    return ResultTy;
+    return computeResultTy();
   }
 
   // Allow block pointers to be compared with null pointer constants.
@@ -9980,7 +10173,7 @@
       RHS = ImpCastExprToType(RHS.get(), LHSType,
                               LHSType->isPointerType() ? CK_BitCast
                                 : CK_AnyPointerToBlockPointerCast);
-    return ResultTy;
+    return computeResultTy();
   }
 
   if (LHSType->isObjCObjectPointerType() ||
@@ -10013,7 +10206,7 @@
         RHS = ImpCastExprToType(E, LHSType,
                                 LPT ? CK_BitCast :CK_CPointerToObjCPointerCast);
       }
-      return ResultTy;
+      return computeResultTy();
     }
     if (LHSType->isObjCObjectPointerType() &&
         RHSType->isObjCObjectPointerType()) {
@@ -10027,20 +10220,20 @@
         LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast);
       else
         RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
-      return ResultTy;
+      return computeResultTy();
     }
 
     if (!IsRelational && LHSType->isBlockPointerType() &&
         RHSType->isBlockCompatibleObjCPointerType(Context)) {
       LHS = ImpCastExprToType(LHS.get(), RHSType,
                               CK_BlockPointerToObjCPointerCast);
-      return ResultTy;
+      return computeResultTy();
     } else if (!IsRelational &&
                LHSType->isBlockCompatibleObjCPointerType(Context) &&
                RHSType->isBlockPointerType()) {
       RHS = ImpCastExprToType(RHS.get(), LHSType,
                               CK_BlockPointerToObjCPointerCast);
-      return ResultTy;
+      return computeResultTy();
     }
   }
   if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) ||
@@ -10080,30 +10273,30 @@
     else
       RHS = ImpCastExprToType(RHS.get(), LHSType,
                         RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer);
-    return ResultTy;
+    return computeResultTy();
   }
   
   // Handle block pointers.
   if (!IsRelational && RHSIsNull
       && LHSType->isBlockPointerType() && RHSType->isIntegerType()) {
     RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
-    return ResultTy;
+    return computeResultTy();
   }
   if (!IsRelational && LHSIsNull
       && LHSType->isIntegerType() && RHSType->isBlockPointerType()) {
     LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
-    return ResultTy;
+    return computeResultTy();
   }
 
   if (getLangOpts().OpenCLVersion >= 200) {
     if (LHSIsNull && RHSType->isQueueT()) {
       LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
-      return ResultTy;
+      return computeResultTy();
     }
 
     if (LHSType->isQueueT() && RHSIsNull) {
       RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
-      return ResultTy;
+      return computeResultTy();
     }
   }
 
@@ -11680,7 +11873,8 @@
   ExprValueKind VK = VK_RValue;
   ExprObjectKind OK = OK_Ordinary;
   bool ConvertHalfVec = false;
-
+  // If the binary operator is <=> and it's an ordered comparison.
+  bool IsCmpOrdered = false;
   std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr);
   if (!LHS.isUsable() || !RHS.isUsable())
     return ExprError();
@@ -11763,11 +11957,12 @@
     ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false);
     break;
   case BO_Cmp:
-    // FIXME: Implement proper semantic checking of '<=>'.
     ConvertHalfVec = true;
     ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
+    assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl());
     if (!ResultTy.isNull())
-      ResultTy = Context.VoidTy;
+      IsCmpOrdered =
+          Context.CompCategories.getInfoForType(ResultTy).isOrdered();
     break;
   case BO_And:
     checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc);
@@ -11873,8 +12068,11 @@
     if (ConvertHalfVec)
       return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false,
                                  OpLoc, FPFeatures);
-    return new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, ResultTy, VK,
-                                        OK, OpLoc, FPFeatures);
+    BinaryOperator *Op = new (Context) BinaryOperator(
+        LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, OpLoc, FPFeatures);
+    if (Opc == BO_Cmp)
+      Op->setIsCmpOrdered(IsCmpOrdered);
+    return Op;
   }
 
   // Handle compound assignments.
Index: lib/Sema/SemaOverload.cpp
===================================================================
--- lib/Sema/SemaOverload.cpp
+++ lib/Sema/SemaOverload.cpp
@@ -288,11 +288,11 @@
 ///        value of the expression prior to the narrowing conversion.
 /// \param ConstantType  If this is an NK_Constant_Narrowing conversion, the
 ///        type of the expression prior to the narrowing conversion.
-NarrowingKind
-StandardConversionSequence::getNarrowingKind(ASTContext &Ctx,
-                                             const Expr *Converted,
-                                             APValue &ConstantValue,
-                                             QualType &ConstantType) const {
+/// \param IgnoreFloatToIntegralConversion If true type-narrowing conversions
+///        from floating point types to integral types should be ignored.
+NarrowingKind StandardConversionSequence::getNarrowingKind(
+    ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue,
+    QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const {
   assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++");
 
   // C++11 [dcl.init.list]p7:
@@ -329,6 +329,8 @@
       return NK_Type_Narrowing;
     } else if (FromType->isIntegralOrUnscopedEnumerationType() &&
                ToType->isRealFloatingType()) {
+      if (IgnoreFloatToIntegralConversion)
+        return NK_Not_Narrowing;
       llvm::APSInt IntConstantValue;
       const Expr *Initializer = IgnoreNarrowingConversion(Converted);
       assert(Initializer && "Unknown conversion expression");
Index: lib/Serialization/ASTReaderStmt.cpp
===================================================================
--- lib/Serialization/ASTReaderStmt.cpp
+++ lib/Serialization/ASTReaderStmt.cpp
@@ -689,6 +689,7 @@
   E->setOpcode((BinaryOperator::Opcode)Record.readInt());
   E->setOperatorLoc(ReadSourceLocation());
   E->setFPFeatures(FPOptions(Record.readInt()));
+  E->setIsCmpOrdered(Record.readInt());
 }
 
 void ASTStmtReader::VisitCompoundAssignOperator(CompoundAssignOperator *E) {
Index: lib/Serialization/ASTWriterStmt.cpp
===================================================================
--- lib/Serialization/ASTWriterStmt.cpp
+++ lib/Serialization/ASTWriterStmt.cpp
@@ -671,6 +671,7 @@
   Record.push_back(E->getOpcode()); // FIXME: stable encoding
   Record.AddSourceLocation(E->getOperatorLoc());
   Record.push_back(E->getFPFeatures().getInt());
+  Record.push_back(E->getIsCmpOrdered());
   Code = serialization::EXPR_BINARY_OPERATOR;
 }
 
Index: test/CodeGenCXX/Inputs/std-compare.h
===================================================================
--- /dev/null
+++ test/CodeGenCXX/Inputs/std-compare.h
@@ -0,0 +1,437 @@
+#ifndef STD_COMPARE_H
+#define STD_COMPARE_H
+
+namespace std {
+inline namespace __1 {
+
+// exposition only
+enum class _EqResult : unsigned char {
+  __zero = 0,
+  __equal = __zero,
+  __equiv = __equal,
+  __nonequal = 1,
+  __nonequiv = __nonequal
+};
+
+enum class _OrdResult : signed char {
+  __less = -1,
+  __greater = 1
+};
+
+enum class _NCmpResult : signed char {
+  __unordered = -127
+};
+
+struct _CmpUnspecifiedType;
+using _CmpUnspecifiedParam = void (_CmpUnspecifiedType::*)();
+
+class weak_equality {
+  constexpr explicit weak_equality(_EqResult __val) noexcept : __value_(__val) {}
+
+public:
+  static const weak_equality equivalent;
+  static const weak_equality nonequivalent;
+
+  friend constexpr bool operator==(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+  friend constexpr bool operator!=(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+  friend constexpr weak_equality operator<=>(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr weak_equality operator<=>(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(weak_equality const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _EqResult __value_;
+};
+
+inline constexpr weak_equality weak_equality::equivalent(_EqResult::__equiv);
+inline constexpr weak_equality weak_equality::nonequivalent(_EqResult::__nonequiv);
+inline constexpr bool operator==(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+inline constexpr bool operator==(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+inline constexpr bool operator!=(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+inline constexpr bool operator!=(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+
+inline constexpr weak_equality operator<=>(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+inline constexpr weak_equality operator<=>(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v;
+}
+
+class strong_equality {
+  explicit constexpr strong_equality(_EqResult __val) noexcept : __value_(__val) {}
+
+public:
+  static const strong_equality equal;
+  static const strong_equality nonequal;
+  static const strong_equality equivalent;
+  static const strong_equality nonequivalent;
+
+  // conversion
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == _EqResult::__zero ? weak_equality::equivalent
+                                         : weak_equality::nonequivalent;
+  }
+
+  // comparisons
+  friend constexpr bool operator==(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+
+  friend constexpr strong_equality operator<=>(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr strong_equality operator<=>(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(strong_equality const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _EqResult __value_;
+};
+
+inline constexpr strong_equality strong_equality::equal(_EqResult::__equal);
+inline constexpr strong_equality strong_equality::nonequal(_EqResult::__nonequal);
+inline constexpr strong_equality strong_equality::equivalent(_EqResult::__equiv);
+inline constexpr strong_equality strong_equality::nonequivalent(_EqResult::__nonequiv);
+constexpr bool operator==(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+constexpr bool operator!=(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+
+constexpr strong_equality operator<=>(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr strong_equality operator<=>(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v;
+}
+
+class partial_ordering {
+  using _ValueT = signed char;
+  explicit constexpr partial_ordering(_EqResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+  explicit constexpr partial_ordering(_OrdResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+  explicit constexpr partial_ordering(_NCmpResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+
+  constexpr bool __is_ordered() const noexcept {
+    return __value_ != _ValueT(_NCmpResult::__unordered);
+  }
+
+public:
+  // valid values
+  static const partial_ordering less;
+  static const partial_ordering equivalent;
+  static const partial_ordering greater;
+  static const partial_ordering unordered;
+
+  // conversion
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent;
+  }
+
+  // comparisons
+  friend constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+
+  friend constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(partial_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr partial_ordering partial_ordering::less(_OrdResult::__less);
+inline constexpr partial_ordering partial_ordering::equivalent(_EqResult::__equiv);
+inline constexpr partial_ordering partial_ordering::greater(_OrdResult::__greater);
+inline constexpr partial_ordering partial_ordering::unordered(_NCmpResult ::__unordered);
+constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ == 0;
+}
+constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ < 0;
+}
+constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ <= 0;
+}
+constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ > 0;
+}
+constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 == __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 >= __v.__value_;
+}
+constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return !__v.__is_ordered() || __v.__value_ != 0;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return !__v.__is_ordered() || __v.__value_ != 0;
+}
+
+constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v < 0 ? partial_ordering::greater : (__v > 0 ? partial_ordering::less : __v);
+}
+
+class weak_ordering {
+  using _ValueT = signed char;
+  explicit constexpr weak_ordering(_EqResult __v) noexcept : __value_(_ValueT(__v)) {}
+  explicit constexpr weak_ordering(_OrdResult __v) noexcept : __value_(_ValueT(__v)) {}
+
+public:
+  static const weak_ordering less;
+  static const weak_ordering equivalent;
+  static const weak_ordering greater;
+
+  // conversions
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent
+                         : weak_equality::nonequivalent;
+  }
+  constexpr operator partial_ordering() const noexcept {
+    return __value_ == 0 ? partial_ordering::equivalent
+                         : (__value_ < 0 ? partial_ordering::less : partial_ordering::greater);
+  }
+
+  // comparisons
+  friend constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+
+  friend constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(weak_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr weak_ordering weak_ordering::less(_OrdResult::__less);
+inline constexpr weak_ordering weak_ordering::equivalent(_EqResult::__equiv);
+inline constexpr weak_ordering weak_ordering::greater(_OrdResult::__greater);
+constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == 0;
+}
+constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != 0;
+}
+constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ < 0;
+}
+constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ <= 0;
+}
+constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ > 0;
+}
+constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 == __v.__value_;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 != __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 >= __v.__value_;
+}
+
+constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return __v < 0 ? weak_ordering::greater : (__v > 0 ? weak_ordering::less : __v);
+}
+
+class strong_ordering {
+  using _ValueT = signed char;
+  explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast<signed char>(__v)) {}
+  explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast<signed char>(__v)) {}
+
+public:
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering equivalent;
+  static const strong_ordering greater;
+
+  // conversions
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent
+                         : weak_equality::nonequivalent;
+  }
+  constexpr operator strong_equality() const noexcept {
+    return __value_ == 0 ? strong_equality::equal
+                         : strong_equality::nonequal;
+  }
+  constexpr operator partial_ordering() const noexcept {
+    return __value_ == 0 ? partial_ordering::equivalent
+                         : (__value_ < 0 ? partial_ordering::less : partial_ordering::greater);
+  }
+  constexpr operator weak_ordering() const noexcept {
+    return __value_ == 0 ? weak_ordering::equivalent
+                         : (__value_ < 0 ? weak_ordering::less : weak_ordering::greater);
+  }
+
+  // comparisons
+  friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+
+  friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(strong_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less);
+inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal);
+inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv);
+inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater);
+
+constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == 0;
+}
+constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != 0;
+}
+constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ < 0;
+}
+constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ <= 0;
+}
+constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ > 0;
+}
+constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 == __v.__value_;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 != __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 >= __v.__value_;
+}
+
+constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return __v < 0 ? strong_ordering::greater : (__v > 0 ? strong_ordering::less : __v);
+}
+
+// named comparison functions
+constexpr bool is_eq(weak_equality __cmp) noexcept { return __cmp == 0; }
+constexpr bool is_neq(weak_equality __cmp) noexcept { return __cmp != 0; }
+constexpr bool is_lt(partial_ordering __cmp) noexcept { return __cmp < 0; }
+constexpr bool is_lteq(partial_ordering __cmp) noexcept { return __cmp <= 0; }
+constexpr bool is_gt(partial_ordering __cmp) noexcept { return __cmp > 0; }
+constexpr bool is_gteq(partial_ordering __cmp) noexcept { return __cmp >= 0; }
+
+} // namespace __1
+} // end namespace std
+
+#endif // STD_COMPARE_H
Index: test/CodeGenCXX/cxx2a-compare.cpp
===================================================================
--- /dev/null
+++ test/CodeGenCXX/cxx2a-compare.cpp
@@ -0,0 +1,113 @@
+// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple | \
+// RUN:    FileCheck %s \
+// RUN:          '-DSO="class.std::__1::strong_ordering"' \
+// RUN:           -DSO_EQ=_ZNSt3__115strong_ordering5equalE \
+// RUN:           -DSO_LT=_ZNSt3__115strong_ordering4lessE \
+// RUN:           -DSO_GT=_ZNSt3__115strong_ordering7greaterE \
+// RUN:          '-DSE="class.std::__1::strong_equality"' \
+// RUN:           -DSE_EQ=_ZNSt3__115strong_equality5equalE \
+// RUN:           -DSE_NE=_ZNSt3__115strong_equality8nonequalE \
+// RUN:          '-DPO="class.std::__1::partial_ordering"' \
+// RUN:           -DPO_EQ=_ZNSt3__116partial_ordering10equivalentE \
+// RUN:           -DPO_LT=_ZNSt3__116partial_ordering4lessE \
+// RUN:           -DPO_GT=_ZNSt3__116partial_ordering7greaterE \
+// RUN:           -DPO_UNORD=_ZNSt3__116partial_ordering9unorderedE
+
+#include "Inputs/std-compare.h"
+
+typedef int INT;
+
+// CHECK-LABEL: @_Z11test_signedii
+auto test_signed(int x, int y) {
+  // CHECK: %retval = alloca %[[SO]]
+  // CHECK: %cmp.lt = icmp slt i32 %0, %1
+  // CHECK: %sel.lt = select i1 %cmp.lt, %[[SO]]* @[[SO_LT]], %[[SO]]* @[[SO_GT]]
+  // CHECK: %cmp.eq = icmp eq i32 %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, %[[SO]]* @[[SO_EQ]], %[[SO]]* %sel.lt
+  // CHECK: %[[TMPRET:.*]] = bitcast %[[SO]]* %retval
+  // CHECK: %[[TMPSRC:.*]] = bitcast %[[SO]]* %sel.eq
+  // CHECK: call void @llvm.memcpy{{.*}}(i8* align 1 %[[TMPRET]], i8* align 1 %[[TMPSRC]]
+  // CHECK: ret
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z13test_unsignedjj
+auto test_unsigned(unsigned x, unsigned y) {
+  // CHECK: %cmp.lt = icmp ult i32 %0, %1
+  // CHECK: %sel.lt = select i1 %cmp.lt, %[[SO]]* @[[SO_LT]], %[[SO]]* @[[SO_GT]]
+  // CHECK: %cmp.eq = icmp eq i32 %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, %[[SO]]* @[[SO_EQ]], %[[SO]]* %sel.lt
+  // CHECK: %retval
+  // CHECK: %sel.eq
+  // CHECK: ret
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z10float_testdd
+auto float_test(double x, double y) {
+  // CHECK: %retval = alloca %[[PO]]
+  // CHECK: %cmp.eq = fcmp oeq double %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, %[[PO]]* @[[PO_EQ]], %[[PO]]* @[[PO_UNORD]]
+  // CHECK: %cmp.gt = fcmp ogt double %0, %1
+  // CHECK: %sel.gt = select i1 %cmp.gt, %[[PO]]* @[[PO_GT]], %[[PO]]* %sel.eq
+  // CHECK: %cmp.lt = fcmp olt double %0, %1
+  // CHECK: %sel.lt = select i1 %cmp.lt, %[[PO]]* @[[PO_LT]], %[[PO]]* %sel.gt
+  // CHECK: %retval
+  // CHECK: %sel.lt
+  // CHECK: ret
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z8ptr_testPiS_
+auto ptr_test(int *x, int *y) {
+  // CHECK: %cmp.lt = icmp ult i32* %0, %1
+  // CHECK: %sel.lt = select i1 %cmp.lt, %[[SO]]* @[[SO_LT]], %[[SO]]* @[[SO_GT]]
+  // CHECK: %cmp.eq = icmp eq i32* %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, %[[SO]]* @[[SO_EQ]], %[[SO]]* %sel.lt
+  // CHECK: %retval
+  // CHECK: %sel.eq
+  // CHECK: ret
+  return x <=> y;
+}
+
+struct MemPtr {};
+using MemPtrT = void (MemPtr::*)();
+using MemDataT = int(MemPtr::*);
+
+// CHECK-LABEL: @_Z12mem_ptr_testM6MemPtrFvvES1_
+auto mem_ptr_test(MemPtrT x, MemPtrT y) {
+  // CHECK: %retval = alloca %[[SE]]
+  // CHECK: %cmp.ptr = icmp eq i64 %lhs.memptr.ptr, %rhs.memptr.ptr
+  // CHECK: %cmp.ptr.null = icmp eq i64 %lhs.memptr.ptr, 0
+  // CHECK: %cmp.adj = icmp eq i64 %lhs.memptr.adj, %rhs.memptr.adj
+  // CHECK: %6 = or i1 %cmp.ptr.null, %cmp.adj
+  // CHECK: %memptr.eq = and i1 %cmp.ptr, %6
+  // CHECK: %sel.eq = select i1 %memptr.eq, %[[SE]]* @[[SE_EQ]], %[[SE]]* @[[SE_NE]]
+  // CHECK: %retval
+  // CHECK: %sel.eq
+  // CHECK: ret
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z13mem_data_testM6MemPtriS0_
+auto mem_data_test(MemDataT x, MemDataT y) {
+  // CHECK: %retval = alloca %[[SE]]
+  // CHECK: %[[CMP:.*]] = icmp eq i64 %0, %1
+  // CHECK: %sel.eq = select i1 %[[CMP]], %[[SE]]* @[[SE_EQ]], %[[SE]]* @[[SE_NE]]
+  // CHECK: %retval
+  // CHECK: %sel.eq
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z13test_constantv
+auto test_constant() {
+  // CHECK: entry:
+  // CHECK-NOT: icmp
+  // CHECK-NOT: [[SO_GT]]
+  // CHECK-NOT: [[SO_EQ]]
+  // CHECK: memcpy{{.*}}[[SO_LT]]
+  // CHECK: ret
+  const int x = 42;
+  const int y = 101;
+  return x <=> y;
+}
Index: test/PCH/Inputs/std-compare.h
===================================================================
--- /dev/null
+++ test/PCH/Inputs/std-compare.h
@@ -0,0 +1,437 @@
+#ifndef STD_COMPARE_H
+#define STD_COMPARE_H
+
+namespace std {
+inline namespace __1 {
+
+// exposition only
+enum class _EqResult : unsigned char {
+  __zero = 0,
+  __equal = __zero,
+  __equiv = __equal,
+  __nonequal = 1,
+  __nonequiv = __nonequal
+};
+
+enum class _OrdResult : signed char {
+  __less = -1,
+  __greater = 1
+};
+
+enum class _NCmpResult : signed char {
+  __unordered = -127
+};
+
+struct _CmpUnspecifiedType;
+using _CmpUnspecifiedParam = void (_CmpUnspecifiedType::*)();
+
+class weak_equality {
+  constexpr explicit weak_equality(_EqResult __val) noexcept : __value_(__val) {}
+
+public:
+  static const weak_equality equivalent;
+  static const weak_equality nonequivalent;
+
+  friend constexpr bool operator==(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+  friend constexpr bool operator!=(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+  friend constexpr weak_equality operator<=>(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr weak_equality operator<=>(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(weak_equality const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _EqResult __value_;
+};
+
+inline constexpr weak_equality weak_equality::equivalent(_EqResult::__equiv);
+inline constexpr weak_equality weak_equality::nonequivalent(_EqResult::__nonequiv);
+inline constexpr bool operator==(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+inline constexpr bool operator==(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+inline constexpr bool operator!=(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+inline constexpr bool operator!=(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+
+inline constexpr weak_equality operator<=>(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+inline constexpr weak_equality operator<=>(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v;
+}
+
+class strong_equality {
+  explicit constexpr strong_equality(_EqResult __val) noexcept : __value_(__val) {}
+
+public:
+  static const strong_equality equal;
+  static const strong_equality nonequal;
+  static const strong_equality equivalent;
+  static const strong_equality nonequivalent;
+
+  // conversion
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == _EqResult::__zero ? weak_equality::equivalent
+                                         : weak_equality::nonequivalent;
+  }
+
+  // comparisons
+  friend constexpr bool operator==(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+
+  friend constexpr strong_equality operator<=>(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr strong_equality operator<=>(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(strong_equality const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _EqResult __value_;
+};
+
+inline constexpr strong_equality strong_equality::equal(_EqResult::__equal);
+inline constexpr strong_equality strong_equality::nonequal(_EqResult::__nonequal);
+inline constexpr strong_equality strong_equality::equivalent(_EqResult::__equiv);
+inline constexpr strong_equality strong_equality::nonequivalent(_EqResult::__nonequiv);
+constexpr bool operator==(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+constexpr bool operator!=(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+
+constexpr strong_equality operator<=>(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr strong_equality operator<=>(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v;
+}
+
+class partial_ordering {
+  using _ValueT = signed char;
+  explicit constexpr partial_ordering(_EqResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+  explicit constexpr partial_ordering(_OrdResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+  explicit constexpr partial_ordering(_NCmpResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+
+  constexpr bool __is_ordered() const noexcept {
+    return __value_ != _ValueT(_NCmpResult::__unordered);
+  }
+
+public:
+  // valid values
+  static const partial_ordering less;
+  static const partial_ordering equivalent;
+  static const partial_ordering greater;
+  static const partial_ordering unordered;
+
+  // conversion
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent;
+  }
+
+  // comparisons
+  friend constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+
+  friend constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(partial_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr partial_ordering partial_ordering::less(_OrdResult::__less);
+inline constexpr partial_ordering partial_ordering::equivalent(_EqResult::__equiv);
+inline constexpr partial_ordering partial_ordering::greater(_OrdResult::__greater);
+inline constexpr partial_ordering partial_ordering::unordered(_NCmpResult ::__unordered);
+constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ == 0;
+}
+constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ < 0;
+}
+constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ <= 0;
+}
+constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ > 0;
+}
+constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 == __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 >= __v.__value_;
+}
+constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return !__v.__is_ordered() || __v.__value_ != 0;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return !__v.__is_ordered() || __v.__value_ != 0;
+}
+
+constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v < 0 ? partial_ordering::greater : (__v > 0 ? partial_ordering::less : __v);
+}
+
+class weak_ordering {
+  using _ValueT = signed char;
+  explicit constexpr weak_ordering(_EqResult __v) noexcept : __value_(_ValueT(__v)) {}
+  explicit constexpr weak_ordering(_OrdResult __v) noexcept : __value_(_ValueT(__v)) {}
+
+public:
+  static const weak_ordering less;
+  static const weak_ordering equivalent;
+  static const weak_ordering greater;
+
+  // conversions
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent
+                         : weak_equality::nonequivalent;
+  }
+  constexpr operator partial_ordering() const noexcept {
+    return __value_ == 0 ? partial_ordering::equivalent
+                         : (__value_ < 0 ? partial_ordering::less : partial_ordering::greater);
+  }
+
+  // comparisons
+  friend constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+
+  friend constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(weak_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr weak_ordering weak_ordering::less(_OrdResult::__less);
+inline constexpr weak_ordering weak_ordering::equivalent(_EqResult::__equiv);
+inline constexpr weak_ordering weak_ordering::greater(_OrdResult::__greater);
+constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == 0;
+}
+constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != 0;
+}
+constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ < 0;
+}
+constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ <= 0;
+}
+constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ > 0;
+}
+constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 == __v.__value_;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 != __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 >= __v.__value_;
+}
+
+constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return __v < 0 ? weak_ordering::greater : (__v > 0 ? weak_ordering::less : __v);
+}
+
+class strong_ordering {
+  using _ValueT = signed char;
+  explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast<signed char>(__v)) {}
+  explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast<signed char>(__v)) {}
+
+public:
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering equivalent;
+  static const strong_ordering greater;
+
+  // conversions
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent
+                         : weak_equality::nonequivalent;
+  }
+  constexpr operator strong_equality() const noexcept {
+    return __value_ == 0 ? strong_equality::equal
+                         : strong_equality::nonequal;
+  }
+  constexpr operator partial_ordering() const noexcept {
+    return __value_ == 0 ? partial_ordering::equivalent
+                         : (__value_ < 0 ? partial_ordering::less : partial_ordering::greater);
+  }
+  constexpr operator weak_ordering() const noexcept {
+    return __value_ == 0 ? weak_ordering::equivalent
+                         : (__value_ < 0 ? weak_ordering::less : weak_ordering::greater);
+  }
+
+  // comparisons
+  friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+
+  friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(strong_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less);
+inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal);
+inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv);
+inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater);
+
+constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == 0;
+}
+constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != 0;
+}
+constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ < 0;
+}
+constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ <= 0;
+}
+constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ > 0;
+}
+constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 == __v.__value_;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 != __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 >= __v.__value_;
+}
+
+constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return __v < 0 ? strong_ordering::greater : (__v > 0 ? strong_ordering::less : __v);
+}
+
+// named comparison functions
+constexpr bool is_eq(weak_equality __cmp) noexcept { return __cmp == 0; }
+constexpr bool is_neq(weak_equality __cmp) noexcept { return __cmp != 0; }
+constexpr bool is_lt(partial_ordering __cmp) noexcept { return __cmp < 0; }
+constexpr bool is_lteq(partial_ordering __cmp) noexcept { return __cmp <= 0; }
+constexpr bool is_gt(partial_ordering __cmp) noexcept { return __cmp > 0; }
+constexpr bool is_gteq(partial_ordering __cmp) noexcept { return __cmp >= 0; }
+
+} // namespace __1
+} // end namespace std
+
+#endif // STD_COMPARE_H
Index: test/PCH/cxx2a-compare.cpp
===================================================================
--- /dev/null
+++ test/PCH/cxx2a-compare.cpp
@@ -0,0 +1,16 @@
+// RUN: %clang_cc1 -pedantic-errors -std=c++2a -emit-pch  %s -o %t
+// RUN: %clang_cc1 -pedantic-errors -std=c++2a -include-pch %t -verify %s
+
+#ifndef HEADER
+#define HEADER
+
+#include "Inputs/std-compare.h"
+
+#else
+
+// expected-no-diagnostics
+void foo() {
+  (void)(42 <=> 101);
+}
+
+#endif
Index: test/SemaCXX/Inputs/std-compare.h
===================================================================
--- /dev/null
+++ test/SemaCXX/Inputs/std-compare.h
@@ -0,0 +1,437 @@
+#ifndef STD_COMPARE_H
+#define STD_COMPARE_H
+
+namespace std {
+inline namespace __1 {
+
+// exposition only
+enum class _EqResult : unsigned char {
+  __zero = 0,
+  __equal = __zero,
+  __equiv = __equal,
+  __nonequal = 1,
+  __nonequiv = __nonequal
+};
+
+enum class _OrdResult : signed char {
+  __less = -1,
+  __greater = 1
+};
+
+enum class _NCmpResult : signed char {
+  __unordered = -127
+};
+
+struct _CmpUnspecifiedType;
+using _CmpUnspecifiedParam = void (_CmpUnspecifiedType::*)();
+
+class weak_equality {
+  constexpr explicit weak_equality(_EqResult __val) noexcept : __value_(__val) {}
+
+public:
+  static const weak_equality equivalent;
+  static const weak_equality nonequivalent;
+
+  friend constexpr bool operator==(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+  friend constexpr bool operator!=(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+  friend constexpr weak_equality operator<=>(weak_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr weak_equality operator<=>(_CmpUnspecifiedParam, weak_equality __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(weak_equality const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _EqResult __value_;
+};
+
+inline constexpr weak_equality weak_equality::equivalent(_EqResult::__equiv);
+inline constexpr weak_equality weak_equality::nonequivalent(_EqResult::__nonequiv);
+inline constexpr bool operator==(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+inline constexpr bool operator==(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+inline constexpr bool operator!=(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+inline constexpr bool operator!=(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+
+inline constexpr weak_equality operator<=>(weak_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+inline constexpr weak_equality operator<=>(_CmpUnspecifiedParam, weak_equality __v) noexcept {
+  return __v;
+}
+
+class strong_equality {
+  explicit constexpr strong_equality(_EqResult __val) noexcept : __value_(__val) {}
+
+public:
+  static const strong_equality equal;
+  static const strong_equality nonequal;
+  static const strong_equality equivalent;
+  static const strong_equality nonequivalent;
+
+  // conversion
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == _EqResult::__zero ? weak_equality::equivalent
+                                         : weak_equality::nonequivalent;
+  }
+
+  // comparisons
+  friend constexpr bool operator==(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+
+  friend constexpr strong_equality operator<=>(strong_equality __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr strong_equality operator<=>(_CmpUnspecifiedParam, strong_equality __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(strong_equality const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _EqResult __value_;
+};
+
+inline constexpr strong_equality strong_equality::equal(_EqResult::__equal);
+inline constexpr strong_equality strong_equality::nonequal(_EqResult::__nonequal);
+inline constexpr strong_equality strong_equality::equivalent(_EqResult::__equiv);
+inline constexpr strong_equality strong_equality::nonequivalent(_EqResult::__nonequiv);
+constexpr bool operator==(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v.__value_ == _EqResult::__zero;
+}
+constexpr bool operator!=(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v.__value_ != _EqResult::__zero;
+}
+
+constexpr strong_equality operator<=>(strong_equality __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr strong_equality operator<=>(_CmpUnspecifiedParam, strong_equality __v) noexcept {
+  return __v;
+}
+
+class partial_ordering {
+  using _ValueT = signed char;
+  explicit constexpr partial_ordering(_EqResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+  explicit constexpr partial_ordering(_OrdResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+  explicit constexpr partial_ordering(_NCmpResult __v) noexcept
+      : __value_(_ValueT(__v)) {}
+
+  constexpr bool __is_ordered() const noexcept {
+    return __value_ != _ValueT(_NCmpResult::__unordered);
+  }
+
+public:
+  // valid values
+  static const partial_ordering less;
+  static const partial_ordering equivalent;
+  static const partial_ordering greater;
+  static const partial_ordering unordered;
+
+  // conversion
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent : weak_equality::nonequivalent;
+  }
+
+  // comparisons
+  friend constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+
+  friend constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(partial_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr partial_ordering partial_ordering::less(_OrdResult::__less);
+inline constexpr partial_ordering partial_ordering::equivalent(_EqResult::__equiv);
+inline constexpr partial_ordering partial_ordering::greater(_OrdResult::__greater);
+inline constexpr partial_ordering partial_ordering::unordered(_NCmpResult ::__unordered);
+constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ == 0;
+}
+constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ < 0;
+}
+constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ <= 0;
+}
+constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ > 0;
+}
+constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__is_ordered() && __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 == __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v.__is_ordered() && 0 >= __v.__value_;
+}
+constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return !__v.__is_ordered() || __v.__value_ != 0;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return !__v.__is_ordered() || __v.__value_ != 0;
+}
+
+constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept {
+  return __v < 0 ? partial_ordering::greater : (__v > 0 ? partial_ordering::less : __v);
+}
+
+class weak_ordering {
+  using _ValueT = signed char;
+  explicit constexpr weak_ordering(_EqResult __v) noexcept : __value_(_ValueT(__v)) {}
+  explicit constexpr weak_ordering(_OrdResult __v) noexcept : __value_(_ValueT(__v)) {}
+
+public:
+  static const weak_ordering less;
+  static const weak_ordering equivalent;
+  static const weak_ordering greater;
+
+  // conversions
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent
+                         : weak_equality::nonequivalent;
+  }
+  constexpr operator partial_ordering() const noexcept {
+    return __value_ == 0 ? partial_ordering::equivalent
+                         : (__value_ < 0 ? partial_ordering::less : partial_ordering::greater);
+  }
+
+  // comparisons
+  friend constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+
+  friend constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(weak_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr weak_ordering weak_ordering::less(_OrdResult::__less);
+inline constexpr weak_ordering weak_ordering::equivalent(_EqResult::__equiv);
+inline constexpr weak_ordering weak_ordering::greater(_OrdResult::__greater);
+constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == 0;
+}
+constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != 0;
+}
+constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ < 0;
+}
+constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ <= 0;
+}
+constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ > 0;
+}
+constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 == __v.__value_;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 != __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return 0 >= __v.__value_;
+}
+
+constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept {
+  return __v < 0 ? weak_ordering::greater : (__v > 0 ? weak_ordering::less : __v);
+}
+
+class strong_ordering {
+  using _ValueT = signed char;
+  explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast<signed char>(__v)) {}
+  explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast<signed char>(__v)) {}
+
+public:
+  static const strong_ordering less;
+  static const strong_ordering equal;
+  static const strong_ordering equivalent;
+  static const strong_ordering greater;
+
+  // conversions
+  constexpr operator weak_equality() const noexcept {
+    return __value_ == 0 ? weak_equality::equivalent
+                         : weak_equality::nonequivalent;
+  }
+  constexpr operator strong_equality() const noexcept {
+    return __value_ == 0 ? strong_equality::equal
+                         : strong_equality::nonequal;
+  }
+  constexpr operator partial_ordering() const noexcept {
+    return __value_ == 0 ? partial_ordering::equivalent
+                         : (__value_ < 0 ? partial_ordering::less : partial_ordering::greater);
+  }
+  constexpr operator weak_ordering() const noexcept {
+    return __value_ == 0 ? weak_ordering::equivalent
+                         : (__value_ < 0 ? weak_ordering::less : weak_ordering::greater);
+  }
+
+  // comparisons
+  friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+  friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+
+  friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept;
+  friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept;
+
+  // test helper
+  constexpr bool test_eq(strong_ordering const &other) const noexcept {
+    return __value_ == other.__value_;
+  }
+
+private:
+  _ValueT __value_;
+};
+
+inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less);
+inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal);
+inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv);
+inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater);
+
+constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ == 0;
+}
+constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ != 0;
+}
+constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ < 0;
+}
+constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ <= 0;
+}
+constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ > 0;
+}
+constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v.__value_ >= 0;
+}
+constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 == __v.__value_;
+}
+constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 != __v.__value_;
+}
+constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 < __v.__value_;
+}
+constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 <= __v.__value_;
+}
+constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 > __v.__value_;
+}
+constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return 0 >= __v.__value_;
+}
+
+constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept {
+  return __v;
+}
+constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept {
+  return __v < 0 ? strong_ordering::greater : (__v > 0 ? strong_ordering::less : __v);
+}
+
+// named comparison functions
+constexpr bool is_eq(weak_equality __cmp) noexcept { return __cmp == 0; }
+constexpr bool is_neq(weak_equality __cmp) noexcept { return __cmp != 0; }
+constexpr bool is_lt(partial_ordering __cmp) noexcept { return __cmp < 0; }
+constexpr bool is_lteq(partial_ordering __cmp) noexcept { return __cmp <= 0; }
+constexpr bool is_gt(partial_ordering __cmp) noexcept { return __cmp > 0; }
+constexpr bool is_gteq(partial_ordering __cmp) noexcept { return __cmp >= 0; }
+
+} // namespace __1
+} // end namespace std
+
+#endif // STD_COMPARE_H
Index: test/SemaCXX/compare-cxx2a.cpp
===================================================================
--- test/SemaCXX/compare-cxx2a.cpp
+++ test/SemaCXX/compare-cxx2a.cpp
@@ -1,7 +1,9 @@
 // Force x86-64 because some of our heuristics are actually based
 // on integer sizes.
 
-// RUN: %clang_cc1 -triple x86_64-apple-darwin -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s
+
+#include "Inputs/std-compare.h"
 
 void self_compare() {
   int a;
@@ -19,16 +21,16 @@
 
   // FIXME: <=> should never produce -Wsign-compare warnings. All the possible error
   // cases involve narrowing conversions and so are ill-formed.
-  (void)(a <=> (unsigned long) b); // expected-warning {{comparison of integers of different signs}}
+  (void)(a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
   (void)(a <=> (unsigned int) b);
   (void)(a <=> (unsigned short) b);
   (void)(a <=> (unsigned char) b);
-  (void)((long) a <=> b);  // expected-warning {{comparison of integers of different signs}}
-  (void)((int) a <=> b);  // expected-warning {{comparison of integers of different signs}}
-  (void)((short) a <=> b);  // expected-warning {{comparison of integers of different signs}}
-  (void)((signed char) a <=> b);  // expected-warning {{comparison of integers of different signs}}
-  (void)((long) a <=> (unsigned long) b);  // expected-warning {{comparison of integers of different signs}}
-  (void)((int) a <=> (unsigned int) b);  // expected-warning {{comparison of integers of different signs}}
+  (void)((long)a <=> b);                // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((int)a <=> b);                 // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((short)a <=> b);               // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((signed char)a <=> b);         // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((long)a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((int)a <=> (unsigned int)b);   // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
   (void)((short) a <=> (unsigned short) b);
   (void)((signed char) a <=> (unsigned char) b);
 
@@ -105,7 +107,7 @@
   (void)((signed char) 0x80000 <=> (unsigned char) b);
 
   // (a,0x80000)
-  (void)(a <=> (unsigned long) 0x80000); // expected-warning {{comparison of integers of different signs}}
+  (void)(a <=> (unsigned long)0x80000); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
   (void)(a <=> (unsigned int) 0x80000);
   (void)(a <=> (unsigned short) 0x80000);
   (void)(a <=> (unsigned char) 0x80000);
@@ -113,19 +115,21 @@
   (void)((int) a <=> 0x80000);
   (void)((short) a <=> 0x80000); // expected-warning {{comparison of constant 524288 with expression of type 'short' is always 'std::strong_ordering::less'}}
   (void)((signed char) a <=> 0x80000); // expected-warning {{comparison of constant 524288 with expression of type 'signed char' is always 'std::strong_ordering::less'}}
-  (void)((long) a <=> (unsigned long) 0x80000); // expected-warning {{comparison of integers of different signs}}
-  (void)((int) a <=> (unsigned int) 0x80000); // expected-warning {{comparison of integers of different signs}}
+  (void)((long)a <=> (unsigned long)0x80000); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((int)a <=> (unsigned int)0x80000);   // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
   (void)((short) a <=> (unsigned short) 0x80000);
   (void)((signed char) a <=> (unsigned char) 0x80000);
 }
 
-void test5(bool b) {
-  (void) (b <=> -10); // expected-warning{{comparison of constant -10 with expression of type 'bool' is always 'std::strong_ordering::greater'}}
-  (void) (b <=> -1); // expected-warning{{comparison of constant -1 with expression of type 'bool' is always 'std::strong_ordering::greater'}}
-  (void) (b <=> 0);
-  (void) (b <=> 1);
-  (void) (b <=> 2); // expected-warning{{comparison of constant 2 with expression of type 'bool' is always 'std::strong_ordering::less'}}
-  (void) (b <=> 10); // expected-warning{{comparison of constant 10 with expression of type 'bool' is always 'std::strong_ordering::less'}}
+void test5(bool b, bool b2) {
+  (void)(b <=> b2);      // OK
+  (void)(true <=> b);    // OK
+  (void)(b <=> -10);     // expected-error {{invalid operands to binary expression ('bool' and 'int')}}
+  (void)(b <=> char(1)); // expected-error {{invalid operands to binary expression ('bool' and 'char')}}
+
+  // FIXME: Should this be accepted when narrowing doesn't occur?
+  (void)(b <=> 0); // expected-error {{invalid operands to binary expression ('bool' and 'int')}}
+  (void)(b <=> 1); // expected-error {{invalid operands to binary expression ('bool' and 'int')}}
 }
 
 void test6(signed char sc) {
@@ -142,13 +146,13 @@
   (void)((unsigned long)other <=> (unsigned)(0xffff'ffff));
 
   // Common unsigned, other signed, constant unsigned
-  (void)((int)other <=> (unsigned long)(0xffff'ffff'ffff'ffff)); // expected-warning{{different signs}}
-  (void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-warning{{different signs}}
-  (void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-warning{{different signs}}
-  (void)((int)other <=> (unsigned)(0x8000'0000));                // expected-warning{{different signs}}
+  (void)((int)other <=> (unsigned long)(0xffff'ffff'ffff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
+  (void)((int)other <=> (unsigned)(0x8000'0000));                // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}}
 
   // Common unsigned, other unsigned, constant signed
-  (void)((unsigned long)other <=> (int)(0xffff'ffff));  // expected-warning{{different signs}}
+  (void)((unsigned long)other <=> (int)(0xffff'ffff)); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned long'}}
 
   // Common unsigned, other signed, constant signed
   // Should not be possible as the common type should also be signed.
@@ -172,3 +176,132 @@
   (void)((unsigned char)other <=> (unsigned short)(0x100)); // expected-warning{{less}}
   (void)((unsigned short)other <=> (unsigned char)(0xff));
 }
+
+// test void pointer diagnostics
+void test8(void *vp, const void *cvp, int *ip) {
+  (void)(vp <=> cvp); // expected-error {{three-way comparison with void pointer operand types ('void *' and 'const void *')}}
+  (void)(vp <=> ip);  // expected-error {{three-way comparison with void pointer operand type ('void *' and 'int *')}}
+  (void)(ip <=> cvp); // expected-error {{three-way comparison with void pointer operand type ('int *' and 'const void *')}}
+}
+
+void test9(long double ld, double d, float f, int i, long long ll) {
+  (void)(f <=> ll); // OK, floating-point to integer is OK
+  (void)(d <=> ld);
+  (void)(i <=> f);
+}
+
+struct MemPtr {
+  void foo() {}
+  void bar() {}
+};
+using MemPtrT = void (MemPtr::*)();
+
+using FnPtrT = void (*)();
+
+void FnPtr1() {}
+void FnPtr2() {}
+
+#define CHECK(...) ((__VA_ARGS__) ? void() : throw "error")
+#define CHECK_TYPE(...) static_assert(__is_same(__VA_ARGS__));
+
+constexpr bool test_constexpr = [] {
+  {
+    auto &EQ = std::strong_ordering::equal;
+    auto &LESS = std::strong_ordering::less;
+    auto &GREATER = std::strong_ordering::greater;
+    using SO = std::strong_ordering;
+    auto eq = (42 <=> 42);
+    CHECK_TYPE(decltype(eq), SO);
+    CHECK(eq.test_eq(EQ));
+
+    auto less = (-1 <=> 0);
+    CHECK_TYPE(decltype(less), SO);
+    CHECK(less.test_eq(LESS));
+
+    auto greater = (42l <=> 1u);
+    CHECK_TYPE(decltype(greater), SO);
+    CHECK(greater.test_eq(GREATER));
+  }
+  {
+    using PO = std::partial_ordering;
+    auto EQUIV = PO::equivalent;
+    auto LESS = PO::less;
+    auto GREATER = PO::greater;
+
+    auto eq = (42.0 <=> 42.0);
+    CHECK_TYPE(decltype(eq), PO);
+    CHECK(eq.test_eq(EQUIV));
+
+    auto less = (39.0 <=> 42.0);
+    CHECK_TYPE(decltype(less), PO);
+    CHECK(less.test_eq(LESS));
+
+    auto greater = (-10.123 <=> -101.1);
+    CHECK_TYPE(decltype(greater), PO);
+    CHECK(greater.test_eq(GREATER));
+  }
+  {
+    using SE = std::strong_equality;
+    auto EQ = SE::equal;
+    auto NEQ = SE::nonequal;
+
+    MemPtrT P1 = &MemPtr::foo;
+    MemPtrT P12 = &MemPtr::foo;
+    MemPtrT P2 = &MemPtr::bar;
+    MemPtrT P3 = nullptr;
+
+    auto eq = (P1 <=> P12);
+    CHECK_TYPE(decltype(eq), SE);
+    CHECK(eq.test_eq(EQ));
+
+    auto neq = (P1 <=> P2);
+    CHECK_TYPE(decltype(eq), SE);
+    CHECK(neq.test_eq(NEQ));
+
+    auto eq2 = (P3 <=> nullptr);
+    CHECK_TYPE(decltype(eq2), SE);
+    CHECK(eq2.test_eq(EQ));
+  }
+  {
+    using SE = std::strong_equality;
+    auto EQ = SE::equal;
+    auto NEQ = SE::nonequal;
+
+    FnPtrT F1 = &FnPtr1;
+    FnPtrT F12 = &FnPtr1;
+    FnPtrT F2 = &FnPtr2;
+    FnPtrT F3 = nullptr;
+
+    auto eq = (F1 <=> F12);
+    CHECK_TYPE(decltype(eq), SE);
+    CHECK(eq.test_eq(EQ));
+
+    auto neq = (F1 <=> F2);
+    CHECK_TYPE(decltype(neq), SE);
+    CHECK(neq.test_eq(NEQ));
+  }
+  {
+    using SO = std::strong_ordering;
+    auto EQ = SO::equal;
+    auto LESS = SO::less;
+    auto GREATER = SO::greater;
+
+    int Arr[5] = {};
+    using ArrRef = decltype((Arr));
+
+    int *P1 = (Arr + 1);
+    int *P12 = (Arr + 1);
+    int *P2 = (Arr + 2);
+
+    auto eq = (Arr <=> (ArrRef)Arr); //(P1 <=> P12);
+    CHECK_TYPE(decltype(eq), SO);
+    CHECK(eq.test_eq(EQ));
+  }
+
+  return true;
+}();
+
+typedef int *INTPTR;
+void test_typedef_bug(int *x, INTPTR y) {
+  (void)(x <=> y);
+}