r331677 - [C++2a] Implement operator<=> CodeGen and ExprConstant

Eric Fiselier via cfe-commits cfe-commits at lists.llvm.org
Mon May 7 14:07:10 PDT 2018


Author: ericwf
Date: Mon May  7 14:07:10 2018
New Revision: 331677

URL: http://llvm.org/viewvc/llvm-project?rev=331677&view=rev
Log:
[C++2a] Implement operator<=> CodeGen and ExprConstant

Summary:
This patch tackles long hanging fruit for the builtin operator<=> expressions. It is currently needs some cleanup before landing, but I want to get some initial feedback.

The main changes are:

* Lookup, build, and store the required standard library types and expressions in `ASTContext`. By storing them in ASTContext we don't need to store (and duplicate) the required expressions in the BinaryOperator AST nodes. 

* Implement [expr.spaceship] checking, including diagnosing narrowing conversions. 

* Implement `ExprConstant` for builtin spaceship operators.

* Implement builitin operator<=> support in `CodeGenAgg`. Initially I emitted the required comparisons using `ScalarExprEmitter::VisitBinaryOperator`, but this caused the operand expressions to be emitted once for every required cmp.

* Implement [builtin.over] with modifications to support the intent of P0946R0. See the note on `BuiltinOperatorOverloadBuilder::addThreeWayArithmeticOverloads` for more information about the workaround.




Reviewers: rsmith, aaron.ballman, majnemer, rnk, compnerd, rjmccall

Reviewed By: rjmccall

Subscribers: rjmccall, rsmith, aaron.ballman, junbuml, mgorny, cfe-commits

Differential Revision: https://reviews.llvm.org/D45476

Added:
    cfe/trunk/include/clang/AST/ComparisonCategories.h
    cfe/trunk/lib/AST/ComparisonCategories.cpp
    cfe/trunk/test/CodeGenCXX/Inputs/std-compare.h
    cfe/trunk/test/CodeGenCXX/cxx2a-compare.cpp
    cfe/trunk/test/PCH/Inputs/std-compare.h
    cfe/trunk/test/PCH/cxx2a-compare.cpp
    cfe/trunk/test/SemaCXX/Inputs/std-compare.h
    cfe/trunk/test/SemaCXX/std-compare-cxx2a.cpp
Modified:
    cfe/trunk/include/clang/AST/ASTContext.h
    cfe/trunk/include/clang/AST/Expr.h
    cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
    cfe/trunk/include/clang/Sema/Overload.h
    cfe/trunk/include/clang/Sema/Sema.h
    cfe/trunk/lib/AST/ASTContext.cpp
    cfe/trunk/lib/AST/CMakeLists.txt
    cfe/trunk/lib/AST/Expr.cpp
    cfe/trunk/lib/AST/ExprConstant.cpp
    cfe/trunk/lib/CodeGen/CGExprAgg.cpp
    cfe/trunk/lib/Sema/Sema.cpp
    cfe/trunk/lib/Sema/SemaDeclCXX.cpp
    cfe/trunk/lib/Sema/SemaExpr.cpp
    cfe/trunk/lib/Sema/SemaOverload.cpp
    cfe/trunk/test/SemaCXX/compare-cxx2a.cpp
    cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp

Modified: cfe/trunk/include/clang/AST/ASTContext.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ASTContext.h?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/ASTContext.h (original)
+++ cfe/trunk/include/clang/AST/ASTContext.h Mon May  7 14:07:10 2018
@@ -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"
@@ -1978,6 +1979,10 @@ public:
   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.
+  ComparisonCategories CompCategories;
+
 private:
   CanQualType getFromTargetType(unsigned Type) const;
   TypeInfo getTypeInfoImpl(const Type *T) const;

Added: cfe/trunk/include/clang/AST/ComparisonCategories.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/ComparisonCategories.h?rev=331677&view=auto
==============================================================================
--- cfe/trunk/include/clang/AST/ComparisonCategories.h (added)
+++ cfe/trunk/include/clang/AST/ComparisonCategories.h Mon May  7 14:07:10 2018
@@ -0,0 +1,255 @@
+//===- 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 "clang/Basic/LLVM.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include <array>
+#include <cassert>
+
+namespace llvm {
+  class StringRef;
+  class APSInt;
+}
+
+namespace clang {
+
+class ASTContext;
+class VarDecl;
+class CXXRecordDecl;
+class Sema;
+class QualType;
+class NamespaceDecl;
+
+/// \brief An enumeration representing the different comparison categories
+/// types.
+///
+/// C++2a [cmp.categories.pre] The types weak_equality, strong_equality,
+/// partial_ordering, weak_ordering, and strong_ordering are collectively
+/// termed the comparison category types.
+enum class ComparisonCategoryType : unsigned char {
+  WeakEquality,
+  StrongEquality,
+  PartialOrdering,
+  WeakOrdering,
+  StrongOrdering,
+  First = WeakEquality,
+  Last = StrongOrdering
+};
+
+/// \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 {
+  Equal,
+  Equivalent,
+  Nonequivalent,
+  Nonequal,
+  Less,
+  Greater,
+  Unordered,
+  Last = Unordered
+};
+
+class ComparisonCategoryInfo {
+  friend class ComparisonCategories;
+  friend class Sema;
+
+public:
+  ComparisonCategoryInfo(const ASTContext &Ctx, CXXRecordDecl *RD,
+                         ComparisonCategoryType Kind)
+      : Ctx(Ctx), Record(RD), Kind(Kind) {}
+
+  struct ValueInfo {
+    ComparisonCategoryResult Kind;
+    VarDecl *VD;
+
+    ValueInfo(ComparisonCategoryResult Kind, VarDecl *VD)
+        : Kind(Kind), VD(VD), HasValue(false) {}
+
+    /// \brief True iff we've successfully evaluated the variable as a constant
+    /// expression and extracted its integer value.
+    bool hasValidIntValue() const { return HasValue; }
+
+    /// \brief Get the constant integer value used by this variable to represent
+    /// the comparison category result type.
+    llvm::APSInt getIntValue() const {
+      assert(hasValidIntValue());
+      return IntValue;
+    }
+
+    void setIntValue(llvm::APSInt Val) {
+      IntValue = Val;
+      HasValue = true;
+    }
+
+  private:
+    friend class ComparisonCategoryInfo;
+    llvm::APSInt IntValue;
+    bool HasValue : 1;
+  };
+private:
+  const ASTContext &Ctx;
+
+  /// \brief A map containing the comparison category result decls from the
+  /// standard library. The key is a value of ComparisonCategoryResult.
+  mutable llvm::SmallVector<
+      ValueInfo, static_cast<unsigned>(ComparisonCategoryResult::Last) + 1>
+      Objects;
+
+  /// \brief Lookup the ValueInfo struct for the specified ValueKind. If the
+  /// VarDecl for the value cannot be found, nullptr is returned.
+  ///
+  /// If the ValueInfo does not have a valid integer value the variable
+  /// is evaluated as a constant expression to determine that value.
+  ValueInfo *lookupValueInfo(ComparisonCategoryResult ValueKind) const;
+
+public:
+  /// \brief The declaration for the comparison category type from the
+  /// standard library.
+  // FIXME: Make this const
+  CXXRecordDecl *Record = nullptr;
+
+  /// \brief The Kind of the comparison category type
+  ComparisonCategoryType Kind;
+
+public:
+  QualType getType() const;
+
+  const ValueInfo *getValueInfo(ComparisonCategoryResult ValueKind) const {
+    ValueInfo *Info = lookupValueInfo(ValueKind);
+    assert(Info &&
+           "comparison category does not contain the specified result kind");
+    assert(Info->hasValidIntValue() &&
+           "couldn't determine the integer constant for this value");
+    return Info;
+  }
+
+  /// \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 = ComparisonCategoryType;
+    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 = ComparisonCategoryType;
+    return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering;
+  }
+
+  /// \brief True iff the comparison is not totally ordered.
+  bool isPartial() const {
+    using CCK = ComparisonCategoryType;
+    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 ValueInfo *getEqualOrEquiv() const {
+    return getValueInfo(makeWeakResult(ComparisonCategoryResult::Equal));
+  }
+  const ValueInfo *getNonequalOrNonequiv() const {
+    assert(isEquality());
+    return getValueInfo(makeWeakResult(ComparisonCategoryResult::Nonequal));
+  }
+  const ValueInfo *getLess() const {
+    assert(isOrdered());
+    return getValueInfo(ComparisonCategoryResult::Less);
+  }
+  const ValueInfo *getGreater() const {
+    assert(isOrdered());
+    return getValueInfo(ComparisonCategoryResult::Greater);
+  }
+  const ValueInfo *getUnordered() const {
+    assert(isPartial());
+    return getValueInfo(ComparisonCategoryResult::Unordered);
+  }
+};
+
+class ComparisonCategories {
+public:
+  static StringRef getCategoryString(ComparisonCategoryType Kind);
+  static StringRef getResultString(ComparisonCategoryResult Kind);
+
+  /// \brief Return the list of results which are valid for the specified
+  ///   comparison category type.
+  static std::vector<ComparisonCategoryResult>
+  getPossibleResultsForType(ComparisonCategoryType Type);
+
+  /// \brief Return the comparison category information for the category
+  ///   specified by 'Kind'.
+  const ComparisonCategoryInfo &getInfo(ComparisonCategoryType Kind) const {
+    const ComparisonCategoryInfo *Result = lookupInfo(Kind);
+    assert(Result != nullptr &&
+           "information for specified comparison category has not been built");
+    return *Result;
+  }
+
+  /// \brief Return the comparison category information as specified by
+  ///   `getCategoryForType(Ty)`. If the information is not already cached,
+  ///    the declaration is looked up and a cache entry is created.
+  /// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is possible.
+  const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
+
+public:
+  /// \brief Return the cached comparison category information for the
+  ///   specified 'Kind'. If no cache entry is present the comparison category
+  ///   type is looked up. If lookup fails nullptr is returned. Otherwise, a
+  ///   new cache entry is created and returned
+  const ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) const;
+
+  ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) {
+    const auto &This = *this;
+    return const_cast<ComparisonCategoryInfo *>(This.lookupInfo(Kind));
+  }
+
+private:
+  const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
+
+private:
+  friend class ASTContext;
+
+  explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}
+
+  const ASTContext &Ctx;
+
+  /// A map from the ComparisonCategoryType (represented as 'char') to the
+  /// cached information for the specified category.
+  mutable llvm::DenseMap<char, ComparisonCategoryInfo> Data;
+  mutable NamespaceDecl *StdNS = nullptr;
+};
+
+} // namespace clang
+
+#endif

Modified: cfe/trunk/include/clang/AST/Expr.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/AST/Expr.h?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/include/clang/AST/Expr.h (original)
+++ cfe/trunk/include/clang/AST/Expr.h Mon May  7 14:07:10 2018
@@ -2778,7 +2778,9 @@ protected:
 public:
   CastKind getCastKind() const { return (CastKind) CastExprBits.Kind; }
   void setCastKind(CastKind K) { CastExprBits.Kind = K; }
-  const char *getCastKindName() const;
+
+  static const char *getCastKindName(CastKind CK);
+  const char *getCastKindName() const { return getCastKindName(getCastKind()); }
 
   Expr *getSubExpr() { return cast<Expr>(Op); }
   const Expr *getSubExpr() const { return cast<Expr>(Op); }

Modified: cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td (original)
+++ cfe/trunk/include/clang/Basic/DiagnosticSemaKinds.td Mon May  7 14:07:10 2018
@@ -9424,4 +9424,18 @@ def err_multiversion_not_allowed_on_main
 def err_multiversion_not_supported : Error<
  "function multiversioning is not supported on the current target">;
 
+// three-way comparison operator diagnostics
+def err_implied_comparison_category_type_not_found : Error<
+  "cannot deduce return type of 'operator<=>' because type %0 was not found; "
+  "include <compare>">;
+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_std_compare_type_not_supported : Error<
+  "standard library implementation of %0 is not supported; "
+   "%select{member '%2' does not have expected form|"
+   "member '%2' is missing|"
+   "the type is not trivially copyable|"
+   "the type does not have the expected form}1">;
 } // end of sema component.

Modified: cfe/trunk/include/clang/Sema/Overload.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Overload.h?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Overload.h (original)
+++ cfe/trunk/include/clang/Sema/Overload.h Mon May  7 14:07:10 2018
@@ -330,9 +330,10 @@ class Sema;
     }
     
     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;

Modified: cfe/trunk/include/clang/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/Sema.h?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/Sema.h (original)
+++ cfe/trunk/include/clang/Sema/Sema.h Mon May  7 14:07:10 2018
@@ -17,8 +17,9 @@
 
 #include "clang/AST/Attr.h"
 #include "clang/AST/Availability.h"
-#include "clang/AST/DeclarationName.h"
+#include "clang/AST/ComparisonCategories.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DeclarationName.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprObjC.h"
 #include "clang/AST/ExternalASTSource.h"
@@ -49,6 +50,7 @@
 #include "llvm/ADT/ArrayRef.h"
 #include "llvm/ADT/Optional.h"
 #include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallBitVector.h"
 #include "llvm/ADT/SmallPtrSet.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/ADT/TinyPtrVector.h"
@@ -4545,6 +4547,22 @@ public:
   CXXRecordDecl *getStdBadAlloc() const;
   EnumDecl *getStdAlignValT() const;
 
