[c++20] P1959R0: Remove support for std::*_equality.
authorRichard Smith <richard@metafoo.co.uk>
Tue, 17 Dec 2019 01:40:03 +0000 (17:40 -0800)
committerRichard Smith <richard@metafoo.co.uk>
Tue, 17 Dec 2019 01:49:45 +0000 (17:49 -0800)
17 files changed:
clang/include/clang/AST/ComparisonCategories.h
clang/include/clang/AST/Type.h
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/AST/ComparisonCategories.cpp
clang/lib/AST/ExprConstant.cpp
clang/lib/AST/Interp/Interp.h
clang/lib/CodeGen/CGExprAgg.cpp
clang/lib/Sema/SemaExpr.cpp
clang/lib/Sema/SemaOverload.cpp
clang/test/CodeGenCXX/Inputs/std-compare.h
clang/test/CodeGenCXX/cxx2a-compare.cpp
clang/test/PCH/Inputs/std-compare.h
clang/test/SemaCXX/Inputs/std-compare.h
clang/test/SemaCXX/compare-cxx2a.cpp
clang/test/SemaCXX/constant-expression-cxx2a.cpp
clang/test/SemaCXX/std-compare-cxx2a.cpp
clang/www/cxx_status.html

index e28d499..70a7896 100644 (file)
@@ -41,12 +41,10 @@ class NamespaceDecl;
 /// 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,
+  First = PartialOrdering,
   Last = StrongOrdering
 };
 
@@ -54,9 +52,6 @@ enum class ComparisonCategoryType : unsigned char {
 /// [class.spaceship]p4.
 inline ComparisonCategoryType commonComparisonType(ComparisonCategoryType A,
                                                    ComparisonCategoryType B) {
-  if ((A == ComparisonCategoryType::StrongEquality) ^
-      (B == ComparisonCategoryType::StrongEquality))
-    return ComparisonCategoryType::WeakEquality;
   return A < B ? A : B;
 }
 
@@ -70,8 +65,6 @@ Optional<ComparisonCategoryType> getComparisonCategoryForBuiltinCmp(QualType T);
 enum class ComparisonCategoryResult : unsigned char {
   Equal,
   Equivalent,
-  Nonequivalent,
-  Nonequal,
   Less,
   Greater,
   Unordered,
@@ -139,21 +132,11 @@ public:
     return Info;
   }
 
-  /// True iff the comparison category is an equality comparison.
-  bool isEquality() const { return !isOrdered(); }
-
-  /// 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;
-  }
-
   /// 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;
+    return Kind == CCK::StrongOrdering;
   }
 
   /// True iff the comparison is not totally ordered.
@@ -167,28 +150,18 @@ public:
   /// 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;
