[c++20] P1959R0: Remove support for std::*_equality.

This commit is contained in:
Richard Smith 2019-12-16 17:40:03 -08:00
parent 4e9f1379b9
commit f495de43bd
17 changed files with 181 additions and 797 deletions

View 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 {

View 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();

View 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<

View 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;

View 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, [&]() {

View 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;
}

View 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()),

View 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,

View 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;

View 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

View 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

View 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

View 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

View 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 r7 = (0 <=> nullptr);
ASSERT_EXPR_TYPE(r7, 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}}
}
void test_compatible_pointer(FnTy *f1, FnTy2 *f2, MemFnTy mf1, MemFnTyB mfb,
MemFnTy2 mf2, MemFnTy3 mf3) {
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, 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}}
}
}

View 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() {

View 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

View File

@ -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>