+private:
+  // A cache representing if we've fully checked the various comparison category
+  // types stored in ASTContext. The bit-index corresponds to the integer value
+  // of a ComparisonCategoryType enumerator.
+  llvm::SmallBitVector FullyCheckedComparisonCategories;
+
+public:
+  /// \brief Lookup the specified comparison category types in the standard
+  ///   library, an check the VarDecls possibly returned by the operator<=>
+  ///   builtins for that type.
+  ///
+  /// \return The type of the comparison category type corresponding to the
+  ///   specified Kind, or a null type if an error occurs
+  QualType CheckComparisonCategoryType(ComparisonCategoryType Kind,
+                                       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);
@@ -9574,8 +9592,8 @@ public:
     ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
     BinaryOperatorKind Opc, bool IsCompAssign = false);
   QualType CheckCompareOperands( // C99 6.5.8/9
-    ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
-    BinaryOperatorKind Opc, bool isRelational);
+      ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
+      BinaryOperatorKind Opc);
   QualType CheckBitwiseOperands( // C99 6.5.[10...12]
       ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
       BinaryOperatorKind Opc);

Modified: cfe/trunk/lib/AST/ASTContext.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ASTContext.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ASTContext.cpp (original)
+++ cfe/trunk/lib/AST/ASTContext.cpp Mon May  7 14:07:10 2018
@@ -792,7 +792,8 @@ ASTContext::ASTContext(LangOptions &LOpt
                                         LangOpts.XRayAttrListFiles, SM)),
       PrintingPolicy(LOpts), Idents(idents), Selectors(sels),
       BuiltinInfo(builtins), DeclarationNames(*this), Comments(SM),
-      CommentCommandTraits(BumpAlloc, LOpts.CommentOpts), LastSDM(nullptr, 0) {
+      CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
+      CompCategories(this_()), LastSDM(nullptr, 0) {
   TUDecl = TranslationUnitDecl::Create(*this);
 }
 

Modified: cfe/trunk/lib/AST/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/CMakeLists.txt?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/AST/CMakeLists.txt (original)
+++ cfe/trunk/lib/AST/CMakeLists.txt Mon May  7 14:07:10 2018
@@ -20,6 +20,7 @@ add_clang_library(clangAST
   CommentLexer.cpp
   CommentParser.cpp
   CommentSema.cpp
+  ComparisonCategories.cpp
   DataCollection.cpp
   Decl.cpp
   DeclarationName.cpp

Added: cfe/trunk/lib/AST/ComparisonCategories.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ComparisonCategories.cpp?rev=331677&view=auto
==============================================================================
--- cfe/trunk/lib/AST/ComparisonCategories.cpp (added)
+++ cfe/trunk/lib/AST/ComparisonCategories.cpp Mon May  7 14:07:10 2018
@@ -0,0 +1,220 @@
+//===- 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"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace clang;
+
+/// Attempt to determine the integer value used to represent the comparison
+/// category result by evaluating the initializer for the specified VarDecl as
+/// a constant expression and retreiving the value of the classes first
+/// (and only) field.
+///
+/// Note: The STL types are expected to have the form:
+///    struct X { T value; };
+/// where T is an integral or enumeration type.
+static bool evaluateIntValue(const ASTContext &Ctx,
+                             ComparisonCategoryInfo::ValueInfo *Info) {
+  if (Info->hasValidIntValue())
+    return false;
+
+  // Before we attempt to get the value of the first field, ensure that we
+  // actually have one (and only one) field.
+  auto *Record = Info->VD->getType()->getAsCXXRecordDecl();
+  if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
+      !Record->field_begin()->getType()->isIntegralOrEnumerationType())
+    return true;
+
+  Expr::EvalResult Result;
+  if (!Info->VD->hasInit() ||
+      !Info->VD->getInit()->EvaluateAsRValue(Result, Ctx))
+    return true;
+
+  assert(Result.Val.isStruct());
+  Info->setIntValue(Result.Val.getStructField(0).getInt());
+  return false;
+}
+
+ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
+    ComparisonCategoryResult ValueKind) const {
+  // Check if we already have a cache entry for this value.
+  auto It = llvm::find_if(
+      Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
+
+  // We don't have a cached result. Lookup the variable declaration and create
+  // a new entry representing it.
+  if (It == Objects.end()) {
+    DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
+        &Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
+    if (Lookup.size() != 1 || !isa<VarDecl>(Lookup.front()))
+      return nullptr;
+    Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
+    It = Objects.end() - 1;
+  }
+  assert(It != Objects.end());
+  // Success! Attempt to update the int value in case the variables initializer
+  // wasn't present the last time we were here.
+  ValueInfo *Info = &(*It);
+  evaluateIntValue(Ctx, Info);
+
+  return Info;
+}
+
+static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
+                                               NamespaceDecl *&StdNS) {
+  if (!StdNS) {
+    DeclContextLookupResult Lookup =
+        Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
+    if (Lookup.size() == 1)
+      StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
+  }
+  return StdNS;
+}
+
+static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
+                                          const NamespaceDecl *StdNS,
+                                          ComparisonCategoryType Kind) {
+  StringRef Name = ComparisonCategories::getCategoryString(Kind);
+  DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
+  if (Lookup.size() == 1)
+    if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
+      return RD;
+  return nullptr;
+}
+
+const ComparisonCategoryInfo *
+ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
+  auto It = Data.find(static_cast<char>(Kind));
+  if (It != Data.end())
+    return &It->second;
+
+  if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
+    if (CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
+      return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
+
+  return nullptr;
+}
+
+const ComparisonCategoryInfo *
+ComparisonCategories::lookupInfoForType(QualType Ty) const {
+  assert(!Ty.isNull() && "type must be non-null");
+  using CCT = ComparisonCategoryType;
+  auto *RD = Ty->getAsCXXRecordDecl();
+  if (!RD)
+    return nullptr;
+
+  // Check to see if we have information for the specified type cached.
+  const auto *CanonRD = RD->getCanonicalDecl();
+  for (auto &KV : Data) {
+    const ComparisonCategoryInfo &Info = KV.second;
+    if (CanonRD == Info.Record->getCanonicalDecl())
+      return &Info;
+  }
+
+  if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
+    return nullptr;
+
+  // If not, check to see if the decl names a type in namespace std with a name
+  // matching one of the comparison category types.
+  for (unsigned I = static_cast<unsigned>(CCT::First),
+                End = static_cast<unsigned>(CCT::Last);
+       I <= End; ++I) {
+    CCT Kind = static_cast<CCT>(I);
+
+    // We've found the comparison category type. Build a new cache entry for
+    // it.
+    if (getCategoryString(Kind) == RD->getName())
+      return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
+  }
+
+  // We've found nothing. This isn't a comparison category type.
+  return nullptr;
+}
+
+const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
+  const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
+  assert(Info && "info for comparison category not found");
+  return *Info;
+}
+
+QualType ComparisonCategoryInfo::getType() const {
+  assert(Record);
+  return QualType(Record->getTypeForDecl(), 0);
+}
+
+StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
+  using CCKT = ComparisonCategoryType;
+  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";
+  }
+  llvm_unreachable("unhandled case in switch");
+}
+
+std::vector<ComparisonCategoryResult>
+ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
+  using CCT = ComparisonCategoryType;
+  using CCR = ComparisonCategoryResult;
+  std::vector<CCR> Values;
+  Values.reserve(6);
+  Values.push_back(CCR::Equivalent);
+  bool IsStrong = (Type == CCT::StrongEquality || Type == CCT::StrongOrdering);
+  if (IsStrong)
+    Values.push_back(CCR::Equal);
+  if (Type == CCT::StrongOrdering || Type == CCT::WeakOrdering ||
+      Type == CCT::PartialOrdering) {
+    Values.push_back(CCR::Less);
+    Values.push_back(CCR::Greater);
+  } else {
+    Values.push_back(CCR::Nonequivalent);
+    if (IsStrong)
+      Values.push_back(CCR::Nonequal);
+  }
+  if (Type == CCT::PartialOrdering)
+    Values.push_back(CCR::Unordered);
+  return Values;
+}

Modified: cfe/trunk/lib/AST/Expr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/Expr.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/AST/Expr.cpp (original)
+++ cfe/trunk/lib/AST/Expr.cpp Mon May  7 14:07:10 2018
@@ -1633,8 +1633,8 @@ bool CastExpr::CastConsistency() const {
   return true;
 }
 
-const char *CastExpr::getCastKindName() const {
-  switch (getCastKind()) {
+const char *CastExpr::getCastKindName(CastKind CK) {
+  switch (CK) {
 #define CAST_OPERATION(Name) case CK_##Name: return #Name;
 #include "clang/AST/OperationKinds.def"
   }

Modified: cfe/trunk/lib/AST/ExprConstant.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/AST/ExprConstant.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/AST/ExprConstant.cpp (original)
+++ cfe/trunk/lib/AST/ExprConstant.cpp Mon May  7 14:07:10 2018
@@ -2242,6 +2242,8 @@ static bool handleIntIntBinOp(EvalInfo &
   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 @@ public:
   }
 };
 
-}
+} // namespace
 
 //===----------------------------------------------------------------------===//
 // Common base class for lvalue and temporary evaluation.
@@ -6232,6 +6234,8 @@ namespace {
     bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E);
     bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
     bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
+
+    bool VisitBinCmp(const BinaryOperator *E);
   };
 }
 
@@ -7072,11 +7076,11 @@ bool ArrayExprEvaluator::VisitCXXConstru
 
 namespace {
 class IntExprEvaluator
-  : public ExprEvaluatorBase<IntExprEvaluator> {
+        : public ExprEvaluatorBase<IntExprEvaluator> {
   APValue &Result;
 public:
   IntExprEvaluator(EvalInfo &info, APValue &result)
-    : ExprEvaluatorBaseTy(info), Result(result) {}
+      : ExprEvaluatorBaseTy(info), Result(result) {}
 
   bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) {
     assert(E->getType()->isIntegralOrEnumerationType() &&
@@ -7107,7 +7111,7 @@ public:
   }
 
   bool Success(uint64_t Value, const Expr *E, APValue &Result) {
-    assert(E->getType()->isIntegralOrEnumerationType() && 
+    assert(E->getType()->isIntegralOrEnumerationType() &&
            "Invalid evaluation result.");
     Result = APValue(Info.Ctx.MakeIntValue(Value, E->getType()));
     return true;
@@ -8226,10 +8230,8 @@ public:
   /// 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_Comma || E->isLogicalOp() ||
+           (E->isRValue() && E->getType()->isIntegralOrEnumerationType() &&
             E->getLHS()->getType()->isIntegralOrEnumerationType() &&
             E->getRHS()->getType()->isIntegralOrEnumerationType());
   }
@@ -8508,19 +8510,47 @@ public:
 };
 }
 
-bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
-  // We don't call noteFailure immediately because the assignment happens after
-  // we evaluate LHS and RHS.
-  if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
-    return Error(E);
+template <class SuccessCB, class AfterCB>
+static bool
+EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
+                                 SuccessCB &&Success, AfterCB &&DoAfter) {
+  assert(E->isComparisonOp() && "expected comparison operator");
+  assert((E->getOpcode() == BO_Cmp ||
+          E->getType()->isIntegralOrEnumerationType()) &&
+         "unsupported binary expression evaluation");
+  auto Error = [&](const Expr *E) {
+    Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
+    return false;
+  };
 
-  DelayedNoteFailureRAII MaybeNoteFailureLater(Info, E->isAssignmentOp());
-  if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E))
-    return DataRecursiveIntBinOpEvaluator(*this, Result).Traverse(E);
+  using CCR = ComparisonCategoryResult;
+  bool IsRelational = E->isRelationalOp();
+  bool IsEquality = E->isEqualityOp();
+  if (E->getOpcode() == BO_Cmp) {
+    const ComparisonCategoryInfo &CmpInfo =
+        Info.Ctx.CompCategories.getInfoForType(E->getType());
+    IsRelational = CmpInfo.isOrdered();
+    IsEquality = CmpInfo.isEquality();
+  }
 
   QualType LHSTy = E->getLHS()->getType();
   QualType RHSTy = E->getRHS()->getType();
 
+  if (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);
+  }
+
   if (LHSTy->isAnyComplexType() || RHSTy->isAnyComplexType()) {
     ComplexValue LHS, RHS;
     bool LHSOK;
@@ -8553,30 +8583,13 @@ bool IntExprEvaluator::VisitBinaryOperat
         LHS.getComplexFloatReal().compare(RHS.getComplexFloatReal());
       APFloat::cmpResult CR_i =
         LHS.getComplexFloatImag().compare(RHS.getComplexFloatImag());
-
-      if (E->getOpcode() == BO_EQ)
-        return Success((CR_r == APFloat::cmpEqual &&
-                        CR_i == APFloat::cmpEqual), E);
-      else {
-        assert(E->getOpcode() == BO_NE &&
-               "Invalid complex comparison.");
-        return Success(((CR_r == APFloat::cmpGreaterThan ||
-                         CR_r == APFloat::cmpLessThan ||
-                         CR_r == APFloat::cmpUnordered) ||
-                        (CR_i == APFloat::cmpGreaterThan ||
-                         CR_i == APFloat::cmpLessThan ||
-                         CR_i == APFloat::cmpUnordered)), E);
-      }
+      bool IsEqual = CR_r == APFloat::cmpEqual && CR_i == APFloat::cmpEqual;
+      return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E);
     } else {
-      if (E->getOpcode() == BO_EQ)
-        return Success((LHS.getComplexIntReal() == RHS.getComplexIntReal() &&
-                        LHS.getComplexIntImag() == RHS.getComplexIntImag()), E);
-      else {
-        assert(E->getOpcode() == BO_NE &&
-               "Invalid compex comparison.");
-        return Success((LHS.getComplexIntReal() != RHS.getComplexIntReal() ||
-                        LHS.getComplexIntImag() != RHS.getComplexIntImag()), E);
-      }
+      assert(IsEquality && "invalid complex comparison");
+      bool IsEqual = LHS.getComplexIntReal() == RHS.getComplexIntReal() &&
+                     LHS.getComplexIntImag() == RHS.getComplexIntImag();
+      return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E);
     }
   }
 
@@ -8591,243 +8604,160 @@ bool IntExprEvaluator::VisitBinaryOperat
     if (!EvaluateFloat(E->getLHS(), LHS, Info) || !LHSOK)
       return false;
 