-    }
+    if (!isStrong() && Res == CCR::Equal)
+      return CCR::Equivalent;
     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 {
index 25ab44c..2968efa 100644 (file)
@@ -2005,6 +2005,7 @@ public:
   bool isReferenceType() const;
   bool isLValueReferenceType() const;
   bool isRValueReferenceType() const;
+  bool isObjectPointerType() const;
   bool isFunctionPointerType() const;
   bool isFunctionReferenceType() const;
   bool isMemberPointerType() const;
@@ -6456,6 +6457,16 @@ inline bool Type::isRValueReferenceType() const {
   return isa<RValueReferenceType>(CanonicalType);
 }
 
+inline bool Type::isObjectPointerType() const {
+  // Note: an "object pointer type" is not the same thing as a pointer to an
+  // object type; rather, it is a pointer to an object type or a pointer to cv
+  // void.
+  if (const auto *T = getAs<PointerType>())
+    return !T->getPointeeType()->isFunctionType();
+  else
+    return false;
+}
+
 inline bool Type::isFunctionPointerType() const {
   if (const auto *T = getAs<PointerType>())
     return T->getPointeeType()->isFunctionType();
index 8f303f2..2529f7f 100644 (file)
@@ -6190,6 +6190,8 @@ def ext_typecheck_ordered_comparison_of_pointer_and_zero : Extension<
   "ordered comparison between pointer and zero (%0 and %1) is an extension">;
 def err_typecheck_ordered_comparison_of_pointer_and_zero : Error<
   "ordered comparison between pointer and zero (%0 and %1)">;
+def err_typecheck_three_way_comparison_of_pointer_and_zero : Error<
+  "three-way comparison between pointer and zero">;
 def ext_typecheck_ordered_comparison_of_function_pointers : ExtWarn<
   "ordered comparison of function pointers (%0 and %1)">,
   InGroup<DiagGroup<"ordered-compare-function-pointers">>;
@@ -10197,6 +10199,8 @@ def err_std_compare_type_not_supported : Error<
    "the type does not have the expected form}1">;
 def note_rewriting_operator_as_spaceship : Note<
   "while rewriting comparison as call to 'operator<=>' declared here">;
+def err_three_way_vector_comparison : Error<
+  "three-way comparison between vectors is not supported">;
 
 // Memory Tagging Extensions (MTE) diagnostics
 def err_memtag_arg_null_or_pointer : Error<
index 9a07c24..0767323 100644 (file)
@@ -23,31 +23,16 @@ Optional<ComparisonCategoryType>
 clang::getComparisonCategoryForBuiltinCmp(QualType T) {
   using CCT = ComparisonCategoryType;
 
-  if (const ComplexType *CT = T->getAs<ComplexType>()) {
-    if (CT->getElementType()->hasFloatingRepresentation())
-      return CCT::WeakEquality;
-    // FIXME: Remove this, consistent with P1959R0.
-    return CCT::StrongEquality;
-  }
-
   if (T->isIntegralOrEnumerationType())
     return CCT::StrongOrdering;
 
-  if (T->hasFloatingRepresentation())
+  if (T->isRealFloatingType())
     return CCT::PartialOrdering;
 
-  // C++2a [expr.spaceship]p7: If the composite pointer type is a function
-  // pointer type, a pointer-to-member type, or std::nullptr_t, the
-  // result is of type std::strong_equality
-  if (T->isFunctionPointerType() || T->isMemberPointerType() ||
-      T->isNullPtrType())
-    // FIXME: This case was removed by P1959R0.
-    return CCT::StrongEquality;
-
   // C++2a [expr.spaceship]p8: If the composite pointer type is an object
   // pointer type, p <=> q is of type std::strong_ordering.
   // Note: this assumes neither operand is a null pointer constant.
-  if (T->isPointerType())
+  if (T->isObjectPointerType())
     return CCT::StrongOrdering;
 
   // TODO: Extend support for operator<=> to ObjC types.
@@ -185,10 +170,6 @@ QualType ComparisonCategoryInfo::getType() const {
 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:
@@ -204,12 +185,8 @@ StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) {
   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:
@@ -226,16 +203,10 @@ ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
   using CCR = ComparisonCategoryResult;
   std::vector<CCR> Values;
   Values.reserve(4);
-  bool IsStrong = (Type == CCT::StrongEquality || Type == CCT::StrongOrdering);
-  if (IsStrong)
+  bool IsStrong = Type == CCT::StrongOrdering;
   Values.push_back(IsStrong ? CCR::Equal : CCR::Equivalent);
-  if (Type == CCT::StrongOrdering || Type == CCT::WeakOrdering ||
-      Type == CCT::PartialOrdering) {
-    Values.push_back(CCR::Less);
-    Values.push_back(CCR::Greater);
-  } else {
-    Values.push_back(IsStrong ? CCR::Nonequal : CCR::Nonequivalent);
-  }
+  Values.push_back(CCR::Less);
+  Values.push_back(CCR::Greater);
   if (Type == CCT::PartialOrdering)
     Values.push_back(CCR::Unordered);
   return Values;
index 9b6d0cb..0b658cb 100644 (file)
@@ -11481,6 +11481,14 @@ public:
     }
   }
 };
+
+enum class CmpResult {
+  Unequal,
+  Less,
+  Equal,
+  Greater,
+  Unordered,
+};
 }
 
 template <class SuccessCB, class AfterCB>
@@ -11496,15 +11504,8 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     return false;
   };
 
-  using CCR = ComparisonCategoryResult;
-  bool IsRelational = E->isRelationalOp();
+  bool IsRelational = E->isRelationalOp() || E->getOpcode() == BO_Cmp;
   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();
@@ -11518,10 +11519,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     if (!EvaluateInteger(E->getRHS(), RHS, Info) || !LHSOK)
       return false;
     if (LHS < RHS)
-      return Success(CCR::Less, E);
+      return Success(CmpResult::Less, E);
     if (LHS > RHS)
-      return Success(CCR::Greater, E);
-    return Success(CCR::Equal, E);
+      return Success(CmpResult::Greater, E);
+    return Success(CmpResult::Equal, E);
   }
 
   if (LHSTy->isFixedPointType() || RHSTy->isFixedPointType()) {
@@ -11534,10 +11535,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     if (!EvaluateFixedPointOrInteger(E->getRHS(), RHSFX, Info) || !LHSOK)
       return false;
     if (LHSFX < RHSFX)
-      return Success(CCR::Less, E);
+      return Success(CmpResult::Less, E);
     if (LHSFX > RHSFX)
-      return Success(CCR::Greater, E);
-    return Success(CCR::Equal, E);
+      return Success(CmpResult::Greater, E);
+    return Success(CmpResult::Equal, E);
   }
 
   if (LHSTy->isAnyComplexType() || RHSTy->isAnyComplexType()) {
@@ -11573,12 +11574,12 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
       APFloat::cmpResult CR_i =
         LHS.getComplexFloatImag().compare(RHS.getComplexFloatImag());
       bool IsEqual = CR_r == APFloat::cmpEqual && CR_i == APFloat::cmpEqual;
-      return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E);
+      return Success(IsEqual ? CmpResult::Equal : CmpResult::Unequal, E);
     } else {
       assert(IsEquality && "invalid complex comparison");
       bool IsEqual = LHS.getComplexIntReal() == RHS.getComplexIntReal() &&
                      LHS.getComplexIntImag() == RHS.getComplexIntImag();
-      return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E);
+      return Success(IsEqual ? CmpResult::Equal : CmpResult::Unequal, E);
     }
   }
 
