From f495de43bd5da50286da6020e508d106cfc60f57 Mon Sep 17 00:00:00 2001 From: Richard Smith Date: Mon, 16 Dec 2019 17:40:03 -0800 Subject: [PATCH] [c++20] P1959R0: Remove support for std::*_equality. --- clang/include/clang/AST/ComparisonCategories.h | 35 +----- clang/include/clang/AST/Type.h | 11 ++ clang/include/clang/Basic/DiagnosticSemaKinds.td | 4 + clang/lib/AST/ComparisonCategories.cpp | 39 +------ clang/lib/AST/ExprConstant.cpp | 104 ++++++++++-------- clang/lib/AST/Interp/Interp.h | 7 +- clang/lib/CodeGen/CGExprAgg.cpp | 8 -- clang/lib/Sema/SemaExpr.cpp | 57 ++++++---- clang/lib/Sema/SemaOverload.cpp | 25 ++--- clang/test/CodeGenCXX/Inputs/std-compare.h | 132 +---------------------- clang/test/CodeGenCXX/cxx2a-compare.cpp | 83 -------------- clang/test/PCH/Inputs/std-compare.h | 132 +---------------------- clang/test/SemaCXX/Inputs/std-compare.h | 132 +---------------------- clang/test/SemaCXX/compare-cxx2a.cpp | 77 ++++++------- clang/test/SemaCXX/constant-expression-cxx2a.cpp | 106 +----------------- clang/test/SemaCXX/std-compare-cxx2a.cpp | 21 ++-- clang/www/cxx_status.html | 3 +- 17 files changed, 180 insertions(+), 796 deletions(-) diff --git a/clang/include/clang/AST/ComparisonCategories.h b/clang/include/clang/AST/ComparisonCategories.h index e28d499..70a7896 100644 --- a/clang/include/clang/AST/ComparisonCategories.h +++ b/clang/include/clang/AST/ComparisonCategories.h @@ -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 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 { diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h index 25ab44c..2968efa 100644 --- a/clang/include/clang/AST/Type.h +++ b/clang/include/clang/AST/Type.h @@ -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(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()) + return !T->getPointeeType()->isFunctionType(); + else + return false; +} + inline bool Type::isFunctionPointerType() const { if (const auto *T = getAs()) return T->getPointeeType()->isFunctionType(); diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8f303f2..2529f7f 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -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>; @@ -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< diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp index 9a07c24..0767323 100644 --- a/clang/lib/AST/ComparisonCategories.cpp +++ b/clang/lib/AST/ComparisonCategories.cpp @@ -23,31 +23,16 @@ Optional clang::getComparisonCategoryForBuiltinCmp(QualType T) { using CCT = ComparisonCategoryType; - if (const ComplexType *CT = T->getAs()) { - 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 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; diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 9b6d0cb..0b658cb 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -11481,6 +11481,14 @@ public: } } }; + +enum class CmpResult { + Unequal, + Less, + Equal, + Greater, + Unordered, +}; } template @@ -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, [&]() { diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index 8934efa..c12caa6 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -196,11 +196,8 @@ inline bool CmpHelperEQ(InterpState &S, CodePtr OpPC, CompareFn Fn) { const Pointer &RHS = S.Stk.pop(); const Pointer &LHS = S.Stk.pop(); - if (LHS.isZero() || RHS.isZero()) { - if (LHS.isZero() && RHS.isZero()) - S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Equal))); - else - S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Nonequal))); + if (LHS.isZero() && RHS.isZero()) { + S.Stk.push(BoolT::from(Fn(ComparisonCategoryResult::Equal))); return true; } diff --git a/clang/lib/CodeGen/CGExprAgg.cpp b/clang/lib/CodeGen/CGExprAgg.cpp index 9fb399c..8de609a 100644 --- a/clang/lib/CodeGen/CGExprAgg.cpp +++ b/clang/lib/CodeGen/CGExprAgg.cpp @@ -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()), diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 86c3684..15e86ba 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -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 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()->getPointeeType(); QualType rpointee = RHSType->castAs()->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, diff --git a/clang/lib/Sema/SemaOverload.cpp b/clang/lib/Sema/SemaOverload.cpp index 9c784ef..32541d6 100644 --- a/clang/lib/Sema/SemaOverload.cpp +++ b/clang/lib/Sema/SemaOverload.cpp @@ -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; diff --git a/clang/test/CodeGenCXX/Inputs/std-compare.h b/clang/test/CodeGenCXX/Inputs/std-compare.h index a6ab605..eaf7951 100644 --- a/clang/test/CodeGenCXX/Inputs/std-compare.h +++ b/clang/test/CodeGenCXX/Inputs/std-compare.h @@ -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 diff --git a/clang/test/CodeGenCXX/cxx2a-compare.cpp b/clang/test/CodeGenCXX/cxx2a-compare.cpp index 57b4449..262fead 100644 --- a/clang/test/CodeGenCXX/cxx2a-compare.cpp +++ b/clang/test/CodeGenCXX/cxx2a-compare.cpp @@ -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 diff --git a/clang/test/PCH/Inputs/std-compare.h b/clang/test/PCH/Inputs/std-compare.h index a6ab605..eaf7951 100644 --- a/clang/test/PCH/Inputs/std-compare.h +++ b/clang/test/PCH/Inputs/std-compare.h @@ -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 diff --git a/clang/test/SemaCXX/Inputs/std-compare.h b/clang/test/SemaCXX/Inputs/std-compare.h index a6ab605..eaf7951 100644 --- a/clang/test/SemaCXX/Inputs/std-compare.h +++ b/clang/test/SemaCXX/Inputs/std-compare.h @@ -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 diff --git a/clang/test/SemaCXX/compare-cxx2a.cpp b/clang/test/SemaCXX/compare-cxx2a.cpp index da834e9..46e38f7 100644 --- a/clang/test/SemaCXX/compare-cxx2a.cpp +++ b/clang/test/SemaCXX/compare-cxx2a.cpp @@ -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}} + } +} diff --git a/clang/test/SemaCXX/constant-expression-cxx2a.cpp b/clang/test/SemaCXX/constant-expression-cxx2a.cpp index 3cd16d8..9012f37 100644 --- a/clang/test/SemaCXX/constant-expression-cxx2a.cpp +++ b/clang/test/SemaCXX/constant-expression-cxx2a.cpp @@ -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 -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(); -constexpr bool tc2 = test_constexpr<&dummy, nullptr>(); - -// OK, equality comparison only -constexpr bool tc3 = test_constexpr<&MemPtr::foo, nullptr>(); -constexpr bool tc4 = test_constexpr(); -constexpr bool tc5 = test_constexpr<&MemPtr::foo, &MemPtr::bar>(); - -constexpr bool tc6 = test_constexpr<&MemPtr::data, nullptr>(); -constexpr bool tc7 = test_constexpr(); -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 constexpr T makeComplex(R r, I i) { T res{r, i}; return res; }; - -template -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() { diff --git a/clang/test/SemaCXX/std-compare-cxx2a.cpp b/clang/test/SemaCXX/std-compare-cxx2a.cpp index 3febb8f..4e85b37 100644 --- a/clang/test/SemaCXX/std-compare-cxx2a.cpp +++ b/clang/test/SemaCXX/std-compare-cxx2a.cpp @@ -1,7 +1,9 @@ // Test diagnostics for ill-formed STL 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 }} (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 diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html index 0829a2b..f46733e 100755 --- a/clang/www/cxx_status.html +++ b/clang/www/cxx_status.html @@ -926,7 +926,7 @@ as the draft C++2a standard evolves. Consistent comparison (operator<=>) P0515R3 - SVN + SVN P0905R1 @@ -948,7 +948,6 @@ as the draft C++2a standard evolves. P1959R0 - No Access checking on specializations -- 2.7.4