-    APFloat::cmpResult CR = LHS.compare(RHS);
-
-    switch (E->getOpcode()) {
-    default:
-      llvm_unreachable("Invalid binary operator!");
-    case BO_LT:
-      return Success(CR == APFloat::cmpLessThan, E);
-    case BO_GT:
-      return Success(CR == APFloat::cmpGreaterThan, E);
-    case BO_LE:
-      return Success(CR == APFloat::cmpLessThan || CR == APFloat::cmpEqual, E);
-    case BO_GE:
-      return Success(CR == APFloat::cmpGreaterThan || CR == APFloat::cmpEqual,
-                     E);
-    case BO_EQ:
-      return Success(CR == APFloat::cmpEqual, E);
-    case BO_NE:
-      return Success(CR == APFloat::cmpGreaterThan
-                     || CR == APFloat::cmpLessThan
-                     || CR == APFloat::cmpUnordered, E);
-    }
+    assert(E->isComparisonOp() && "Invalid binary operator!");
+    auto GetCmpRes = [&]() {
+      switch (LHS.compare(RHS)) {
+      case APFloat::cmpEqual:
+        return CCR::Equal;
+      case APFloat::cmpLessThan:
+        return CCR::Less;
+      case APFloat::cmpGreaterThan:
+        return CCR::Greater;
+      case APFloat::cmpUnordered:
+        return CCR::Unordered;
+      }
+    };
+    return Success(GetCmpRes(), E);
   }
 
   if (LHSTy->isPointerType() && RHSTy->isPointerType()) {
-    if (E->getOpcode() == BO_Sub || E->isComparisonOp()) {
-      LValue LHSValue, RHSValue;
-
-      bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
-      if (!LHSOK && !Info.noteFailure())
-        return false;
-
-      if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
-        return false;
-
-      // Reject differing bases from the normal codepath; we special-case
-      // comparisons to null.
-      if (!HasSameBase(LHSValue, RHSValue)) {
-        if (E->getOpcode() == BO_Sub) {
-          // Handle &&A - &&B.
-          if (!LHSValue.Offset.isZero() || !RHSValue.Offset.isZero())
-            return Error(E);
-          const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr*>();
-          const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr*>();
-          if (!LHSExpr || !RHSExpr)
-            return Error(E);
-          const AddrLabelExpr *LHSAddrExpr = dyn_cast<AddrLabelExpr>(LHSExpr);
-          const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
-          if (!LHSAddrExpr || !RHSAddrExpr)
-            return Error(E);
-          // Make sure both labels come from the same function.
-          if (LHSAddrExpr->getLabel()->getDeclContext() !=
-              RHSAddrExpr->getLabel()->getDeclContext())
-            return Error(E);
-          return Success(APValue(LHSAddrExpr, RHSAddrExpr), E);
-        }
-        // Inequalities and subtractions between unrelated pointers have
-        // unspecified or undefined behavior.
-        if (!E->isEqualityOp())
-          return Error(E);
-        // A constant address may compare equal to the address of a symbol.
-        // The one exception is that address of an object cannot compare equal
-        // to a null pointer constant.
-        if ((!LHSValue.Base && !LHSValue.Offset.isZero()) ||
-            (!RHSValue.Base && !RHSValue.Offset.isZero()))
-          return Error(E);
-        // It's implementation-defined whether distinct literals will have
-        // distinct addresses. In clang, the result of such a comparison is
-        // unspecified, so it is not a constant expression. However, we do know
-        // that the address of a literal will be non-null.
-        if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) &&
-            LHSValue.Base && RHSValue.Base)
-          return Error(E);
-        // We can't tell whether weak symbols will end up pointing to the same
-        // object.
-        if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue))
-          return Error(E);
-        // We can't compare the address of the start of one object with the
-        // past-the-end address of another object, per C++ DR1652.
-        if ((LHSValue.Base && LHSValue.Offset.isZero() &&
-             isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) ||
-            (RHSValue.Base && RHSValue.Offset.isZero() &&
-             isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue)))
-          return Error(E);
-        // We can't tell whether an object is at the same address as another
-        // zero sized object.
-        if ((RHSValue.Base && isZeroSized(LHSValue)) ||
-            (LHSValue.Base && isZeroSized(RHSValue)))
-          return Error(E);
-        // Pointers with different bases cannot represent the same object.
-        return Success(E->getOpcode() == BO_NE, E);
-      }
-
-      const CharUnits &LHSOffset = LHSValue.getLValueOffset();
-      const CharUnits &RHSOffset = RHSValue.getLValueOffset();
-
-      SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
-      SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
-
-      if (E->getOpcode() == BO_Sub) {
-        // C++11 [expr.add]p6:
-        //   Unless both pointers point to elements of the same array object, or
-        //   one past the last element of the array object, the behavior is
-        //   undefined.
-        if (!LHSDesignator.Invalid && !RHSDesignator.Invalid &&
-            !AreElementsOfSameArray(getType(LHSValue.Base),
-                                    LHSDesignator, RHSDesignator))
-          CCEDiag(E, diag::note_constexpr_pointer_subtraction_not_same_array);
+    LValue LHSValue, RHSValue;
 
-        QualType Type = E->getLHS()->getType();
-        QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
+    bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
+    if (!LHSOK && !Info.noteFailure())
+      return false;
 
-        CharUnits ElementSize;
-        if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize))
-          return false;
+    if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
+      return false;
 
-        // As an extension, a type may have zero size (empty struct or union in
-        // C, array of zero length). Pointer subtraction in such cases has
-        // undefined behavior, so is not constant.
-        if (ElementSize.isZero()) {
-          Info.FFDiag(E, diag::note_constexpr_pointer_subtraction_zero_size)
-            << ElementType;
-          return false;
-        }
+    // Reject differing bases from the normal codepath; we special-case
+    // comparisons to null.
+    if (!HasSameBase(LHSValue, RHSValue)) {
+      // Inequalities and subtractions between unrelated pointers have
+      // unspecified or undefined behavior.
+      if (!IsEquality)
+        return Error(E);
+      // A constant address may compare equal to the address of a symbol.
+      // The one exception is that address of an object cannot compare equal
+      // to a null pointer constant.
+      if ((!LHSValue.Base && !LHSValue.Offset.isZero()) ||
+          (!RHSValue.Base && !RHSValue.Offset.isZero()))
+        return Error(E);
+      // It's implementation-defined whether distinct literals will have
+      // distinct addresses. In clang, the result of such a comparison is
+      // unspecified, so it is not a constant expression. However, we do know
+      // that the address of a literal will be non-null.
+      if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) &&
+          LHSValue.Base && RHSValue.Base)
+        return Error(E);
+      // We can't tell whether weak symbols will end up pointing to the same
+      // object.
+      if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue))
+        return Error(E);
+      // We can't compare the address of the start of one object with the
+      // past-the-end address of another object, per C++ DR1652.
+      if ((LHSValue.Base && LHSValue.Offset.isZero() &&
+           isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) ||
+          (RHSValue.Base && RHSValue.Offset.isZero() &&
+           isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue)))
+        return Error(E);
+      // We can't tell whether an object is at the same address as another
+      // zero sized object.
+      if ((RHSValue.Base && isZeroSized(LHSValue)) ||
+          (LHSValue.Base && isZeroSized(RHSValue)))
+        return Error(E);
+      return Success(CCR::Nonequal, E);
+    }
 
-        // FIXME: LLVM and GCC both compute LHSOffset - RHSOffset at runtime,
-        // and produce incorrect results when it overflows. Such behavior
-        // appears to be non-conforming, but is common, so perhaps we should
-        // assume the standard intended for such cases to be undefined behavior
-        // and check for them.
-
-        // Compute (LHSOffset - RHSOffset) / Size carefully, checking for
-        // overflow in the final conversion to ptrdiff_t.
-        APSInt LHS(
-          llvm::APInt(65, (int64_t)LHSOffset.getQuantity(), true), false);
-        APSInt RHS(
-          llvm::APInt(65, (int64_t)RHSOffset.getQuantity(), true), false);
-        APSInt ElemSize(
-          llvm::APInt(65, (int64_t)ElementSize.getQuantity(), true), false);
-        APSInt TrueResult = (LHS - RHS) / ElemSize;
-        APSInt Result = TrueResult.trunc(Info.Ctx.getIntWidth(E->getType()));
+    const CharUnits &LHSOffset = LHSValue.getLValueOffset();
+    const CharUnits &RHSOffset = RHSValue.getLValueOffset();
 
-        if (Result.extend(65) != TrueResult &&
-            !HandleOverflow(Info, E, TrueResult, E->getType()))
-          return false;
-        return Success(Result, E);
-      }
+    SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
+    SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
 
-      // C++11 [expr.rel]p3:
-      //   Pointers to void (after pointer conversions) can be compared, with a
-      //   result defined as follows: If both pointers represent the same
-      //   address or are both the null pointer value, the result is true if the
-      //   operator is <= or >= and false otherwise; otherwise the result is
-      //   unspecified.
-      // We interpret this as applying to pointers to *cv* void.
-      if (LHSTy->isVoidPointerType() && LHSOffset != RHSOffset &&
-          E->isRelationalOp())
-        CCEDiag(E, diag::note_constexpr_void_comparison);
-
-      // C++11 [expr.rel]p2:
-      // - If two pointers point to non-static data members of the same object,
-      //   or to subobjects or array elements fo such members, recursively, the
-      //   pointer to the later declared member compares greater provided the
-      //   two members have the same access control and provided their class is
-      //   not a union.
-      //   [...]
-      // - Otherwise pointer comparisons are unspecified.
-      if (!LHSDesignator.Invalid && !RHSDesignator.Invalid &&
-          E->isRelationalOp()) {
-        bool WasArrayIndex;
-        unsigned Mismatch =
-          FindDesignatorMismatch(getType(LHSValue.Base), LHSDesignator,
-                                 RHSDesignator, WasArrayIndex);
-        // At the point where the designators diverge, the comparison has a
-        // specified value if:
-        //  - we are comparing array indices
-        //  - we are comparing fields of a union, or fields with the same access
-        // Otherwise, the result is unspecified and thus the comparison is not a
-        // constant expression.
-        if (!WasArrayIndex && Mismatch < LHSDesignator.Entries.size() &&
-            Mismatch < RHSDesignator.Entries.size()) {
-          const FieldDecl *LF = getAsField(LHSDesignator.Entries[Mismatch]);
-          const FieldDecl *RF = getAsField(RHSDesignator.Entries[Mismatch]);
-          if (!LF && !RF)
-            CCEDiag(E, diag::note_constexpr_pointer_comparison_base_classes);
-          else if (!LF)
-            CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
+    // C++11 [expr.rel]p3:
+    //   Pointers to void (after pointer conversions) can be compared, with a
+    //   result defined as follows: If both pointers represent the same
+    //   address or are both the null pointer value, the result is true if the
+    //   operator is <= or >= and false otherwise; otherwise the result is
+    //   unspecified.
+    // We interpret this as applying to pointers to *cv* void.
+    if (LHSTy->isVoidPointerType() && LHSOffset != RHSOffset && IsRelational)
+      Info.CCEDiag(E, diag::note_constexpr_void_comparison);
+
+    // C++11 [expr.rel]p2:
+    // - If two pointers point to non-static data members of the same object,
+    //   or to subobjects or array elements fo such members, recursively, the
+    //   pointer to the later declared member compares greater provided the
+    //   two members have the same access control and provided their class is
+    //   not a union.
+    //   [...]
+    // - Otherwise pointer comparisons are unspecified.
+    if (!LHSDesignator.Invalid && !RHSDesignator.Invalid && IsRelational) {
+      bool WasArrayIndex;
+      unsigned Mismatch = FindDesignatorMismatch(
+          getType(LHSValue.Base), LHSDesignator, RHSDesignator, WasArrayIndex);
+      // At the point where the designators diverge, the comparison has a
+      // specified value if:
+      //  - we are comparing array indices
+      //  - we are comparing fields of a union, or fields with the same access
+      // Otherwise, the result is unspecified and thus the comparison is not a
+      // constant expression.
+      if (!WasArrayIndex && Mismatch < LHSDesignator.Entries.size() &&
+          Mismatch < RHSDesignator.Entries.size()) {
+        const FieldDecl *LF = getAsField(LHSDesignator.Entries[Mismatch]);
+        const FieldDecl *RF = getAsField(RHSDesignator.Entries[Mismatch]);
+        if (!LF && !RF)
+          Info.CCEDiag(E, diag::note_constexpr_pointer_comparison_base_classes);
+        else if (!LF)
+          Info.CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
               << getAsBaseClass(LHSDesignator.Entries[Mismatch])
               << RF->getParent() << RF;
-          else if (!RF)
-            CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
+        else if (!RF)
+          Info.CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
               << getAsBaseClass(RHSDesignator.Entries[Mismatch])
               << LF->getParent() << LF;
-          else if (!LF->getParent()->isUnion() &&
-                   LF->getAccess() != RF->getAccess())
-            CCEDiag(E, diag::note_constexpr_pointer_comparison_differing_access)
+        else if (!LF->getParent()->isUnion() &&
+                 LF->getAccess() != RF->getAccess())
+          Info.CCEDiag(E,
+                       diag::note_constexpr_pointer_comparison_differing_access)
               << LF << LF->getAccess() << RF << RF->getAccess()
               << LF->getParent();
-        }
-      }
-
-      // The comparison here must be unsigned, and performed with the same
-      // width as the pointer.
-      unsigned PtrSize = Info.Ctx.getTypeSize(LHSTy);
-      uint64_t CompareLHS = LHSOffset.getQuantity();
-      uint64_t CompareRHS = RHSOffset.getQuantity();
-      assert(PtrSize <= 64 && "Unexpected pointer width");
-      uint64_t Mask = ~0ULL >> (64 - PtrSize);
-      CompareLHS &= Mask;
-      CompareRHS &= Mask;
-
-      // If there is a base and this is a relational operator, we can only
-      // compare pointers within the object in question; otherwise, the result
-      // depends on where the object is located in memory.
-      if (!LHSValue.Base.isNull() && E->isRelationalOp()) {
-        QualType BaseTy = getType(LHSValue.Base);
-        if (BaseTy->isIncompleteType())
-          return Error(E);
-        CharUnits Size = Info.Ctx.getTypeSizeInChars(BaseTy);
-        uint64_t OffsetLimit = Size.getQuantity();
-        if (CompareLHS > OffsetLimit || CompareRHS > OffsetLimit)
-          return Error(E);
       }
+    }
 
-      switch (E->getOpcode()) {
-      default: llvm_unreachable("missing comparison operator");
-      case BO_LT: return Success(CompareLHS < CompareRHS, E);
-      case BO_GT: return Success(CompareLHS > CompareRHS, E);
-      case BO_LE: return Success(CompareLHS <= CompareRHS, E);
-      case BO_GE: return Success(CompareLHS >= CompareRHS, E);
-      case BO_EQ: return Success(CompareLHS == CompareRHS, E);
-      case BO_NE: return Success(CompareLHS != CompareRHS, E);
-      }
+    // The comparison here must be unsigned, and performed with the same
+    // width as the pointer.
+    unsigned PtrSize = Info.Ctx.getTypeSize(LHSTy);
+    uint64_t CompareLHS = LHSOffset.getQuantity();
+    uint64_t CompareRHS = RHSOffset.getQuantity();
+    assert(PtrSize <= 64 && "Unexpected pointer width");
+    uint64_t Mask = ~0ULL >> (64 - PtrSize);
+    CompareLHS &= Mask;
+    CompareRHS &= Mask;
+
+    // If there is a base and this is a relational operator, we can only
+    // compare pointers within the object in question; otherwise, the result
+    // depends on where the object is located in memory.
+    if (!LHSValue.Base.isNull() && IsRelational) {
+      QualType BaseTy = getType(LHSValue.Base);
+      if (BaseTy->isIncompleteType())
+        return Error(E);
+      CharUnits Size = Info.Ctx.getTypeSizeInChars(BaseTy);
+      uint64_t OffsetLimit = Size.getQuantity();
+      if (CompareLHS > OffsetLimit || CompareRHS > OffsetLimit)
+        return Error(E);
     }
+
+    if (CompareLHS < CompareRHS)
+      return Success(CCR::Less, E);
+    if (CompareLHS > CompareRHS)
+      return Success(CCR::Greater, E);
+    return Success(CCR::Equal, E);
   }
 
   if (LHSTy->isMemberPointerType()) {
-    assert(E->isEqualityOp() && "unexpected member pointer operation");
+    assert(IsEquality && "unexpected member pointer operation");
     assert(RHSTy->isMemberPointerType() && "invalid comparison");
 
     MemberPtr LHSValue, RHSValue;
@@ -8844,24 +8774,24 @@ bool IntExprEvaluator::VisitBinaryOperat
     //   null, they compare unequal.
     if (!LHSValue.getDecl() || !RHSValue.getDecl()) {
       bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl();
-      return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E);
+      return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
     }
 
     //   Otherwise if either is a pointer to a virtual member function, the
     //   result is unspecified.
     if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(LHSValue.getDecl()))
       if (MD->isVirtual())
