mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-12-14 03:29:57 +00:00
[c++20] P1959R0: Remove support for std::*_equality.
This commit is contained in:
parent
4e9f1379b9
commit
f495de43bd
@ -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 {
|
||||
|
@ -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();
|
||||
|
@ -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<
|
||||
|
@ -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;
|
||||
|
@ -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, [&]() {
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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()),
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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}}
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
|
@ -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
|
||||
|
@ -926,7 +926,7 @@ as the draft C++2a standard evolves.
|
||||
<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>
|
||||
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user