/// 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
};
/// [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;
}
enum class ComparisonCategoryResult : unsigned char {
Equal,
Equivalent,
- Nonequivalent,
- Nonequal,
Less,
Greater,
Unordered,
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.
/// 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 {
bool isReferenceType() const;
bool isLValueReferenceType() const;
bool isRValueReferenceType() const;
+ bool isObjectPointerType() const;
bool isFunctionPointerType() const;
bool isFunctionReferenceType() const;
bool isMemberPointerType() 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();
"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">>;
"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<
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.
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:
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:
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;
}
}
};
+
+enum class CmpResult {
+ Unequal,
+ Less,
+ Equal,
+ Greater,
+ Unordered,
+};
}
template <class SuccessCB, class AfterCB>
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();
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()) {
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()) {
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);
}
}
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");
};
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();
}
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()) {
// 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
// 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()) {
// 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();
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);
"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:
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, [&]() {
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;
}
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()) {
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()),
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());
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,
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();
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,
// 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.
// 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))
// 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();
// 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();
// 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();
}
// 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();
}
// 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) {
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,
// 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
} 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;
}
// 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();
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,
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())
{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;
// exposition only
enum class _EqResult : unsigned char {
- __zero = 0,
- __equal = __zero,
+ __equal = 0,
__equiv = __equal,
- __nonequal = 1,
- __nonequiv = __nonequal
};
enum class _OrdResult : 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
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;
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);
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);
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
// 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
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]]
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 };
// 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
// exposition only
enum class _EqResult : unsigned char {
- __zero = 0,
- __equal = __zero,
+ __equal = 0,
__equiv = __equal,
- __nonequal = 1,
- __nonequiv = __nonequal
};
enum class _OrdResult : 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
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;
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);
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);
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
// exposition only
enum class _EqResult : unsigned char {
- __zero = 0,
- __equal = __zero,
+ __equal = 0,
__equiv = __equal,
- __nonequal = 1,
- __nonequiv = __nonequal
};
enum class _OrdResult : 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
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;
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);
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);
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
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
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
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}}
+ }
+}
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() {
// 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);
} // 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);
}
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
<tr>
<td rowspan="8">Consistent comparison (<tt>operator<=></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>
</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>