-        CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
+        Info.CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
     if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(RHSValue.getDecl()))
       if (MD->isVirtual())
-        CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
+        Info.CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
 
     //   Otherwise they compare equal if and only if they would refer to the
     //   same member of the same most derived object or the same subobject if
     //   they were dereferenced with a hypothetical object of the associated
     //   class type.
     bool Equal = LHSValue == RHSValue;
-    return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E);
+    return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
   }
 
   if (LHSTy->isNullPtrType()) {
@@ -8870,14 +8800,163 @@ bool IntExprEvaluator::VisitBinaryOperat
     // C++11 [expr.rel]p4, [expr.eq]p3: If two operands of type std::nullptr_t
     // are compared, the result is true of the operator is <=, >= or ==, and
     // false otherwise.
-    BinaryOperator::Opcode Opcode = E->getOpcode();
-    return Success(Opcode == BO_EQ || Opcode == BO_LE || Opcode == BO_GE, E);
+    return Success(CCR::Equal, E);
   }
 
-  assert((!LHSTy->isIntegralOrEnumerationType() ||
-          !RHSTy->isIntegralOrEnumerationType()) &&
+  return DoAfter();
+}
+
+bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) {
+  if (!CheckLiteralType(Info, E))
+    return false;
+
+  auto OnSuccess = [&](ComparisonCategoryResult ResKind,
+                       const BinaryOperator *E) {
+    // Evaluation succeeded. Lookup the information for the comparison category
+    // type and fetch the VarDecl for the result.
+    const ComparisonCategoryInfo &CmpInfo =
+        Info.Ctx.CompCategories.getInfoForType(E->getType());
+    const VarDecl *VD =
+        CmpInfo.getValueInfo(CmpInfo.makeWeakResult(ResKind))->VD;
+    // Check and evaluate the result as a constant expression.
+    LValue LV;
+    LV.set(VD);
+    if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
+      return false;
+    return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result);
+  };
+  return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
+    return ExprEvaluatorBaseTy::VisitBinCmp(E);
+  });
+}
+
+bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
+  // We don't call noteFailure immediately because the assignment happens after
+  // we evaluate LHS and RHS.
+  if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
+    return Error(E);
+
+  DelayedNoteFailureRAII MaybeNoteFailureLater(Info, E->isAssignmentOp());
+  if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E))
+    return DataRecursiveIntBinOpEvaluator(*this, Result).Traverse(E);
+
+  assert((!E->getLHS()->getType()->isIntegralOrEnumerationType() ||
+          !E->getRHS()->getType()->isIntegralOrEnumerationType()) &&
          "DataRecursiveIntBinOpEvaluator should have handled integral types");
-  // We can't continue from here for non-integral types.
+
+  if (E->isComparisonOp()) {
+    // Evaluate builtin binary comparisons by evaluating them as C++2a three-way
+    // comparisons and then translating the result.
+    auto OnSuccess = [&](ComparisonCategoryResult ResKind,
+                         const BinaryOperator *E) {
+      using CCR = ComparisonCategoryResult;
+      bool IsEqual   = ResKind == CCR::Equal,
+           IsLess    = ResKind == CCR::Less,
+           IsGreater = ResKind == CCR::Greater;
+      auto Op = E->getOpcode();
+      switch (Op) {
+      default:
+        llvm_unreachable("unsupported binary operator");
+      case BO_EQ:
+      case BO_NE:
+        return Success(IsEqual == (Op == BO_EQ), E);
+      case BO_LT: return Success(IsLess, E);
+      case BO_GT: return Success(IsGreater, E);
+      case BO_LE: return Success(IsEqual || IsLess, E);
+      case BO_GE: return Success(IsEqual || IsGreater, E);
+      }
+    };
+    return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
+      return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
+    });
+  }
+
+  QualType LHSTy = E->getLHS()->getType();
+  QualType RHSTy = E->getRHS()->getType();
+
+  if (LHSTy->isPointerType() && RHSTy->isPointerType() &&
+      E->getOpcode() == BO_Sub) {
+    LValue LHSValue, RHSValue;
+
+    bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
+    if (!LHSOK && !Info.noteFailure())
+      return false;
+
+    if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
+      return false;
+
+    // Reject differing bases from the normal codepath; we special-case
+    // comparisons to null.
+    if (!HasSameBase(LHSValue, RHSValue)) {
+      // Handle &&A - &&B.
+      if (!LHSValue.Offset.isZero() || !RHSValue.Offset.isZero())
+        return Error(E);
+      const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr *>();
+      const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr *>();
+      if (!LHSExpr || !RHSExpr)
+        return Error(E);
+      const AddrLabelExpr *LHSAddrExpr = dyn_cast<AddrLabelExpr>(LHSExpr);
+      const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
+      if (!LHSAddrExpr || !RHSAddrExpr)
+        return Error(E);
+      // Make sure both labels come from the same function.
+      if (LHSAddrExpr->getLabel()->getDeclContext() !=
+          RHSAddrExpr->getLabel()->getDeclContext())
+        return Error(E);
+      return Success(APValue(LHSAddrExpr, RHSAddrExpr), E);
+    }
+    const CharUnits &LHSOffset = LHSValue.getLValueOffset();
+    const CharUnits &RHSOffset = RHSValue.getLValueOffset();
+
+    SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
+    SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
+
+    // C++11 [expr.add]p6:
+    //   Unless both pointers point to elements of the same array object, or
+    //   one past the last element of the array object, the behavior is
+    //   undefined.
+    if (!LHSDesignator.Invalid && !RHSDesignator.Invalid &&
+        !AreElementsOfSameArray(getType(LHSValue.Base), LHSDesignator,
+                                RHSDesignator))
+      Info.CCEDiag(E, diag::note_constexpr_pointer_subtraction_not_same_array);
+
+    QualType Type = E->getLHS()->getType();
+    QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
+
+    CharUnits ElementSize;
+    if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize))
+      return false;
+
+    // As an extension, a type may have zero size (empty struct or union in
+    // C, array of zero length). Pointer subtraction in such cases has
+    // undefined behavior, so is not constant.
+    if (ElementSize.isZero()) {
+      Info.FFDiag(E, diag::note_constexpr_pointer_subtraction_zero_size)
+          << ElementType;
+      return false;
+    }
+
+    // FIXME: LLVM and GCC both compute LHSOffset - RHSOffset at runtime,
+    // and produce incorrect results when it overflows. Such behavior
+    // appears to be non-conforming, but is common, so perhaps we should
+    // assume the standard intended for such cases to be undefined behavior
+    // and check for them.
+
+    // Compute (LHSOffset - RHSOffset) / Size carefully, checking for
+    // overflow in the final conversion to ptrdiff_t.
+    APSInt LHS(llvm::APInt(65, (int64_t)LHSOffset.getQuantity(), true), false);
+    APSInt RHS(llvm::APInt(65, (int64_t)RHSOffset.getQuantity(), true), false);
+    APSInt ElemSize(llvm::APInt(65, (int64_t)ElementSize.getQuantity(), true),
+                    false);
+    APSInt TrueResult = (LHS - RHS) / ElemSize;
+    APSInt Result = TrueResult.trunc(Info.Ctx.getIntWidth(E->getType()));
+
+    if (Result.extend(65) != TrueResult &&
+        !HandleOverflow(Info, E, TrueResult, E->getType()))
+      return false;
+    return Success(Result, E);
+  }
+
   return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
 }
 
@@ -10620,7 +10699,6 @@ static ICEDiag CheckICE(const Expr* E, c
     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.
@@ -10642,7 +10720,8 @@ static ICEDiag CheckICE(const Expr* E, c
     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 ||

Modified: cfe/trunk/lib/CodeGen/CGExprAgg.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGExprAgg.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/CodeGen/CGExprAgg.cpp (original)
+++ cfe/trunk/lib/CodeGen/CGExprAgg.cpp Mon May  7 14:07:10 2018
@@ -12,6 +12,7 @@
 //===----------------------------------------------------------------------===//
 
 #include "CodeGenFunction.h"
+#include "CGCXXABI.h"
 #include "CGObjCRuntime.h"
 #include "CodeGenModule.h"
 #include "ConstantEmitter.h"
@@ -145,6 +146,7 @@ public:
   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) {
@@ -879,6 +881,149 @@ void AggExprEmitter::VisitStmtExpr(const
   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,
+                                const char *NameSuffix = "") {
+  QualType ArgTy = E->getLHS()->getType();
+  if (const ComplexType *CT = ArgTy->getAs<ComplexType>())
+    ArgTy = CT->getElementType();
+
+  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->hasFloatingRepresentation())
+    return Builder.CreateFCmp(InstInfo.FCmp, LHS, RHS,
+                              llvm::Twine(InstInfo.Name) + NameSuffix);
+  if (ArgTy->isIntegralOrEnumerationType() || ArgTy->isPointerType()) {
+    auto Inst =
+        ArgTy->hasSignedIntegerRepresentation() ? InstInfo.SCmp : InstInfo.UCmp;
+    return Builder.CreateICmp(Inst, LHS, RHS,
+                              llvm::Twine(InstInfo.Name) + NameSuffix);
+  }
+
+  llvm_unreachable("unsupported aggregate binary expression should have "
+                   "already been handled");
+}
+
+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());
+  assert(CmpInfo.Record->isTriviallyCopyable() &&
+         "cannot copy non-trivially copyable aggregate");
+
+  QualType ArgTy = E->getLHS()->getType();
+
+  // TODO: Handle comparing these types.
+  if (ArgTy->isVectorType())
+    return CGF.ErrorUnsupported(
+        E, "aggregate three-way comparison with vector arguments");
+  if (!ArgTy->isIntegralOrEnumerationType() && !ArgTy->isRealFloatingType() &&
+      !ArgTy->isNullPtrType() && !ArgTy->isPointerType() &&
+      !ArgTy->isMemberPointerType() && !ArgTy->isAnyComplexType()) {
+    return CGF.ErrorUnsupported(E, "aggregate three-way comparisoaoeun");
+  }
+  bool IsComplex = ArgTy->isAnyComplexType();
+
+  // Evaluate the operands to the expression and extract their values.
+  auto EmitOperand = [&](Expr *E) -> std::pair<Value *, Value *> {
+    RValue RV = CGF.EmitAnyExpr(E);
+    if (RV.isScalar())
+      return {RV.getScalarVal(), nullptr};
+    if (RV.isAggregate())
+      return {RV.getAggregatePointer(), nullptr};
+    assert(RV.isComplex());
+    return RV.getComplexVal();
+  };
+  auto LHSValues = EmitOperand(E->getLHS()),
+       RHSValues = EmitOperand(E->getRHS());
+
+  auto EmitCmp = [&](CompareKind K) {
+    Value *Cmp = EmitCompare(Builder, CGF, E, LHSValues.first, RHSValues.first,
+                             K, IsComplex ? ".r" : "");
+    if (!IsComplex)
+      return Cmp;
+    assert(K == CompareKind::CK_Equal);
+    Value *CmpImag = EmitCompare(Builder, CGF, E, LHSValues.second,
+                                 RHSValues.second, K, ".i");
+    return Builder.CreateAnd(Cmp, CmpImag, "and.eq");
+  };
+  auto EmitCmpRes = [&](const ComparisonCategoryInfo::ValueInfo *VInfo) {
+    return Builder.getInt(VInfo->getIntValue());
+  };
+
+  Value *Select;
+  if (ArgTy->isNullPtrType()) {
+    Select = EmitCmpRes(CmpInfo.getEqualOrEquiv());
+  } else 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");
+  }
+  // Create the return value in the destination slot.
+  EnsureDest(E->getType());
+  LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
+
+  // Emit the address of the first (and only) field in the comparison category
+  // type, and initialize it from the constant integer value selected above.
+  LValue FieldLV = CGF.EmitLValueForFieldInitialization(
+      DestLV, *CmpInfo.Record->field_begin());
+  CGF.EmitStoreThroughLValue(RValue::get(Select), FieldLV, /*IsInit*/ true);
+
+  // All done! The result is in the Dest slot.
+}
+
 void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) {
   if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI)
     VisitPointerToDataMemberBinaryOperator(E);

