Index: include/clang/AST/ASTContext.h =================================================================== --- include/clang/AST/ASTContext.h +++ include/clang/AST/ASTContext.h @@ -18,6 +18,7 @@ #include "clang/AST/ASTTypeTraits.h" #include "clang/AST/CanonicalType.h" #include "clang/AST/CommentCommandTraits.h" +#include "clang/AST/ComparisonCategories.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclarationName.h" @@ -1973,6 +1974,13 @@ QualType GetBuiltinType(unsigned ID, GetBuiltinTypeError &Error, unsigned *IntegerConstantArgs = nullptr) const; + /// \brief Types and expressions required to build C++2a three-way comparisons + /// using operator<=>, including the values return by builtin <=> operators. + /// + /// This object needs to be initialized by Sema the first time it checks + /// a three-way comparison. + ComparisonCategories CompCategories; + private: CanQualType getFromTargetType(unsigned Type) const; TypeInfo getTypeInfoImpl(const Type *T) const; Index: include/clang/AST/ComparisonCategories.h =================================================================== --- /dev/null +++ include/clang/AST/ComparisonCategories.h @@ -0,0 +1,199 @@ +//===- ComparisonCategories.h - Three Way Comparison Data -------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Comparison Category enum and data types, which +// store the types and expressions needed to support operator<=> +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_AST_COMPARISONCATEGORIES_H +#define LLVM_CLANG_AST_COMPARISONCATEGORIES_H + +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/Optional.h" +#include "clang/Basic/LLVM.h" +#include +#include +#include + +namespace llvm { + class StringRef; +} + +namespace clang { + +class DeclRefExpr; +class RecordDecl; +class QualType; + +/// \brief An enumeration representing the different comparison categories. +/// The values map to the category types defined in the standard library. +/// i.e. 'std::weak_equality'. +enum class ComparisonCategoryKind : unsigned char { + WeakEquality, + StrongEquality, + PartialOrdering, + WeakOrdering, + StrongOrdering, + Last = StrongOrdering, + First = WeakEquality +}; + +/// \brief An enumeration representing the possible results of a three-way +/// comparison. These values map onto instances of comparison category types +/// defined in the standard library. i.e. 'std::strong_ordering::less'. +enum class ComparisonCategoryResult : unsigned char { + Invalid, // Internal implementation detail + Equal, + Equivalent, + Nonequivalent, + Nonequal, + Less, + Greater, + Unordered, +}; + +struct ComparisonCategoryInfo { + /// \brief The declaration for the comparison category type from the + /// standard library. + RecordDecl *CCDecl = nullptr; + + /// \brief A map containing the comparison category values built from the + /// standard library. The key is a value of ComparisonCategoryValue. + llvm::DenseMap Objects; + + /// \brief The Kind of the comparison category type + ComparisonCategoryKind Kind; + +public: + /// \brief Return an expression referencing the member of the specified + /// comparison category. For example 'std::strong_equality::equal' + const DeclRefExpr *getResultValue(ComparisonCategoryResult ValueKind) const { + const DeclRefExpr *DR = getResultValueUnsafe(ValueKind); + assert(DR && + "comparison category does not contain the specified result kind"); + return DR; + } + + const DeclRefExpr * + getResultValueUnsafe(ComparisonCategoryResult ValueKind) const { + assert(ValueKind != ComparisonCategoryResult::Invalid && + "invalid value specified"); + char Key = static_cast(ValueKind); + return Objects.lookup(Key); + } + + /// \brief True iff the comparison category is an equality comparison. + bool isEquality() const { return !isOrdered(); } + + /// \brief True iff the comparison category is a relational comparison. + bool isOrdered() const { + using CCK = ComparisonCategoryKind; + return Kind == CCK::PartialOrdering || Kind == CCK::WeakOrdering || + Kind == CCK::StrongOrdering; + } + + /// \brief True iff the comparison is "strong". i.e. it checks equality and + /// not equivalence. + bool isStrong() const { + using CCK = ComparisonCategoryKind; + return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering; + } + + /// \brief True iff the comparison is not totally ordered. + bool isPartial() const { + using CCK = ComparisonCategoryKind; + return Kind == CCK::PartialOrdering; + } + + /// \brief Converts the specified result kind into the the correct result kind + /// for this category. Specifically it lowers strong equality results to + /// 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; + } + return Res; + } + + const DeclRefExpr *getEqualOrEquiv() const { + return getResultValue(makeWeakResult(ComparisonCategoryResult::Equal)); + } + const DeclRefExpr *getNonequalOrNonequiv() const { + return getResultValue(makeWeakResult(ComparisonCategoryResult::Nonequal)); + } + const DeclRefExpr *getLess() const { + assert(isOrdered()); + return getResultValue(ComparisonCategoryResult::Less); + } + const DeclRefExpr *getGreater() const { + assert(isOrdered()); + return getResultValue(ComparisonCategoryResult::Greater); + } + const DeclRefExpr *getUnordered() const { + assert(isPartial()); + return getResultValue(ComparisonCategoryResult::Unordered); + } +}; + +struct ComparisonCategories { + static StringRef getCategoryString(ComparisonCategoryKind Kind); + static StringRef getResultString(ComparisonCategoryResult Kind); + + /// \brief Return the comparison category information for the category + /// specified by 'Kind'. + const ComparisonCategoryInfo &getInfo(ComparisonCategoryKind Kind) const { + assert(HasData && "comparison category information not yet built"); + return Data[static_cast(Kind)]; + } + + /// \brief Return the comparison category decl for the category + /// specified by 'Kind'. + const RecordDecl *getDecl(ComparisonCategoryKind Kind) const { + return getInfo(Kind).CCDecl; + } + + /// \brief Return the comparison category information as specified by + /// `getCategoryForType(Ty)`. + const ComparisonCategoryInfo &getInfoForType(QualType Ty) const; + + /// \brief Return the comparison category kind corresponding to the specified + /// type. 'Ty' is expected to refer to the type of one of the comparison + /// category decls; if it doesn't no value is returned. + Optional getCategoryForType(QualType Ty) const; + + /// \brief returns true if the comparison category data has already been + /// built, and false otherwise. + bool hasData() const { return HasData; } + +public: + // implementation details which should only be used by the function creating + // and setting the data. + using InfoList = + std::array(ComparisonCategoryKind::Last) + 1>; + + void setData(InfoList &&NewData) { + assert(!HasData && "comparison categories already built"); + Data = std::move(NewData); + HasData = true; + } + +private: + bool HasData = false; + InfoList Data; +}; + +} // namespace clang + +#endif Index: include/clang/AST/Expr.h =================================================================== --- include/clang/AST/Expr.h +++ include/clang/AST/Expr.h @@ -3014,15 +3014,19 @@ // This is only meaningful for operations on floating point types and 0 // otherwise. unsigned FPFeatures : 2; + + // When Opc is BO_Cmp, this is set only when the comparison category is an + // ordered comparison. Otherwise the comparison is an equality comparison. + unsigned IsCmpOrdered : 1; + SourceLocation OpLoc; enum { LHS, RHS, END_EXPR }; Stmt* SubExprs[END_EXPR]; public: - BinaryOperator(Expr *lhs, Expr *rhs, Opcode opc, QualType ResTy, - ExprValueKind VK, ExprObjectKind OK, - SourceLocation opLoc, FPOptions FPFeatures) + ExprValueKind VK, ExprObjectKind OK, SourceLocation OpLoc, + FPOptions FPFeatures) : Expr(BinaryOperatorClass, ResTy, VK, OK, lhs->isTypeDependent() || rhs->isTypeDependent(), lhs->isValueDependent() || rhs->isValueDependent(), @@ -3030,7 +3034,8 @@ rhs->isInstantiationDependent()), (lhs->containsUnexpandedParameterPack() || rhs->containsUnexpandedParameterPack())), - Opc(opc), FPFeatures(FPFeatures.getInt()), OpLoc(opLoc) { + Opc(opc), FPFeatures(FPFeatures.getInt()), IsCmpOrdered(false), + OpLoc(OpLoc) { SubExprs[LHS] = lhs; SubExprs[RHS] = rhs; assert(!isCompoundAssignmentOp() && @@ -3089,10 +3094,16 @@ bool isBitwiseOp() const { return isBitwiseOp(getOpcode()); } static bool isRelationalOp(Opcode Opc) { return Opc >= BO_LT && Opc<=BO_GE; } - bool isRelationalOp() const { return isRelationalOp(getOpcode()); } + bool isRelationalOp() const { + return isRelationalOp(getOpcode()) || + (getOpcode() == BO_Cmp && IsCmpOrdered); + } static bool isEqualityOp(Opcode Opc) { return Opc == BO_EQ || Opc == BO_NE; } - bool isEqualityOp() const { return isEqualityOp(getOpcode()); } + bool isEqualityOp() const { + return isEqualityOp(getOpcode()) || + (getOpcode() == BO_Cmp && !IsCmpOrdered); + } static bool isComparisonOp(Opcode Opc) { return Opc >= BO_Cmp && Opc<=BO_NE; } bool isComparisonOp() const { return isComparisonOp(getOpcode()); } @@ -3153,6 +3164,9 @@ return isShiftAssignOp(getOpcode()); } + void setIsCmpOrdered(bool Val) { IsCmpOrdered = Val; } + bool getIsCmpOrdered() const { return IsCmpOrdered; } + // Return true if a binary operator using the specified opcode and operands // would match the 'p = (i8*)nullptr + n' idiom for casting a pointer-sized // integer to a pointer. Index: include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- include/clang/Basic/DiagnosticSemaKinds.td +++ include/clang/Basic/DiagnosticSemaKinds.td @@ -9372,4 +9372,23 @@ def err_multiversion_not_supported : Error< "function multiversioning is not supported on the current target">; +// spaceship operator diagnostics +def err_implied_comparison_category_type_not_found : Error< + "%0 type was not found; include before defining " + "or using 'operator<=>'">; +def err_spaceship_argument_narrowing : Error< + "argument to 'operator<=>' " + "%select{cannot be narrowed from type %1 to %2|" + "evaluates to %1, which cannot be narrowed to type %2}0">; +def err_spaceship_comparison_of_void_ptr : Error< + "three-way comparison with void pointer %select{operand type|operand types}0 " + "(%1 and %2)">; +def err_spaceship_comparison_of_invalid_comp_type : Error< + "three-way comparison of operands has composite type %0 (%1 and %2)">; +def err_std_compare_type_missing_member : Error< + "%0 missing a member named '%1'">; +def err_std_compare_type_invalid_member : Error< + "member '%1' of %0 is ill-formed">; +def note_spaceship_operand_not_cce : Note< + "argument is not a constant expression">; } // end of sema component. Index: include/clang/Sema/Overload.h =================================================================== --- include/clang/Sema/Overload.h +++ include/clang/Sema/Overload.h @@ -330,9 +330,10 @@ } ImplicitConversionRank getRank() const; - NarrowingKind getNarrowingKind(ASTContext &Context, const Expr *Converted, - APValue &ConstantValue, - QualType &ConstantType) const; + NarrowingKind + getNarrowingKind(ASTContext &Context, const Expr *Converted, + APValue &ConstantValue, QualType &ConstantType, + bool IgnoreFloatToIntegralConversion = false) const; bool isPointerConversionToBool() const; bool isPointerConversionToVoidPointer(ASTContext& Context) const; void dump() const; Index: include/clang/Sema/Sema.h =================================================================== --- include/clang/Sema/Sema.h +++ include/clang/Sema/Sema.h @@ -4541,6 +4541,14 @@ CXXRecordDecl *getStdBadAlloc() const; EnumDecl *getStdAlignValT() const; + /// \brief Lookup the comparison category types in the standard library, and + /// build DeclRefExprs to values returned by the operator<=> builtins. The + /// results are cached in ASTContext so they are accessible outside of Sema. + /// An error is emitted if the types are not found or another error occurs. + /// + /// \return true if an error occurred. False otherwise. + bool BuildComparisonCategoryData(SourceLocation Loc); + /// \brief Tests whether Ty is an instance of std::initializer_list and, if /// it is and Element is not NULL, assigns the element type to Element. bool isStdInitializerList(QualType Ty, QualType *Element); Index: lib/AST/CMakeLists.txt =================================================================== --- lib/AST/CMakeLists.txt +++ lib/AST/CMakeLists.txt @@ -20,6 +20,7 @@ CommentLexer.cpp CommentParser.cpp CommentSema.cpp + ComparisonCategories.cpp DataCollection.cpp Decl.cpp DeclarationName.cpp Index: lib/AST/ComparisonCategories.cpp =================================================================== --- /dev/null +++ lib/AST/ComparisonCategories.cpp @@ -0,0 +1,82 @@ +//===- ComparisonCategories.cpp - Three Way Comparison Data -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the Comparison Category enum and data types, which +// store the types and expressions needed to support operator<=> +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ComparisonCategories.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Type.h" + +using namespace clang; + +Optional +ComparisonCategories::getCategoryForType(QualType Ty) const { + assert(hasData() && "comparison category data not built"); + assert(!Ty.isNull() && "type must be non-null"); + if (const auto *RD = Ty->getAsCXXRecordDecl()) { + const auto *CanonRD = RD->getCanonicalDecl(); + for (auto &Info : Data) { + if (CanonRD == Info.CCDecl->getCanonicalDecl()) + return Info.Kind; + } + } + return None; +} + +const ComparisonCategoryInfo & +ComparisonCategories::getInfoForType(QualType Ty) const { + Optional OptKind = getCategoryForType(Ty); + assert(OptKind && + "return type for operator<=> is not a comparison category type"); + return getInfo(OptKind.getValue()); +} + +StringRef ComparisonCategories::getCategoryString(ComparisonCategoryKind Kind) { + using CCKT = ComparisonCategoryKind; + switch (Kind) { + case CCKT::WeakEquality: + return "weak_equality"; + case CCKT::StrongEquality: + return "strong_equality"; + case CCKT::PartialOrdering: + return "partial_ordering"; + case CCKT::WeakOrdering: + return "weak_ordering"; + case CCKT::StrongOrdering: + return "strong_ordering"; + } + llvm_unreachable("unhandled cases in switch"); +} + +StringRef ComparisonCategories::getResultString(ComparisonCategoryResult Kind) { + using CCVT = ComparisonCategoryResult; + 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: + return "greater"; + case CCVT::Unordered: + return "unordered"; + case CCVT::Invalid: + return "invalid"; + } + llvm_unreachable("unhandled case in switch"); +} Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -2242,6 +2242,8 @@ case BO_GE: Result = LHS >= RHS; return true; case BO_EQ: Result = LHS == RHS; return true; case BO_NE: Result = LHS != RHS; return true; + case BO_Cmp: + llvm_unreachable("BO_Cmp should be handled elsewhere"); } } @@ -5059,7 +5061,7 @@ } }; -} +} // namespace //===----------------------------------------------------------------------===// // Common base class for lvalue and temporary evaluation. @@ -6232,6 +6234,33 @@ bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E); bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T); bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E); + + bool VisitBinCmp(const BinaryOperator *E) { + using CCR = ComparisonCategoryResult; + const ComparisonCategoryInfo &CmpInfo = + Info.Ctx.CompCategories.getInfoForType(E->getType()); + + // Build a new version of the binary operator which returns an integer + // representing the ComparisonCategoryResult. Then defer to + // IntExprEvaluator to evaluate it. + BinaryOperator NewOp(E->getLHS(), E->getRHS(), BO_Cmp, Info.Ctx.IntTy, + VK_RValue, OK_Ordinary, E->getOperatorLoc(), + E->getFPFeatures()); + NewOp.setIsCmpOrdered(E->getIsCmpOrdered()); + + APSInt Result; + if (!EvaluateInteger(&NewOp, Result, Info)) + return false; + CCR CmpRes = static_cast(Result.getExtValue()); + + const DeclRefExpr *Value = + CmpInfo.getResultValue(CmpInfo.makeWeakResult(CmpRes)); + + APValue Res; + if (!EvaluateAsRValue(Info, Value, Res)) + return false; + return Success(Res, E); + } }; } @@ -7071,12 +7100,11 @@ //===----------------------------------------------------------------------===// namespace { -class IntExprEvaluator - : public ExprEvaluatorBase { +class IntExprEvaluator : public ExprEvaluatorBase { APValue &Result; public: IntExprEvaluator(EvalInfo &info, APValue &result) - : ExprEvaluatorBaseTy(info), Result(result) {} + : ExprEvaluatorBase(info), Result(result) {} bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) { assert(E->getType()->isIntegralOrEnumerationType() && @@ -7116,6 +7144,13 @@ return Success(Value, E, Result); } + bool Success(ComparisonCategoryResult Res, const Expr *E) { + assert(isa(E) && + cast(E)->getOpcode() == BO_Cmp && + "operator<=> result reported for non-operator<=>?"); + return Success(static_cast(Res), E); + } + bool Success(CharUnits Size, const Expr *E) { return Success(Size.getQuantity(), E); } @@ -8215,12 +8250,11 @@ /// We handle binary operators that are comma, logical, or that have operands /// with integral or enumeration type. static bool shouldEnqueue(const BinaryOperator *E) { - return E->getOpcode() == BO_Comma || - E->isLogicalOp() || - (E->isRValue() && - E->getType()->isIntegralOrEnumerationType() && + return E->getOpcode() != BO_Cmp && // TODO Make this work with <=> + (E->getOpcode() == BO_Comma || E->isLogicalOp() || + (E->isRValue() && E->getType()->isIntegralOrEnumerationType() && E->getLHS()->getType()->isIntegralOrEnumerationType() && - E->getRHS()->getType()->isIntegralOrEnumerationType()); + E->getRHS()->getType()->isIntegralOrEnumerationType())); } bool Traverse(const BinaryOperator *E) { @@ -8498,6 +8532,9 @@ } bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { + using CCR = ComparisonCategoryResult; + bool IsSpaceship = E->getOpcode() == BO_Cmp; + auto &Info = this->Info; // We don't call noteFailure immediately because the assignment happens after // we evaluate LHS and RHS. if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp()) @@ -8600,10 +8637,26 @@ return Success(CR == APFloat::cmpGreaterThan || CR == APFloat::cmpLessThan || CR == APFloat::cmpUnordered, E); + case BO_Cmp: { + auto GetCmpRes = [&]() { + switch (CR) { + case APFloat::cmpEqual: + return CCR::Equal; + case APFloat::cmpUnordered: + return CCR::Unordered; + case APFloat::cmpLessThan: + return CCR::Less; + case APFloat::cmpGreaterThan: + return CCR::Greater; + } + }; + return Success(GetCmpRes(), E); + } } } if (LHSTy->isPointerType() && RHSTy->isPointerType()) { + assert(!LHSTy->isMemberPointerType()); if (E->getOpcode() == BO_Sub || E->isComparisonOp()) { LValue LHSValue, RHSValue; @@ -8668,6 +8721,8 @@ if ((RHSValue.Base && isZeroSized(LHSValue)) || (LHSValue.Base && isZeroSized(RHSValue))) return Error(E); + if (E->getOpcode() == BO_Cmp) + return Success(CCR::Nonequal, E); // Pointers with different bases cannot represent the same object. return Success(E->getOpcode() == BO_NE, E); } @@ -8811,6 +8866,14 @@ case BO_GE: return Success(CompareLHS >= CompareRHS, E); case BO_EQ: return Success(CompareLHS == CompareRHS, E); case BO_NE: return Success(CompareLHS != CompareRHS, E); + case BO_Cmp: { + if (CompareLHS < CompareRHS) + return Success(CCR::Less, E); + if (CompareLHS > CompareRHS) + return Success(CCR::Greater, E); + return Success(CCR::Equal, E); + + } } } } @@ -8833,6 +8896,8 @@ // null, they compare unequal. if (!LHSValue.getDecl() || !RHSValue.getDecl()) { bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl(); + if (E->getOpcode() == BO_Cmp) + return Success(Equal ? CCR::Equal : CCR::Nonequal, E); return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E); } @@ -8850,6 +8915,9 @@ // they were dereferenced with a hypothetical object of the associated // class type. bool Equal = LHSValue == RHSValue; + if (E->getOpcode() == BO_Cmp) + return Success(Equal ? CCR::Equal : CCR::Nonequal, E); + return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E); } @@ -8860,14 +8928,37 @@ // are compared, the result is true of the operator is <=, >= or ==, and // false otherwise. BinaryOperator::Opcode Opcode = E->getOpcode(); + if (Opcode == BO_Cmp) + return Success(CCR::Equal, E); return Success(Opcode == BO_EQ || Opcode == BO_LE || Opcode == BO_GE, E); } + if (IsSpaceship && LHSTy->isIntegralOrEnumerationType() && + RHSTy->isIntegralOrEnumerationType()) { + APSInt LHS, RHS; + bool LHSOK = EvaluateInteger(E->getLHS(), LHS, Info); + if (!LHSOK && !Info.noteFailure()) + return false; + + if (!EvaluateInteger(E->getRHS(), RHS, Info) || !LHSOK) + return false; + + if (LHS < RHS) + return Success(CCR::Less, E); + if (LHS > RHS) + return Success(CCR::Greater, E); + return Success(CCR::Equal, E); + } + assert((!LHSTy->isIntegralOrEnumerationType() || !RHSTy->isIntegralOrEnumerationType()) && "DataRecursiveIntBinOpEvaluator should have handled integral types"); + assert(!IsSpaceship && "case not handled for operator<=>"); // We can't continue from here for non-integral types. return ExprEvaluatorBaseTy::VisitBinaryOperator(E); + + + } /// VisitUnaryExprOrTypeTraitExpr - Evaluate a sizeof, alignof or vec_step with @@ -10609,7 +10700,6 @@ case BO_AndAssign: case BO_XorAssign: case BO_OrAssign: - case BO_Cmp: // FIXME: Re-enable once we can evaluate this. // C99 6.6/3 allows assignments within unevaluated subexpressions of // constant expressions, but they can never be ICEs because an ICE cannot // contain an lvalue operand. @@ -10631,7 +10721,8 @@ case BO_And: case BO_Xor: case BO_Or: - case BO_Comma: { + case BO_Comma: + case BO_Cmp: { ICEDiag LHSResult = CheckICE(Exp->getLHS(), Ctx); ICEDiag RHSResult = CheckICE(Exp->getRHS(), Ctx); if (Exp->getOpcode() == BO_Div || @@ -10660,6 +10751,16 @@ return ICEDiag(IK_NotICE, E->getLocStart()); } } + if (Exp->getOpcode() == BO_Cmp) { + // Check that all of the references to the result objects are ICE. + const ComparisonCategoryInfo &CmpInfo = + Ctx.CompCategories.getInfoForType(Exp->getType()); + ICEDiag RetDiag(IK_ICE, E->getLocStart()); + for (const auto &KV : CmpInfo.Objects) + RetDiag = Worst(RetDiag, CheckICE(KV.second, Ctx)); + + return Worst(Worst(LHSResult, RHSResult), RetDiag); + } return Worst(LHSResult, RHSResult); } case BO_LAnd: Index: lib/CodeGen/CGExprAgg.cpp =================================================================== --- lib/CodeGen/CGExprAgg.cpp +++ lib/CodeGen/CGExprAgg.cpp @@ -11,19 +11,21 @@ // //===----------------------------------------------------------------------===// -#include "CodeGenFunction.h" +#include "CGCXXABI.h" #include "CGObjCRuntime.h" +#include "CodeGenFunction.h" #include "CodeGenModule.h" #include "ConstantEmitter.h" #include "clang/AST/ASTContext.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" +#include "llvm/ADT/SmallVector.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalVariable.h" -#include "llvm/IR/Intrinsics.h" #include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/Intrinsics.h" using namespace clang; using namespace CodeGen; @@ -145,6 +147,7 @@ void VisitPointerToDataMemberBinaryOperator(const BinaryOperator *BO); void VisitBinAssign(const BinaryOperator *E); void VisitBinComma(const BinaryOperator *E); + void VisitBinCmp(const BinaryOperator *E); void VisitObjCMessageExpr(ObjCMessageExpr *E); void VisitObjCIvarRefExpr(ObjCIvarRefExpr *E) { @@ -196,6 +199,7 @@ RValue Res = CGF.EmitAtomicExpr(E); EmitFinalDestCopy(E->getType(), Res); } + }; } // end anonymous namespace. @@ -879,10 +883,125 @@ CGF.EmitCompoundStmt(*E->getSubStmt(), true, Dest); } +enum CompareKind { + CK_Less, + CK_Greater, + CK_Equal, +}; + +static llvm::Value *EmitCompare(CGBuilderTy &Builder, CodeGenFunction &CGF, + const BinaryOperator *E, llvm::Value *LHS, + llvm::Value *RHS, CompareKind Kind) { + QualType ArgTy = E->getLHS()->getType(); + if (const auto *MPT = ArgTy->getAs()) { + assert(Kind == CK_Equal && + "member pointers may only be compared for equality"); + return CGF.CGM.getCXXABI().EmitMemberPointerComparison( + CGF, LHS, RHS, MPT, /*IsInequality*/ false); + } + + // Compute the comparison instructions for the specified comparison kind. + struct CmpInstInfo { + const char *Name; + llvm::CmpInst::Predicate FCmp; + llvm::CmpInst::Predicate SCmp; + llvm::CmpInst::Predicate UCmp; + }; + CmpInstInfo InstInfo = [&]() -> CmpInstInfo { + using FI = llvm::FCmpInst; + using II = llvm::ICmpInst; + switch (Kind) { + case CK_Less: + return {"cmp.lt", FI::FCMP_OLT, II::ICMP_SLT, II::ICMP_ULT}; + case CK_Greater: + return {"cmp.gt", FI::FCMP_OGT, II::ICMP_SGT, II::ICMP_UGT}; + case CK_Equal: + return {"cmp.eq", FI::FCMP_OEQ, II::ICMP_EQ, II::ICMP_EQ}; + } + }(); + + if (ArgTy->isRealFloatingType()) { + return Builder.CreateFCmp(InstInfo.FCmp, LHS, RHS, InstInfo.Name); + } else if (ArgTy->isIntegralOrEnumerationType() || ArgTy->isPointerType()) { + auto Inst = + ArgTy->hasSignedIntegerRepresentation() ? InstInfo.SCmp : InstInfo.UCmp; + return Builder.CreateICmp(Inst, LHS, RHS, InstInfo.Name); + } else if (ArgTy->isAnyComplexType()) { + llvm_unreachable("support for complex types is unimplemented"); + } else if (ArgTy->isVectorType()) { + llvm_unreachable("support for vector types is unimplemented"); + } else { + llvm_unreachable("unexpected argument type when building operator<=>"); + } +} + +void AggExprEmitter::VisitBinCmp(const BinaryOperator *E) { + using llvm::BasicBlock; + using llvm::PHINode; + using llvm::Value; + assert(CGF.getContext().hasSameType(E->getLHS()->getType(), + E->getRHS()->getType())); + const ComparisonCategoryInfo &CmpInfo = + CGF.getContext().CompCategories.getInfoForType(E->getType()); + + QualType ArgTy = E->getLHS()->getType(); + Value *LHS = nullptr, *RHS = nullptr; + switch (CGF.getEvaluationKind(ArgTy)) { + case TEK_Scalar: + LHS = CGF.EmitScalarExpr(E->getLHS()); + RHS = CGF.EmitScalarExpr(E->getRHS()); + break; + case TEK_Aggregate: + // FIXME: Is this ever used? + LHS = CGF.EmitAnyExpr(E->getLHS()).getAggregatePointer(); + RHS = CGF.EmitAnyExpr(E->getRHS()).getAggregatePointer(); + break; + case TEK_Complex: + llvm_unreachable("support for complex types is unimplemented"); + } + assert(LHS && RHS); + + auto EmitCmpRes = [&](const DeclRefExpr *DRE) { + return CGF.EmitLValue(DRE).getPointer(); + }; + auto EmitCmp = [&](CompareKind K) { + return EmitCompare(Builder, CGF, E, LHS, RHS, K); + }; + Value *Select = nullptr; + 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()), + EmitCmpRes(CmpInfo.getGreater()), "sel.lt"); + Select = Builder.CreateSelect(EmitCmp(CK_Equal), + EmitCmpRes(CmpInfo.getEqualOrEquiv()), + SelectOne, "sel.eq"); + } else { + Value *SelectEq = Builder.CreateSelect( + EmitCmp(CK_Equal), EmitCmpRes(CmpInfo.getEqualOrEquiv()), + EmitCmpRes(CmpInfo.getUnordered()), "sel.eq"); + + Value *SelectGT = Builder.CreateSelect(EmitCmp(CK_Greater), + EmitCmpRes(CmpInfo.getGreater()), + SelectEq, "sel.gt"); + Select = Builder.CreateSelect( + EmitCmp(CK_Less), EmitCmpRes(CmpInfo.getLess()), SelectGT, "sel.lt"); + } + assert(Select != nullptr); + return EmitFinalDestCopy( + E->getType(), CGF.MakeNaturalAlignAddrLValue(Select, E->getType())); +} + void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) { - if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) + + if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI) { VisitPointerToDataMemberBinaryOperator(E); - else + return; + } + CGF.ErrorUnsupported(E, "aggregate binary expression"); } Index: lib/Sema/SemaDeclCXX.cpp =================================================================== --- lib/Sema/SemaDeclCXX.cpp +++ lib/Sema/SemaDeclCXX.cpp @@ -17,6 +17,7 @@ #include "clang/AST/ASTMutationListener.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/CharUnits.h" +#include "clang/AST/ComparisonCategories.h" #include "clang/AST/EvaluatedExprVisitor.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecordLayout.h" @@ -8884,6 +8885,96 @@ return StdExperimentalNamespaceCache; } +bool Sema::BuildComparisonCategoryData(SourceLocation Loc) { + using CCKT = ComparisonCategoryKind; + using CCVT = ComparisonCategoryResult; + assert(getLangOpts().CPlusPlus && + "Looking for comparison category type outside of C++."); + + // Check if we've already successfully built the comparison category data. + if (Context.CompCategories.hasData()) + return false; + + ComparisonCategories::InfoList NewData; + // Otherwise, look up each of the category types and build their required + // data. + for (unsigned I = static_cast(CCKT::First), + End = static_cast(CCKT::Last); + I <= End; ++I) { + CCKT CCK = static_cast(I); + + StringRef Name = ComparisonCategories::getCategoryString(CCK); + + // Build the initial category information + ComparisonCategoryInfo Info; + Info.Kind = CCK; + + // Lookup the record for the category type + if (auto Std = getStdNamespace()) { + LookupResult Result(*this, &PP.getIdentifierTable().get(Name), + SourceLocation(), Sema::LookupTagName); + if (LookupQualifiedName(Result, Std)) + Info.CCDecl = Result.getAsSingle(); + Result.suppressDiagnostics(); + } + if (!Info.CCDecl) { + Diag(Loc, diag::err_implied_comparison_category_type_not_found) << Name; + return true; + } + + // Calculate the list of values belonging to this comparison category type. + SmallVector Values; + Values.push_back(CCVT::Equivalent); + if (Info.isStrong()) + Values.push_back(CCVT::Equal); + if (Info.isOrdered()) { + Values.push_back(CCVT::Less); + Values.push_back(CCVT::Greater); + } else { + Values.push_back(CCVT::Nonequivalent); + if (Info.isStrong()) + Values.push_back(CCVT::Nonequal); + } + if (Info.isPartial()) + Values.push_back(CCVT::Unordered); + + // Build each of the require values and store them in Info. + for (CCVT CCV : Values) { + StringRef ValueName = ComparisonCategories::getResultString(CCV); + QualType Ty(Info.CCDecl->getTypeForDecl(), 0); + DeclContext *LookupCtx = computeDeclContext(Ty); + LookupResult Found(*this, &PP.getIdentifierTable().get(ValueName), Loc, + Sema::LookupOrdinaryName); + if (!LookupQualifiedName(Found, LookupCtx)) { + Diag(Loc, diag::err_std_compare_type_missing_member) + << Info.CCDecl << ValueName; + return true; + } + auto *VD = Found.getAsSingle(); + if (!VD || !VD->isStaticDataMember()) { + // FIXME: Handle more ways the lookup can result in a invalid value. + Diag(Loc, diag::err_std_compare_type_invalid_member) + << Info.CCDecl << ValueName; + return true; + } + + ExprResult Res = + BuildDeclRefExpr(VD, VD->getType(), VK_LValue, SourceLocation()); + if (Res.isInvalid()) + return true; + + Info.Objects.try_emplace((char)CCV, cast(Res.get())); + } + + // Success. Set the value in ASTContext. + NewData[I] = std::move(Info); + } + + // All of the objects and values have been built successfully. + Context.CompCategories.setData(std::move(NewData)); + return false; +} + /// \brief Retrieve the special "std" namespace, which may require us to /// implicitly define the namespace. NamespaceDecl *Sema::getOrCreateStdNamespace() { @@ -11440,11 +11531,10 @@ // Create the comparison against the array bound. llvm::APInt Upper = ArrayTy->getSize().zextOrTrunc(S.Context.getTypeSize(SizeType)); - Expr *Comparison - = new (S.Context) BinaryOperator(IterationVarRefRVal.build(S, Loc), - IntegerLiteral::Create(S.Context, Upper, SizeType, Loc), - BO_NE, S.Context.BoolTy, - VK_RValue, OK_Ordinary, Loc, FPOptions()); + Expr *Comparison = new (S.Context) BinaryOperator( + IterationVarRefRVal.build(S, Loc), + IntegerLiteral::Create(S.Context, Upper, SizeType, Loc), BO_NE, + S.Context.BoolTy, VK_RValue, OK_Ordinary, Loc, FPOptions()); // Create the pre-increment of the iteration variable. We can determine // whether the increment will overflow based on the value of the array Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -37,6 +37,7 @@ #include "clang/Sema/Designator.h" #include "clang/Sema/Initialization.h" #include "clang/Sema/Lookup.h" +#include "clang/Sema/Overload.h" #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" @@ -9703,10 +9704,161 @@ } } +static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) { + switch (CK) { + default: + llvm_unreachable("unhandled cast kind"); + case CK_LValueToRValue: + return ICK_Lvalue_To_Rvalue; + case CK_ArrayToPointerDecay: + return ICK_Array_To_Pointer; + case CK_FunctionToPointerDecay: + return ICK_Function_To_Pointer; + case CK_IntegralCast: + return ICK_Integral_Conversion; + case CK_FloatingCast: + return ICK_Floating_Conversion; + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + return ICK_Floating_Integral; + } +} + +static bool checkNarrowingConversion(Sema &S, QualType ToType, Expr *E, + QualType FromType, SourceLocation Loc) { + // Check for a narrowing implicit conversion. + StandardConversionSequence SCS; + SCS.setToType(0, FromType); + SCS.setToType(1, ToType); + if (const auto *ICE = dyn_cast(E)) { + auto CastK = ICE->getCastKind(); + SCS.Second = castKindToImplicitConversionKind(CastK); + } + APValue PreNarrowingValue; + QualType PreNarrowingType; + switch (SCS.getNarrowingKind(S.Context, E, PreNarrowingValue, + PreNarrowingType, + /*IgnoreFloatToIntegralConversion*/ true)) { + case NK_Dependent_Narrowing: + // Implicit conversion to a narrower type, but the expression is + // value-dependent so we can't tell whether it's actually narrowing. + case NK_Not_Narrowing: + return false; + + case NK_Constant_Narrowing: + S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 1 + << PreNarrowingValue.getAsString(S.Context, PreNarrowingType) << ToType; + return true; + + case NK_Variable_Narrowing: + // Implicit conversion to a narrower type, and the value is not a constant + // expression. We'll diagnose this in a moment. + case NK_Type_Narrowing: + break; + } + + if (E->isValueDependent()) + return false; + + // Check the expression is a constant expression. + SmallVector Notes; + Expr::EvalResult Eval; + Eval.Diag = &Notes; + + if (ToType->isReferenceType() ? !E->EvaluateAsLValue(Eval, S.Context) + : !E->EvaluateAsRValue(Eval, S.Context)) { + // The expression can't be folded, so we can't keep it at this position in + // the AST. + } else if (Notes.empty()) { + // It's a constant expression. + return false; + } + S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing) + << /*Constant*/ 0 << FromType << ToType; + // It's not a constant expression. Produce an appropriate diagnostic. + if (Notes.size() >= 1 && + Notes[0].second.getDiagID() == diag::note_invalid_subexpr_in_const_expr) + S.Diag(Notes[0].first, diag::note_spaceship_operand_not_cce); + else { + S.Diag(E->getLocStart(), diag::note_spaceship_operand_not_cce); + // FIXME: Re-enable these notes? + // for (unsigned I = 0; I < Notes.size(); ++I) + // S.Diag(Notes[I].first, Notes[I].second); + } + return true; +} + +static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, + ExprResult &LHS, + ExprResult &RHS, + SourceLocation Loc) { + if (S.BuildComparisonCategoryData(Loc)) + return QualType(); + + QualType LHSType = LHS.get()->getType(); + QualType RHSType = RHS.get()->getType(); + + // C++2a [expr.spaceship]p3 + if (int Count = (LHSType->isBooleanType() + RHSType->isBooleanType())) { + // TODO: The spec says that if one but not both of the operands is 'bool' + // the program is ill-formed. However, what about bool non-narrowing cases? + if (Count != 2) { + S.InvalidOperands(Loc, LHS, RHS); + return QualType(); + } + } + + QualType Type; + if (LHSType->isEnumeralType() || RHSType->isEnumeralType()) { + // C++2a [expr.spaceship]p5 + if (!S.Context.hasSameUnqualifiedType(LHSType, RHSType)) { + S.InvalidOperands(Loc, LHS, RHS); + return QualType(); + } + Type = LHSType->getAs()->getDecl()->getIntegerType(); + assert(Type->isArithmeticType()); + + LHS = S.ImpCastExprToType(LHS.get(), Type, CK_BitCast); + RHS = S.ImpCastExprToType(RHS.get(), Type, CK_BitCast); + } else { + // C++2a [expr.spaceship]p4 + Type = S.UsualArithmeticConversions(LHS, RHS); + if (LHS.isInvalid() || RHS.isInvalid()) + return QualType(); + if (Type.isNull()) + return S.InvalidOperands(Loc, LHS, RHS); + assert(Type->isArithmeticType()); + + bool HasNarrowing = checkNarrowingConversion(S, Type, LHS.get(), LHSType, + LHS.get()->getLocStart()); + HasNarrowing |= checkNarrowingConversion(S, Type, RHS.get(), RHSType, + RHS.get()->getLocStart()); + if (HasNarrowing) + return QualType(); + } + assert(!Type.isNull() && "composite type for <=> has not been set"); + + auto TypeKind = [&]() { + using CCK = ComparisonCategoryKind; + if (Type->isIntegralOrEnumerationType()) + return CCK::StrongOrdering; + if (Type->hasFloatingRepresentation()) + return CCK::PartialOrdering; + llvm_unreachable("other types are unimplemented"); + }(); + + return QualType(S.Context.CompCategories.getDecl(TypeKind)->getTypeForDecl(), + 0); +} + static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc) { + if (Opc == BO_Cmp) + return checkArithmeticOrEnumeralThreeWayCompare(S, LHS, RHS, Loc); + // C99 6.5.8p3 / C99 6.5.9p4 QualType Type = S.UsualArithmeticConversions(LHS, RHS); if (LHS.isInvalid() || RHS.isInvalid()) @@ -9733,7 +9885,6 @@ S.CheckFloatComparison(Loc, LHS.get(), RHS.get()); // The result of comparisons is 'bool' in C++, 'int' in C. - // FIXME: For BO_Cmp, return the relevant comparison category type. return S.Context.getLogicalOperationType(); } @@ -9741,6 +9892,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, SourceLocation Loc, BinaryOperatorKind Opc, bool IsRelational) { + bool IsSpaceship = Opc == BO_Cmp; // Comparisons expect an rvalue, so convert to rvalue before any // type-related checks. LHS = DefaultFunctionArrayLvalueConversion(LHS.get()); @@ -9766,7 +9918,46 @@ (RHSType->isArithmeticType() || RHSType->isEnumeralType())) return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc); - QualType ResultTy = Context.getLogicalOperationType(); + auto computeResultTy = [&]() { + if (!IsSpaceship) + return Context.getLogicalOperationType(); + assert(getLangOpts().CPlusPlus); + assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType())); + + if (BuildComparisonCategoryData(Loc)) + return QualType(); + + QualType CompositeTy = LHS.get()->getType(); + assert(!CompositeTy->isReferenceType()); + const RecordDecl *CompDecl = nullptr; + if (CompositeTy->isVoidPointerType()) { + if (!isSFINAEContext()) { + Diag(Loc, diag::err_spaceship_comparison_of_void_ptr) + << (LHSType->isVoidPointerType() + RHSType->isVoidPointerType() - 1) + << LHSType << RHSType; + } + return QualType(); + } + if (CompositeTy->isFunctionPointerType() || + CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType()) + CompDecl = Context.CompCategories.getDecl( + ComparisonCategoryKind::StrongEquality); + else if (CompositeTy->isPointerType()) { + auto PointeeTy = CompositeTy->getPointeeType(); + if (!PointeeTy->isObjectType()) { + // TODO: Can this case actually occur? + Diag(Loc, diag::err_spaceship_comparison_of_invalid_comp_type) + << CompositeTy << LHSType << RHSType; + return QualType(); + } + CompDecl = Context.CompCategories.getDecl( + ComparisonCategoryKind::StrongOrdering); + } else + llvm_unreachable("unhandled three-way comparison composite type"); + if (!CompDecl) + return QualType(); + return QualType(CompDecl->getTypeForDecl(), 0); + }; const Expr::NullPointerConstantKind LHSNullKind = LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull); @@ -9807,24 +9998,25 @@ return QualType(); RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; + return computeResultTy(); } // C++ [expr.eq]p2: // If at least one operand is a pointer [...] bring them to their // composite pointer type. + // C++ [expr.spaceship]p6 + // If at least one of the operands is of pointer type, [...] bring them + // to their composite pointer type. // C++ [expr.rel]p2: // If both operands are pointers, [...] bring them to their composite // pointer type. if ((int)LHSType->isPointerType() + (int)RHSType->isPointerType() >= - (IsRelational ? 2 : 1) && - (!LangOpts.ObjCAutoRefCount || - !(LHSType->isObjCObjectPointerType() || + ((IsRelational && !IsSpaceship) ? 2 : 1) && + (!LangOpts.ObjCAutoRefCount || !(LHSType->isObjCObjectPointerType() || RHSType->isObjCObjectPointerType()))) { if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) return QualType(); - else - return ResultTy; + return computeResultTy(); } } else if (LHSType->isPointerType() && RHSType->isPointerType()) { // C99 6.5.8p2 @@ -9839,7 +10031,7 @@ if (Context.typesAreCompatible(LCanPointeeTy.getUnqualifiedType(), RCanPointeeTy.getUnqualifiedType())) { // Valid unless a relational comparison of function pointers - if (IsRelational && LCanPointeeTy->isFunctionType()) { + if ((IsRelational && !IsSpaceship) && LCanPointeeTy->isFunctionType()) { Diag(Loc, diag::ext_typecheck_ordered_comparison_of_function_pointers) << LHSType << RHSType << LHS.get()->getSourceRange() << RHS.get()->getSourceRange(); @@ -9875,21 +10067,21 @@ else RHS = ImpCastExprToType(RHS.get(), LHSType, Kind); } - return ResultTy; + return computeResultTy(); } if (getLangOpts().CPlusPlus) { // 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 ((!IsRelational || IsSpaceship) && LHSIsNull && RHSIsNull) { if (LHSType->isNullPtrType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (RHSType->isNullPtrType()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } } @@ -9898,12 +10090,12 @@ if (!IsRelational && RHSType->isNullPtrType() && (LHSType->isObjCObjectPointerType() || LHSType->isBlockPointerType())) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (!IsRelational && LHSType->isNullPtrType() && (RHSType->isObjCObjectPointerType() || RHSType->isBlockPointerType())) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (IsRelational && @@ -9926,7 +10118,7 @@ RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); else LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } } } @@ -9934,12 +10126,12 @@ // C++ [expr.eq]p2: // If at least one operand is a pointer to member, [...] bring them to // their composite pointer type. - if (!IsRelational && + if ((!IsRelational || IsSpaceship) && (LHSType->isMemberPointerType() || RHSType->isMemberPointerType())) { if (convertPointersToCompositeType(*this, Loc, LHS, RHS)) return QualType(); else - return ResultTy; + return computeResultTy(); } } @@ -9956,7 +10148,7 @@ << RHS.get()->getSourceRange(); } RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; + return computeResultTy(); } // Allow block pointers to be compared with null pointer constants. @@ -9980,7 +10172,7 @@ RHS = ImpCastExprToType(RHS.get(), LHSType, LHSType->isPointerType() ? CK_BitCast : CK_AnyPointerToBlockPointerCast); - return ResultTy; + return computeResultTy(); } if (LHSType->isObjCObjectPointerType() || @@ -10013,7 +10205,7 @@ RHS = ImpCastExprToType(E, LHSType, LPT ? CK_BitCast :CK_CPointerToObjCPointerCast); } - return ResultTy; + return computeResultTy(); } if (LHSType->isObjCObjectPointerType() && RHSType->isObjCObjectPointerType()) { @@ -10027,20 +10219,20 @@ LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BitCast); else RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast); - return ResultTy; + return computeResultTy(); } if (!IsRelational && LHSType->isBlockPointerType() && RHSType->isBlockCompatibleObjCPointerType(Context)) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_BlockPointerToObjCPointerCast); - return ResultTy; + return computeResultTy(); } else if (!IsRelational && LHSType->isBlockCompatibleObjCPointerType(Context) && RHSType->isBlockPointerType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BlockPointerToObjCPointerCast); - return ResultTy; + return computeResultTy(); } } if ((LHSType->isAnyPointerType() && RHSType->isIntegerType()) || @@ -10080,30 +10272,30 @@ else RHS = ImpCastExprToType(RHS.get(), LHSType, RHSIsNull ? CK_NullToPointer : CK_IntegralToPointer); - return ResultTy; + return computeResultTy(); } // Handle block pointers. if (!IsRelational && RHSIsNull && LHSType->isBlockPointerType() && RHSType->isIntegerType()) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (!IsRelational && LHSIsNull && LHSType->isIntegerType() && RHSType->isBlockPointerType()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (getLangOpts().OpenCLVersion >= 200) { if (LHSIsNull && RHSType->isQueueT()) { LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } if (LHSType->isQueueT() && RHSIsNull) { RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer); - return ResultTy; + return computeResultTy(); } } @@ -11680,7 +11872,8 @@ ExprValueKind VK = VK_RValue; ExprObjectKind OK = OK_Ordinary; bool ConvertHalfVec = false; - + // If the binary operator is <=> and it's an ordered comparison. + bool IsCmpOrdered = false; std::tie(LHS, RHS) = CorrectDelayedTyposInBinOp(*this, Opc, LHSExpr, RHSExpr); if (!LHS.isUsable() || !RHS.isUsable()) return ExprError(); @@ -11763,11 +11956,12 @@ ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false); break; case BO_Cmp: - // FIXME: Implement proper semantic checking of '<=>'. ConvertHalfVec = true; ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true); + assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl()); if (!ResultTy.isNull()) - ResultTy = Context.VoidTy; + IsCmpOrdered = + Context.CompCategories.getInfoForType(ResultTy).isOrdered(); break; case BO_And: checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc); @@ -11873,8 +12067,11 @@ if (ConvertHalfVec) return convertHalfVecBinOp(*this, LHS, RHS, Opc, ResultTy, VK, OK, false, OpLoc, FPFeatures); - return new (Context) BinaryOperator(LHS.get(), RHS.get(), Opc, ResultTy, VK, - OK, OpLoc, FPFeatures); + BinaryOperator *Op = new (Context) BinaryOperator( + LHS.get(), RHS.get(), Opc, ResultTy, VK, OK, OpLoc, FPFeatures); + if (Opc == BO_Cmp) + Op->setIsCmpOrdered(IsCmpOrdered); + return Op; } // Handle compound assignments. Index: lib/Sema/SemaOverload.cpp =================================================================== --- lib/Sema/SemaOverload.cpp +++ lib/Sema/SemaOverload.cpp @@ -288,11 +288,11 @@ /// value of the expression prior to the narrowing conversion. /// \param ConstantType If this is an NK_Constant_Narrowing conversion, the /// type of the expression prior to the narrowing conversion. -NarrowingKind -StandardConversionSequence::getNarrowingKind(ASTContext &Ctx, - const Expr *Converted, - APValue &ConstantValue, - QualType &ConstantType) const { +/// \param IgnoreFloatToIntegralConversion If true type-narrowing conversions +/// from floating point types to integral types should be ignored. +NarrowingKind StandardConversionSequence::getNarrowingKind( + ASTContext &Ctx, const Expr *Converted, APValue &ConstantValue, + QualType &ConstantType, bool IgnoreFloatToIntegralConversion) const { assert(Ctx.getLangOpts().CPlusPlus && "narrowing check outside C++"); // C++11 [dcl.init.list]p7: @@ -329,6 +329,8 @@ return NK_Type_Narrowing; } else if (FromType->isIntegralOrUnscopedEnumerationType() && ToType->isRealFloatingType()) { + if (IgnoreFloatToIntegralConversion) + return NK_Not_Narrowing; llvm::APSInt IntConstantValue; const Expr *Initializer = IgnoreNarrowingConversion(Converted); assert(Initializer && "Unknown conversion expression"); Index: lib/Serialization/ASTReaderStmt.cpp =================================================================== --- lib/Serialization/ASTReaderStmt.cpp +++ lib/Serialization/ASTReaderStmt.cpp @@ -689,6 +689,7 @@ E->setOpcode((BinaryOperator::Opcode)Record.readInt()); E->setOperatorLoc(ReadSourceLocation()); E->setFPFeatures(FPOptions(Record.readInt())); + E->setIsCmpOrdered(Record.readInt()); } void ASTStmtReader::VisitCompoundAssignOperator(CompoundAssignOperator *E) { Index: lib/Serialization/ASTWriterStmt.cpp =================================================================== --- lib/Serialization/ASTWriterStmt.cpp +++ lib/Serialization/ASTWriterStmt.cpp @@ -671,6 +671,7 @@ Record.push_back(E->getOpcode()); // FIXME: stable encoding Record.AddSourceLocation(E->getOperatorLoc()); Record.push_back(E->getFPFeatures().getInt()); + Record.push_back(E->getIsCmpOrdered()); Code = serialization::EXPR_BINARY_OPERATOR; } Index: test/CodeGenCXX/Inputs/std-compare.h =================================================================== --- /dev/null +++ test/CodeGenCXX/Inputs/std-compare.h @@ -0,0 +1,437 @@ +#ifndef STD_COMPARE_H +#define STD_COMPARE_H + +namespace std { +inline namespace __1 { + +// exposition only +enum class _EqResult : unsigned char { + __zero = 0, + __equal = __zero, + __equiv = __equal, + __nonequal = 1, + __nonequiv = __nonequal +}; + +enum class _OrdResult : signed char { + __less = -1, + __greater = 1 +}; + +enum class _NCmpResult : signed char { + __unordered = -127 +}; + +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 + : __value_(_ValueT(__v)) {} + explicit constexpr partial_ordering(_OrdResult __v) noexcept + : __value_(_ValueT(__v)) {} + explicit constexpr partial_ordering(_NCmpResult __v) noexcept + : __value_(_ValueT(__v)) {} + + constexpr bool __is_ordered() const noexcept { + return __value_ != _ValueT(_NCmpResult::__unordered); + } + +public: + // valid values + static const partial_ordering less; + static const partial_ordering equivalent; + 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; + friend constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + + friend constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(partial_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr partial_ordering partial_ordering::less(_OrdResult::__less); +inline constexpr partial_ordering partial_ordering::equivalent(_EqResult::__equiv); +inline constexpr partial_ordering partial_ordering::greater(_OrdResult::__greater); +inline constexpr partial_ordering partial_ordering::unordered(_NCmpResult ::__unordered); +constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ == 0; +} +constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ < 0; +} +constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ <= 0; +} +constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ > 0; +} +constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 == __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 >= __v.__value_; +} +constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return !__v.__is_ordered() || __v.__value_ != 0; +} +constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return !__v.__is_ordered() || __v.__value_ != 0; +} + +constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v < 0 ? partial_ordering::greater : (__v > 0 ? partial_ordering::less : __v); +} + +class weak_ordering { + using _ValueT = signed char; + explicit constexpr weak_ordering(_EqResult __v) noexcept : __value_(_ValueT(__v)) {} + explicit constexpr weak_ordering(_OrdResult __v) noexcept : __value_(_ValueT(__v)) {} + +public: + static const weak_ordering less; + static const weak_ordering equivalent; + 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); + } + + // comparisons + friend constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + + friend constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(weak_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr weak_ordering weak_ordering::less(_OrdResult::__less); +inline constexpr weak_ordering weak_ordering::equivalent(_EqResult::__equiv); +inline constexpr weak_ordering weak_ordering::greater(_OrdResult::__greater); +constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return __v < 0 ? weak_ordering::greater : (__v > 0 ? weak_ordering::less : __v); +} + +class strong_ordering { + using _ValueT = signed char; + explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast(__v)) {} + explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast(__v)) {} + +public: + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + 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); + } + constexpr operator weak_ordering() const noexcept { + return __value_ == 0 ? weak_ordering::equivalent + : (__value_ < 0 ? weak_ordering::less : weak_ordering::greater); + } + + // comparisons + friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(strong_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less); +inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal); +inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv); +inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater); + +constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + 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 + +#endif // STD_COMPARE_H Index: test/CodeGenCXX/cxx2a-compare.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/cxx2a-compare.cpp @@ -0,0 +1,113 @@ +// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple | \ +// RUN: FileCheck %s \ +// RUN: '-DSO="class.std::__1::strong_ordering"' \ +// RUN: -DSO_EQ=_ZNSt3__115strong_ordering5equalE \ +// RUN: -DSO_LT=_ZNSt3__115strong_ordering4lessE \ +// RUN: -DSO_GT=_ZNSt3__115strong_ordering7greaterE \ +// RUN: '-DSE="class.std::__1::strong_equality"' \ +// RUN: -DSE_EQ=_ZNSt3__115strong_equality5equalE \ +// RUN: -DSE_NE=_ZNSt3__115strong_equality8nonequalE \ +// RUN: '-DPO="class.std::__1::partial_ordering"' \ +// RUN: -DPO_EQ=_ZNSt3__116partial_ordering10equivalentE \ +// RUN: -DPO_LT=_ZNSt3__116partial_ordering4lessE \ +// RUN: -DPO_GT=_ZNSt3__116partial_ordering7greaterE \ +// RUN: -DPO_UNORD=_ZNSt3__116partial_ordering9unorderedE + +#include "Inputs/std-compare.h" + +typedef int INT; + +// CHECK-LABEL: @_Z11test_signedii +auto test_signed(int x, int y) { + // CHECK: %retval = alloca %[[SO]] + // CHECK: %cmp.lt = icmp slt i32 %0, %1 + // CHECK: %sel.lt = select i1 %cmp.lt, %[[SO]]* @[[SO_LT]], %[[SO]]* @[[SO_GT]] + // CHECK: %cmp.eq = icmp eq i32 %0, %1 + // CHECK: %sel.eq = select i1 %cmp.eq, %[[SO]]* @[[SO_EQ]], %[[SO]]* %sel.lt + // CHECK: %[[TMPRET:.*]] = bitcast %[[SO]]* %retval + // CHECK: %[[TMPSRC:.*]] = bitcast %[[SO]]* %sel.eq + // CHECK: call void @llvm.memcpy{{.*}}(i8* align 1 %[[TMPRET]], i8* align 1 %[[TMPSRC]] + // CHECK: ret + return x <=> y; +} + +// CHECK-LABEL: @_Z13test_unsignedjj +auto test_unsigned(unsigned x, unsigned y) { + // CHECK: %cmp.lt = icmp ult i32 %0, %1 + // CHECK: %sel.lt = select i1 %cmp.lt, %[[SO]]* @[[SO_LT]], %[[SO]]* @[[SO_GT]] + // CHECK: %cmp.eq = icmp eq i32 %0, %1 + // CHECK: %sel.eq = select i1 %cmp.eq, %[[SO]]* @[[SO_EQ]], %[[SO]]* %sel.lt + // CHECK: %retval + // CHECK: %sel.eq + // CHECK: ret + return x <=> y; +} + +// CHECK-LABEL: @_Z10float_testdd +auto float_test(double x, double y) { + // CHECK: %retval = alloca %[[PO]] + // CHECK: %cmp.eq = fcmp oeq double %0, %1 + // CHECK: %sel.eq = select i1 %cmp.eq, %[[PO]]* @[[PO_EQ]], %[[PO]]* @[[PO_UNORD]] + // CHECK: %cmp.gt = fcmp ogt double %0, %1 + // CHECK: %sel.gt = select i1 %cmp.gt, %[[PO]]* @[[PO_GT]], %[[PO]]* %sel.eq + // CHECK: %cmp.lt = fcmp olt double %0, %1 + // CHECK: %sel.lt = select i1 %cmp.lt, %[[PO]]* @[[PO_LT]], %[[PO]]* %sel.gt + // CHECK: %retval + // CHECK: %sel.lt + // CHECK: ret + return x <=> y; +} + +// CHECK-LABEL: @_Z8ptr_testPiS_ +auto ptr_test(int *x, int *y) { + // CHECK: %cmp.lt = icmp ult i32* %0, %1 + // CHECK: %sel.lt = select i1 %cmp.lt, %[[SO]]* @[[SO_LT]], %[[SO]]* @[[SO_GT]] + // CHECK: %cmp.eq = icmp eq i32* %0, %1 + // CHECK: %sel.eq = select i1 %cmp.eq, %[[SO]]* @[[SO_EQ]], %[[SO]]* %sel.lt + // CHECK: %retval + // CHECK: %sel.eq + // CHECK: ret + 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: %retval = alloca %[[SE]] + // CHECK: %cmp.ptr = icmp eq i64 %lhs.memptr.ptr, %rhs.memptr.ptr + // CHECK: %cmp.ptr.null = icmp eq i64 %lhs.memptr.ptr, 0 + // CHECK: %cmp.adj = icmp eq i64 %lhs.memptr.adj, %rhs.memptr.adj + // CHECK: %6 = or i1 %cmp.ptr.null, %cmp.adj + // CHECK: %memptr.eq = and i1 %cmp.ptr, %6 + // CHECK: %sel.eq = select i1 %memptr.eq, %[[SE]]* @[[SE_EQ]], %[[SE]]* @[[SE_NE]] + // CHECK: %retval + // CHECK: %sel.eq + // CHECK: ret + return x <=> y; +} + +// CHECK-LABEL: @_Z13mem_data_testM6MemPtriS0_ +auto mem_data_test(MemDataT x, MemDataT y) { + // CHECK: %retval = alloca %[[SE]] + // CHECK: %[[CMP:.*]] = icmp eq i64 %0, %1 + // CHECK: %sel.eq = select i1 %[[CMP]], %[[SE]]* @[[SE_EQ]], %[[SE]]* @[[SE_NE]] + // CHECK: %retval + // CHECK: %sel.eq + return x <=> y; +} + +// CHECK-LABEL: @_Z13test_constantv +auto test_constant() { + // CHECK: entry: + // CHECK-NOT: icmp + // CHECK-NOT: [[SO_GT]] + // CHECK-NOT: [[SO_EQ]] + // CHECK: memcpy{{.*}}[[SO_LT]] + // CHECK: ret + const int x = 42; + const int y = 101; + return x <=> y; +} Index: test/PCH/Inputs/std-compare.h =================================================================== --- /dev/null +++ test/PCH/Inputs/std-compare.h @@ -0,0 +1,437 @@ +#ifndef STD_COMPARE_H +#define STD_COMPARE_H + +namespace std { +inline namespace __1 { + +// exposition only +enum class _EqResult : unsigned char { + __zero = 0, + __equal = __zero, + __equiv = __equal, + __nonequal = 1, + __nonequiv = __nonequal +}; + +enum class _OrdResult : signed char { + __less = -1, + __greater = 1 +}; + +enum class _NCmpResult : signed char { + __unordered = -127 +}; + +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 + : __value_(_ValueT(__v)) {} + explicit constexpr partial_ordering(_OrdResult __v) noexcept + : __value_(_ValueT(__v)) {} + explicit constexpr partial_ordering(_NCmpResult __v) noexcept + : __value_(_ValueT(__v)) {} + + constexpr bool __is_ordered() const noexcept { + return __value_ != _ValueT(_NCmpResult::__unordered); + } + +public: + // valid values + static const partial_ordering less; + static const partial_ordering equivalent; + 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; + friend constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + + friend constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(partial_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr partial_ordering partial_ordering::less(_OrdResult::__less); +inline constexpr partial_ordering partial_ordering::equivalent(_EqResult::__equiv); +inline constexpr partial_ordering partial_ordering::greater(_OrdResult::__greater); +inline constexpr partial_ordering partial_ordering::unordered(_NCmpResult ::__unordered); +constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ == 0; +} +constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ < 0; +} +constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ <= 0; +} +constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ > 0; +} +constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 == __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 >= __v.__value_; +} +constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return !__v.__is_ordered() || __v.__value_ != 0; +} +constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return !__v.__is_ordered() || __v.__value_ != 0; +} + +constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v < 0 ? partial_ordering::greater : (__v > 0 ? partial_ordering::less : __v); +} + +class weak_ordering { + using _ValueT = signed char; + explicit constexpr weak_ordering(_EqResult __v) noexcept : __value_(_ValueT(__v)) {} + explicit constexpr weak_ordering(_OrdResult __v) noexcept : __value_(_ValueT(__v)) {} + +public: + static const weak_ordering less; + static const weak_ordering equivalent; + 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); + } + + // comparisons + friend constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + + friend constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(weak_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr weak_ordering weak_ordering::less(_OrdResult::__less); +inline constexpr weak_ordering weak_ordering::equivalent(_EqResult::__equiv); +inline constexpr weak_ordering weak_ordering::greater(_OrdResult::__greater); +constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return __v < 0 ? weak_ordering::greater : (__v > 0 ? weak_ordering::less : __v); +} + +class strong_ordering { + using _ValueT = signed char; + explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast(__v)) {} + explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast(__v)) {} + +public: + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + 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); + } + constexpr operator weak_ordering() const noexcept { + return __value_ == 0 ? weak_ordering::equivalent + : (__value_ < 0 ? weak_ordering::less : weak_ordering::greater); + } + + // comparisons + friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(strong_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less); +inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal); +inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv); +inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater); + +constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + 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 + +#endif // STD_COMPARE_H Index: test/PCH/cxx2a-compare.cpp =================================================================== --- /dev/null +++ test/PCH/cxx2a-compare.cpp @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -pedantic-errors -std=c++2a -emit-pch %s -o %t +// RUN: %clang_cc1 -pedantic-errors -std=c++2a -include-pch %t -verify %s + +#ifndef HEADER +#define HEADER + +#include "Inputs/std-compare.h" + +#else + +// expected-no-diagnostics +void foo() { + (void)(42 <=> 101); +} + +#endif Index: test/SemaCXX/Inputs/std-compare.h =================================================================== --- /dev/null +++ test/SemaCXX/Inputs/std-compare.h @@ -0,0 +1,437 @@ +#ifndef STD_COMPARE_H +#define STD_COMPARE_H + +namespace std { +inline namespace __1 { + +// exposition only +enum class _EqResult : unsigned char { + __zero = 0, + __equal = __zero, + __equiv = __equal, + __nonequal = 1, + __nonequiv = __nonequal +}; + +enum class _OrdResult : signed char { + __less = -1, + __greater = 1 +}; + +enum class _NCmpResult : signed char { + __unordered = -127 +}; + +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 + : __value_(_ValueT(__v)) {} + explicit constexpr partial_ordering(_OrdResult __v) noexcept + : __value_(_ValueT(__v)) {} + explicit constexpr partial_ordering(_NCmpResult __v) noexcept + : __value_(_ValueT(__v)) {} + + constexpr bool __is_ordered() const noexcept { + return __value_ != _ValueT(_NCmpResult::__unordered); + } + +public: + // valid values + static const partial_ordering less; + static const partial_ordering equivalent; + 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; + friend constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + + friend constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(partial_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr partial_ordering partial_ordering::less(_OrdResult::__less); +inline constexpr partial_ordering partial_ordering::equivalent(_EqResult::__equiv); +inline constexpr partial_ordering partial_ordering::greater(_OrdResult::__greater); +inline constexpr partial_ordering partial_ordering::unordered(_NCmpResult ::__unordered); +constexpr bool operator==(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ == 0; +} +constexpr bool operator<(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ < 0; +} +constexpr bool operator<=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ <= 0; +} +constexpr bool operator>(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ > 0; +} +constexpr bool operator>=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__is_ordered() && __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 == __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v.__is_ordered() && 0 >= __v.__value_; +} +constexpr bool operator!=(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return !__v.__is_ordered() || __v.__value_ != 0; +} +constexpr bool operator!=(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return !__v.__is_ordered() || __v.__value_ != 0; +} + +constexpr partial_ordering operator<=>(partial_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr partial_ordering operator<=>(_CmpUnspecifiedParam, partial_ordering __v) noexcept { + return __v < 0 ? partial_ordering::greater : (__v > 0 ? partial_ordering::less : __v); +} + +class weak_ordering { + using _ValueT = signed char; + explicit constexpr weak_ordering(_EqResult __v) noexcept : __value_(_ValueT(__v)) {} + explicit constexpr weak_ordering(_OrdResult __v) noexcept : __value_(_ValueT(__v)) {} + +public: + static const weak_ordering less; + static const weak_ordering equivalent; + 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); + } + + // comparisons + friend constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + + friend constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(weak_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr weak_ordering weak_ordering::less(_OrdResult::__less); +inline constexpr weak_ordering weak_ordering::equivalent(_EqResult::__equiv); +inline constexpr weak_ordering weak_ordering::greater(_OrdResult::__greater); +constexpr bool operator==(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr weak_ordering operator<=>(weak_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr weak_ordering operator<=>(_CmpUnspecifiedParam, weak_ordering __v) noexcept { + return __v < 0 ? weak_ordering::greater : (__v > 0 ? weak_ordering::less : __v); +} + +class strong_ordering { + using _ValueT = signed char; + explicit constexpr strong_ordering(_EqResult __v) noexcept : __value_(static_cast(__v)) {} + explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast(__v)) {} + +public: + static const strong_ordering less; + static const strong_ordering equal; + static const strong_ordering equivalent; + 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); + } + constexpr operator weak_ordering() const noexcept { + return __value_ == 0 ? weak_ordering::equivalent + : (__value_ < 0 ? weak_ordering::less : weak_ordering::greater); + } + + // comparisons + friend constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + friend constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + friend constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept; + friend constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept; + + // test helper + constexpr bool test_eq(strong_ordering const &other) const noexcept { + return __value_ == other.__value_; + } + +private: + _ValueT __value_; +}; + +inline constexpr strong_ordering strong_ordering::less(_OrdResult::__less); +inline constexpr strong_ordering strong_ordering::equal(_EqResult::__equal); +inline constexpr strong_ordering strong_ordering::equivalent(_EqResult::__equiv); +inline constexpr strong_ordering strong_ordering::greater(_OrdResult::__greater); + +constexpr bool operator==(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ == 0; +} +constexpr bool operator!=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ != 0; +} +constexpr bool operator<(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ < 0; +} +constexpr bool operator<=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ <= 0; +} +constexpr bool operator>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ > 0; +} +constexpr bool operator>=(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v.__value_ >= 0; +} +constexpr bool operator==(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 == __v.__value_; +} +constexpr bool operator!=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 != __v.__value_; +} +constexpr bool operator<(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 < __v.__value_; +} +constexpr bool operator<=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 <= __v.__value_; +} +constexpr bool operator>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 > __v.__value_; +} +constexpr bool operator>=(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + return 0 >= __v.__value_; +} + +constexpr strong_ordering operator<=>(strong_ordering __v, _CmpUnspecifiedParam) noexcept { + return __v; +} +constexpr strong_ordering operator<=>(_CmpUnspecifiedParam, strong_ordering __v) noexcept { + 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 + +#endif // STD_COMPARE_H Index: test/SemaCXX/compare-cxx2a.cpp =================================================================== --- test/SemaCXX/compare-cxx2a.cpp +++ test/SemaCXX/compare-cxx2a.cpp @@ -1,7 +1,12 @@ // Force x86-64 because some of our heuristics are actually based // on integer sizes. -// RUN: %clang_cc1 -triple x86_64-apple-darwin -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 %s + +#include "Inputs/std-compare.h" + +#define ASSERT_TYPE(...) static_assert(__is_same(__VA_ARGS__)) +#define ASSERT_EXPR_TYPE(Expr, Expect) static_assert(__is_same(decltype(Expr), Expect)); void self_compare() { int a; @@ -19,16 +24,16 @@ // FIXME: <=> should never produce -Wsign-compare warnings. All the possible error // cases involve narrowing conversions and so are ill-formed. - (void)(a <=> (unsigned long) b); // expected-warning {{comparison of integers of different signs}} + (void)(a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} (void)(a <=> (unsigned int) b); (void)(a <=> (unsigned short) b); (void)(a <=> (unsigned char) b); - (void)((long) a <=> b); // expected-warning {{comparison of integers of different signs}} - (void)((int) a <=> b); // expected-warning {{comparison of integers of different signs}} - (void)((short) a <=> b); // expected-warning {{comparison of integers of different signs}} - (void)((signed char) a <=> b); // expected-warning {{comparison of integers of different signs}} - (void)((long) a <=> (unsigned long) b); // expected-warning {{comparison of integers of different signs}} - (void)((int) a <=> (unsigned int) b); // expected-warning {{comparison of integers of different signs}} + (void)((long)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((int)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((short)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((signed char)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((long)a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((int)a <=> (unsigned int)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} (void)((short) a <=> (unsigned short) b); (void)((signed char) a <=> (unsigned char) b); @@ -105,7 +110,7 @@ (void)((signed char) 0x80000 <=> (unsigned char) b); // (a,0x80000) - (void)(a <=> (unsigned long) 0x80000); // expected-warning {{comparison of integers of different signs}} + (void)(a <=> (unsigned long)0x80000); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} (void)(a <=> (unsigned int) 0x80000); (void)(a <=> (unsigned short) 0x80000); (void)(a <=> (unsigned char) 0x80000); @@ -113,19 +118,21 @@ (void)((int) a <=> 0x80000); (void)((short) a <=> 0x80000); // expected-warning {{comparison of constant 524288 with expression of type 'short' is always 'std::strong_ordering::less'}} (void)((signed char) a <=> 0x80000); // expected-warning {{comparison of constant 524288 with expression of type 'signed char' is always 'std::strong_ordering::less'}} - (void)((long) a <=> (unsigned long) 0x80000); // expected-warning {{comparison of integers of different signs}} - (void)((int) a <=> (unsigned int) 0x80000); // expected-warning {{comparison of integers of different signs}} + (void)((long)a <=> (unsigned long)0x80000); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((int)a <=> (unsigned int)0x80000); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} (void)((short) a <=> (unsigned short) 0x80000); (void)((signed char) a <=> (unsigned char) 0x80000); } -void test5(bool b) { - (void) (b <=> -10); // expected-warning{{comparison of constant -10 with expression of type 'bool' is always 'std::strong_ordering::greater'}} - (void) (b <=> -1); // expected-warning{{comparison of constant -1 with expression of type 'bool' is always 'std::strong_ordering::greater'}} - (void) (b <=> 0); - (void) (b <=> 1); - (void) (b <=> 2); // expected-warning{{comparison of constant 2 with expression of type 'bool' is always 'std::strong_ordering::less'}} - (void) (b <=> 10); // expected-warning{{comparison of constant 10 with expression of type 'bool' is always 'std::strong_ordering::less'}} +void test5(bool b, bool b2) { + (void)(b <=> b2); // OK + (void)(true <=> b); // OK + (void)(b <=> -10); // expected-error {{invalid operands to binary expression ('bool' and 'int')}} + (void)(b <=> char(1)); // expected-error {{invalid operands to binary expression ('bool' and 'char')}} + + // FIXME: Should this be accepted when narrowing doesn't occur? + (void)(b <=> 0); // expected-error {{invalid operands to binary expression ('bool' and 'int')}} + (void)(b <=> 1); // expected-error {{invalid operands to binary expression ('bool' and 'int')}} } void test6(signed char sc) { @@ -142,13 +149,13 @@ (void)((unsigned long)other <=> (unsigned)(0xffff'ffff)); // Common unsigned, other signed, constant unsigned - (void)((int)other <=> (unsigned long)(0xffff'ffff'ffff'ffff)); // expected-warning{{different signs}} - (void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-warning{{different signs}} - (void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-warning{{different signs}} - (void)((int)other <=> (unsigned)(0x8000'0000)); // expected-warning{{different signs}} + (void)((int)other <=> (unsigned long)(0xffff'ffff'ffff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} + (void)((int)other <=> (unsigned)(0x8000'0000)); // expected-error {{argument to 'operator<=>' cannot be narrowed}} expected-note {{argument is not a constant expression}} // Common unsigned, other unsigned, constant signed - (void)((unsigned long)other <=> (int)(0xffff'ffff)); // expected-warning{{different signs}} + (void)((unsigned long)other <=> (int)(0xffff'ffff)); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned long'}} // Common unsigned, other signed, constant signed // Should not be possible as the common type should also be signed. @@ -172,3 +179,69 @@ (void)((unsigned char)other <=> (unsigned short)(0x100)); // expected-warning{{less}} (void)((unsigned short)other <=> (unsigned char)(0xff)); } + +// test void pointer diagnostics +void test8(void *vp, const void *cvp, int *ip) { + (void)(vp <=> cvp); // expected-error {{three-way comparison with void pointer operand types ('void *' and 'const void *')}} + (void)(vp <=> ip); // expected-error {{three-way comparison with void pointer operand type ('void *' and 'int *')}} + (void)(ip <=> cvp); // expected-error {{three-way comparison with void pointer operand type ('int *' and 'const void *')}} +} + +void test9(long double ld, double d, float f, int i, long long ll) { + (void)(f <=> ll); // OK, floating-point to integer is OK + (void)(d <=> ld); + (void)(i <=> f); +} + +typedef int *INTPTR; +void test_typedef_bug(int *x, INTPTR y) { + (void)(x <=> y); +} + +using nullptr_t = decltype(nullptr); + +struct Class {}; +struct ClassB : Class {}; +struct Class2 {}; +using FnTy = void(int); +using FnTy2 = long(int); +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_ordering); + + 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); +} + +void test_compatible_pointer(FnTy *f1, FnTy2 *f2, MemFnTy mf1, MemFnTyB mfb, + MemFnTy2 mf2, MemFnTy3 mf3) { + (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}} +} Index: test/SemaCXX/constant-expression-cxx2a.cpp =================================================================== --- test/SemaCXX/constant-expression-cxx2a.cpp +++ test/SemaCXX/constant-expression-cxx2a.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -std=c++2a -verify %s -fcxx-exceptions -triple=x86_64-linux-gnu +#include "Inputs/std-compare.h" + namespace ThreeWayComparison { struct A { int n; @@ -11,17 +13,179 @@ static_assert(A{2} <=> A{1} > 0); static_assert(A{2} <=> A{2} == 0); - // Note: not yet supported. - static_assert(1 <=> 2 < 0); // expected-error {{invalid operands}} - static_assert(2 <=> 1 > 0); // expected-error {{invalid operands}} - static_assert(1 <=> 1 == 0); // expected-error {{invalid operands}} + static_assert(1 <=> 2 < 0); + static_assert(2 <=> 1 > 0); + static_assert(1 <=> 1 == 0); constexpr int k = (1 <=> 1, 0); - // expected-error@-1 {{constexpr variable 'k' must be initialized by a constant expression}} - // expected-warning@-2 {{three-way comparison result unused}} + // expected-warning@-1 {{three-way comparison result unused}} + + static_assert(std::strong_ordering::equal == 0); - constexpr void f() { // expected-error {{constant expression}} - void(1 <=> 1); // expected-note {{constant expression}} + constexpr void f() { + void(1 <=> 1); } - // TODO: defaulted operator <=> + struct MemPtr { + void foo() {} + void bar() {} + int data; + int data2; + long data3; + }; + + struct MemPtr2 { + void foo() {} + void bar() {} + int data; + int data2; + long data3; + }; + using MemPtrT = void (MemPtr::*)(); + + using FnPtrT = void (*)(); + + void FnPtr1() {} + void FnPtr2() {} + +#define CHECK(...) ((__VA_ARGS__) ? void() : throw "error") +#define CHECK_TYPE(...) static_assert(__is_same(__VA_ARGS__)); + +constexpr bool test_constexpr_success = [] { + { + auto &EQ = std::strong_ordering::equal; + auto &LESS = std::strong_ordering::less; + auto &GREATER = std::strong_ordering::greater; + using SO = std::strong_ordering; + auto eq = (42 <=> 42); + CHECK_TYPE(decltype(eq), SO); + CHECK(eq.test_eq(EQ)); + + auto less = (-1 <=> 0); + CHECK_TYPE(decltype(less), SO); + CHECK(less.test_eq(LESS)); + + auto greater = (42l <=> 1u); + CHECK_TYPE(decltype(greater), SO); + CHECK(greater.test_eq(GREATER)); + } + { + using PO = std::partial_ordering; + auto EQUIV = PO::equivalent; + auto LESS = PO::less; + auto GREATER = PO::greater; + + auto eq = (42.0 <=> 42.0); + CHECK_TYPE(decltype(eq), PO); + CHECK(eq.test_eq(EQUIV)); + + auto less = (39.0 <=> 42.0); + CHECK_TYPE(decltype(less), PO); + CHECK(less.test_eq(LESS)); + + auto greater = (-10.123 <=> -101.1); + 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)); + } + { + using SO = std::strong_ordering; + auto EQ = SO::equal; + auto LESS = SO::less; + auto GREATER = SO::greater; + + int Arr[5] = {}; + using ArrRef = decltype((Arr)); + + int *P1 = (Arr + 1); + int *P12 = (Arr + 1); + int *P2 = (Arr + 2); + + auto eq = (Arr <=> (ArrRef)Arr); //(P1 <=> P12); + CHECK_TYPE(decltype(eq), SO); + CHECK(eq.test_eq(EQ)); + } + { // mixed nullptr tests + using SO = std::strong_ordering; + using SE = std::strong_equality; + + int x = 42; + int *xp = &x; + + + + MemPtrT mf = nullptr; + MemPtrT mf2 = &MemPtr::foo; + auto r3 = (mf <=> nullptr); + CHECK_TYPE(decltype(r3), std::strong_equality); + CHECK(r3.test_eq(SE::equal)); + } + + return true; +}(); + +template +constexpr bool test_constexpr() { + (void)(LHS <=> RHS); // expected-note 1+ {{subexpression not valid in a constant expression}} + return true; +} +int dummy = 42; +int dummy2 = 101; + +// expected-error@+1 {{must be initialized by a constant expression}} +constexpr bool tc1 = test_constexpr(); // expected-note {{in call}} +// expected-error@+1 {{must be initialized by a constant expression}} +constexpr bool tc2 = test_constexpr<&dummy, nullptr>(); // expected-note {{in call}} + +// OK, equality comparison only +constexpr bool tc3 = test_constexpr<&MemPtr::foo, nullptr>(); +constexpr bool tc4 = test_constexpr(); +constexpr bool tc5 = test_constexpr<&MemPtr::foo, &MemPtr::bar>(); + +constexpr bool tc6 = test_constexpr<&MemPtr::data, nullptr>(); +constexpr bool tc7 = test_constexpr(); +constexpr bool tc8 = test_constexpr<&MemPtr::data, &MemPtr::data2>(); + +// expected-error@+1 {{must be initialized by a constant expression}} +constexpr bool tc9 = test_constexpr<&dummy, &dummy2>(); // expected-note {{in call}} + +// TODO: defaulted operator <=> +} // namespace ThreeWayComparison