@@ -11597,13 +11598,13 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     auto GetCmpRes = [&]() {
       switch (LHS.compare(RHS)) {
       case APFloat::cmpEqual:
-        return CCR::Equal;
+        return CmpResult::Equal;
       case APFloat::cmpLessThan:
-        return CCR::Less;
+        return CmpResult::Less;
       case APFloat::cmpGreaterThan:
-        return CCR::Greater;
+        return CmpResult::Greater;
       case APFloat::cmpUnordered:
-        return CCR::Unordered;
+        return CmpResult::Unordered;
       }
       llvm_unreachable("Unrecognised APFloat::cmpResult enum");
     };
@@ -11658,7 +11659,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
       if ((RHSValue.Base && isZeroSized(LHSValue)) ||
           (LHSValue.Base && isZeroSized(RHSValue)))
         return Error(E);
-      return Success(CCR::Nonequal, E);
+      return Success(CmpResult::Unequal, E);
     }
 
     const CharUnits &LHSOffset = LHSValue.getLValueOffset();
@@ -11742,10 +11743,10 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     }
 
     if (CompareLHS < CompareRHS)
-      return Success(CCR::Less, E);
+      return Success(CmpResult::Less, E);
     if (CompareLHS > CompareRHS)
-      return Success(CCR::Greater, E);
-    return Success(CCR::Equal, E);
+      return Success(CmpResult::Greater, E);
+    return Success(CmpResult::Equal, E);
   }
 
   if (LHSTy->isMemberPointerType()) {
@@ -11766,7 +11767,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     //   null, they compare unequal.
     if (!LHSValue.getDecl() || !RHSValue.getDecl()) {
       bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl();
-      return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
+      return Success(Equal ? CmpResult::Equal : CmpResult::Unequal, E);
     }
 
     //   Otherwise if either is a pointer to a virtual member function, the
@@ -11783,7 +11784,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     //   they were dereferenced with a hypothetical object of the associated
     //   class type.
     bool Equal = LHSValue == RHSValue;
-    return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
+    return Success(Equal ? CmpResult::Equal : CmpResult::Unequal, E);
   }
 
   if (LHSTy->isNullPtrType()) {
@@ -11792,7 +11793,7 @@ EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
     // 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.
-    return Success(CCR::Equal, E);
+    return Success(CmpResult::Equal, E);
   }
 
   return DoAfter();
@@ -11802,14 +11803,29 @@ bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) {
   if (!CheckLiteralType(Info, E))
     return false;
 
-  auto OnSuccess = [&](ComparisonCategoryResult ResKind,
-                       const BinaryOperator *E) {
+  auto OnSuccess = [&](CmpResult CR, const BinaryOperator *E) {
+    ComparisonCategoryResult CCR;
+    switch (CR) {
+    case CmpResult::Unequal:
+      llvm_unreachable("should never produce Unequal for three-way comparison");
+    case CmpResult::Less:
+      CCR = ComparisonCategoryResult::Less;
+      break;
+    case CmpResult::Equal:
+      CCR = ComparisonCategoryResult::Equal;
+      break;
+    case CmpResult::Greater:
+      CCR = ComparisonCategoryResult::Greater;
+      break;
+    case CmpResult::Unordered:
+      CCR = ComparisonCategoryResult::Unordered;
+      break;
+    }
     // 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;
+    const VarDecl *VD = CmpInfo.getValueInfo(CmpInfo.makeWeakResult(CCR))->VD;
     // Check and evaluate the result as a constant expression.
     LValue LV;
     LV.set(VD);
@@ -11837,14 +11853,14 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
          "DataRecursiveIntBinOpEvaluator should have handled integral types");
 
   if (E->isComparisonOp()) {
-    // Evaluate builtin binary comparisons by evaluating them as C++2a three-way
+    // Evaluate builtin binary comparisons by evaluating them as 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 OnSuccess = [&](CmpResult CR, const BinaryOperator *E) {
+      assert((CR != CmpResult::Unequal || E->isEqualityOp()) &&
+             "should only produce Unequal for equality comparisons");
+      bool IsEqual   = CR == CmpResult::Equal,
+           IsLess    = CR == CmpResult::Less,
+           IsGreater = CR == CmpResult::Greater;
       auto Op = E->getOpcode();
       switch (Op) {
       default:
@@ -11852,10 +11868,14 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
       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);
+      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, [&]() {
index 8934efa..c12caa6 100644 (file)
@@ -196,11 +196,8 @@ inline bool CmpHelperEQ<Pointer>(InterpState &S, CodePtr OpPC, CompareFn Fn) {
   const Pointer &RHS = S.Stk.pop<Pointer>();
   const Pointer &LHS = S.Stk.pop<Pointer>();
 
-  if (LHS.isZero() || RHS.isZero()) {
-    if (LHS.isZero() && RHS.isZero())
-      S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
-    else
-      S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Nonequal)));
+  if (LHS.isZero() && RHS.isZero()) {
+    S.Stk.push<BoolT>(BoolT::from(Fn(ComparisonCategoryResult::Equal)));
     return true;
   }
 
index 9fb399c..8de609a 100644 (file)
@@ -981,10 +981,6 @@ void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) {
 
   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()) {
@@ -1022,10 +1018,6 @@ void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) {
   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()),
index 86c3684..15e86ba 100644 (file)
@@ -10648,9 +10648,11 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
     return QualType();
   if (Type.isNull())
     return S.InvalidOperands(Loc, LHS, RHS);
-  assert(Type->isArithmeticType() || Type->isEnumeralType());
 
-  // FIXME: Reject complex types, consistent with P1959R0.
+  Optional<ComparisonCategoryType> CCT =
+      getComparisonCategoryForBuiltinCmp(Type);
+  if (!CCT)
+    return S.InvalidOperands(Loc, LHS, RHS);
 
   bool HasNarrowing = checkThreeWayNarrowingConversion(
       S, Type, LHS.get(), LHSType, LHS.get()->getBeginLoc());
@@ -10662,8 +10664,7 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
   assert(!Type.isNull() && "composite type for <=> has not been set");
 
   return S.CheckComparisonCategoryType(
-      *getComparisonCategoryForBuiltinCmp(Type), Loc,
-      Sema::ComparisonCategoryUsage::OperatorInExpression);
+      *CCT, Loc, Sema::ComparisonCategoryUsage::OperatorInExpression);
 }
 
 static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