Modified: cfe/trunk/lib/Sema/Sema.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.cpp (original)
+++ cfe/trunk/lib/Sema/Sema.cpp Mon May  7 14:07:10 2018
@@ -137,10 +137,13 @@ Sema::Sema(Preprocessor &pp, ASTContext
       ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
       ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
       DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),
-      TUKind(TUKind), NumSFINAEErrors(0), AccessCheckingSFINAE(false),
-      InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
-      ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr),
-      DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this),
+      TUKind(TUKind), NumSFINAEErrors(0),
+      FullyCheckedComparisonCategories(
+          static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
+      AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
+      NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
+      CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
+      TyposCorrected(0), AnalysisWarnings(*this),
       ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr),
       CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) {
   TUScope = nullptr;

Modified: cfe/trunk/lib/Sema/SemaDeclCXX.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaDeclCXX.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaDeclCXX.cpp (original)
+++ cfe/trunk/lib/Sema/SemaDeclCXX.cpp Mon May  7 14:07:10 2018
@@ -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"
@@ -8891,6 +8892,134 @@ NamespaceDecl *Sema::lookupStdExperiment
   return StdExperimentalNamespaceCache;
 }
 
+namespace {
+
+enum UnsupportedSTLSelect {
+  USS_InvalidMember,
+  USS_MissingMember,
+  USS_NonTrivial,
+  USS_Other
+};
+
+struct InvalidSTLDiagnoser {
+  Sema &S;
+  SourceLocation Loc;
+  QualType TyForDiags;
+
+  QualType operator()(UnsupportedSTLSelect Sel = USS_Other, StringRef Name = "",
+                      const VarDecl *VD = nullptr) {
+    {
+      auto D = S.Diag(Loc, diag::err_std_compare_type_not_supported)
+               << TyForDiags << ((int)Sel);
+      if (Sel == USS_InvalidMember || Sel == USS_MissingMember) {
+        assert(!Name.empty());
+        D << Name;
+      }
+    }
+    if (Sel == USS_InvalidMember) {
+      S.Diag(VD->getLocation(), diag::note_var_declared_here)
+          << VD << VD->getSourceRange();
+    }
+    return QualType();
+  }
+};
+} // namespace
+
+QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind,
+                                           SourceLocation Loc) {
+  assert(getLangOpts().CPlusPlus &&
+         "Looking for comparison category type outside of C++.");
+
+  // Check if we've already successfully checked the comparison category type
+  // before. If so, skip checking it again.
+  ComparisonCategoryInfo *Info = Context.CompCategories.lookupInfo(Kind);
+  if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)])
+    return Info->getType();
+
+  // If lookup failed
+  if (!Info) {
+    Diag(Loc, diag::err_implied_comparison_category_type_not_found)
+        << ComparisonCategories::getCategoryString(Kind);
+    return QualType();
+  }
+
+  assert(Info->Kind == Kind);
+  assert(Info->Record);
+
+  // Update the Record decl in case we encountered a forward declaration on our
+  // first pass. FIXME(EricWF): This is a bit of a hack.
+  if (Info->Record->hasDefinition())
+    Info->Record = Info->Record->getDefinition();
+
+  // Use an elaborated type for diagnostics which has a name containing the
+  // prepended 'std' namespace but not any inline namespace names.
+  QualType TyForDiags = [&]() {
+    auto *NNS =
+        NestedNameSpecifier::Create(Context, nullptr, getStdNamespace());
+    return Context.getElaboratedType(ETK_None, NNS, Info->getType());
+  }();
+
+  if (RequireCompleteType(Loc, TyForDiags, diag::err_incomplete_type))
+    return QualType();
+
+  InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags};
+
+  if (!Info->Record->isTriviallyCopyable())
+    return UnsupportedSTLError(USS_NonTrivial);
+
+  for (const CXXBaseSpecifier &BaseSpec : Info->Record->bases()) {
+    CXXRecordDecl *Base = BaseSpec.getType()->getAsCXXRecordDecl();
+    // Tolerate empty base classes.
+    if (Base->isEmpty())
+      continue;
+    // Reject STL implementations which have at least one non-empty base.
+    return UnsupportedSTLError();
+  }
+
+  // Check that the STL has implemented the types using a single integer field.
+  // This expectation allows better codegen for builtin operators. We require:
+  //   (1) The class has exactly one field.
+  //   (2) The field is an integral or enumeration type.
+  auto FIt = Info->Record->field_begin(), FEnd = Info->Record->field_end();
+  if (std::distance(FIt, FEnd) != 1 ||
+      !FIt->getType()->isIntegralOrEnumerationType()) {
+    return UnsupportedSTLError();
+  }
+
+  // Build each of the require values and store them in Info.
+  for (ComparisonCategoryResult CCR :
+       ComparisonCategories::getPossibleResultsForType(Kind)) {
+    StringRef MemName = ComparisonCategories::getResultString(CCR);
+    ComparisonCategoryInfo::ValueInfo *ValInfo = Info->lookupValueInfo(CCR);
+
+    if (!ValInfo)
+      return UnsupportedSTLError(USS_MissingMember, MemName);
+
+    VarDecl *VD = ValInfo->VD;
+    assert(VD && "should not be null!");
+
+    // Attempt to diagnose reasons why the STL definition of this type
+    // might be foobar, including it failing to be a constant expression.
+    // TODO Handle more ways the lookup or result can be invalid.
+    if (!VD->isStaticDataMember() || !VD->isConstexpr() || !VD->hasInit() ||
+        !VD->checkInitIsICE())
+      return UnsupportedSTLError(USS_InvalidMember, MemName, VD);
+
+    // Attempt to evaluate the var decl as a constant expression and extract
+    // the value of its first field as a ICE. If this fails, the STL
+    // implementation is not supported.
+    if (!ValInfo->hasValidIntValue())
+      return UnsupportedSTLError();
+
+    MarkVariableReferenced(Loc, VD);
+  }
+
+  // We've successfully built the required types and expressions. Update
+  // the cache and return the newly cached value.
+  FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)] = true;
+  return Info->getType();
+}
+
 /// \brief Retrieve the special "std" namespace, which may require us to
 /// implicitly define the namespace.
 NamespaceDecl *Sema::getOrCreateStdNamespace() {

Modified: cfe/trunk/lib/Sema/SemaExpr.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaExpr.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaExpr.cpp (original)
+++ cfe/trunk/lib/Sema/SemaExpr.cpp Mon May  7 14:07:10 2018
@@ -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"
@@ -9619,12 +9620,18 @@ static void diagnoseTautologicalComparis
   Expr *RHSStripped = RHS->IgnoreParenImpCasts();
 
   QualType LHSType = LHS->getType();
+  QualType RHSType = RHS->getType();
   if (LHSType->hasFloatingRepresentation() ||
       (LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) ||
       LHS->getLocStart().isMacroID() || RHS->getLocStart().isMacroID() ||
       S.inTemplateInstantiation())
     return;
 
+  // Comparisons between two array types are ill-formed for operator<=>, so
+  // we shouldn't emit any additional warnings about it.
+  if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType())
+    return;
+
   // For non-floating point types, check for self-comparisons of the form
   // x == x, x != x, x < x, etc.  These always evaluate to a constant, and
   // often indicate logic errors in the program.
@@ -9708,10 +9715,183 @@ static void diagnoseTautologicalComparis
   }
 }
 
+static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) {
+  switch (CK) {
+  default: {
+#ifndef NDEBUG
+    llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK)
+                 << "\n";
+#endif
+    llvm_unreachable("unhandled cast kind");
+  }
+  case CK_UserDefinedConversion:
+    return ICK_Identity;
+  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;
+  case CK_IntegralComplexCast:
+  case CK_FloatingComplexCast:
+  case CK_FloatingComplexToIntegralComplex:
+  case CK_IntegralComplexToFloatingComplex:
+    return ICK_Complex_Conversion;
+  case CK_FloatingComplexToReal:
+  case CK_FloatingRealToComplex:
+  case CK_IntegralComplexToReal:
+  case CK_IntegralRealToComplex:
+    return ICK_Complex_Real;
+  }
+}
+
+static bool checkThreeWayNarrowingConversion(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:
+    // Implicit conversion to a narrower type, and the value is not a constant
+    // expression.
+    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.
+  case NK_Type_Narrowing:
+    S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing)
+        << /*Constant*/ 0 << FromType << ToType;
+    // TODO: It's not a constant expression, but what if the user intended it
+    // to be? Can we produce notes to help them figure out why it isn't?
+    return true;
+  }
+  llvm_unreachable("unhandled case in switch");
+}
+
+static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
+                                                         ExprResult &LHS,
+                                                         ExprResult &RHS,
+                                                         SourceLocation Loc) {
+  using CCT = ComparisonCategoryType;
+
+  QualType LHSType = LHS.get()->getType();
+  QualType RHSType = RHS.get()->getType();
+  // Dig out the original argument type and expression before implicit casts
+  // were applied. These are the types/expressions we need to check the
+  // [expr.spaceship] requirements against.
+  ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts();
+  ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts();
+  QualType LHSStrippedType = LHSStripped.get()->getType();
+  QualType RHSStrippedType = RHSStripped.get()->getType();
+
+  // C++2a [expr.spaceship]p3: If one of the operands is of type bool and the
+  // other is not, the program is ill-formed.
+  if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) {
+    S.InvalidOperands(Loc, LHSStripped, RHSStripped);
+    return QualType();
+  }
+
+  int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() +
+                    RHSStrippedType->isEnumeralType();
+  if (NumEnumArgs == 1) {
+    bool LHSIsEnum = LHSStrippedType->isEnumeralType();
+    QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType;
+    if (OtherTy->hasFloatingRepresentation()) {
+      S.InvalidOperands(Loc, LHSStripped, RHSStripped);
+      return QualType();
+    }
+  }
+  if (NumEnumArgs == 2) {
+    // C++2a [expr.spaceship]p5: If both operands have the same enumeration
+    // type E, the operator yields the result of converting the operands
+    // to the underlying type of E and applying <=> to the converted operands.
+    if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) {
+      S.InvalidOperands(Loc, LHSStripped, RHSStripped);
+      return QualType();
+    }
+    QualType IntType =
+        LHSStrippedType->getAs<EnumType>()->getDecl()->getIntegerType();
+    assert(IntType->isArithmeticType());
+
+    // We can't use `CK_IntegralCast` when the underlying type is 'bool', so we
+    // promote the boolean type, and all other promotable integer types, to
+    // avoid this.
+    if (IntType->isPromotableIntegerType())
+      IntType = S.Context.getPromotedIntegerType(IntType);
+
+    LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast);
+    RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast);
+    LHSType = RHSType = IntType;
+  }
+
+  // C++2a [expr.spaceship]p4: If both operands have arithmetic types, the
+  // usual arithmetic conversions are applied to the operands.
+  QualType Type = S.UsualArithmeticConversions(LHS, RHS);
+  if (LHS.isInvalid() || RHS.isInvalid())
+    return QualType();
+  if (Type.isNull())
+    return S.InvalidOperands(Loc, LHS, RHS);
+  assert(Type->isArithmeticType() || Type->isEnumeralType());
+
+  bool HasNarrowing = checkThreeWayNarrowingConversion(
+      S, Type, LHS.get(), LHSType, LHS.get()->getLocStart());
+  HasNarrowing |= checkThreeWayNarrowingConversion(
+      S, Type, RHS.get(), RHSType, RHS.get()->getLocStart());
+  if (HasNarrowing)
+    return QualType();
+
+  assert(!Type.isNull() && "composite type for <=> has not been set");
+
+  auto TypeKind = [&]() {
+    if (const ComplexType *CT = Type->getAs<ComplexType>()) {
+      if (CT->getElementType()->hasFloatingRepresentation())
+        return CCT::WeakEquality;
+      return CCT::StrongEquality;
+    }
+    if (Type->isIntegralOrEnumerationType())
+      return CCT::StrongOrdering;
+    if (Type->hasFloatingRepresentation())
+      return CCT::PartialOrdering;
+    llvm_unreachable("other types are unimplemented");
+  }();
+
+  return S.CheckComparisonCategoryType(TypeKind, Loc);
+}
+
 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())
@@ -9722,15 +9902,7 @@ static QualType checkArithmeticOrEnumera
 
   checkEnumComparison(S, Loc, LHS.get(), RHS.get());
 
-  enum { StrongEquality, PartialOrdering, StrongOrdering } Ordering;
-  if (Type->isAnyComplexType())
-    Ordering = StrongEquality;
-  else if (Type->isFloatingType())
-    Ordering = PartialOrdering;
-  else
-    Ordering = StrongOrdering;
-
-  if (Ordering == StrongEquality && BinaryOperator::isRelationalOp(Opc))
+  if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc))
     return S.InvalidOperands(Loc, LHS, RHS);
 
   // Check for comparisons of floating point operands using != and ==.
@@ -9738,22 +9910,40 @@ static QualType checkArithmeticOrEnumera
     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();
 }
 
 // C99 6.5.8, C++ [expr.rel]
 QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
-                                    SourceLocation Loc, BinaryOperatorKind Opc,
-                                    bool IsRelational) {
-  // Comparisons expect an rvalue, so convert to rvalue before any
-  // type-related checks.
-  LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
-  if (LHS.isInvalid())
-    return QualType();
-  RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
-  if (RHS.isInvalid())
-    return QualType();
+                                    SourceLocation Loc,
+                                    BinaryOperatorKind Opc) {
+  bool IsRelational = BinaryOperator::isRelationalOp(Opc);
+  bool IsThreeWay = Opc == BO_Cmp;
+  auto IsAnyPointerType = [](ExprResult E) {
+    QualType Ty = E.get()->getType();
+    return Ty->isPointerType() || Ty->isMemberPointerType();
+  };
+
+  // C++2a [expr.spaceship]p6: If at least one of the operands is of pointer
+  // type, array-to-pointer, ..., conversions are performed on both operands to
+  // bring them to their composite type.
+  // Otherwise, all comparisons expect an rvalue, so convert to rvalue before
+  // any type-related checks.
+  if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) {
+    LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
+    if (LHS.isInvalid())
+      return QualType();
+    RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
+    if (RHS.isInvalid())
+      return QualType();
+  } else {
+    LHS = DefaultLvalueConversion(LHS.get());
+    if (LHS.isInvalid())
+      return QualType();
+    RHS = DefaultLvalueConversion(RHS.get());
+    if (RHS.isInvalid())
+      return QualType();
+  }
 
   checkArithmeticNull(*this, LHS, RHS, Loc, /*isCompare=*/true);
 
@@ -9771,8 +9961,6 @@ QualType Sema::CheckCompareOperands(Expr
       (RHSType->isArithmeticType() || RHSType->isEnumeralType()))
     return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc);
 
-  QualType ResultTy = Context.getLogicalOperationType();
-
   const Expr::NullPointerConstantKind LHSNullKind =
       LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull);
   const Expr::NullPointerConstantKind RHSNullKind =
@@ -9780,6 +9968,44 @@ QualType Sema::CheckCompareOperands(Expr
   bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull;
   bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull;
 
+  auto computeResultTy = [&]() {
+    if (Opc != BO_Cmp)
+      return Context.getLogicalOperationType();
+    assert(getLangOpts().CPlusPlus);
+    assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType()));
+
+    QualType CompositeTy = LHS.get()->getType();
+    assert(!CompositeTy->isReferenceType());
+
+    auto buildResultTy = [&](ComparisonCategoryType Kind) {
+      return CheckComparisonCategoryType(Kind, Loc);
+    };
+
+    // C++2a [expr.spaceship]p7: If the composite pointer type is a function
+    // pointer type, a pointer-to-member type, or std::nullptr_t, the
+    // result is of type std::strong_equality
+    if (CompositeTy->isFunctionPointerType() ||
+        CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType())
+      // FIXME: consider making the function pointer case produce
+      // strong_ordering not strong_equality, per P0946R0-Jax18 discussion
+      // and direction polls
+      return buildResultTy(ComparisonCategoryType::StrongEquality);
+
+    // C++2a [expr.spaceship]p8: If the composite pointer type is an object
+    // pointer type, p <=> q is of type std::strong_ordering.
+    if (CompositeTy->isPointerType()) {
+      // P0946R0: Comparisons between a null pointer constant and an object
+      // pointer result in std::strong_equality
+      if (LHSIsNull != RHSIsNull)
+        return buildResultTy(ComparisonCategoryType::StrongEquality);
+      return buildResultTy(ComparisonCategoryType::StrongOrdering);
+    }
+    // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed.
+    // TODO: Extend support for operator<=> to ObjC types.
+    return InvalidOperands(Loc, LHS, RHS);
+  };
+
+
   if (!IsRelational && LHSIsNull != RHSIsNull) {
     bool IsEquality = Opc == BO_EQ;
     if (RHSIsNull)
@@ -9807,29 +10033,30 @@ QualType Sema::CheckCompareOperands(Expr
       // conformance with the C++ standard.
       diagnoseFunctionPointerToVoidComparison(
           *this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext());
-      
+
       if (isSFINAEContext())
         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.
     if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >=
             (IsRelational ? 2 : 1) &&
-        (!LangOpts.ObjCAutoRefCount ||
-         !(LHSType->isObjCObjectPointerType() ||
-           RHSType->isObjCObjectPointerType()))) {
+        (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() ||
+                                         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
@@ -9880,7 +10107,7 @@ QualType Sema::CheckCompareOperands(Expr
       else
         RHS = ImpCastExprToType(RHS.get(), LHSType, Kind);
     }
-    return ResultTy;
+    return computeResultTy();
   }
 
   if (getLangOpts().CPlusPlus) {
@@ -9890,11 +10117,11 @@ QualType Sema::CheckCompareOperands(Expr
     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();
       }
     }
 
@@ -9903,12 +10130,12 @@ QualType Sema::CheckCompareOperands(Expr
     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 &&
@@ -9931,7 +10158,7 @@ QualType Sema::CheckCompareOperands(Expr
             RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
           else
             LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
-          return ResultTy;
+          return computeResultTy();
         }
       }
     }
@@ -9944,7 +10171,7 @@ QualType Sema::CheckCompareOperands(Expr
       if (convertPointersToCompositeType(*this, Loc, LHS, RHS))
         return QualType();
       else
-        return ResultTy;
+        return computeResultTy();
     }
   }
 
@@ -9961,7 +10188,7 @@ QualType Sema::CheckCompareOperands(Expr
         << RHS.get()->getSourceRange();
     }
     RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
-    return ResultTy;
+    return computeResultTy();
   }
 
   // Allow block pointers to be compared with null pointer constants.
@@ -9985,7 +10212,7 @@ QualType Sema::CheckCompareOperands(Expr
       RHS = ImpCastExprToType(RHS.get(), LHSType,
                               LHSType->isPointerType() ? CK_BitCast
                                 : CK_AnyPointerToBlockPointerCast);
-    return ResultTy;
+    return computeResultTy();
   }
 
   if (LHSType->isObjCObjectPointerType() ||
@@ -10018,7 +10245,7 @@ QualType Sema::CheckCompareOperands(Expr
         RHS = ImpCastExprToType(E, LHSType,
                                 LPT ? CK_BitCast :CK_CPointerToObjCPointerCast);
       }
-      return ResultTy;
+      return computeResultTy();
     }
     if (LHSType->isObjCObjectPointerType() &&
         RHSType->isObjCObjectPointerType()) {
@@ -10032,20 +10259,20 @@ QualType Sema::CheckCompareOperands(Expr
         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()) ||
@@ -10085,30 +10312,30 @@ QualType Sema::CheckCompareOperands(Expr
     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();
     }
   }
 
@@ -11761,19 +11988,17 @@ ExprResult Sema::CreateBuiltinBinOp(Sour
   case BO_GE:
   case BO_GT:
     ConvertHalfVec = true;
-    ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
+    ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc);
     break;
   case BO_EQ:
   case BO_NE:
     ConvertHalfVec = true;
-    ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false);
+    ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc);
     break;
   case BO_Cmp:
-    // FIXME: Implement proper semantic checking of '<=>'.
     ConvertHalfVec = true;
-    ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
-    if (!ResultTy.isNull())
-      ResultTy = Context.VoidTy;
+    ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc);
+    assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl());
     break;
   case BO_And:
     checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc);

Modified: cfe/trunk/lib/Sema/SemaOverload.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaOverload.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaOverload.cpp (original)
+++ cfe/trunk/lib/Sema/SemaOverload.cpp Mon May  7 14:07:10 2018
@@ -288,11 +288,11 @@ static const Expr *IgnoreNarrowingConver
 ///        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 @@ StandardConversionSequence::getNarrowing
       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");
@@ -7988,7 +7990,8 @@ public:
   //        bool       operator>=(T, T);
   //        bool       operator==(T, T);
   //        bool       operator!=(T, T);
-  void addRelationalPointerOrEnumeralOverloads() {
+  //           R       operator<=>(T, T)
+  void addGenericBinaryPointerOrEnumeralOverloads() {
     // C++ [over.match.oper]p3:
     //   [...]the built-in candidates include all of the candidate operator
     //   functions defined in 13.6 that, compared to the given operator, [...]
@@ -8061,7 +8064,6 @@ public:
             UserDefinedBinaryOperators.count(std::make_pair(CanonType,
                                                             CanonType)))
           continue;
-
         QualType ParamTypes[2] = { *Enum, *Enum };
         S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
       }
@@ -8179,6 +8181,41 @@ public:
     }
   }
 
+  // C++2a [over.built]p14:
+  //
+  //   For every integral type T there exists a candidate operator function
+  //   of the form
+  //
+  //        std::strong_ordering operator<=>(T, T)
+  //
+  // C++2a [over.built]p15:
+  //
+  //   For every pair of floating-point types L and R, there exists a candidate
+  //   operator function of the form
+  //
+  //       std::partial_ordering operator<=>(L, R);
+  //
+  // FIXME: The current specification for integral types doesn't play nice with
+  // the direction of p0946r0, which allows mixed integral and unscoped-enum
+  // comparisons. Under the current spec this can lead to ambiguity during
+  // overload resolution. For example:
+  //
+  //   enum A : int {a};
+  //   auto x = (a <=> (long)42);
+  //
+  //   error: call is ambiguous for arguments 'A' and 'long'.
+  //   note: candidate operator<=>(int, int)
+  //   note: candidate operator<=>(long, long)
+  //
+  // To avoid this error, this function deviates from the specification and adds
+  // the mixed overloads `operator<=>(L, R)` where L and R are promoted
+  // arithmetic types (the same as the generic relational overloads).
+  //
+  // For now this function acts as a placeholder.
+  void addThreeWayArithmeticOverloads() {
+    addGenericBinaryArithmeticOverloads();
+  }
+
   // C++ [over.built]p17:
   //
   //   For every pair of promoted integral types L and R, there