@@ -10725,6 +10726,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
                                     BinaryOperatorKind Opc) {
   bool IsRelational = BinaryOperator::isRelationalOp(Opc);
   bool IsThreeWay = Opc == BO_Cmp;
+  bool IsOrdered = IsRelational || IsThreeWay;
   auto IsAnyPointerType = [](ExprResult E) {
     QualType Ty = E.get()->getType();
     return Ty->isPointerType() || Ty->isMemberPointerType();
@@ -10794,16 +10796,19 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
 
     if (CompositeTy->isPointerType() && LHSIsNull != RHSIsNull) {
       // P0946R0: Comparisons between a null pointer constant and an object
-      // pointer result in std::strong_equality
-      // FIXME: Reject this, consistent with P1959R0 + P0946R0.
-      CCT = ComparisonCategoryType::StrongEquality;
+      // pointer result in std::strong_equality, which is ill-formed under
+      // P1959R0.
+      Diag(Loc, diag::err_typecheck_three_way_comparison_of_pointer_and_zero)
+          << (LHSIsNull ? LHS.get()->getSourceRange()
+                        : RHS.get()->getSourceRange());
+      return QualType();
     }
 
     return CheckComparisonCategoryType(
         *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression);
   };
 
-  if (!IsRelational && LHSIsNull != RHSIsNull) {
+  if (!IsOrdered && LHSIsNull != RHSIsNull) {
     bool IsEquality = Opc == BO_EQ;
     if (RHSIsNull)
       DiagnoseAlwaysNonNullPointer(LHS.get(), RHSNullKind, IsEquality,
@@ -10822,7 +10827,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
     // but we allow it as an extension.
     // FIXME: If we really want to allow this, should it be part of composite
     // pointer type computation so it works in conditionals too?
-    if (!IsRelational &&
+    if (!IsOrdered &&
         ((LHSType->isFunctionPointerType() && RHSType->isVoidPointerType()) ||
          (RHSType->isFunctionPointerType() && LHSType->isVoidPointerType()))) {
       // This is a gcc extension compatibility comparison.
@@ -10847,8 +10852,11 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
     // C++ [expr.rel]p2:
     //   If both operands are pointers, [...] bring them to their composite
     //   pointer type.
+    // For <=>, the only valid non-pointer types are arrays and functions, and
+    // we already decayed those, so this is really the same as the relational
+    // comparison rule.
     if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >=
-            (IsRelational ? 2 : 1) &&
+            (IsOrdered ? 2 : 1) &&
         (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() ||
                                          RHSType->isObjCObjectPointerType()))) {
       if (convertPointersToCompositeType(*this, Loc, LHS, RHS))
@@ -10911,7 +10919,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
     // C++ [expr.eq]p4:
     //   Two operands of type std::nullptr_t or one operand of type
     //   std::nullptr_t and the other a null pointer constant compare equal.
-    if (!IsRelational && LHSIsNull && RHSIsNull) {
+    if (!IsOrdered && LHSIsNull && RHSIsNull) {
       if (LHSType->isNullPtrType()) {
         RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
         return computeResultTy();
@@ -10924,12 +10932,12 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
 
     // Comparison of Objective-C pointers and block pointers against nullptr_t.
     // These aren't covered by the composite pointer type rules.
-    if (!IsRelational && RHSType->isNullPtrType() &&
+    if (!IsOrdered && RHSType->isNullPtrType() &&
         (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) {
       RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
       return computeResultTy();
     }
-    if (!IsRelational && LHSType->isNullPtrType() &&
+    if (!IsOrdered && LHSType->isNullPtrType() &&
         (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) {
       LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
       return computeResultTy();
@@ -10963,7 +10971,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
     // C++ [expr.eq]p2:
     //   If at least one operand is a pointer to member, [...] bring them to
     //   their composite pointer type.
-    if (!IsRelational &&
+    if (!IsOrdered &&
         (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) {
       if (convertPointersToCompositeType(*this, Loc, LHS, RHS))
         return QualType();
@@ -10973,7 +10981,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
   }
 
   // Handle block pointer types.
-  if (!IsRelational && LHSType->isBlockPointerType() &&
+  if (!IsOrdered && LHSType->isBlockPointerType() &&
       RHSType->isBlockPointerType()) {
     QualType lpointee = LHSType->castAs<BlockPointerType>()->getPointeeType();
     QualType rpointee = RHSType->castAs<BlockPointerType>()->getPointeeType();
@@ -10989,7 +10997,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
   }
 
   // Allow block pointers to be compared with null pointer constants.
-  if (!IsRelational
+  if (!IsOrdered
       && ((LHSType->isBlockPointerType() && RHSType->isPointerType())
           || (LHSType->isPointerType() && RHSType->isBlockPointerType()))) {
     if (!LHSIsNull && !RHSIsNull) {
@@ -11059,12 +11067,12 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
       return computeResultTy();
     }
 
-    if (!IsRelational && LHSType->isBlockPointerType() &&
+    if (!IsOrdered && LHSType->isBlockPointerType() &&
         RHSType->isBlockCompatibleObjCPointerType(Context)) {
       LHS = ImpCastExprToType(LHS.get(), RHSType,
                               CK_BlockPointerToObjCPointerCast);
       return computeResultTy();
-    } else if (!IsRelational &&
+    } else if (!IsOrdered &&
                LHSType->isBlockCompatibleObjCPointerType(Context) &&
                RHSType->isBlockPointerType()) {
       RHS = ImpCastExprToType(RHS.get(), LHSType,
@@ -11081,7 +11089,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
       // since users tend to want to compare addresses.
     } else if ((LHSIsNull && LHSType->isIntegerType()) ||
                (RHSIsNull && RHSType->isIntegerType())) {
-      if (IsRelational) {
+      if (IsOrdered) {
         isError = getLangOpts().CPlusPlus;
         DiagID =
           isError ? diag::err_typecheck_ordered_comparison_of_pointer_and_zero
@@ -11090,7 +11098,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
     } else if (getLangOpts().CPlusPlus) {
       DiagID = diag::err_typecheck_comparison_of_pointer_integer;
       isError = true;
-    } else if (IsRelational)
+    } else if (IsOrdered)
       DiagID = diag::ext_typecheck_ordered_comparison_of_pointer_integer;
     else
       DiagID = diag::ext_typecheck_comparison_of_pointer_integer;
@@ -11113,12 +11121,12 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
   }
 
   // Handle block pointers.
-  if (!IsRelational && RHSIsNull
+  if (!IsOrdered && RHSIsNull
       && LHSType->isBlockPointerType() && RHSType->isIntegerType()) {
     RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
     return computeResultTy();
   }
-  if (!IsRelational && LHSIsNull
+  if (!IsOrdered && LHSIsNull
       && LHSType->isIntegerType() && RHSType->isBlockPointerType()) {
     LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
     return computeResultTy();
@@ -11195,6 +11203,11 @@ QualType Sema::GetSignedVectorType(QualType V) {
 QualType Sema::CheckVectorCompareOperands(ExprResult &LHS, ExprResult &RHS,
                                           SourceLocation Loc,
                                           BinaryOperatorKind Opc) {
+  if (Opc == BO_Cmp) {
+    Diag(Loc, diag::err_three_way_vector_comparison);
+    return QualType();
+  }
+
   // Check to make sure we're operating on vectors of the same type and width,
   // Allowing one side to be a scalar of element type.
   QualType vType = CheckVectorOperands(LHS, RHS, Loc, /*isCompAssign*/false,
index 9c784ef..32541d6 100644 (file)
@@ -13259,16 +13259,13 @@ ExprResult Sema::BuildSynthesizedThreeWayComparison(
   if (Eq.isInvalid())
     return ExprError();
 
-  ExprResult Less;
-  if (Info->isOrdered()) {
-    Less = CreateOverloadedBinOp(OpLoc, BO_LT, Fns, LHS, RHS, true, true,
-                                 DefaultedFn);
-    if (Less.isInvalid())
-      return ExprError();
-  }
+  ExprResult Less = CreateOverloadedBinOp(OpLoc, BO_LT, Fns, LHS, RHS, true,
+                                          true, DefaultedFn);
+  if (Less.isInvalid())
+    return ExprError();
 
   ExprResult Greater;
-  if (Info->isOrdered()) {
+  if (Info->isPartial()) {
     Greater = CreateOverloadedBinOp(OpLoc, BO_LT, Fns, RHS, LHS, true, true,
                                     DefaultedFn);
     if (Greater.isInvalid())
@@ -13287,17 +13284,7 @@ ExprResult Sema::BuildSynthesizedThreeWayComparison(
     {ExprResult(), ComparisonCategoryResult::Unordered},
   };
 
-  int I;
-  if (Info->isEquality()) {
-    Comparisons[1].Result = Info->isStrong()
-                                ? ComparisonCategoryResult::Nonequal
-                                : ComparisonCategoryResult::Nonequivalent;
-    I = 1;
-  } else if (!Info->isPartial()) {
-    I = 2;
-  } else {
-    I = 3;
-  }
+  int I = Info->isPartial() ? 3 : 2;
 
   // Combine the comparisons with suitable conditional expressions.
   ExprResult Result;
index a6ab605..eaf7951 100644 (file)
@@ -6,11 +6,8 @@ inline namespace __1 {
 
 // exposition only
 enum class _EqResult : unsigned char {
-  __zero = 0,
-  __equal = __zero,
+  __equal = 0,
   __equiv = __equal,
-  __nonequal = 1,
-  __nonequiv = __nonequal
 };
 
 enum class _OrdResult : signed char {
@@ -25,108 +22,6 @@ enum class _NCmpResult : signed char {
 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
@@ -147,11 +42,6 @@ public:
   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;
@@ -237,10 +127,6 @@ public:
   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);
@@ -331,14 +217,6 @@ public:
   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);
@@ -423,14 +301,6 @@ constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v)
   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
 
index 57b4449..262fead 100644 (file)
@@ -1,8 +1,6 @@
 // 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
 
@@ -69,36 +67,6 @@ auto ptr_test(int *x, int *y) {
   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: %[[DEST:retval|agg.result]]
-  // CHECK: %cmp.ptr = icmp eq [[TY:i[0-9]+]] %lhs.memptr.ptr, %rhs.memptr.ptr
-  // CHECK: %cmp.ptr.null = icmp eq [[TY]] %lhs.memptr.ptr, 0
-  // CHECK: %cmp.adj = icmp eq [[TY]] %lhs.memptr.adj, %rhs.memptr.adj
-  // CHECK: %[[OR:.*]] = or i1
-  // CHECK-SAME: %cmp.adj
-  // CHECK: %memptr.eq = and i1 %cmp.ptr, %[[OR]]
-  // CHECK: %sel.eq = select i1 %memptr.eq, i8 [[EQ]], i8 [[NE]]
-  // CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %[[DEST]]
-  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
-  // CHECK: ret
-  return x <=> y;
-}
-
-// CHECK-LABEL: @_Z13mem_data_testM6MemPtriS0_
-auto mem_data_test(MemDataT x, MemDataT y) {
-  // CHECK: %[[DEST:retval|agg.result]]
-  // CHECK: %[[CMP:.*]] = icmp eq i{{[0-9]+}} %{{.+}}, %{{.+}}
-  // CHECK: %sel.eq = select i1 %[[CMP]], i8 [[EQ]], i8 [[NE]]
-  // CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %[[DEST]]
-  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
-  return x <=> y;
-}
-
 // CHECK-LABEL: @_Z13test_constantv
 auto test_constant() {
   // CHECK: %[[DEST:retval|agg.result]]
@@ -111,16 +79,6 @@ auto test_constant() {
   return x <=> y;
 }
 
-// CHECK-LABEL: @_Z16test_nullptr_objPiDn
-auto test_nullptr_obj(int* x, decltype(nullptr) y) {
-  // CHECK: %[[DEST:retval|agg.result]]
-  // CHECK: %cmp.eq = icmp eq i32* %{{.+}}, null
-  // CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 [[NE]]
-  // CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %[[DEST]]
-  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
-  return x <=> y;
-}
-
 // CHECK-LABEL: @_Z18unscoped_enum_testijxy
 void unscoped_enum_test(int i, unsigned u, long long l, unsigned long long ul) {
   enum EnumA : int { A };
@@ -145,44 +103,3 @@ void unscoped_enum_test(int i, unsigned u, long long l, unsigned long long ul) {
   // 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: %[[DEST:retval|agg.result]]
-  // CHECK-NOT: select
-  // CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %[[DEST]]
-  // 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: %[[DEST:retval|agg.result]]
-  // 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]]* %[[DEST]]
-  // 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: %[[DEST:retval|agg.result]]
-  // 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]]* %[[DEST]]
-  // CHECK: store i8 %sel.eq, i8* %__value_, align 1
-  return x <=> y;
-}
-
-} // namespace ComplexTest
index a6ab605..eaf7951 100644 (file)
@@ -6,11 +6,8 @@ inline namespace __1 {
 
 // exposition only
 enum class _EqResult : unsigned char {
-  __zero = 0,
-  __equal = __zero,
+  __equal = 0,
   __equiv = __equal,
-  __nonequal = 1,
-  __nonequiv = __nonequal
 };
 
 enum class _OrdResult : signed char {
@@ -25,108 +22,6 @@ enum class _NCmpResult : signed char {
 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
@@ -147,11 +42,6 @@ public:
   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;
@@ -237,10 +127,6 @@ public:
   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);
@@ -331,14 +217,6 @@ public:
   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);
@@ -423,14 +301,6 @@ constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v)
   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
 
index a6ab605..eaf7951 100644 (file)
@@ -6,11 +6,8 @@ inline namespace __1 {
 
 // exposition only
 enum class _EqResult : unsigned char {
-  __zero = 0,
-  __equal = __zero,
+  __equal = 0,
   __equiv = __equal,
-  __nonequal = 1,
-  __nonequiv = __nonequal
 };
 
 enum class _OrdResult : signed char {
@@ -25,108 +22,6 @@ enum class _NCmpResult : signed char {
 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
@@ -147,11 +42,6 @@ public:
   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;
@@ -237,10 +127,6 @@ public:
   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);
@@ -331,14 +217,6 @@ public:
   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);
@@ -423,14 +301,6 @@ constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v)
   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
 
index da834e9..46e38f7 100644 (file)
@@ -213,45 +213,28 @@ struct ClassB : Class {};
 struct Class2 {};
 using FnTy = void(int);
 using FnTy2 = long(int);
+using FnTy3 = void(int) noexcept;
 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 r1 = (nullptr <=> nullptr); // expected-error {{invalid operands}}
+  auto r2 = (nullptr <=> x); // expected-error {{invalid operands}}
+  auto r3 = (fp <=> nullptr); // expected-error {{invalid operands}}
+  auto r4 = (0 <=> fp); // expected-error {{ordered comparison between pointer and zero}}
+  auto r5 = (nullptr <=> memp); // expected-error {{invalid operands}}
+  auto r6 = (0 <=> memdp); // expected-error {{invalid operands}}
+  auto r7 = (0 <=> nullptr); // expected-error {{invalid operands}}
+}
 
-  auto r7 = (0 <=> nullptr);
-  ASSERT_EXPR_TYPE(r7, std::strong_equality);
+void test_memptr(MemFnTy mf, MemDataTy md) {
+  (void)(mf <=> mf); // expected-error {{invalid operands}} expected-warning {{self-comparison}}
+  (void)(md <=> md); // expected-error {{invalid operands}} expected-warning {{self-comparison}}
 }
 
-void test_compatible_pointer(FnTy *f1, FnTy2 *f2, MemFnTy mf1, MemFnTyB mfb,
-                             MemFnTy2 mf2, MemFnTy3 mf3) {
+void test_compatible_pointer(FnTy *f1, FnTy2 *f2, FnTy3 *f3) {
   (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}}
+  (void)(f1 <=> f3); // expected-error {{invalid operands}}
 }
 
 // Test that variable narrowing is deferred for value dependent expressions
@@ -401,8 +384,7 @@ void test_mixed_float_int(float f, double d, long double ld) {
 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);
+  auto r = x <=> y; // expected-error {{invalid operands}}
 }
 } // namespace NullptrTest
 
@@ -413,25 +395,34 @@ 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) {  // expected-warning 3 {{'_Complex' is a C99 extension}}
-  (void)(ci <=> (_Complex int &)ci); // expected-warning {{'_Complex' is a C99 extension}}
-  (void)(ci <=> cf);
-  (void)(ci <=> i);
-  (void)(ci <=> f);
-  (void)(cf <=> i);
-  (void)(cf <=> f);
+  (void)(ci <=> (_Complex int &)ci); // expected-warning {{'_Complex' is a C99 extension}} expected-error {{invalid operands}}
+  (void)(ci <=> cf); // expected-error {{invalid operands}}
+  (void)(ci <=> i); // expected-error {{invalid operands}}
+  (void)(ci <=> f); // expected-error {{invalid operands}}
+  (void)(cf <=> i); // expected-error {{invalid operands}}
+  (void)(cf <=> f); // expected-error {{invalid operands}}
   (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) { // expected-warning 2 {{'_Complex' is a C99 extension}}
-  auto r = x <=> y;
-  ASSERT_EXPR_TYPE(r, std::strong_equality);
+  auto r = x <=> y; // expected-error {{invalid operands}}
 }
 
 void test_double(_Complex double x, _Complex double y) { // expected-warning 2 {{'_Complex' is a C99 extension}}
-  auto r = x <=> y;
-  ASSERT_EXPR_TYPE(r, std::weak_equality);
+  auto r = x <=> y; // expected-error {{invalid operands}}
 }
 
 } // namespace ComplexTest
+
+namespace Vector {
+  typedef __attribute__((ext_vector_type(4))) int V;
+  void f(V v1, V v2) {
+    // This would logically result in a vector of std::strong_ordering, but we
+    // don't support vectors of class type. We could model this as a vector of
+    // int (-1 / 0 / 1), but that doesn't extend to floating-point types (how
+    // to represent 'unordered')? For now, just reject.
+    (void)(v1 <=> v2); // expected-error {{three-way comparison between vectors is not supported}}
+  }
+}
index 3cd16d8..9012f37 100644 (file)
@@ -112,123 +112,19 @@ constexpr bool test_constexpr_success = [] {
     CHECK_TYPE(decltype(greater), PO);
     CHECK(greater.test_eq(GREATER));
   }
-  {
-    using SE = std::strong_equality;
-    auto EQ = SE::equal;
-    auto NEQ = SE::nonequal;
-
-    MemPtrT P1 = &MemPtr::foo;
-    MemPtrT P12 = &MemPtr::foo;
-    MemPtrT P2 = &MemPtr::bar;
-    MemPtrT P3 = nullptr;
-
-    auto eq = (P1 <=> P12);
-    CHECK_TYPE(decltype(eq), SE);
-    CHECK(eq.test_eq(EQ));
-
-    auto neq = (P1 <=> P2);
-    CHECK_TYPE(decltype(eq), SE);
-    CHECK(neq.test_eq(NEQ));
-
-    auto eq2 = (P3 <=> nullptr);
-    CHECK_TYPE(decltype(eq2), SE);
-    CHECK(eq2.test_eq(EQ));
-  }
-  {
-    using SE = std::strong_equality;
-    auto EQ = SE::equal;
-    auto NEQ = SE::nonequal;
-
-    FnPtrT F1 = &FnPtr1;
-    FnPtrT F12 = &FnPtr1;
-    FnPtrT F2 = &FnPtr2;
-    FnPtrT F3 = nullptr;
-
-    auto eq = (F1 <=> F12);
-    CHECK_TYPE(decltype(eq), SE);
-    CHECK(eq.test_eq(EQ));
-
-    auto neq = (F1 <=> F2);
-    CHECK_TYPE(decltype(neq), SE);
-    CHECK(neq.test_eq(NEQ));
-  }
-  { // 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));
-  }
 
   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@+1 {{unspecified}}
-  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@+1 {{must be initialized by a constant expression}}
-constexpr bool tc9 = test_constexpr<&dummy, &dummy2>(); // expected-note {{in call}}
+constexpr bool tc9 = (&dummy <=> &dummy2) != 0; // expected-error {{constant expression}} expected-note {{unspecified}}
 
 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
 
 constexpr bool for_range_init() {
index 3febb8f..4e85b37 100644 (file)
@@ -1,7 +1,9 @@
 // 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
+// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a -DTEST_TRIVIAL=1 %s
 
+#ifndef TEST_TRIVIAL
 void compare_not_found_test() {
   // expected-error@+1 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include <compare>}}
   (void)(0.0 <=> 42.123);
@@ -41,7 +43,7 @@ struct partial_ordering {
 } // namespace std
 
 auto missing_member_test() {
-  // expected-error@+1 {{standard library implementation of 'std::partial_ordering' is not supported; member 'less' is missing}}
+  // expected-error@+1 {{standard library implementation of 'std::partial_ordering' is not supported; member 'equivalent' is missing}}
   return (1.0 <=> 1.0);
 }
 
@@ -59,21 +61,22 @@ auto test_non_constexpr_var() {
   return (1 <=> 0);
 }
 
+#else
+
 namespace std {
 inline namespace __1 {
-struct strong_equality {
+struct strong_ordering {
   char value = 0;
-  constexpr strong_equality() = default;
+  constexpr strong_ordering() = default;
   // non-trivial
-  constexpr strong_equality(strong_equality const &other) : value(other.value) {}
+  constexpr strong_ordering(strong_ordering 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@+1 {{standard library implementation of 'std::strong_equality' is not supported; the type is not trivially copyable}}
+auto test_non_trivial(int LHS, int RHS) {
+  // expected-error@+1 {{standard library implementation of 'std::strong_ordering' is not supported; the type is not trivially copyable}}
   return LHS <=> RHS;
 }
+
+#endif
index 0829a2b..f46733e 100755 (executable)
@@ -926,7 +926,7 @@ as the draft C++2a standard evolves.
     <tr>
       <td rowspan="8">Consistent comparison (<tt>operator&lt;=&gt;</tt>)</td>
       <td><a href="https://wg21.link/p0515r3">P0515R3</a></td>
-      <td rowspan="7" class="svn" align="center">SVN</td>
+      <td rowspan="8" class="svn" align="center">SVN</td>
     </tr>
       <tr> <!-- from Jacksonville -->
         <td><a href="https://wg21.link/p0905r1">P0905R1</a></td>
@@ -948,7 +948,6 @@ as the draft C++2a standard evolves.
       </tr>
       <tr>
         <td><a href="https://wg21.link/p1959r0">P1959R0</a></td>
-        <td class="none" align="center">No</td>
       </tr>
     <tr>
       <td>Access checking on specializations</td>