@@ -8747,12 +8784,14 @@ void Sema::AddBuiltinOperatorCandidates(
   case OO_Greater:
   case OO_LessEqual:
   case OO_GreaterEqual:
-    OpBuilder.addRelationalPointerOrEnumeralOverloads();
+    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads();
     OpBuilder.addGenericBinaryArithmeticOverloads();
     break;
 
   case OO_Spaceship:
-    llvm_unreachable("<=> expressions not supported yet");
+    OpBuilder.addGenericBinaryPointerOrEnumeralOverloads();
+    OpBuilder.addThreeWayArithmeticOverloads();
+    break;
 
   case OO_Percent:
   case OO_Caret:

Added: cfe/trunk/test/CodeGenCXX/Inputs/std-compare.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/Inputs/std-compare.h?rev=331677&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/Inputs/std-compare.h (added)
+++ cfe/trunk/test/CodeGenCXX/Inputs/std-compare.h Mon May  7 14:07:10 2018
@@ -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

Added: cfe/trunk/test/CodeGenCXX/cxx2a-compare.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGenCXX/cxx2a-compare.cpp?rev=331677&view=auto
==============================================================================
--- cfe/trunk/test/CodeGenCXX/cxx2a-compare.cpp (added)
+++ cfe/trunk/test/CodeGenCXX/cxx2a-compare.cpp Mon May  7 14:07:10 2018
@@ -0,0 +1,186 @@
+// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple | \
+// RUN:    FileCheck %s \
+// RUN:          '-DWE="class.std::__1::weak_equality"' \
+// RUN:          '-DSO="class.std::__1::strong_ordering"' \
+// RUN:          '-DSE="class.std::__1::strong_equality"' \
+// RUN:          '-DPO="class.std::__1::partial_ordering"' \
+// RUN:           -DEQ=0 -DLT=-1 -DGT=1 -DUNORD=-127 -DNE=1
+
+#include "Inputs/std-compare.h"
+
+// Ensure we don't emit definitions for the global variables
+// since the builtins shouldn't ODR use them.
+// CHECK-NOT: constant %[[SO]]
+// CHECK-NOT: constant %[[SE]]
+// CHECK-NOT: constant %[[WE]]
+// CHECK-NOT: constant %[[PO]]
+
+// 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, i8 [[LT]], i8 [[GT]]
+  // CHECK: %cmp.eq = icmp eq i32 %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 %sel.lt
+  // CHECK: %__value_ = getelementptr inbounds %[[SO]], %[[SO]]* %retval, i32 0, i32 0
+  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
+  // CHECK: %[[FINAL:.*]] = getelementptr inbounds %[[SO]], %[[SO]]* %retval
+  // CHECK: %[[RET:.*]] = load i8, i8* %[[FINAL]]
+  // CHECK: ret i8 %[[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, i8 [[LT]], i8 [[GT]]
+  // CHECK: %cmp.eq = icmp eq i32 %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 %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, i8 [[EQ]], i8 [[UNORD]]
+  // CHECK: %cmp.gt = fcmp ogt double %0, %1
+  // CHECK: %sel.gt = select i1 %cmp.gt, i8 [[GT]], i8 %sel.eq
+  // CHECK: %cmp.lt = fcmp olt double %0, %1
+  // CHECK: %sel.lt = select i1 %cmp.lt, i8 [[LT]], i8 %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, i8 [[LT]], i8 [[GT]]
+  // CHECK: %cmp.eq = icmp eq i32* %0, %1
+  // CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 %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, i8 [[EQ]], i8 [[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]], i8 [[EQ]], i8 [[NE]]
+  // CHECK: %retval
+  // CHECK: %sel.eq
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z13test_constantv
+auto test_constant() {
+  // CHECK: entry:
+  // CHECK-NOT: icmp
+  // CHECK: %__value_ = getelementptr inbounds %[[SO]], %[[SO]]* %retval
+  // CHECK-NEXT: store i8 [[LT]], i8* %__value_
+  // CHECK-NEXT: %[[TMP:.*]] = getelementptr inbounds %[[SO]], %[[SO]]* %retval
+  // CHECK-NEXT: %[[RET:.*]] = load i8, i8* %[[TMP]]
+  // CHECK-NEXT: ret i8 %[[RET]]
+  const int x = 42;
+  const int y = 101;
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z16test_nullptr_objPiDn
+auto test_nullptr_obj(int* x, decltype(nullptr) y) {
+  // CHECK: %retval = alloca %[[SE]]
+  // CHECK: %cmp.eq = icmp eq i32* %0, null
+  // CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 [[NE]]
+  // CHECK: %retval
+  // CHECK: %sel.eq
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_Z18unscoped_enum_testijlm
+void unscoped_enum_test(int i, unsigned u, long l, unsigned long ul) {
+  enum EnumA : int { A };
+  enum EnumB : unsigned { B };
+  // CHECK: %[[I:.*]] = load {{.*}} %i.addr
+  // CHECK: icmp slt i32 {{.*}} %[[I]]
+  (void)(A <=> i);
+
+  // CHECK: %[[U:.*]] = load {{.*}} %u.addr
+  // CHECK: icmp ult i32 {{.*}} %[[U]]
+  (void)(A <=> u);
+
+  // CHECK: %[[L:.*]] = load {{.*}} %l.addr
+  // CHECK: icmp slt i64 {{.*}} %[[L]]
+  (void)(A <=> l);
+
+  // CHECK: %[[U2:.*]] = load {{.*}} %u.addr
+  // CHECK: icmp ult i32 {{.*}} %[[U2]]
+  (void)(B <=> u);
+
+  // CHECK: %[[UL:.*]] = load {{.*}} %ul.addr
+  // CHECK: icmp ult i64 {{.*}} %[[UL]]
+  (void)(B <=> ul);
+}
+
+namespace NullptrTest {
+using nullptr_t = decltype(nullptr);
+
+// CHECK-LABEL: @_ZN11NullptrTest4testEDnDn(
+auto test(nullptr_t x, nullptr_t y) {
+  // CHECK-NOT: select
+  // CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %retval
+  // CHECK-NEXT: store i8 [[EQ]], i8* %__value_
+  // CHECK: ret
+  return x <=> y;
+}
+} // namespace NullptrTest
+
+namespace ComplexTest {
+
+auto test_float(_Complex float x, _Complex float y) {
+  // CHECK: %cmp.eq.r = fcmp oeq float %x.real, %y.real
+  // CHECK: %cmp.eq.i = fcmp oeq float %x.imag, %y.imag
+  // CHECK: %and.eq = and i1 %cmp.eq.r, %cmp.eq.i
+  // CHECK: %sel.eq = select i1 %and.eq, i8 [[EQ]], i8 [[NE]]
+  // CHECK: %__value_ = getelementptr inbounds %[[WE]], %[[WE]]* %retval
+  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
+  return x <=> y;
+}
+
+// CHECK-LABEL: @_ZN11ComplexTest8test_intECiS0_(
+auto test_int(_Complex int x, _Complex int y) {
+  // CHECK: %cmp.eq.r = icmp eq i32 %x.real, %y.real
+  // CHECK: %cmp.eq.i = icmp eq i32 %x.imag, %y.imag
+  // CHECK: %and.eq = and i1 %cmp.eq.r, %cmp.eq.i
+  // CHECK: %sel.eq = select i1 %and.eq, i8 [[EQ]], i8 [[NE]]
+  // CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %retval
+  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
+  return x <=> y;
+}
+
+} // namespace ComplexTest

Added: cfe/trunk/test/PCH/Inputs/std-compare.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/Inputs/std-compare.h?rev=331677&view=auto
==============================================================================
--- cfe/trunk/test/PCH/Inputs/std-compare.h (added)
+++ cfe/trunk/test/PCH/Inputs/std-compare.h Mon May  7 14:07:10 2018
@@ -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

Added: cfe/trunk/test/PCH/cxx2a-compare.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/PCH/cxx2a-compare.cpp?rev=331677&view=auto
==============================================================================
--- cfe/trunk/test/PCH/cxx2a-compare.cpp (added)
+++ cfe/trunk/test/PCH/cxx2a-compare.cpp Mon May  7 14:07:10 2018
@@ -0,0 +1,28 @@
+// 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
+// RUN: %clang_cc1 -pedantic-errors -std=c++2a -include-pch %t -emit-llvm %s -o -
+
+
+#ifndef HEADER
+#define HEADER
+
+#include "Inputs/std-compare.h"
+constexpr auto foo() {
+  return (42 <=> 101);
+}
+
+inline auto bar(int x) {
+  return (1 <=> x);
+}
+
+#else
+
+// expected-no-diagnostics
+
+static_assert(foo() < 0);
+
+auto bar2(int x) {
+  return bar(x);
+}
+
+#endif

Added: cfe/trunk/test/SemaCXX/Inputs/std-compare.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/Inputs/std-compare.h?rev=331677&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/Inputs/std-compare.h (added)
+++ cfe/trunk/test/SemaCXX/Inputs/std-compare.h Mon May  7 14:07:10 2018
@@ -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

Modified: cfe/trunk/test/SemaCXX/compare-cxx2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/compare-cxx2a.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/compare-cxx2a.cpp (original)
+++ cfe/trunk/test/SemaCXX/compare-cxx2a.cpp Mon May  7 14:07:10 2018
@@ -1,38 +1,43 @@
 // 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"
+
+#define ASSERT_TYPE(...) static_assert(__is_same(__VA_ARGS__))
+#define ASSERT_EXPR_TYPE(Expr, Expect) static_assert(__is_same(decltype(Expr), Expect));
 
 void self_compare() {
   int a;
-  int b[3], c[3];
+  int *b = nullptr;
+
   (void)(a <=> a); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}}
   (void)(b <=> b); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}}
-  (void)(b <=> c); // expected-warning {{array comparison always evaluates to a constant}}
 }
 
 void test0(long a, unsigned long b) {
-  enum EnumA {A};
+  enum EnumA : int {A};
   enum EnumB {B};
   enum EnumC {C = 0x10000};
-         // (a,b)
 
-  // 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)((short)a <=> (unsigned short)b);
+
+  // (a,b)
+  (void)(a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
   (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}}
+  (void)((int)a <=> b);                 // expected-error {{argument to 'operator<=>' cannot be narrowed}}
+  (void)((short)a <=> b);               // expected-error {{argument to 'operator<=>' cannot be narrowed}}
+  (void)((signed char)a <=> b);         // expected-error {{argument to 'operator<=>' cannot be narrowed}}
+  (void)((long)a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
+  (void)((int)a <=> (unsigned int)b);   // expected-error {{argument to 'operator<=>' cannot be narrowed}}
   (void)((short) a <=> (unsigned short) b);
   (void)((signed char) a <=> (unsigned char) b);
 
-#if 0
+  (void)(A < 42);
   // (A,b)
   (void)(A <=> (unsigned long) b);
   (void)(A <=> (unsigned int) b);
@@ -48,7 +53,7 @@ void test0(long a, unsigned long b) {
   (void)((signed char) A <=> (unsigned char) b);
 
   // (a,B)
-  (void)(a <=> (unsigned long) B);
+  (void)(a <=> (unsigned long) B); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
   (void)(a <=> (unsigned int) B);
   (void)(a <=> (unsigned short) B);
   (void)(a <=> (unsigned char) B);
@@ -56,8 +61,8 @@ void test0(long a, unsigned long b) {
   (void)((int) a <=> B);
   (void)((short) a <=> B);
   (void)((signed char) a <=> B);
-  (void)((long) a <=> (unsigned long) B);
-  (void)((int) a <=> (unsigned int) B);
+  (void)((long) a <=> (unsigned long) B); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
+  (void)((int) a <=> (unsigned int) B); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'}}
   (void)((short) a <=> (unsigned short) B);
   (void)((signed char) a <=> (unsigned char) B);
 
@@ -76,7 +81,7 @@ void test0(long a, unsigned long b) {
   (void)((signed char) C <=> (unsigned char) b);
 
   // (a,C)
-  (void)(a <=> (unsigned long) C);
+  (void)(a <=> (unsigned long) C); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
   (void)(a <=> (unsigned int) C);
   (void)(a <=> (unsigned short) C);
   (void)(a <=> (unsigned char) C);
@@ -84,11 +89,10 @@ void test0(long a, unsigned long b) {
   (void)((int) a <=> C);
   (void)((short) a <=> C); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'short' is always 'std::strong_ordering::less'}}
   (void)((signed char) a <=> C); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'signed char' is always 'std::strong_ordering::less'}}
-  (void)((long) a <=> (unsigned long) C);
-  (void)((int) a <=> (unsigned int) C);
+  (void)((long) a <=> (unsigned long) C); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
+  (void)((int) a <=> (unsigned int) C); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'}}
   (void)((short) a <=> (unsigned short) C);
   (void)((signed char) a <=> (unsigned char) C);
-#endif
 
   // (0x80000,b)
   (void)(0x80000 <=> (unsigned long) b);
@@ -105,7 +109,7 @@ void test0(long a, unsigned long b) {
   (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}}
   (void)(a <=> (unsigned int) 0x80000);
   (void)(a <=> (unsigned short) 0x80000);
   (void)(a <=> (unsigned char) 0x80000);
@@ -113,19 +117,23 @@ void test0(long a, unsigned long b) {
   (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}}
+  (void)((int)a <=> (unsigned int)0x80000);   // expected-error {{argument to 'operator<=>' cannot be narrowed}}
   (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) {
+  enum EnumA { A };
+  (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')}}
+  (void)(b <=> A);       // expected-error {{invalid operands to binary expression ('bool' and 'EnumA')}}
+
+  // 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 +150,13 @@ void test7(unsigned long other) {
   (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}}
+  (void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
+  (void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
+  (void)((int)other <=> (unsigned)(0x8000'0000));                // expected-error {{argument to 'operator<=>' cannot be narrowed}}
 
   // 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 +180,245 @@ void test7(unsigned long other) {
   (void)((unsigned char)other <=> (unsigned short)(0x100)); // expected-warning{{less}}
   (void)((unsigned short)other <=> (unsigned char)(0xff));
 }
+
+void test8(void *vp, const void *cvp, int *ip) {
+  (void)(vp <=> cvp); // OK, void* comparisons are allowed.
+  (void)(vp <=> ip);
+  (void)(ip <=> cvp);
+}
+
+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);
+}
+
+typedef int *INTPTR;
+void test_typedef_bug(int *x, INTPTR y) {
+  (void)(x <=> y);
+}
+
+using nullptr_t = decltype(nullptr);
+
+struct Class {};
+struct ClassB : Class {};
+struct Class2 {};
+using FnTy = void(int);
+using FnTy2 = long(int);
+using MemFnTy = void (Class::*)() const;
+using MemFnTyB = void (ClassB::*)() const;
+using MemFnTy2 = void (Class::*)();
+using MemFnTy3 = void (Class2::*)() const;
+using MemDataTy = long(Class::*);
+
+void test_nullptr(int *x, FnTy *fp, MemFnTy memp, MemDataTy memdp) {
+  auto r1 = (nullptr <=> nullptr);
+  ASSERT_EXPR_TYPE(r1, std::strong_equality);
+
+  auto r2 = (nullptr <=> x);
+  ASSERT_EXPR_TYPE(r2, std::strong_equality);
+
+  auto r3 = (fp <=> nullptr);
+  ASSERT_EXPR_TYPE(r3, std::strong_equality);
+
+  auto r4 = (0 <=> fp);
+  ASSERT_EXPR_TYPE(r4, std::strong_equality);
+
+  auto r5 = (nullptr <=> memp);
+  ASSERT_EXPR_TYPE(r5, std::strong_equality);
+
+  auto r6 = (0 <=> memdp);
+  ASSERT_EXPR_TYPE(r6, std::strong_equality);
+
+  auto r7 = (0 <=> nullptr);
+  ASSERT_EXPR_TYPE(r7, std::strong_equality);
+}
+
+void test_compatible_pointer(FnTy *f1, FnTy2 *f2, MemFnTy mf1, MemFnTyB mfb,
+                             MemFnTy2 mf2, MemFnTy3 mf3) {
+  (void)(f1 <=> f2); // expected-error {{distinct pointer types}}
+
+  auto r1 = (mf1 <=> mfb); // OK
+  ASSERT_EXPR_TYPE(r1, std::strong_equality);
+  ASSERT_EXPR_TYPE((mf1 <=> mfb), std::strong_equality);
+
+  (void)(mf1 <=> mf2); // expected-error {{distinct pointer types}}
+  (void)(mf3 <=> mf1); // expected-error {{distinct pointer types}}
+}
+
+// Test that variable narrowing is deferred for value dependent expressions
+template <int Val>
+auto test_template_overflow() {
+  // expected-error at +1 {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned long'}}
+  return (Val <=> (unsigned long)0);
+}
+template auto test_template_overflow<0>();
+template auto test_template_overflow<-1>(); // expected-note {{requested here}}
+
+void test_enum_integral_compare() {
+  enum EnumA : int {A, ANeg = -1, AMax = __INT_MAX__};
+  enum EnumB : unsigned {B, BMax = __UINT32_MAX__ };
+  enum EnumC : int {C = -1, C0 = 0};
+
+  (void)(A <=> C); // expected-error {{invalid operands to binary expression ('EnumA' and 'EnumC')}}
+
+  (void)(A <=> (unsigned)0);
+  (void)((unsigned)0 <=> A);
+  (void)(ANeg <=> (unsigned)0); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
+  (void)((unsigned)0 <=> ANeg); // expected-error {{cannot be narrowed}}
+
+  (void)(B <=> 42);
+  (void)(42 <=> B);
+  (void)(B <=> (unsigned long long)42);
+  (void)(B <=> -1); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
+  (void)(BMax <=> (unsigned long)-1);
+
+  (void)(C0 <=> (unsigned)42);
+  (void)(C <=> (unsigned)42); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
+}
+
+namespace EnumCompareTests {
+
+enum class EnumA { A, A2 };
+enum class EnumB { B };
+enum class EnumC : unsigned { C };
+
+void test_enum_enum_compare_no_builtin() {
+  auto r1 = (EnumA::A <=> EnumA::A2); // OK
+  ASSERT_EXPR_TYPE(r1, std::strong_ordering);
+  (void)(EnumA::A <=> EnumA::A); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}}
+  (void)(EnumA::A <=> EnumB::B); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumA' and 'EnumCompareTests::EnumB')}}
+  (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands}}
+}
+
+template <int>
+struct Tag {};
+// expected-note at +1 {{candidate}}
+Tag<0> operator<=>(EnumA, EnumA) {
+  return {};
+}
+Tag<1> operator<=>(EnumA, EnumB) {
+  return {};
+}
+
+void test_enum_ovl_provided() {
+  auto r1 = (EnumA::A <=> EnumA::A);
+  ASSERT_EXPR_TYPE(r1, Tag<0>);
+  auto r2 = (EnumA::A <=> EnumB::B);
+  ASSERT_EXPR_TYPE(r2, Tag<1>);
+  (void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumA')}}
+}
+
+void enum_float_test() {
+  enum EnumA { A };
+  (void)(A <=> (float)0);       // expected-error {{invalid operands to binary expression ('EnumA' and 'float')}}
+  (void)((double)0 <=> A);      // expected-error {{invalid operands to binary expression ('double' and 'EnumA')}}
+  (void)((long double)0 <=> A); // expected-error {{invalid operands to binary expression ('long double' and 'EnumA')}}
+}
+
+enum class Bool1 : bool { Zero,
+                          One };
+enum Bool2 : bool { B2_Zero,
+                    B2_One };
+
+void test_bool_enum(Bool1 A1, Bool1 A2, Bool2 B1, Bool2 B2) {
+  (void)(A1 <=> A2);
+  (void)(B1 <=> B2);
+}
+
+} // namespace EnumCompareTests
+
+namespace TestUserDefinedConvSeq {
+
+template <class T, T Val>
+struct Conv {
+  constexpr operator T() const { return Val; }
+  operator T() { return Val; }
+};
+
+void test_user_conv() {
+  {
+    using C = Conv<int, 0>;
+    C c;
+    const C cc;
+    (void)(0 <=> c);
+    (void)(c <=> -1);
+    (void)((unsigned)0 <=> cc);
+    (void)((unsigned)0 <=> c); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'}}
+  }
+  {
+    using C = Conv<int, -1>;
+    C c;
+    const C cc;
+    (void)(c <=> 0);
+    (void)(cc <=> (unsigned)0); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
+    (void)(c <=> (unsigned)0);  // expected-error {{cannot be narrowed from type 'int' to 'unsigned int'}}
+  }
+}
+
+} // namespace TestUserDefinedConvSeq
+
+void test_array_conv() {
+  int arr[5];
+  int *ap = arr + 2;
+  int arr2[3];
+  (void)(arr <=> arr); // expected-error {{invalid operands to binary expression ('int [5]' and 'int [5]')}}
+  (void)(+arr <=> arr);
+}
+
+void test_mixed_float_int(float f, double d, long double ld) {
+  extern int i;
+  extern unsigned u;
+  extern long l;
+  extern short s;
+  extern unsigned short us;
+  auto r1 = (f <=> i);
+  ASSERT_EXPR_TYPE(r1, std::partial_ordering);
+
+  auto r2 = (us <=> ld);
+  ASSERT_EXPR_TYPE(r2, std::partial_ordering);
+
+  auto r3 = (s <=> f);
+  ASSERT_EXPR_TYPE(r3, std::partial_ordering);
+
+  auto r4 = (0.0 <=> i);
+  ASSERT_EXPR_TYPE(r4, std::partial_ordering);
+}
+
+namespace NullptrTest {
+using nullptr_t = decltype(nullptr);
+void foo(nullptr_t x, nullptr_t y) {
+  auto r = x <=> y;
+  ASSERT_EXPR_TYPE(r, std::strong_equality);
+}
+} // namespace NullptrTest
+
+namespace ComplexTest {
+
+enum class StrongE {};
+enum WeakE { E_One,
+             E_Two };
+
+void test_diag(_Complex int ci, _Complex float cf, _Complex double cd, int i, float f, StrongE E1, WeakE E2, int *p) {
+  (void)(ci <=> (_Complex int &)ci);
+  (void)(ci <=> cf);
+  (void)(ci <=> i);
+  (void)(ci <=> f);
+  (void)(cf <=> i);
+  (void)(cf <=> f);
+  (void)(ci <=> p); // expected-error {{invalid operands}}
+  (void)(ci <=> E1); // expected-error {{invalid operands}}
+  (void)(E2 <=> cf); // expected-error {{invalid operands}}
+}
+
+void test_int(_Complex int x, _Complex int y) {
+  auto r = x <=> y;
+  ASSERT_EXPR_TYPE(r, std::strong_equality);
+}
+
+void test_double(_Complex double x, _Complex double y) {
+  auto r = x <=> y;
+  ASSERT_EXPR_TYPE(r, std::weak_equality);
+}
+
+} // namespace ComplexTest

Modified: cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp?rev=331677&r1=331676&r2=331677&view=diff
==============================================================================
--- cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp (original)
+++ cfe/trunk/test/SemaCXX/constant-expression-cxx2a.cpp Mon May  7 14:07:10 2018
@@ -1,5 +1,7 @@
 // RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu
 
+#include "Inputs/std-compare.h"
+
 namespace ThreeWayComparison {
   struct A {
     int n;
@@ -11,17 +13,194 @@ namespace ThreeWayComparison {
   static_assert(A{2} <=> A{1} > 0);
   static_assert(A{2} <=> A{2} == 0);
 
-  // Note: not yet supported.
-  static_assert(1 <=> 2 < 0); // expected-error {{invalid operands}}
-  static_assert(2 <=> 1 > 0); // expected-error {{invalid operands}}
-  static_assert(1 <=> 1 == 0); // expected-error {{invalid operands}}
+  static_assert(1 <=> 2 < 0);
+  static_assert(2 <=> 1 > 0);
+  static_assert(1 <=> 1 == 0);
   constexpr int k = (1 <=> 1, 0);
-  // expected-error at -1 {{constexpr variable 'k' must be initialized by a constant expression}}
-  // expected-warning at -2 {{three-way comparison result unused}}
+  // expected-warning at -1 {{three-way comparison result unused}}
+
+  static_assert(std::strong_ordering::equal == 0);
+
+  constexpr void f() {
+    void(1 <=> 1);
+  }
+
+  struct MemPtr {
+    void foo() {}
+    void bar() {}
+    int data;
+    int data2;
+    long data3;
+  };
+
+  struct MemPtr2 {
+    void foo() {}
+    void bar() {}
+    int data;
+    int data2;
+    long data3;
+  };
+  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_success = [] {
+  {
+    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;
 
-  constexpr void f() { // expected-error {{constant expression}}
-    void(1 <=> 1); // expected-note {{constant expression}}
+    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));
+  }
+  { // mixed nullptr tests
+    using SO = std::strong_ordering;
+    using SE = std::strong_equality;
+
+    int x = 42;
+    int *xp = &x;
+
+    MemPtrT mf = nullptr;
+    MemPtrT mf2 = &MemPtr::foo;
+    auto r3 = (mf <=> nullptr);
+    CHECK_TYPE(decltype(r3), std::strong_equality);
+    CHECK(r3.test_eq(SE::equal));
   }
 
-  // TODO: defaulted operator <=>
+  return true;
+}();
+
+template <auto LHS, auto RHS, bool ExpectTrue = false>
+constexpr bool test_constexpr() {
+  using nullptr_t = decltype(nullptr);
+  using LHSTy = decltype(LHS);
+  using RHSTy = decltype(RHS);
+  // expected-note at +1 {{subexpression not valid in a constant expression}}
+  auto Res = (LHS <=> RHS);
+  if constexpr (__is_same(LHSTy, nullptr_t) || __is_same(RHSTy, nullptr_t)) {
+    CHECK_TYPE(decltype(Res), std::strong_equality);
+  }
+  if (ExpectTrue)
+    return Res == 0;
+  return Res != 0;
+}
+int dummy = 42;
+int dummy2 = 101;
+
+constexpr bool tc1 = test_constexpr<nullptr, &dummy>();
+constexpr bool tc2 = test_constexpr<&dummy, nullptr>();
+
+// OK, equality comparison only
+constexpr bool tc3 = test_constexpr<&MemPtr::foo, nullptr>();
+constexpr bool tc4 = test_constexpr<nullptr, &MemPtr::foo>();
+constexpr bool tc5 = test_constexpr<&MemPtr::foo, &MemPtr::bar>();
+
+constexpr bool tc6 = test_constexpr<&MemPtr::data, nullptr>();
+constexpr bool tc7 = test_constexpr<nullptr, &MemPtr::data>();
+constexpr bool tc8 = test_constexpr<&MemPtr::data, &MemPtr::data2>();
+
+// expected-error at +1 {{must be initialized by a constant expression}}
+constexpr bool tc9 = test_constexpr<&dummy, &dummy2>(); // expected-note {{in call}}
+
+template <class T, class R, class I>
+constexpr T makeComplex(R r, I i) {
+  T res{r, i};
+  return res;
+};
+
+template <class T, class ResultT>
+constexpr bool complex_test(T x, T y, ResultT Expect) {
+  auto res = x <=> y;
+  CHECK_TYPE(decltype(res), ResultT);
+  return res.test_eq(Expect);
 }
+static_assert(complex_test(makeComplex<_Complex double>(0.0, 0.0),
+                           makeComplex<_Complex double>(0.0, 0.0),
+                           std::weak_equality::equivalent));
+static_assert(complex_test(makeComplex<_Complex double>(0.0, 0.0),
+                           makeComplex<_Complex double>(1.0, 0.0),
+                           std::weak_equality::nonequivalent));
+static_assert(complex_test(makeComplex<_Complex double>(0.0, 0.0),
+                           makeComplex<_Complex double>(0.0, 1.0),
+                           std::weak_equality::nonequivalent));
+static_assert(complex_test(makeComplex<_Complex int>(0, 0),
+                           makeComplex<_Complex int>(0, 0),
+                           std::strong_equality::equal));
+static_assert(complex_test(makeComplex<_Complex int>(0, 0),
+                           makeComplex<_Complex int>(1, 0),
+                           std::strong_equality::nonequal));
+// TODO: defaulted operator <=>
+} // namespace ThreeWayComparison

Added: cfe/trunk/test/SemaCXX/std-compare-cxx2a.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/SemaCXX/std-compare-cxx2a.cpp?rev=331677&view=auto
==============================================================================
--- cfe/trunk/test/SemaCXX/std-compare-cxx2a.cpp (added)
+++ cfe/trunk/test/SemaCXX/std-compare-cxx2a.cpp Mon May  7 14:07:10 2018
@@ -0,0 +1,65 @@
+// Test diagnostics for ill-formed STL <compare> headers.
+
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s
+
+void compare_not_found_test() {
+  // expected-error at +1 {{cannot deduce return type of 'operator<=>' because type partial_ordering was not found; include <compare>}}
+  (void)(0.0 <=> 42.123);
+}
+
+namespace std {
+inline namespace __1 {
+struct partial_ordering; // expected-note {{forward declaration}}
+}
+} // namespace std
+
+auto compare_incomplete_test() {
+  // expected-error at +1 {{incomplete type 'std::partial_ordering' where a complete type is required}}
+  return (-1.2 <=> 123.0);
+}
+
+namespace std {
+inline namespace __1 {
+struct partial_ordering {
+  unsigned value;
+};
+} // namespace __1
+} // namespace std
+
+auto missing_member_test() {
+  // expected-error at +1 {{standard library implementation of 'std::partial_ordering' is not supported; member 'equivalent' is missing}}
+  return (1.0 <=> 1.0);
+}
+
+namespace std {
+inline namespace __1 {
+struct strong_ordering {
+  long long value;
+  static const strong_ordering equivalent; // expected-note {{declared here}}
+};
+} // namespace __1
+} // namespace std
+
+auto test_non_constexpr_var() {
+  // expected-error at +1 {{standard library implementation of 'std::strong_ordering' is not supported; member 'equivalent' does not have expected form}}
+  return (1 <=> 0);
+}
+
+namespace std {
+inline namespace __1 {
+struct strong_equality {
+  char value = 0;
+  constexpr strong_equality() = default;
+  // non-trivial
+  constexpr strong_equality(strong_equality const &other) : value(other.value) {}
+};
+} // namespace __1
+} // namespace std
+
+struct Class {};
+using MemPtr = void (Class::*)(int);
+
+auto test_non_trivial(MemPtr LHS, MemPtr RHS) {
+  // expected-error at +1 {{standard library implementation of 'std::strong_equality' is not supported; the type is not trivially copyable}}
+  return LHS <=> RHS;
+}




More information about the cfe-commits mailing list