mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2024-11-28 08:02:08 +00:00
[C++2a] Implement operator<=> CodeGen and ExprConstant
Summary: This patch tackles long hanging fruit for the builtin operator<=> expressions. It is currently needs some cleanup before landing, but I want to get some initial feedback. The main changes are: * Lookup, build, and store the required standard library types and expressions in `ASTContext`. By storing them in ASTContext we don't need to store (and duplicate) the required expressions in the BinaryOperator AST nodes. * Implement [expr.spaceship] checking, including diagnosing narrowing conversions. * Implement `ExprConstant` for builtin spaceship operators. * Implement builitin operator<=> support in `CodeGenAgg`. Initially I emitted the required comparisons using `ScalarExprEmitter::VisitBinaryOperator`, but this caused the operand expressions to be emitted once for every required cmp. * Implement [builtin.over] with modifications to support the intent of P0946R0. See the note on `BuiltinOperatorOverloadBuilder::addThreeWayArithmeticOverloads` for more information about the workaround. Reviewers: rsmith, aaron.ballman, majnemer, rnk, compnerd, rjmccall Reviewed By: rjmccall Subscribers: rjmccall, rsmith, aaron.ballman, junbuml, mgorny, cfe-commits Differential Revision: https://reviews.llvm.org/D45476 llvm-svn: 331677
This commit is contained in:
parent
f53d9abd7e
commit
0683c0e68d
@ -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"
|
||||
@ -1978,6 +1979,10 @@ public:
|
||||
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.
|
||||
ComparisonCategories CompCategories;
|
||||
|
||||
private:
|
||||
CanQualType getFromTargetType(unsigned Type) const;
|
||||
TypeInfo getTypeInfoImpl(const Type *T) const;
|
||||
|
255
clang/include/clang/AST/ComparisonCategories.h
Normal file
255
clang/include/clang/AST/ComparisonCategories.h
Normal file
@ -0,0 +1,255 @@
|
||||
//===- 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 "clang/Basic/LLVM.h"
|
||||
#include "llvm/ADT/APSInt.h"
|
||||
#include "llvm/ADT/DenseMap.h"
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
|
||||
namespace llvm {
|
||||
class StringRef;
|
||||
class APSInt;
|
||||
}
|
||||
|
||||
namespace clang {
|
||||
|
||||
class ASTContext;
|
||||
class VarDecl;
|
||||
class CXXRecordDecl;
|
||||
class Sema;
|
||||
class QualType;
|
||||
class NamespaceDecl;
|
||||
|
||||
/// \brief An enumeration representing the different comparison categories
|
||||
/// types.
|
||||
///
|
||||
/// C++2a [cmp.categories.pre] The types weak_equality, strong_equality,
|
||||
/// partial_ordering, weak_ordering, and strong_ordering are collectively
|
||||
/// termed the comparison category types.
|
||||
enum class ComparisonCategoryType : unsigned char {
|
||||
WeakEquality,
|
||||
StrongEquality,
|
||||
PartialOrdering,
|
||||
WeakOrdering,
|
||||
StrongOrdering,
|
||||
First = WeakEquality,
|
||||
Last = StrongOrdering
|
||||
};
|
||||
|
||||
/// \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 {
|
||||
Equal,
|
||||
Equivalent,
|
||||
Nonequivalent,
|
||||
Nonequal,
|
||||
Less,
|
||||
Greater,
|
||||
Unordered,
|
||||
Last = Unordered
|
||||
};
|
||||
|
||||
class ComparisonCategoryInfo {
|
||||
friend class ComparisonCategories;
|
||||
friend class Sema;
|
||||
|
||||
public:
|
||||
ComparisonCategoryInfo(const ASTContext &Ctx, CXXRecordDecl *RD,
|
||||
ComparisonCategoryType Kind)
|
||||
: Ctx(Ctx), Record(RD), Kind(Kind) {}
|
||||
|
||||
struct ValueInfo {
|
||||
ComparisonCategoryResult Kind;
|
||||
VarDecl *VD;
|
||||
|
||||
ValueInfo(ComparisonCategoryResult Kind, VarDecl *VD)
|
||||
: Kind(Kind), VD(VD), HasValue(false) {}
|
||||
|
||||
/// \brief True iff we've successfully evaluated the variable as a constant
|
||||
/// expression and extracted its integer value.
|
||||
bool hasValidIntValue() const { return HasValue; }
|
||||
|
||||
/// \brief Get the constant integer value used by this variable to represent
|
||||
/// the comparison category result type.
|
||||
llvm::APSInt getIntValue() const {
|
||||
assert(hasValidIntValue());
|
||||
return IntValue;
|
||||
}
|
||||
|
||||
void setIntValue(llvm::APSInt Val) {
|
||||
IntValue = Val;
|
||||
HasValue = true;
|
||||
}
|
||||
|
||||
private:
|
||||
friend class ComparisonCategoryInfo;
|
||||
llvm::APSInt IntValue;
|
||||
bool HasValue : 1;
|
||||
};
|
||||
private:
|
||||
const ASTContext &Ctx;
|
||||
|
||||
/// \brief A map containing the comparison category result decls from the
|
||||
/// standard library. The key is a value of ComparisonCategoryResult.
|
||||
mutable llvm::SmallVector<
|
||||
ValueInfo, static_cast<unsigned>(ComparisonCategoryResult::Last) + 1>
|
||||
Objects;
|
||||
|
||||
/// \brief Lookup the ValueInfo struct for the specified ValueKind. If the
|
||||
/// VarDecl for the value cannot be found, nullptr is returned.
|
||||
///
|
||||
/// If the ValueInfo does not have a valid integer value the variable
|
||||
/// is evaluated as a constant expression to determine that value.
|
||||
ValueInfo *lookupValueInfo(ComparisonCategoryResult ValueKind) const;
|
||||
|
||||
public:
|
||||
/// \brief The declaration for the comparison category type from the
|
||||
/// standard library.
|
||||
// FIXME: Make this const
|
||||
CXXRecordDecl *Record = nullptr;
|
||||
|
||||
/// \brief The Kind of the comparison category type
|
||||
ComparisonCategoryType Kind;
|
||||
|
||||
public:
|
||||
QualType getType() const;
|
||||
|
||||
const ValueInfo *getValueInfo(ComparisonCategoryResult ValueKind) const {
|
||||
ValueInfo *Info = lookupValueInfo(ValueKind);
|
||||
assert(Info &&
|
||||
"comparison category does not contain the specified result kind");
|
||||
assert(Info->hasValidIntValue() &&
|
||||
"couldn't determine the integer constant for this value");
|
||||
return Info;
|
||||
}
|
||||
|
||||
/// \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 = ComparisonCategoryType;
|
||||
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 = ComparisonCategoryType;
|
||||
return Kind == CCK::StrongEquality || Kind == CCK::StrongOrdering;
|
||||
}
|
||||
|
||||
/// \brief True iff the comparison is not totally ordered.
|
||||
bool isPartial() const {
|
||||
using CCK = ComparisonCategoryType;
|
||||
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 ValueInfo *getEqualOrEquiv() const {
|
||||
return getValueInfo(makeWeakResult(ComparisonCategoryResult::Equal));
|
||||
}
|
||||
const ValueInfo *getNonequalOrNonequiv() const {
|
||||
assert(isEquality());
|
||||
return getValueInfo(makeWeakResult(ComparisonCategoryResult::Nonequal));
|
||||
}
|
||||
const ValueInfo *getLess() const {
|
||||
assert(isOrdered());
|
||||
return getValueInfo(ComparisonCategoryResult::Less);
|
||||
}
|
||||
const ValueInfo *getGreater() const {
|
||||
assert(isOrdered());
|
||||
return getValueInfo(ComparisonCategoryResult::Greater);
|
||||
}
|
||||
const ValueInfo *getUnordered() const {
|
||||
assert(isPartial());
|
||||
return getValueInfo(ComparisonCategoryResult::Unordered);
|
||||
}
|
||||
};
|
||||
|
||||
class ComparisonCategories {
|
||||
public:
|
||||
static StringRef getCategoryString(ComparisonCategoryType Kind);
|
||||
static StringRef getResultString(ComparisonCategoryResult Kind);
|
||||
|
||||
/// \brief Return the list of results which are valid for the specified
|
||||
/// comparison category type.
|
||||
static std::vector<ComparisonCategoryResult>
|
||||
getPossibleResultsForType(ComparisonCategoryType Type);
|
||||
|
||||
/// \brief Return the comparison category information for the category
|
||||
/// specified by 'Kind'.
|
||||
const ComparisonCategoryInfo &getInfo(ComparisonCategoryType Kind) const {
|
||||
const ComparisonCategoryInfo *Result = lookupInfo(Kind);
|
||||
assert(Result != nullptr &&
|
||||
"information for specified comparison category has not been built");
|
||||
return *Result;
|
||||
}
|
||||
|
||||
/// \brief Return the comparison category information as specified by
|
||||
/// `getCategoryForType(Ty)`. If the information is not already cached,
|
||||
/// the declaration is looked up and a cache entry is created.
|
||||
/// NOTE: Lookup is expected to succeed. Use lookupInfo if failure is possible.
|
||||
const ComparisonCategoryInfo &getInfoForType(QualType Ty) const;
|
||||
|
||||
public:
|
||||
/// \brief Return the cached comparison category information for the
|
||||
/// specified 'Kind'. If no cache entry is present the comparison category
|
||||
/// type is looked up. If lookup fails nullptr is returned. Otherwise, a
|
||||
/// new cache entry is created and returned
|
||||
const ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) const;
|
||||
|
||||
ComparisonCategoryInfo *lookupInfo(ComparisonCategoryType Kind) {
|
||||
const auto &This = *this;
|
||||
return const_cast<ComparisonCategoryInfo *>(This.lookupInfo(Kind));
|
||||
}
|
||||
|
||||
private:
|
||||
const ComparisonCategoryInfo *lookupInfoForType(QualType Ty) const;
|
||||
|
||||
private:
|
||||
friend class ASTContext;
|
||||
|
||||
explicit ComparisonCategories(const ASTContext &Ctx) : Ctx(Ctx) {}
|
||||
|
||||
const ASTContext &Ctx;
|
||||
|
||||
/// A map from the ComparisonCategoryType (represented as 'char') to the
|
||||
/// cached information for the specified category.
|
||||
mutable llvm::DenseMap<char, ComparisonCategoryInfo> Data;
|
||||
mutable NamespaceDecl *StdNS = nullptr;
|
||||
};
|
||||
|
||||
} // namespace clang
|
||||
|
||||
#endif
|
@ -2778,7 +2778,9 @@ protected:
|
||||
public:
|
||||
CastKind getCastKind() const { return (CastKind) CastExprBits.Kind; }
|
||||
void setCastKind(CastKind K) { CastExprBits.Kind = K; }
|
||||
const char *getCastKindName() const;
|
||||
|
||||
static const char *getCastKindName(CastKind CK);
|
||||
const char *getCastKindName() const { return getCastKindName(getCastKind()); }
|
||||
|
||||
Expr *getSubExpr() { return cast<Expr>(Op); }
|
||||
const Expr *getSubExpr() const { return cast<Expr>(Op); }
|
||||
|
@ -9424,4 +9424,18 @@ def err_multiversion_not_allowed_on_main : Error<
|
||||
def err_multiversion_not_supported : Error<
|
||||
"function multiversioning is not supported on the current target">;
|
||||
|
||||
// three-way comparison operator diagnostics
|
||||
def err_implied_comparison_category_type_not_found : Error<
|
||||
"cannot deduce return type of 'operator<=>' because type %0 was not found; "
|
||||
"include <compare>">;
|
||||
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_std_compare_type_not_supported : Error<
|
||||
"standard library implementation of %0 is not supported; "
|
||||
"%select{member '%2' does not have expected form|"
|
||||
"member '%2' is missing|"
|
||||
"the type is not trivially copyable|"
|
||||
"the type does not have the expected form}1">;
|
||||
} // end of sema component.
|
||||
|
@ -330,9 +330,10 @@ class Sema;
|
||||
}
|
||||
|
||||
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;
|
||||
|
@ -17,8 +17,9 @@
|
||||
|
||||
#include "clang/AST/Attr.h"
|
||||
#include "clang/AST/Availability.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/ComparisonCategories.h"
|
||||
#include "clang/AST/DeclTemplate.h"
|
||||
#include "clang/AST/DeclarationName.h"
|
||||
#include "clang/AST/Expr.h"
|
||||
#include "clang/AST/ExprObjC.h"
|
||||
#include "clang/AST/ExternalASTSource.h"
|
||||
@ -49,6 +50,7 @@
|
||||
#include "llvm/ADT/ArrayRef.h"
|
||||
#include "llvm/ADT/Optional.h"
|
||||
#include "llvm/ADT/SetVector.h"
|
||||
#include "llvm/ADT/SmallBitVector.h"
|
||||
#include "llvm/ADT/SmallPtrSet.h"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
#include "llvm/ADT/TinyPtrVector.h"
|
||||
@ -4545,6 +4547,22 @@ public:
|
||||
CXXRecordDecl *getStdBadAlloc() const;
|
||||
EnumDecl *getStdAlignValT() const;
|
||||
|
||||
private:
|
||||
// A cache representing if we've fully checked the various comparison category
|
||||
// types stored in ASTContext. The bit-index corresponds to the integer value
|
||||
// of a ComparisonCategoryType enumerator.
|
||||
llvm::SmallBitVector FullyCheckedComparisonCategories;
|
||||
|
||||
public:
|
||||
/// \brief Lookup the specified comparison category types in the standard
|
||||
/// library, an check the VarDecls possibly returned by the operator<=>
|
||||
/// builtins for that type.
|
||||
///
|
||||
/// \return The type of the comparison category type corresponding to the
|
||||
/// specified Kind, or a null type if an error occurs
|
||||
QualType CheckComparisonCategoryType(ComparisonCategoryType Kind,
|
||||
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);
|
||||
@ -9574,8 +9592,8 @@ public:
|
||||
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
|
||||
BinaryOperatorKind Opc, bool IsCompAssign = false);
|
||||
QualType CheckCompareOperands( // C99 6.5.8/9
|
||||
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
|
||||
BinaryOperatorKind Opc, bool isRelational);
|
||||
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
|
||||
BinaryOperatorKind Opc);
|
||||
QualType CheckBitwiseOperands( // C99 6.5.[10...12]
|
||||
ExprResult &LHS, ExprResult &RHS, SourceLocation Loc,
|
||||
BinaryOperatorKind Opc);
|
||||
|
@ -792,7 +792,8 @@ ASTContext::ASTContext(LangOptions &LOpts, SourceManager &SM,
|
||||
LangOpts.XRayAttrListFiles, SM)),
|
||||
PrintingPolicy(LOpts), Idents(idents), Selectors(sels),
|
||||
BuiltinInfo(builtins), DeclarationNames(*this), Comments(SM),
|
||||
CommentCommandTraits(BumpAlloc, LOpts.CommentOpts), LastSDM(nullptr, 0) {
|
||||
CommentCommandTraits(BumpAlloc, LOpts.CommentOpts),
|
||||
CompCategories(this_()), LastSDM(nullptr, 0) {
|
||||
TUDecl = TranslationUnitDecl::Create(*this);
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ add_clang_library(clangAST
|
||||
CommentLexer.cpp
|
||||
CommentParser.cpp
|
||||
CommentSema.cpp
|
||||
ComparisonCategories.cpp
|
||||
DataCollection.cpp
|
||||
Decl.cpp
|
||||
DeclarationName.cpp
|
||||
|
220
clang/lib/AST/ComparisonCategories.cpp
Normal file
220
clang/lib/AST/ComparisonCategories.cpp
Normal file
@ -0,0 +1,220 @@
|
||||
//===- 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"
|
||||
#include "llvm/ADT/SmallVector.h"
|
||||
|
||||
using namespace clang;
|
||||
|
||||
/// Attempt to determine the integer value used to represent the comparison
|
||||
/// category result by evaluating the initializer for the specified VarDecl as
|
||||
/// a constant expression and retreiving the value of the classes first
|
||||
/// (and only) field.
|
||||
///
|
||||
/// Note: The STL types are expected to have the form:
|
||||
/// struct X { T value; };
|
||||
/// where T is an integral or enumeration type.
|
||||
static bool evaluateIntValue(const ASTContext &Ctx,
|
||||
ComparisonCategoryInfo::ValueInfo *Info) {
|
||||
if (Info->hasValidIntValue())
|
||||
return false;
|
||||
|
||||
// Before we attempt to get the value of the first field, ensure that we
|
||||
// actually have one (and only one) field.
|
||||
auto *Record = Info->VD->getType()->getAsCXXRecordDecl();
|
||||
if (std::distance(Record->field_begin(), Record->field_end()) != 1 ||
|
||||
!Record->field_begin()->getType()->isIntegralOrEnumerationType())
|
||||
return true;
|
||||
|
||||
Expr::EvalResult Result;
|
||||
if (!Info->VD->hasInit() ||
|
||||
!Info->VD->getInit()->EvaluateAsRValue(Result, Ctx))
|
||||
return true;
|
||||
|
||||
assert(Result.Val.isStruct());
|
||||
Info->setIntValue(Result.Val.getStructField(0).getInt());
|
||||
return false;
|
||||
}
|
||||
|
||||
ComparisonCategoryInfo::ValueInfo *ComparisonCategoryInfo::lookupValueInfo(
|
||||
ComparisonCategoryResult ValueKind) const {
|
||||
// Check if we already have a cache entry for this value.
|
||||
auto It = llvm::find_if(
|
||||
Objects, [&](ValueInfo const &Info) { return Info.Kind == ValueKind; });
|
||||
|
||||
// We don't have a cached result. Lookup the variable declaration and create
|
||||
// a new entry representing it.
|
||||
if (It == Objects.end()) {
|
||||
DeclContextLookupResult Lookup = Record->getCanonicalDecl()->lookup(
|
||||
&Ctx.Idents.get(ComparisonCategories::getResultString(ValueKind)));
|
||||
if (Lookup.size() != 1 || !isa<VarDecl>(Lookup.front()))
|
||||
return nullptr;
|
||||
Objects.emplace_back(ValueKind, cast<VarDecl>(Lookup.front()));
|
||||
It = Objects.end() - 1;
|
||||
}
|
||||
assert(It != Objects.end());
|
||||
// Success! Attempt to update the int value in case the variables initializer
|
||||
// wasn't present the last time we were here.
|
||||
ValueInfo *Info = &(*It);
|
||||
evaluateIntValue(Ctx, Info);
|
||||
|
||||
return Info;
|
||||
}
|
||||
|
||||
static const NamespaceDecl *lookupStdNamespace(const ASTContext &Ctx,
|
||||
NamespaceDecl *&StdNS) {
|
||||
if (!StdNS) {
|
||||
DeclContextLookupResult Lookup =
|
||||
Ctx.getTranslationUnitDecl()->lookup(&Ctx.Idents.get("std"));
|
||||
if (Lookup.size() == 1)
|
||||
StdNS = dyn_cast<NamespaceDecl>(Lookup.front());
|
||||
}
|
||||
return StdNS;
|
||||
}
|
||||
|
||||
static CXXRecordDecl *lookupCXXRecordDecl(const ASTContext &Ctx,
|
||||
const NamespaceDecl *StdNS,
|
||||
ComparisonCategoryType Kind) {
|
||||
StringRef Name = ComparisonCategories::getCategoryString(Kind);
|
||||
DeclContextLookupResult Lookup = StdNS->lookup(&Ctx.Idents.get(Name));
|
||||
if (Lookup.size() == 1)
|
||||
if (CXXRecordDecl *RD = dyn_cast<CXXRecordDecl>(Lookup.front()))
|
||||
return RD;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ComparisonCategoryInfo *
|
||||
ComparisonCategories::lookupInfo(ComparisonCategoryType Kind) const {
|
||||
auto It = Data.find(static_cast<char>(Kind));
|
||||
if (It != Data.end())
|
||||
return &It->second;
|
||||
|
||||
if (const NamespaceDecl *NS = lookupStdNamespace(Ctx, StdNS))
|
||||
if (CXXRecordDecl *RD = lookupCXXRecordDecl(Ctx, NS, Kind))
|
||||
return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ComparisonCategoryInfo *
|
||||
ComparisonCategories::lookupInfoForType(QualType Ty) const {
|
||||
assert(!Ty.isNull() && "type must be non-null");
|
||||
using CCT = ComparisonCategoryType;
|
||||
auto *RD = Ty->getAsCXXRecordDecl();
|
||||
if (!RD)
|
||||
return nullptr;
|
||||
|
||||
// Check to see if we have information for the specified type cached.
|
||||
const auto *CanonRD = RD->getCanonicalDecl();
|
||||
for (auto &KV : Data) {
|
||||
const ComparisonCategoryInfo &Info = KV.second;
|
||||
if (CanonRD == Info.Record->getCanonicalDecl())
|
||||
return &Info;
|
||||
}
|
||||
|
||||
if (!RD->getEnclosingNamespaceContext()->isStdNamespace())
|
||||
return nullptr;
|
||||
|
||||
// If not, check to see if the decl names a type in namespace std with a name
|
||||
// matching one of the comparison category types.
|
||||
for (unsigned I = static_cast<unsigned>(CCT::First),
|
||||
End = static_cast<unsigned>(CCT::Last);
|
||||
I <= End; ++I) {
|
||||
CCT Kind = static_cast<CCT>(I);
|
||||
|
||||
// We've found the comparison category type. Build a new cache entry for
|
||||
// it.
|
||||
if (getCategoryString(Kind) == RD->getName())
|
||||
return &Data.try_emplace((char)Kind, Ctx, RD, Kind).first->second;
|
||||
}
|
||||
|
||||
// We've found nothing. This isn't a comparison category type.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const ComparisonCategoryInfo &ComparisonCategories::getInfoForType(QualType Ty) const {
|
||||
const ComparisonCategoryInfo *Info = lookupInfoForType(Ty);
|
||||
assert(Info && "info for comparison category not found");
|
||||
return *Info;
|
||||
}
|
||||
|
||||
QualType ComparisonCategoryInfo::getType() const {
|
||||
assert(Record);
|
||||
return QualType(Record->getTypeForDecl(), 0);
|
||||
}
|
||||
|
||||
StringRef ComparisonCategories::getCategoryString(ComparisonCategoryType Kind) {
|
||||
using CCKT = ComparisonCategoryType;
|
||||
switch (Kind) {
|
||||
case CCKT::WeakEquality:
|
||||
return "weak_equality";
|
||||
case CCKT::StrongEquality:
|
||||
return "strong_equality";
|
||||
case CCKT::PartialOrdering:
|
||||
return "partial_ordering";
|
||||
case CCKT::WeakOrdering:
|
||||
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";
|
||||
}
|
||||
llvm_unreachable("unhandled case in switch");
|
||||
}
|
||||
|
||||
std::vector<ComparisonCategoryResult>
|
||||
ComparisonCategories::getPossibleResultsForType(ComparisonCategoryType Type) {
|
||||
using CCT = ComparisonCategoryType;
|
||||
using CCR = ComparisonCategoryResult;
|
||||
std::vector<CCR> Values;
|
||||
Values.reserve(6);
|
||||
Values.push_back(CCR::Equivalent);
|
||||
bool IsStrong = (Type == CCT::StrongEquality || Type == CCT::StrongOrdering);
|
||||
if (IsStrong)
|
||||
Values.push_back(CCR::Equal);
|
||||
if (Type == CCT::StrongOrdering || Type == CCT::WeakOrdering ||
|
||||
Type == CCT::PartialOrdering) {
|
||||
Values.push_back(CCR::Less);
|
||||
Values.push_back(CCR::Greater);
|
||||
} else {
|
||||
Values.push_back(CCR::Nonequivalent);
|
||||
if (IsStrong)
|
||||
Values.push_back(CCR::Nonequal);
|
||||
}
|
||||
if (Type == CCT::PartialOrdering)
|
||||
Values.push_back(CCR::Unordered);
|
||||
return Values;
|
||||
}
|
@ -1633,8 +1633,8 @@ bool CastExpr::CastConsistency() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
const char *CastExpr::getCastKindName() const {
|
||||
switch (getCastKind()) {
|
||||
const char *CastExpr::getCastKindName(CastKind CK) {
|
||||
switch (CK) {
|
||||
#define CAST_OPERATION(Name) case CK_##Name: return #Name;
|
||||
#include "clang/AST/OperationKinds.def"
|
||||
}
|
||||
|
@ -2242,6 +2242,8 @@ static bool handleIntIntBinOp(EvalInfo &Info, const Expr *E, const APSInt &LHS,
|
||||
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 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Common base class for lvalue and temporary evaluation.
|
||||
@ -6232,6 +6234,8 @@ namespace {
|
||||
bool VisitCXXInheritedCtorInitExpr(const CXXInheritedCtorInitExpr *E);
|
||||
bool VisitCXXConstructExpr(const CXXConstructExpr *E, QualType T);
|
||||
bool VisitCXXStdInitializerListExpr(const CXXStdInitializerListExpr *E);
|
||||
|
||||
bool VisitBinCmp(const BinaryOperator *E);
|
||||
};
|
||||
}
|
||||
|
||||
@ -7072,11 +7076,11 @@ bool ArrayExprEvaluator::VisitCXXConstructExpr(const CXXConstructExpr *E,
|
||||
|
||||
namespace {
|
||||
class IntExprEvaluator
|
||||
: public ExprEvaluatorBase<IntExprEvaluator> {
|
||||
: public ExprEvaluatorBase<IntExprEvaluator> {
|
||||
APValue &Result;
|
||||
public:
|
||||
IntExprEvaluator(EvalInfo &info, APValue &result)
|
||||
: ExprEvaluatorBaseTy(info), Result(result) {}
|
||||
: ExprEvaluatorBaseTy(info), Result(result) {}
|
||||
|
||||
bool Success(const llvm::APSInt &SI, const Expr *E, APValue &Result) {
|
||||
assert(E->getType()->isIntegralOrEnumerationType() &&
|
||||
@ -7107,7 +7111,7 @@ public:
|
||||
}
|
||||
|
||||
bool Success(uint64_t Value, const Expr *E, APValue &Result) {
|
||||
assert(E->getType()->isIntegralOrEnumerationType() &&
|
||||
assert(E->getType()->isIntegralOrEnumerationType() &&
|
||||
"Invalid evaluation result.");
|
||||
Result = APValue(Info.Ctx.MakeIntValue(Value, E->getType()));
|
||||
return true;
|
||||
@ -8226,10 +8230,8 @@ public:
|
||||
/// 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_Comma || E->isLogicalOp() ||
|
||||
(E->isRValue() && E->getType()->isIntegralOrEnumerationType() &&
|
||||
E->getLHS()->getType()->isIntegralOrEnumerationType() &&
|
||||
E->getRHS()->getType()->isIntegralOrEnumerationType());
|
||||
}
|
||||
@ -8508,19 +8510,47 @@ public:
|
||||
};
|
||||
}
|
||||
|
||||
bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
// We don't call noteFailure immediately because the assignment happens after
|
||||
// we evaluate LHS and RHS.
|
||||
if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
|
||||
return Error(E);
|
||||
template <class SuccessCB, class AfterCB>
|
||||
static bool
|
||||
EvaluateComparisonBinaryOperator(EvalInfo &Info, const BinaryOperator *E,
|
||||
SuccessCB &&Success, AfterCB &&DoAfter) {
|
||||
assert(E->isComparisonOp() && "expected comparison operator");
|
||||
assert((E->getOpcode() == BO_Cmp ||
|
||||
E->getType()->isIntegralOrEnumerationType()) &&
|
||||
"unsupported binary expression evaluation");
|
||||
auto Error = [&](const Expr *E) {
|
||||
Info.FFDiag(E, diag::note_invalid_subexpr_in_const_expr);
|
||||
return false;
|
||||
};
|
||||
|
||||
DelayedNoteFailureRAII MaybeNoteFailureLater(Info, E->isAssignmentOp());
|
||||
if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E))
|
||||
return DataRecursiveIntBinOpEvaluator(*this, Result).Traverse(E);
|
||||
using CCR = ComparisonCategoryResult;
|
||||
bool IsRelational = E->isRelationalOp();
|
||||
bool IsEquality = E->isEqualityOp();
|
||||
if (E->getOpcode() == BO_Cmp) {
|
||||
const ComparisonCategoryInfo &CmpInfo =
|
||||
Info.Ctx.CompCategories.getInfoForType(E->getType());
|
||||
IsRelational = CmpInfo.isOrdered();
|
||||
IsEquality = CmpInfo.isEquality();
|
||||
}
|
||||
|
||||
QualType LHSTy = E->getLHS()->getType();
|
||||
QualType RHSTy = E->getRHS()->getType();
|
||||
|
||||
if (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);
|
||||
}
|
||||
|
||||
if (LHSTy->isAnyComplexType() || RHSTy->isAnyComplexType()) {
|
||||
ComplexValue LHS, RHS;
|
||||
bool LHSOK;
|
||||
@ -8553,30 +8583,13 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
LHS.getComplexFloatReal().compare(RHS.getComplexFloatReal());
|
||||
APFloat::cmpResult CR_i =
|
||||
LHS.getComplexFloatImag().compare(RHS.getComplexFloatImag());
|
||||
|
||||
if (E->getOpcode() == BO_EQ)
|
||||
return Success((CR_r == APFloat::cmpEqual &&
|
||||
CR_i == APFloat::cmpEqual), E);
|
||||
else {
|
||||
assert(E->getOpcode() == BO_NE &&
|
||||
"Invalid complex comparison.");
|
||||
return Success(((CR_r == APFloat::cmpGreaterThan ||
|
||||
CR_r == APFloat::cmpLessThan ||
|
||||
CR_r == APFloat::cmpUnordered) ||
|
||||
(CR_i == APFloat::cmpGreaterThan ||
|
||||
CR_i == APFloat::cmpLessThan ||
|
||||
CR_i == APFloat::cmpUnordered)), E);
|
||||
}
|
||||
bool IsEqual = CR_r == APFloat::cmpEqual && CR_i == APFloat::cmpEqual;
|
||||
return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E);
|
||||
} else {
|
||||
if (E->getOpcode() == BO_EQ)
|
||||
return Success((LHS.getComplexIntReal() == RHS.getComplexIntReal() &&
|
||||
LHS.getComplexIntImag() == RHS.getComplexIntImag()), E);
|
||||
else {
|
||||
assert(E->getOpcode() == BO_NE &&
|
||||
"Invalid compex comparison.");
|
||||
return Success((LHS.getComplexIntReal() != RHS.getComplexIntReal() ||
|
||||
LHS.getComplexIntImag() != RHS.getComplexIntImag()), E);
|
||||
}
|
||||
assert(IsEquality && "invalid complex comparison");
|
||||
bool IsEqual = LHS.getComplexIntReal() == RHS.getComplexIntReal() &&
|
||||
LHS.getComplexIntImag() == RHS.getComplexIntImag();
|
||||
return Success(IsEqual ? CCR::Equal : CCR::Nonequal, E);
|
||||
}
|
||||
}
|
||||
|
||||
@ -8591,243 +8604,160 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
if (!EvaluateFloat(E->getLHS(), LHS, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
APFloat::cmpResult CR = LHS.compare(RHS);
|
||||
|
||||
switch (E->getOpcode()) {
|
||||
default:
|
||||
llvm_unreachable("Invalid binary operator!");
|
||||
case BO_LT:
|
||||
return Success(CR == APFloat::cmpLessThan, E);
|
||||
case BO_GT:
|
||||
return Success(CR == APFloat::cmpGreaterThan, E);
|
||||
case BO_LE:
|
||||
return Success(CR == APFloat::cmpLessThan || CR == APFloat::cmpEqual, E);
|
||||
case BO_GE:
|
||||
return Success(CR == APFloat::cmpGreaterThan || CR == APFloat::cmpEqual,
|
||||
E);
|
||||
case BO_EQ:
|
||||
return Success(CR == APFloat::cmpEqual, E);
|
||||
case BO_NE:
|
||||
return Success(CR == APFloat::cmpGreaterThan
|
||||
|| CR == APFloat::cmpLessThan
|
||||
|| CR == APFloat::cmpUnordered, E);
|
||||
}
|
||||
assert(E->isComparisonOp() && "Invalid binary operator!");
|
||||
auto GetCmpRes = [&]() {
|
||||
switch (LHS.compare(RHS)) {
|
||||
case APFloat::cmpEqual:
|
||||
return CCR::Equal;
|
||||
case APFloat::cmpLessThan:
|
||||
return CCR::Less;
|
||||
case APFloat::cmpGreaterThan:
|
||||
return CCR::Greater;
|
||||
case APFloat::cmpUnordered:
|
||||
return CCR::Unordered;
|
||||
}
|
||||
};
|
||||
return Success(GetCmpRes(), E);
|
||||
}
|
||||
|
||||
if (LHSTy->isPointerType() && RHSTy->isPointerType()) {
|
||||
if (E->getOpcode() == BO_Sub || E->isComparisonOp()) {
|
||||
LValue LHSValue, RHSValue;
|
||||
LValue LHSValue, RHSValue;
|
||||
|
||||
bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
|
||||
if (!LHSOK && !Info.noteFailure())
|
||||
return false;
|
||||
bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
|
||||
if (!LHSOK && !Info.noteFailure())
|
||||
return false;
|
||||
|
||||
if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
|
||||
return false;
|
||||
if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
// Reject differing bases from the normal codepath; we special-case
|
||||
// comparisons to null.
|
||||
if (!HasSameBase(LHSValue, RHSValue)) {
|
||||
if (E->getOpcode() == BO_Sub) {
|
||||
// Handle &&A - &&B.
|
||||
if (!LHSValue.Offset.isZero() || !RHSValue.Offset.isZero())
|
||||
return Error(E);
|
||||
const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr*>();
|
||||
const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr*>();
|
||||
if (!LHSExpr || !RHSExpr)
|
||||
return Error(E);
|
||||
const AddrLabelExpr *LHSAddrExpr = dyn_cast<AddrLabelExpr>(LHSExpr);
|
||||
const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
|
||||
if (!LHSAddrExpr || !RHSAddrExpr)
|
||||
return Error(E);
|
||||
// Make sure both labels come from the same function.
|
||||
if (LHSAddrExpr->getLabel()->getDeclContext() !=
|
||||
RHSAddrExpr->getLabel()->getDeclContext())
|
||||
return Error(E);
|
||||
return Success(APValue(LHSAddrExpr, RHSAddrExpr), E);
|
||||
}
|
||||
// Inequalities and subtractions between unrelated pointers have
|
||||
// unspecified or undefined behavior.
|
||||
if (!E->isEqualityOp())
|
||||
return Error(E);
|
||||
// A constant address may compare equal to the address of a symbol.
|
||||
// The one exception is that address of an object cannot compare equal
|
||||
// to a null pointer constant.
|
||||
if ((!LHSValue.Base && !LHSValue.Offset.isZero()) ||
|
||||
(!RHSValue.Base && !RHSValue.Offset.isZero()))
|
||||
return Error(E);
|
||||
// It's implementation-defined whether distinct literals will have
|
||||
// distinct addresses. In clang, the result of such a comparison is
|
||||
// unspecified, so it is not a constant expression. However, we do know
|
||||
// that the address of a literal will be non-null.
|
||||
if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) &&
|
||||
LHSValue.Base && RHSValue.Base)
|
||||
return Error(E);
|
||||
// We can't tell whether weak symbols will end up pointing to the same
|
||||
// object.
|
||||
if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue))
|
||||
return Error(E);
|
||||
// We can't compare the address of the start of one object with the
|
||||
// past-the-end address of another object, per C++ DR1652.
|
||||
if ((LHSValue.Base && LHSValue.Offset.isZero() &&
|
||||
isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) ||
|
||||
(RHSValue.Base && RHSValue.Offset.isZero() &&
|
||||
isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue)))
|
||||
return Error(E);
|
||||
// We can't tell whether an object is at the same address as another
|
||||
// zero sized object.
|
||||
if ((RHSValue.Base && isZeroSized(LHSValue)) ||
|
||||
(LHSValue.Base && isZeroSized(RHSValue)))
|
||||
return Error(E);
|
||||
// Pointers with different bases cannot represent the same object.
|
||||
return Success(E->getOpcode() == BO_NE, E);
|
||||
}
|
||||
// Reject differing bases from the normal codepath; we special-case
|
||||
// comparisons to null.
|
||||
if (!HasSameBase(LHSValue, RHSValue)) {
|
||||
// Inequalities and subtractions between unrelated pointers have
|
||||
// unspecified or undefined behavior.
|
||||
if (!IsEquality)
|
||||
return Error(E);
|
||||
// A constant address may compare equal to the address of a symbol.
|
||||
// The one exception is that address of an object cannot compare equal
|
||||
// to a null pointer constant.
|
||||
if ((!LHSValue.Base && !LHSValue.Offset.isZero()) ||
|
||||
(!RHSValue.Base && !RHSValue.Offset.isZero()))
|
||||
return Error(E);
|
||||
// It's implementation-defined whether distinct literals will have
|
||||
// distinct addresses. In clang, the result of such a comparison is
|
||||
// unspecified, so it is not a constant expression. However, we do know
|
||||
// that the address of a literal will be non-null.
|
||||
if ((IsLiteralLValue(LHSValue) || IsLiteralLValue(RHSValue)) &&
|
||||
LHSValue.Base && RHSValue.Base)
|
||||
return Error(E);
|
||||
// We can't tell whether weak symbols will end up pointing to the same
|
||||
// object.
|
||||
if (IsWeakLValue(LHSValue) || IsWeakLValue(RHSValue))
|
||||
return Error(E);
|
||||
// We can't compare the address of the start of one object with the
|
||||
// past-the-end address of another object, per C++ DR1652.
|
||||
if ((LHSValue.Base && LHSValue.Offset.isZero() &&
|
||||
isOnePastTheEndOfCompleteObject(Info.Ctx, RHSValue)) ||
|
||||
(RHSValue.Base && RHSValue.Offset.isZero() &&
|
||||
isOnePastTheEndOfCompleteObject(Info.Ctx, LHSValue)))
|
||||
return Error(E);
|
||||
// We can't tell whether an object is at the same address as another
|
||||
// zero sized object.
|
||||
if ((RHSValue.Base && isZeroSized(LHSValue)) ||
|
||||
(LHSValue.Base && isZeroSized(RHSValue)))
|
||||
return Error(E);
|
||||
return Success(CCR::Nonequal, E);
|
||||
}
|
||||
|
||||
const CharUnits &LHSOffset = LHSValue.getLValueOffset();
|
||||
const CharUnits &RHSOffset = RHSValue.getLValueOffset();
|
||||
const CharUnits &LHSOffset = LHSValue.getLValueOffset();
|
||||
const CharUnits &RHSOffset = RHSValue.getLValueOffset();
|
||||
|
||||
SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
|
||||
SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
|
||||
SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
|
||||
SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
|
||||
|
||||
if (E->getOpcode() == BO_Sub) {
|
||||
// C++11 [expr.add]p6:
|
||||
// Unless both pointers point to elements of the same array object, or
|
||||
// one past the last element of the array object, the behavior is
|
||||
// undefined.
|
||||
if (!LHSDesignator.Invalid && !RHSDesignator.Invalid &&
|
||||
!AreElementsOfSameArray(getType(LHSValue.Base),
|
||||
LHSDesignator, RHSDesignator))
|
||||
CCEDiag(E, diag::note_constexpr_pointer_subtraction_not_same_array);
|
||||
// C++11 [expr.rel]p3:
|
||||
// Pointers to void (after pointer conversions) can be compared, with a
|
||||
// result defined as follows: If both pointers represent the same
|
||||
// address or are both the null pointer value, the result is true if the
|
||||
// operator is <= or >= and false otherwise; otherwise the result is
|
||||
// unspecified.
|
||||
// We interpret this as applying to pointers to *cv* void.
|
||||
if (LHSTy->isVoidPointerType() && LHSOffset != RHSOffset && IsRelational)
|
||||
Info.CCEDiag(E, diag::note_constexpr_void_comparison);
|
||||
|
||||
QualType Type = E->getLHS()->getType();
|
||||
QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
|
||||
|
||||
CharUnits ElementSize;
|
||||
if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize))
|
||||
return false;
|
||||
|
||||
// As an extension, a type may have zero size (empty struct or union in
|
||||
// C, array of zero length). Pointer subtraction in such cases has
|
||||
// undefined behavior, so is not constant.
|
||||
if (ElementSize.isZero()) {
|
||||
Info.FFDiag(E, diag::note_constexpr_pointer_subtraction_zero_size)
|
||||
<< ElementType;
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: LLVM and GCC both compute LHSOffset - RHSOffset at runtime,
|
||||
// and produce incorrect results when it overflows. Such behavior
|
||||
// appears to be non-conforming, but is common, so perhaps we should
|
||||
// assume the standard intended for such cases to be undefined behavior
|
||||
// and check for them.
|
||||
|
||||
// Compute (LHSOffset - RHSOffset) / Size carefully, checking for
|
||||
// overflow in the final conversion to ptrdiff_t.
|
||||
APSInt LHS(
|
||||
llvm::APInt(65, (int64_t)LHSOffset.getQuantity(), true), false);
|
||||
APSInt RHS(
|
||||
llvm::APInt(65, (int64_t)RHSOffset.getQuantity(), true), false);
|
||||
APSInt ElemSize(
|
||||
llvm::APInt(65, (int64_t)ElementSize.getQuantity(), true), false);
|
||||
APSInt TrueResult = (LHS - RHS) / ElemSize;
|
||||
APSInt Result = TrueResult.trunc(Info.Ctx.getIntWidth(E->getType()));
|
||||
|
||||
if (Result.extend(65) != TrueResult &&
|
||||
!HandleOverflow(Info, E, TrueResult, E->getType()))
|
||||
return false;
|
||||
return Success(Result, E);
|
||||
}
|
||||
|
||||
// C++11 [expr.rel]p3:
|
||||
// Pointers to void (after pointer conversions) can be compared, with a
|
||||
// result defined as follows: If both pointers represent the same
|
||||
// address or are both the null pointer value, the result is true if the
|
||||
// operator is <= or >= and false otherwise; otherwise the result is
|
||||
// unspecified.
|
||||
// We interpret this as applying to pointers to *cv* void.
|
||||
if (LHSTy->isVoidPointerType() && LHSOffset != RHSOffset &&
|
||||
E->isRelationalOp())
|
||||
CCEDiag(E, diag::note_constexpr_void_comparison);
|
||||
|
||||
// C++11 [expr.rel]p2:
|
||||
// - If two pointers point to non-static data members of the same object,
|
||||
// or to subobjects or array elements fo such members, recursively, the
|
||||
// pointer to the later declared member compares greater provided the
|
||||
// two members have the same access control and provided their class is
|
||||
// not a union.
|
||||
// [...]
|
||||
// - Otherwise pointer comparisons are unspecified.
|
||||
if (!LHSDesignator.Invalid && !RHSDesignator.Invalid &&
|
||||
E->isRelationalOp()) {
|
||||
bool WasArrayIndex;
|
||||
unsigned Mismatch =
|
||||
FindDesignatorMismatch(getType(LHSValue.Base), LHSDesignator,
|
||||
RHSDesignator, WasArrayIndex);
|
||||
// At the point where the designators diverge, the comparison has a
|
||||
// specified value if:
|
||||
// - we are comparing array indices
|
||||
// - we are comparing fields of a union, or fields with the same access
|
||||
// Otherwise, the result is unspecified and thus the comparison is not a
|
||||
// constant expression.
|
||||
if (!WasArrayIndex && Mismatch < LHSDesignator.Entries.size() &&
|
||||
Mismatch < RHSDesignator.Entries.size()) {
|
||||
const FieldDecl *LF = getAsField(LHSDesignator.Entries[Mismatch]);
|
||||
const FieldDecl *RF = getAsField(RHSDesignator.Entries[Mismatch]);
|
||||
if (!LF && !RF)
|
||||
CCEDiag(E, diag::note_constexpr_pointer_comparison_base_classes);
|
||||
else if (!LF)
|
||||
CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
|
||||
// C++11 [expr.rel]p2:
|
||||
// - If two pointers point to non-static data members of the same object,
|
||||
// or to subobjects or array elements fo such members, recursively, the
|
||||
// pointer to the later declared member compares greater provided the
|
||||
// two members have the same access control and provided their class is
|
||||
// not a union.
|
||||
// [...]
|
||||
// - Otherwise pointer comparisons are unspecified.
|
||||
if (!LHSDesignator.Invalid && !RHSDesignator.Invalid && IsRelational) {
|
||||
bool WasArrayIndex;
|
||||
unsigned Mismatch = FindDesignatorMismatch(
|
||||
getType(LHSValue.Base), LHSDesignator, RHSDesignator, WasArrayIndex);
|
||||
// At the point where the designators diverge, the comparison has a
|
||||
// specified value if:
|
||||
// - we are comparing array indices
|
||||
// - we are comparing fields of a union, or fields with the same access
|
||||
// Otherwise, the result is unspecified and thus the comparison is not a
|
||||
// constant expression.
|
||||
if (!WasArrayIndex && Mismatch < LHSDesignator.Entries.size() &&
|
||||
Mismatch < RHSDesignator.Entries.size()) {
|
||||
const FieldDecl *LF = getAsField(LHSDesignator.Entries[Mismatch]);
|
||||
const FieldDecl *RF = getAsField(RHSDesignator.Entries[Mismatch]);
|
||||
if (!LF && !RF)
|
||||
Info.CCEDiag(E, diag::note_constexpr_pointer_comparison_base_classes);
|
||||
else if (!LF)
|
||||
Info.CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
|
||||
<< getAsBaseClass(LHSDesignator.Entries[Mismatch])
|
||||
<< RF->getParent() << RF;
|
||||
else if (!RF)
|
||||
CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
|
||||
else if (!RF)
|
||||
Info.CCEDiag(E, diag::note_constexpr_pointer_comparison_base_field)
|
||||
<< getAsBaseClass(RHSDesignator.Entries[Mismatch])
|
||||
<< LF->getParent() << LF;
|
||||
else if (!LF->getParent()->isUnion() &&
|
||||
LF->getAccess() != RF->getAccess())
|
||||
CCEDiag(E, diag::note_constexpr_pointer_comparison_differing_access)
|
||||
else if (!LF->getParent()->isUnion() &&
|
||||
LF->getAccess() != RF->getAccess())
|
||||
Info.CCEDiag(E,
|
||||
diag::note_constexpr_pointer_comparison_differing_access)
|
||||
<< LF << LF->getAccess() << RF << RF->getAccess()
|
||||
<< LF->getParent();
|
||||
}
|
||||
}
|
||||
|
||||
// The comparison here must be unsigned, and performed with the same
|
||||
// width as the pointer.
|
||||
unsigned PtrSize = Info.Ctx.getTypeSize(LHSTy);
|
||||
uint64_t CompareLHS = LHSOffset.getQuantity();
|
||||
uint64_t CompareRHS = RHSOffset.getQuantity();
|
||||
assert(PtrSize <= 64 && "Unexpected pointer width");
|
||||
uint64_t Mask = ~0ULL >> (64 - PtrSize);
|
||||
CompareLHS &= Mask;
|
||||
CompareRHS &= Mask;
|
||||
|
||||
// If there is a base and this is a relational operator, we can only
|
||||
// compare pointers within the object in question; otherwise, the result
|
||||
// depends on where the object is located in memory.
|
||||
if (!LHSValue.Base.isNull() && E->isRelationalOp()) {
|
||||
QualType BaseTy = getType(LHSValue.Base);
|
||||
if (BaseTy->isIncompleteType())
|
||||
return Error(E);
|
||||
CharUnits Size = Info.Ctx.getTypeSizeInChars(BaseTy);
|
||||
uint64_t OffsetLimit = Size.getQuantity();
|
||||
if (CompareLHS > OffsetLimit || CompareRHS > OffsetLimit)
|
||||
return Error(E);
|
||||
}
|
||||
|
||||
switch (E->getOpcode()) {
|
||||
default: llvm_unreachable("missing comparison operator");
|
||||
case BO_LT: return Success(CompareLHS < CompareRHS, E);
|
||||
case BO_GT: return Success(CompareLHS > CompareRHS, E);
|
||||
case BO_LE: return Success(CompareLHS <= CompareRHS, E);
|
||||
case BO_GE: return Success(CompareLHS >= CompareRHS, E);
|
||||
case BO_EQ: return Success(CompareLHS == CompareRHS, E);
|
||||
case BO_NE: return Success(CompareLHS != CompareRHS, E);
|
||||
}
|
||||
}
|
||||
|
||||
// The comparison here must be unsigned, and performed with the same
|
||||
// width as the pointer.
|
||||
unsigned PtrSize = Info.Ctx.getTypeSize(LHSTy);
|
||||
uint64_t CompareLHS = LHSOffset.getQuantity();
|
||||
uint64_t CompareRHS = RHSOffset.getQuantity();
|
||||
assert(PtrSize <= 64 && "Unexpected pointer width");
|
||||
uint64_t Mask = ~0ULL >> (64 - PtrSize);
|
||||
CompareLHS &= Mask;
|
||||
CompareRHS &= Mask;
|
||||
|
||||
// If there is a base and this is a relational operator, we can only
|
||||
// compare pointers within the object in question; otherwise, the result
|
||||
// depends on where the object is located in memory.
|
||||
if (!LHSValue.Base.isNull() && IsRelational) {
|
||||
QualType BaseTy = getType(LHSValue.Base);
|
||||
if (BaseTy->isIncompleteType())
|
||||
return Error(E);
|
||||
CharUnits Size = Info.Ctx.getTypeSizeInChars(BaseTy);
|
||||
uint64_t OffsetLimit = Size.getQuantity();
|
||||
if (CompareLHS > OffsetLimit || CompareRHS > OffsetLimit)
|
||||
return Error(E);
|
||||
}
|
||||
|
||||
if (CompareLHS < CompareRHS)
|
||||
return Success(CCR::Less, E);
|
||||
if (CompareLHS > CompareRHS)
|
||||
return Success(CCR::Greater, E);
|
||||
return Success(CCR::Equal, E);
|
||||
}
|
||||
|
||||
if (LHSTy->isMemberPointerType()) {
|
||||
assert(E->isEqualityOp() && "unexpected member pointer operation");
|
||||
assert(IsEquality && "unexpected member pointer operation");
|
||||
assert(RHSTy->isMemberPointerType() && "invalid comparison");
|
||||
|
||||
MemberPtr LHSValue, RHSValue;
|
||||
@ -8844,24 +8774,24 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
// null, they compare unequal.
|
||||
if (!LHSValue.getDecl() || !RHSValue.getDecl()) {
|
||||
bool Equal = !LHSValue.getDecl() && !RHSValue.getDecl();
|
||||
return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E);
|
||||
return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
|
||||
}
|
||||
|
||||
// Otherwise if either is a pointer to a virtual member function, the
|
||||
// result is unspecified.
|
||||
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(LHSValue.getDecl()))
|
||||
if (MD->isVirtual())
|
||||
CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
|
||||
Info.CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
|
||||
if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(RHSValue.getDecl()))
|
||||
if (MD->isVirtual())
|
||||
CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
|
||||
Info.CCEDiag(E, diag::note_constexpr_compare_virtual_mem_ptr) << MD;
|
||||
|
||||
// Otherwise they compare equal if and only if they would refer to the
|
||||
// same member of the same most derived object or the same subobject if
|
||||
// they were dereferenced with a hypothetical object of the associated
|
||||
// class type.
|
||||
bool Equal = LHSValue == RHSValue;
|
||||
return Success(E->getOpcode() == BO_EQ ? Equal : !Equal, E);
|
||||
return Success(Equal ? CCR::Equal : CCR::Nonequal, E);
|
||||
}
|
||||
|
||||
if (LHSTy->isNullPtrType()) {
|
||||
@ -8870,14 +8800,163 @@ bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
// C++11 [expr.rel]p4, [expr.eq]p3: If two operands of type std::nullptr_t
|
||||
// are compared, the result is true of the operator is <=, >= or ==, and
|
||||
// false otherwise.
|
||||
BinaryOperator::Opcode Opcode = E->getOpcode();
|
||||
return Success(Opcode == BO_EQ || Opcode == BO_LE || Opcode == BO_GE, E);
|
||||
return Success(CCR::Equal, E);
|
||||
}
|
||||
|
||||
assert((!LHSTy->isIntegralOrEnumerationType() ||
|
||||
!RHSTy->isIntegralOrEnumerationType()) &&
|
||||
return DoAfter();
|
||||
}
|
||||
|
||||
bool RecordExprEvaluator::VisitBinCmp(const BinaryOperator *E) {
|
||||
if (!CheckLiteralType(Info, E))
|
||||
return false;
|
||||
|
||||
auto OnSuccess = [&](ComparisonCategoryResult ResKind,
|
||||
const BinaryOperator *E) {
|
||||
// Evaluation succeeded. Lookup the information for the comparison category
|
||||
// type and fetch the VarDecl for the result.
|
||||
const ComparisonCategoryInfo &CmpInfo =
|
||||
Info.Ctx.CompCategories.getInfoForType(E->getType());
|
||||
const VarDecl *VD =
|
||||
CmpInfo.getValueInfo(CmpInfo.makeWeakResult(ResKind))->VD;
|
||||
// Check and evaluate the result as a constant expression.
|
||||
LValue LV;
|
||||
LV.set(VD);
|
||||
if (!handleLValueToRValueConversion(Info, E, E->getType(), LV, Result))
|
||||
return false;
|
||||
return CheckConstantExpression(Info, E->getExprLoc(), E->getType(), Result);
|
||||
};
|
||||
return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
|
||||
return ExprEvaluatorBaseTy::VisitBinCmp(E);
|
||||
});
|
||||
}
|
||||
|
||||
bool IntExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
// We don't call noteFailure immediately because the assignment happens after
|
||||
// we evaluate LHS and RHS.
|
||||
if (!Info.keepEvaluatingAfterFailure() && E->isAssignmentOp())
|
||||
return Error(E);
|
||||
|
||||
DelayedNoteFailureRAII MaybeNoteFailureLater(Info, E->isAssignmentOp());
|
||||
if (DataRecursiveIntBinOpEvaluator::shouldEnqueue(E))
|
||||
return DataRecursiveIntBinOpEvaluator(*this, Result).Traverse(E);
|
||||
|
||||
assert((!E->getLHS()->getType()->isIntegralOrEnumerationType() ||
|
||||
!E->getRHS()->getType()->isIntegralOrEnumerationType()) &&
|
||||
"DataRecursiveIntBinOpEvaluator should have handled integral types");
|
||||
// We can't continue from here for non-integral types.
|
||||
|
||||
if (E->isComparisonOp()) {
|
||||
// Evaluate builtin binary comparisons by evaluating them as C++2a three-way
|
||||
// comparisons and then translating the result.
|
||||
auto OnSuccess = [&](ComparisonCategoryResult ResKind,
|
||||
const BinaryOperator *E) {
|
||||
using CCR = ComparisonCategoryResult;
|
||||
bool IsEqual = ResKind == CCR::Equal,
|
||||
IsLess = ResKind == CCR::Less,
|
||||
IsGreater = ResKind == CCR::Greater;
|
||||
auto Op = E->getOpcode();
|
||||
switch (Op) {
|
||||
default:
|
||||
llvm_unreachable("unsupported binary operator");
|
||||
case BO_EQ:
|
||||
case BO_NE:
|
||||
return Success(IsEqual == (Op == BO_EQ), E);
|
||||
case BO_LT: return Success(IsLess, E);
|
||||
case BO_GT: return Success(IsGreater, E);
|
||||
case BO_LE: return Success(IsEqual || IsLess, E);
|
||||
case BO_GE: return Success(IsEqual || IsGreater, E);
|
||||
}
|
||||
};
|
||||
return EvaluateComparisonBinaryOperator(Info, E, OnSuccess, [&]() {
|
||||
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
|
||||
});
|
||||
}
|
||||
|
||||
QualType LHSTy = E->getLHS()->getType();
|
||||
QualType RHSTy = E->getRHS()->getType();
|
||||
|
||||
if (LHSTy->isPointerType() && RHSTy->isPointerType() &&
|
||||
E->getOpcode() == BO_Sub) {
|
||||
LValue LHSValue, RHSValue;
|
||||
|
||||
bool LHSOK = EvaluatePointer(E->getLHS(), LHSValue, Info);
|
||||
if (!LHSOK && !Info.noteFailure())
|
||||
return false;
|
||||
|
||||
if (!EvaluatePointer(E->getRHS(), RHSValue, Info) || !LHSOK)
|
||||
return false;
|
||||
|
||||
// Reject differing bases from the normal codepath; we special-case
|
||||
// comparisons to null.
|
||||
if (!HasSameBase(LHSValue, RHSValue)) {
|
||||
// Handle &&A - &&B.
|
||||
if (!LHSValue.Offset.isZero() || !RHSValue.Offset.isZero())
|
||||
return Error(E);
|
||||
const Expr *LHSExpr = LHSValue.Base.dyn_cast<const Expr *>();
|
||||
const Expr *RHSExpr = RHSValue.Base.dyn_cast<const Expr *>();
|
||||
if (!LHSExpr || !RHSExpr)
|
||||
return Error(E);
|
||||
const AddrLabelExpr *LHSAddrExpr = dyn_cast<AddrLabelExpr>(LHSExpr);
|
||||
const AddrLabelExpr *RHSAddrExpr = dyn_cast<AddrLabelExpr>(RHSExpr);
|
||||
if (!LHSAddrExpr || !RHSAddrExpr)
|
||||
return Error(E);
|
||||
// Make sure both labels come from the same function.
|
||||
if (LHSAddrExpr->getLabel()->getDeclContext() !=
|
||||
RHSAddrExpr->getLabel()->getDeclContext())
|
||||
return Error(E);
|
||||
return Success(APValue(LHSAddrExpr, RHSAddrExpr), E);
|
||||
}
|
||||
const CharUnits &LHSOffset = LHSValue.getLValueOffset();
|
||||
const CharUnits &RHSOffset = RHSValue.getLValueOffset();
|
||||
|
||||
SubobjectDesignator &LHSDesignator = LHSValue.getLValueDesignator();
|
||||
SubobjectDesignator &RHSDesignator = RHSValue.getLValueDesignator();
|
||||
|
||||
// C++11 [expr.add]p6:
|
||||
// Unless both pointers point to elements of the same array object, or
|
||||
// one past the last element of the array object, the behavior is
|
||||
// undefined.
|
||||
if (!LHSDesignator.Invalid && !RHSDesignator.Invalid &&
|
||||
!AreElementsOfSameArray(getType(LHSValue.Base), LHSDesignator,
|
||||
RHSDesignator))
|
||||
Info.CCEDiag(E, diag::note_constexpr_pointer_subtraction_not_same_array);
|
||||
|
||||
QualType Type = E->getLHS()->getType();
|
||||
QualType ElementType = Type->getAs<PointerType>()->getPointeeType();
|
||||
|
||||
CharUnits ElementSize;
|
||||
if (!HandleSizeof(Info, E->getExprLoc(), ElementType, ElementSize))
|
||||
return false;
|
||||
|
||||
// As an extension, a type may have zero size (empty struct or union in
|
||||
// C, array of zero length). Pointer subtraction in such cases has
|
||||
// undefined behavior, so is not constant.
|
||||
if (ElementSize.isZero()) {
|
||||
Info.FFDiag(E, diag::note_constexpr_pointer_subtraction_zero_size)
|
||||
<< ElementType;
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: LLVM and GCC both compute LHSOffset - RHSOffset at runtime,
|
||||
// and produce incorrect results when it overflows. Such behavior
|
||||
// appears to be non-conforming, but is common, so perhaps we should
|
||||
// assume the standard intended for such cases to be undefined behavior
|
||||
// and check for them.
|
||||
|
||||
// Compute (LHSOffset - RHSOffset) / Size carefully, checking for
|
||||
// overflow in the final conversion to ptrdiff_t.
|
||||
APSInt LHS(llvm::APInt(65, (int64_t)LHSOffset.getQuantity(), true), false);
|
||||
APSInt RHS(llvm::APInt(65, (int64_t)RHSOffset.getQuantity(), true), false);
|
||||
APSInt ElemSize(llvm::APInt(65, (int64_t)ElementSize.getQuantity(), true),
|
||||
false);
|
||||
APSInt TrueResult = (LHS - RHS) / ElemSize;
|
||||
APSInt Result = TrueResult.trunc(Info.Ctx.getIntWidth(E->getType()));
|
||||
|
||||
if (Result.extend(65) != TrueResult &&
|
||||
!HandleOverflow(Info, E, TrueResult, E->getType()))
|
||||
return false;
|
||||
return Success(Result, E);
|
||||
}
|
||||
|
||||
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
|
||||
}
|
||||
|
||||
@ -10620,7 +10699,6 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
|
||||
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.
|
||||
@ -10642,7 +10720,8 @@ static ICEDiag CheckICE(const Expr* E, const ASTContext &Ctx) {
|
||||
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 ||
|
||||
|
@ -12,6 +12,7 @@
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "CodeGenFunction.h"
|
||||
#include "CGCXXABI.h"
|
||||
#include "CGObjCRuntime.h"
|
||||
#include "CodeGenModule.h"
|
||||
#include "ConstantEmitter.h"
|
||||
@ -145,6 +146,7 @@ public:
|
||||
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) {
|
||||
@ -879,6 +881,149 @@ void AggExprEmitter::VisitStmtExpr(const StmtExpr *E) {
|
||||
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,
|
||||
const char *NameSuffix = "") {
|
||||
QualType ArgTy = E->getLHS()->getType();
|
||||
if (const ComplexType *CT = ArgTy->getAs<ComplexType>())
|
||||
ArgTy = CT->getElementType();
|
||||
|
||||
if (const auto *MPT = ArgTy->getAs<MemberPointerType>()) {
|
||||
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->hasFloatingRepresentation())
|
||||
return Builder.CreateFCmp(InstInfo.FCmp, LHS, RHS,
|
||||
llvm::Twine(InstInfo.Name) + NameSuffix);
|
||||
if (ArgTy->isIntegralOrEnumerationType() || ArgTy->isPointerType()) {
|
||||
auto Inst =
|
||||
ArgTy->hasSignedIntegerRepresentation() ? InstInfo.SCmp : InstInfo.UCmp;
|
||||
return Builder.CreateICmp(Inst, LHS, RHS,
|
||||
llvm::Twine(InstInfo.Name) + NameSuffix);
|
||||
}
|
||||
|
||||
llvm_unreachable("unsupported aggregate binary expression should have "
|
||||
"already been handled");
|
||||
}
|
||||
|
||||
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());
|
||||
assert(CmpInfo.Record->isTriviallyCopyable() &&
|
||||
"cannot copy non-trivially copyable aggregate");
|
||||
|
||||
QualType ArgTy = E->getLHS()->getType();
|
||||
|
||||
// TODO: Handle comparing these types.
|
||||
if (ArgTy->isVectorType())
|
||||
return CGF.ErrorUnsupported(
|
||||
E, "aggregate three-way comparison with vector arguments");
|
||||
if (!ArgTy->isIntegralOrEnumerationType() && !ArgTy->isRealFloatingType() &&
|
||||
!ArgTy->isNullPtrType() && !ArgTy->isPointerType() &&
|
||||
!ArgTy->isMemberPointerType() && !ArgTy->isAnyComplexType()) {
|
||||
return CGF.ErrorUnsupported(E, "aggregate three-way comparisoaoeun");
|
||||
}
|
||||
bool IsComplex = ArgTy->isAnyComplexType();
|
||||
|
||||
// Evaluate the operands to the expression and extract their values.
|
||||
auto EmitOperand = [&](Expr *E) -> std::pair<Value *, Value *> {
|
||||
RValue RV = CGF.EmitAnyExpr(E);
|
||||
if (RV.isScalar())
|
||||
return {RV.getScalarVal(), nullptr};
|
||||
if (RV.isAggregate())
|
||||
return {RV.getAggregatePointer(), nullptr};
|
||||
assert(RV.isComplex());
|
||||
return RV.getComplexVal();
|
||||
};
|
||||
auto LHSValues = EmitOperand(E->getLHS()),
|
||||
RHSValues = EmitOperand(E->getRHS());
|
||||
|
||||
auto EmitCmp = [&](CompareKind K) {
|
||||
Value *Cmp = EmitCompare(Builder, CGF, E, LHSValues.first, RHSValues.first,
|
||||
K, IsComplex ? ".r" : "");
|
||||
if (!IsComplex)
|
||||
return Cmp;
|
||||
assert(K == CompareKind::CK_Equal);
|
||||
Value *CmpImag = EmitCompare(Builder, CGF, E, LHSValues.second,
|
||||
RHSValues.second, K, ".i");
|
||||
return Builder.CreateAnd(Cmp, CmpImag, "and.eq");
|
||||
};
|
||||
auto EmitCmpRes = [&](const ComparisonCategoryInfo::ValueInfo *VInfo) {
|
||||
return Builder.getInt(VInfo->getIntValue());
|
||||
};
|
||||
|
||||
Value *Select;
|
||||
if (ArgTy->isNullPtrType()) {
|
||||
Select = EmitCmpRes(CmpInfo.getEqualOrEquiv());
|
||||
} else if (CmpInfo.isEquality()) {
|
||||
Select = Builder.CreateSelect(
|
||||
EmitCmp(CK_Equal), EmitCmpRes(CmpInfo.getEqualOrEquiv()),
|
||||
EmitCmpRes(CmpInfo.getNonequalOrNonequiv()), "sel.eq");
|
||||
} else if (!CmpInfo.isPartial()) {
|
||||
Value *SelectOne =
|
||||
Builder.CreateSelect(EmitCmp(CK_Less), EmitCmpRes(CmpInfo.getLess()),
|
||||
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");
|
||||
}
|
||||
// Create the return value in the destination slot.
|
||||
EnsureDest(E->getType());
|
||||
LValue DestLV = CGF.MakeAddrLValue(Dest.getAddress(), E->getType());
|
||||
|
||||
// Emit the address of the first (and only) field in the comparison category
|
||||
// type, and initialize it from the constant integer value selected above.
|
||||
LValue FieldLV = CGF.EmitLValueForFieldInitialization(
|
||||
DestLV, *CmpInfo.Record->field_begin());
|
||||
CGF.EmitStoreThroughLValue(RValue::get(Select), FieldLV, /*IsInit*/ true);
|
||||
|
||||
// All done! The result is in the Dest slot.
|
||||
}
|
||||
|
||||
void AggExprEmitter::VisitBinaryOperator(const BinaryOperator *E) {
|
||||
if (E->getOpcode() == BO_PtrMemD || E->getOpcode() == BO_PtrMemI)
|
||||
VisitPointerToDataMemberBinaryOperator(E);
|
||||
|
@ -137,10 +137,13 @@ Sema::Sema(Preprocessor &pp, ASTContext &ctxt, ASTConsumer &consumer,
|
||||
ValueWithBytesObjCTypeMethod(nullptr), NSArrayDecl(nullptr),
|
||||
ArrayWithObjectsMethod(nullptr), NSDictionaryDecl(nullptr),
|
||||
DictionaryWithObjectsMethod(nullptr), GlobalNewDeleteDeclared(false),
|
||||
TUKind(TUKind), NumSFINAEErrors(0), AccessCheckingSFINAE(false),
|
||||
InNonInstantiationSFINAEContext(false), NonInstantiationEntries(0),
|
||||
ArgumentPackSubstitutionIndex(-1), CurrentInstantiationScope(nullptr),
|
||||
DisableTypoCorrection(false), TyposCorrected(0), AnalysisWarnings(*this),
|
||||
TUKind(TUKind), NumSFINAEErrors(0),
|
||||
FullyCheckedComparisonCategories(
|
||||
static_cast<unsigned>(ComparisonCategoryType::Last) + 1),
|
||||
AccessCheckingSFINAE(false), InNonInstantiationSFINAEContext(false),
|
||||
NonInstantiationEntries(0), ArgumentPackSubstitutionIndex(-1),
|
||||
CurrentInstantiationScope(nullptr), DisableTypoCorrection(false),
|
||||
TyposCorrected(0), AnalysisWarnings(*this),
|
||||
ThreadSafetyDeclCache(nullptr), VarDataSharingAttributesStack(nullptr),
|
||||
CurScope(nullptr), Ident_super(nullptr), Ident___float128(nullptr) {
|
||||
TUScope = nullptr;
|
||||
|
@ -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"
|
||||
@ -8891,6 +8892,134 @@ NamespaceDecl *Sema::lookupStdExperimentalNamespace() {
|
||||
return StdExperimentalNamespaceCache;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
enum UnsupportedSTLSelect {
|
||||
USS_InvalidMember,
|
||||
USS_MissingMember,
|
||||
USS_NonTrivial,
|
||||
USS_Other
|
||||
};
|
||||
|
||||
struct InvalidSTLDiagnoser {
|
||||
Sema &S;
|
||||
SourceLocation Loc;
|
||||
QualType TyForDiags;
|
||||
|
||||
QualType operator()(UnsupportedSTLSelect Sel = USS_Other, StringRef Name = "",
|
||||
const VarDecl *VD = nullptr) {
|
||||
{
|
||||
auto D = S.Diag(Loc, diag::err_std_compare_type_not_supported)
|
||||
<< TyForDiags << ((int)Sel);
|
||||
if (Sel == USS_InvalidMember || Sel == USS_MissingMember) {
|
||||
assert(!Name.empty());
|
||||
D << Name;
|
||||
}
|
||||
}
|
||||
if (Sel == USS_InvalidMember) {
|
||||
S.Diag(VD->getLocation(), diag::note_var_declared_here)
|
||||
<< VD << VD->getSourceRange();
|
||||
}
|
||||
return QualType();
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind,
|
||||
SourceLocation Loc) {
|
||||
assert(getLangOpts().CPlusPlus &&
|
||||
"Looking for comparison category type outside of C++.");
|
||||
|
||||
// Check if we've already successfully checked the comparison category type
|
||||
// before. If so, skip checking it again.
|
||||
ComparisonCategoryInfo *Info = Context.CompCategories.lookupInfo(Kind);
|
||||
if (Info && FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)])
|
||||
return Info->getType();
|
||||
|
||||
// If lookup failed
|
||||
if (!Info) {
|
||||
Diag(Loc, diag::err_implied_comparison_category_type_not_found)
|
||||
<< ComparisonCategories::getCategoryString(Kind);
|
||||
return QualType();
|
||||
}
|
||||
|
||||
assert(Info->Kind == Kind);
|
||||
assert(Info->Record);
|
||||
|
||||
// Update the Record decl in case we encountered a forward declaration on our
|
||||
// first pass. FIXME(EricWF): This is a bit of a hack.
|
||||
if (Info->Record->hasDefinition())
|
||||
Info->Record = Info->Record->getDefinition();
|
||||
|
||||
// Use an elaborated type for diagnostics which has a name containing the
|
||||
// prepended 'std' namespace but not any inline namespace names.
|
||||
QualType TyForDiags = [&]() {
|
||||
auto *NNS =
|
||||
NestedNameSpecifier::Create(Context, nullptr, getStdNamespace());
|
||||
return Context.getElaboratedType(ETK_None, NNS, Info->getType());
|
||||
}();
|
||||
|
||||
if (RequireCompleteType(Loc, TyForDiags, diag::err_incomplete_type))
|
||||
return QualType();
|
||||
|
||||
InvalidSTLDiagnoser UnsupportedSTLError{*this, Loc, TyForDiags};
|
||||
|
||||
if (!Info->Record->isTriviallyCopyable())
|
||||
return UnsupportedSTLError(USS_NonTrivial);
|
||||
|
||||
for (const CXXBaseSpecifier &BaseSpec : Info->Record->bases()) {
|
||||
CXXRecordDecl *Base = BaseSpec.getType()->getAsCXXRecordDecl();
|
||||
// Tolerate empty base classes.
|
||||
if (Base->isEmpty())
|
||||
continue;
|
||||
// Reject STL implementations which have at least one non-empty base.
|
||||
return UnsupportedSTLError();
|
||||
}
|
||||
|
||||
// Check that the STL has implemented the types using a single integer field.
|
||||
// This expectation allows better codegen for builtin operators. We require:
|
||||
// (1) The class has exactly one field.
|
||||
// (2) The field is an integral or enumeration type.
|
||||
auto FIt = Info->Record->field_begin(), FEnd = Info->Record->field_end();
|
||||
if (std::distance(FIt, FEnd) != 1 ||
|
||||
!FIt->getType()->isIntegralOrEnumerationType()) {
|
||||
return UnsupportedSTLError();
|
||||
}
|
||||
|
||||
// Build each of the require values and store them in Info.
|
||||
for (ComparisonCategoryResult CCR :
|
||||
ComparisonCategories::getPossibleResultsForType(Kind)) {
|
||||
StringRef MemName = ComparisonCategories::getResultString(CCR);
|
||||
ComparisonCategoryInfo::ValueInfo *ValInfo = Info->lookupValueInfo(CCR);
|
||||
|
||||
if (!ValInfo)
|
||||
return UnsupportedSTLError(USS_MissingMember, MemName);
|
||||
|
||||
VarDecl *VD = ValInfo->VD;
|
||||
assert(VD && "should not be null!");
|
||||
|
||||
// Attempt to diagnose reasons why the STL definition of this type
|
||||
// might be foobar, including it failing to be a constant expression.
|
||||
// TODO Handle more ways the lookup or result can be invalid.
|
||||
if (!VD->isStaticDataMember() || !VD->isConstexpr() || !VD->hasInit() ||
|
||||
!VD->checkInitIsICE())
|
||||
return UnsupportedSTLError(USS_InvalidMember, MemName, VD);
|
||||
|
||||
// Attempt to evaluate the var decl as a constant expression and extract
|
||||
// the value of its first field as a ICE. If this fails, the STL
|
||||
// implementation is not supported.
|
||||
if (!ValInfo->hasValidIntValue())
|
||||
return UnsupportedSTLError();
|
||||
|
||||
MarkVariableReferenced(Loc, VD);
|
||||
}
|
||||
|
||||
// We've successfully built the required types and expressions. Update
|
||||
// the cache and return the newly cached value.
|
||||
FullyCheckedComparisonCategories[static_cast<unsigned>(Kind)] = true;
|
||||
return Info->getType();
|
||||
}
|
||||
|
||||
/// \brief Retrieve the special "std" namespace, which may require us to
|
||||
/// implicitly define the namespace.
|
||||
NamespaceDecl *Sema::getOrCreateStdNamespace() {
|
||||
|
@ -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"
|
||||
@ -9619,12 +9620,18 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
|
||||
Expr *RHSStripped = RHS->IgnoreParenImpCasts();
|
||||
|
||||
QualType LHSType = LHS->getType();
|
||||
QualType RHSType = RHS->getType();
|
||||
if (LHSType->hasFloatingRepresentation() ||
|
||||
(LHSType->isBlockPointerType() && !BinaryOperator::isEqualityOp(Opc)) ||
|
||||
LHS->getLocStart().isMacroID() || RHS->getLocStart().isMacroID() ||
|
||||
S.inTemplateInstantiation())
|
||||
return;
|
||||
|
||||
// Comparisons between two array types are ill-formed for operator<=>, so
|
||||
// we shouldn't emit any additional warnings about it.
|
||||
if (Opc == BO_Cmp && LHSType->isArrayType() && RHSType->isArrayType())
|
||||
return;
|
||||
|
||||
// For non-floating point types, check for self-comparisons of the form
|
||||
// x == x, x != x, x < x, etc. These always evaluate to a constant, and
|
||||
// often indicate logic errors in the program.
|
||||
@ -9708,10 +9715,183 @@ static void diagnoseTautologicalComparison(Sema &S, SourceLocation Loc,
|
||||
}
|
||||
}
|
||||
|
||||
static ImplicitConversionKind castKindToImplicitConversionKind(CastKind CK) {
|
||||
switch (CK) {
|
||||
default: {
|
||||
#ifndef NDEBUG
|
||||
llvm::errs() << "unhandled cast kind: " << CastExpr::getCastKindName(CK)
|
||||
<< "\n";
|
||||
#endif
|
||||
llvm_unreachable("unhandled cast kind");
|
||||
}
|
||||
case CK_UserDefinedConversion:
|
||||
return ICK_Identity;
|
||||
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;
|
||||
case CK_IntegralComplexCast:
|
||||
case CK_FloatingComplexCast:
|
||||
case CK_FloatingComplexToIntegralComplex:
|
||||
case CK_IntegralComplexToFloatingComplex:
|
||||
return ICK_Complex_Conversion;
|
||||
case CK_FloatingComplexToReal:
|
||||
case CK_FloatingRealToComplex:
|
||||
case CK_IntegralComplexToReal:
|
||||
case CK_IntegralRealToComplex:
|
||||
return ICK_Complex_Real;
|
||||
}
|
||||
}
|
||||
|
||||
static bool checkThreeWayNarrowingConversion(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<ImplicitCastExpr>(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:
|
||||
// Implicit conversion to a narrower type, and the value is not a constant
|
||||
// expression.
|
||||
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.
|
||||
case NK_Type_Narrowing:
|
||||
S.Diag(E->getLocStart(), diag::err_spaceship_argument_narrowing)
|
||||
<< /*Constant*/ 0 << FromType << ToType;
|
||||
// TODO: It's not a constant expression, but what if the user intended it
|
||||
// to be? Can we produce notes to help them figure out why it isn't?
|
||||
return true;
|
||||
}
|
||||
llvm_unreachable("unhandled case in switch");
|
||||
}
|
||||
|
||||
static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
|
||||
ExprResult &LHS,
|
||||
ExprResult &RHS,
|
||||
SourceLocation Loc) {
|
||||
using CCT = ComparisonCategoryType;
|
||||
|
||||
QualType LHSType = LHS.get()->getType();
|
||||
QualType RHSType = RHS.get()->getType();
|
||||
// Dig out the original argument type and expression before implicit casts
|
||||
// were applied. These are the types/expressions we need to check the
|
||||
// [expr.spaceship] requirements against.
|
||||
ExprResult LHSStripped = LHS.get()->IgnoreParenImpCasts();
|
||||
ExprResult RHSStripped = RHS.get()->IgnoreParenImpCasts();
|
||||
QualType LHSStrippedType = LHSStripped.get()->getType();
|
||||
QualType RHSStrippedType = RHSStripped.get()->getType();
|
||||
|
||||
// C++2a [expr.spaceship]p3: If one of the operands is of type bool and the
|
||||
// other is not, the program is ill-formed.
|
||||
if (LHSStrippedType->isBooleanType() != RHSStrippedType->isBooleanType()) {
|
||||
S.InvalidOperands(Loc, LHSStripped, RHSStripped);
|
||||
return QualType();
|
||||
}
|
||||
|
||||
int NumEnumArgs = (int)LHSStrippedType->isEnumeralType() +
|
||||
RHSStrippedType->isEnumeralType();
|
||||
if (NumEnumArgs == 1) {
|
||||
bool LHSIsEnum = LHSStrippedType->isEnumeralType();
|
||||
QualType OtherTy = LHSIsEnum ? RHSStrippedType : LHSStrippedType;
|
||||
if (OtherTy->hasFloatingRepresentation()) {
|
||||
S.InvalidOperands(Loc, LHSStripped, RHSStripped);
|
||||
return QualType();
|
||||
}
|
||||
}
|
||||
if (NumEnumArgs == 2) {
|
||||
// C++2a [expr.spaceship]p5: If both operands have the same enumeration
|
||||
// type E, the operator yields the result of converting the operands
|
||||
// to the underlying type of E and applying <=> to the converted operands.
|
||||
if (!S.Context.hasSameUnqualifiedType(LHSStrippedType, RHSStrippedType)) {
|
||||
S.InvalidOperands(Loc, LHSStripped, RHSStripped);
|
||||
return QualType();
|
||||
}
|
||||
QualType IntType =
|
||||
LHSStrippedType->getAs<EnumType>()->getDecl()->getIntegerType();
|
||||
assert(IntType->isArithmeticType());
|
||||
|
||||
// We can't use `CK_IntegralCast` when the underlying type is 'bool', so we
|
||||
// promote the boolean type, and all other promotable integer types, to
|
||||
// avoid this.
|
||||
if (IntType->isPromotableIntegerType())
|
||||
IntType = S.Context.getPromotedIntegerType(IntType);
|
||||
|
||||
LHS = S.ImpCastExprToType(LHS.get(), IntType, CK_IntegralCast);
|
||||
RHS = S.ImpCastExprToType(RHS.get(), IntType, CK_IntegralCast);
|
||||
LHSType = RHSType = IntType;
|
||||
}
|
||||
|
||||
// C++2a [expr.spaceship]p4: If both operands have arithmetic types, the
|
||||
// usual arithmetic conversions are applied to the operands.
|
||||
QualType Type = S.UsualArithmeticConversions(LHS, RHS);
|
||||
if (LHS.isInvalid() || RHS.isInvalid())
|
||||
return QualType();
|
||||
if (Type.isNull())
|
||||
return S.InvalidOperands(Loc, LHS, RHS);
|
||||
assert(Type->isArithmeticType() || Type->isEnumeralType());
|
||||
|
||||
bool HasNarrowing = checkThreeWayNarrowingConversion(
|
||||
S, Type, LHS.get(), LHSType, LHS.get()->getLocStart());
|
||||
HasNarrowing |= checkThreeWayNarrowingConversion(
|
||||
S, Type, RHS.get(), RHSType, RHS.get()->getLocStart());
|
||||
if (HasNarrowing)
|
||||
return QualType();
|
||||
|
||||
assert(!Type.isNull() && "composite type for <=> has not been set");
|
||||
|
||||
auto TypeKind = [&]() {
|
||||
if (const ComplexType *CT = Type->getAs<ComplexType>()) {
|
||||
if (CT->getElementType()->hasFloatingRepresentation())
|
||||
return CCT::WeakEquality;
|
||||
return CCT::StrongEquality;
|
||||
}
|
||||
if (Type->isIntegralOrEnumerationType())
|
||||
return CCT::StrongOrdering;
|
||||
if (Type->hasFloatingRepresentation())
|
||||
return CCT::PartialOrdering;
|
||||
llvm_unreachable("other types are unimplemented");
|
||||
}();
|
||||
|
||||
return S.CheckComparisonCategoryType(TypeKind, Loc);
|
||||
}
|
||||
|
||||
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())
|
||||
@ -9722,15 +9902,7 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
|
||||
|
||||
checkEnumComparison(S, Loc, LHS.get(), RHS.get());
|
||||
|
||||
enum { StrongEquality, PartialOrdering, StrongOrdering } Ordering;
|
||||
if (Type->isAnyComplexType())
|
||||
Ordering = StrongEquality;
|
||||
else if (Type->isFloatingType())
|
||||
Ordering = PartialOrdering;
|
||||
else
|
||||
Ordering = StrongOrdering;
|
||||
|
||||
if (Ordering == StrongEquality && BinaryOperator::isRelationalOp(Opc))
|
||||
if (Type->isAnyComplexType() && BinaryOperator::isRelationalOp(Opc))
|
||||
return S.InvalidOperands(Loc, LHS, RHS);
|
||||
|
||||
// Check for comparisons of floating point operands using != and ==.
|
||||
@ -9738,22 +9910,40 @@ static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
|
||||
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();
|
||||
}
|
||||
|
||||
// C99 6.5.8, C++ [expr.rel]
|
||||
QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
SourceLocation Loc, BinaryOperatorKind Opc,
|
||||
bool IsRelational) {
|
||||
// Comparisons expect an rvalue, so convert to rvalue before any
|
||||
// type-related checks.
|
||||
LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
|
||||
if (LHS.isInvalid())
|
||||
return QualType();
|
||||
RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
|
||||
if (RHS.isInvalid())
|
||||
return QualType();
|
||||
SourceLocation Loc,
|
||||
BinaryOperatorKind Opc) {
|
||||
bool IsRelational = BinaryOperator::isRelationalOp(Opc);
|
||||
bool IsThreeWay = Opc == BO_Cmp;
|
||||
auto IsAnyPointerType = [](ExprResult E) {
|
||||
QualType Ty = E.get()->getType();
|
||||
return Ty->isPointerType() || Ty->isMemberPointerType();
|
||||
};
|
||||
|
||||
// C++2a [expr.spaceship]p6: If at least one of the operands is of pointer
|
||||
// type, array-to-pointer, ..., conversions are performed on both operands to
|
||||
// bring them to their composite type.
|
||||
// Otherwise, all comparisons expect an rvalue, so convert to rvalue before
|
||||
// any type-related checks.
|
||||
if (!IsThreeWay || IsAnyPointerType(LHS) || IsAnyPointerType(RHS)) {
|
||||
LHS = DefaultFunctionArrayLvalueConversion(LHS.get());
|
||||
if (LHS.isInvalid())
|
||||
return QualType();
|
||||
RHS = DefaultFunctionArrayLvalueConversion(RHS.get());
|
||||
if (RHS.isInvalid())
|
||||
return QualType();
|
||||
} else {
|
||||
LHS = DefaultLvalueConversion(LHS.get());
|
||||
if (LHS.isInvalid())
|
||||
return QualType();
|
||||
RHS = DefaultLvalueConversion(RHS.get());
|
||||
if (RHS.isInvalid())
|
||||
return QualType();
|
||||
}
|
||||
|
||||
checkArithmeticNull(*this, LHS, RHS, Loc, /*isCompare=*/true);
|
||||
|
||||
@ -9771,8 +9961,6 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
(RHSType->isArithmeticType() || RHSType->isEnumeralType()))
|
||||
return checkArithmeticOrEnumeralCompare(*this, LHS, RHS, Loc, Opc);
|
||||
|
||||
QualType ResultTy = Context.getLogicalOperationType();
|
||||
|
||||
const Expr::NullPointerConstantKind LHSNullKind =
|
||||
LHS.get()->isNullPointerConstant(Context, Expr::NPC_ValueDependentIsNull);
|
||||
const Expr::NullPointerConstantKind RHSNullKind =
|
||||
@ -9780,6 +9968,44 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
bool LHSIsNull = LHSNullKind != Expr::NPCK_NotNull;
|
||||
bool RHSIsNull = RHSNullKind != Expr::NPCK_NotNull;
|
||||
|
||||
auto computeResultTy = [&]() {
|
||||
if (Opc != BO_Cmp)
|
||||
return Context.getLogicalOperationType();
|
||||
assert(getLangOpts().CPlusPlus);
|
||||
assert(Context.hasSameType(LHS.get()->getType(), RHS.get()->getType()));
|
||||
|
||||
QualType CompositeTy = LHS.get()->getType();
|
||||
assert(!CompositeTy->isReferenceType());
|
||||
|
||||
auto buildResultTy = [&](ComparisonCategoryType Kind) {
|
||||
return CheckComparisonCategoryType(Kind, Loc);
|
||||
};
|
||||
|
||||
// C++2a [expr.spaceship]p7: If the composite pointer type is a function
|
||||
// pointer type, a pointer-to-member type, or std::nullptr_t, the
|
||||
// result is of type std::strong_equality
|
||||
if (CompositeTy->isFunctionPointerType() ||
|
||||
CompositeTy->isMemberPointerType() || CompositeTy->isNullPtrType())
|
||||
// FIXME: consider making the function pointer case produce
|
||||
// strong_ordering not strong_equality, per P0946R0-Jax18 discussion
|
||||
// and direction polls
|
||||
return buildResultTy(ComparisonCategoryType::StrongEquality);
|
||||
|
||||
// C++2a [expr.spaceship]p8: If the composite pointer type is an object
|
||||
// pointer type, p <=> q is of type std::strong_ordering.
|
||||
if (CompositeTy->isPointerType()) {
|
||||
// P0946R0: Comparisons between a null pointer constant and an object
|
||||
// pointer result in std::strong_equality
|
||||
if (LHSIsNull != RHSIsNull)
|
||||
return buildResultTy(ComparisonCategoryType::StrongEquality);
|
||||
return buildResultTy(ComparisonCategoryType::StrongOrdering);
|
||||
}
|
||||
// C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed.
|
||||
// TODO: Extend support for operator<=> to ObjC types.
|
||||
return InvalidOperands(Loc, LHS, RHS);
|
||||
};
|
||||
|
||||
|
||||
if (!IsRelational && LHSIsNull != RHSIsNull) {
|
||||
bool IsEquality = Opc == BO_EQ;
|
||||
if (RHSIsNull)
|
||||
@ -9807,29 +10033,30 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
// conformance with the C++ standard.
|
||||
diagnoseFunctionPointerToVoidComparison(
|
||||
*this, Loc, LHS, RHS, /*isError*/ (bool)isSFINAEContext());
|
||||
|
||||
|
||||
if (isSFINAEContext())
|
||||
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() ||
|
||||
RHSType->isObjCObjectPointerType()))) {
|
||||
(!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
|
||||
@ -9880,7 +10107,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
else
|
||||
RHS = ImpCastExprToType(RHS.get(), LHSType, Kind);
|
||||
}
|
||||
return ResultTy;
|
||||
return computeResultTy();
|
||||
}
|
||||
|
||||
if (getLangOpts().CPlusPlus) {
|
||||
@ -9890,11 +10117,11 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
if (!IsRelational && 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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9903,12 +10130,12 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
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 &&
|
||||
@ -9931,7 +10158,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
RHS = ImpCastExprToType(RHS.get(), LHSType, CK_NullToPointer);
|
||||
else
|
||||
LHS = ImpCastExprToType(LHS.get(), RHSType, CK_NullToPointer);
|
||||
return ResultTy;
|
||||
return computeResultTy();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -9944,7 +10171,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
if (convertPointersToCompositeType(*this, Loc, LHS, RHS))
|
||||
return QualType();
|
||||
else
|
||||
return ResultTy;
|
||||
return computeResultTy();
|
||||
}
|
||||
}
|
||||
|
||||
@ -9961,7 +10188,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
<< RHS.get()->getSourceRange();
|
||||
}
|
||||
RHS = ImpCastExprToType(RHS.get(), LHSType, CK_BitCast);
|
||||
return ResultTy;
|
||||
return computeResultTy();
|
||||
}
|
||||
|
||||
// Allow block pointers to be compared with null pointer constants.
|
||||
@ -9985,7 +10212,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
RHS = ImpCastExprToType(RHS.get(), LHSType,
|
||||
LHSType->isPointerType() ? CK_BitCast
|
||||
: CK_AnyPointerToBlockPointerCast);
|
||||
return ResultTy;
|
||||
return computeResultTy();
|
||||
}
|
||||
|
||||
if (LHSType->isObjCObjectPointerType() ||
|
||||
@ -10018,7 +10245,7 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
RHS = ImpCastExprToType(E, LHSType,
|
||||
LPT ? CK_BitCast :CK_CPointerToObjCPointerCast);
|
||||
}
|
||||
return ResultTy;
|
||||
return computeResultTy();
|
||||
}
|
||||
if (LHSType->isObjCObjectPointerType() &&
|
||||
RHSType->isObjCObjectPointerType()) {
|
||||
@ -10032,20 +10259,20 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
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()) ||
|
||||
@ -10085,30 +10312,30 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@ -11761,19 +11988,17 @@ ExprResult Sema::CreateBuiltinBinOp(SourceLocation OpLoc,
|
||||
case BO_GE:
|
||||
case BO_GT:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc);
|
||||
break;
|
||||
case BO_EQ:
|
||||
case BO_NE:
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, false);
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc);
|
||||
break;
|
||||
case BO_Cmp:
|
||||
// FIXME: Implement proper semantic checking of '<=>'.
|
||||
ConvertHalfVec = true;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc, true);
|
||||
if (!ResultTy.isNull())
|
||||
ResultTy = Context.VoidTy;
|
||||
ResultTy = CheckCompareOperands(LHS, RHS, OpLoc, Opc);
|
||||
assert(ResultTy.isNull() || ResultTy->getAsCXXRecordDecl());
|
||||
break;
|
||||
case BO_And:
|
||||
checkObjCPointerIntrospection(*this, LHS, RHS, OpLoc);
|
||||
|
@ -288,11 +288,11 @@ static const Expr *IgnoreNarrowingConversion(const Expr *Converted) {
|
||||
/// 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 @@ StandardConversionSequence::getNarrowingKind(ASTContext &Ctx,
|
||||
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");
|
||||
@ -7988,7 +7990,8 @@ public:
|
||||
// bool operator>=(T, T);
|
||||
// bool operator==(T, T);
|
||||
// bool operator!=(T, T);
|
||||
void addRelationalPointerOrEnumeralOverloads() {
|
||||
// R operator<=>(T, T)
|
||||
void addGenericBinaryPointerOrEnumeralOverloads() {
|
||||
// C++ [over.match.oper]p3:
|
||||
// [...]the built-in candidates include all of the candidate operator
|
||||
// functions defined in 13.6 that, compared to the given operator, [...]
|
||||
@ -8061,7 +8064,6 @@ public:
|
||||
UserDefinedBinaryOperators.count(std::make_pair(CanonType,
|
||||
CanonType)))
|
||||
continue;
|
||||
|
||||
QualType ParamTypes[2] = { *Enum, *Enum };
|
||||
S.AddBuiltinCandidate(ParamTypes, Args, CandidateSet);
|
||||
}
|
||||
@ -8179,6 +8181,41 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// C++2a [over.built]p14:
|
||||
//
|
||||
// For every integral type T there exists a candidate operator function
|
||||
// of the form
|
||||
//
|
||||
// std::strong_ordering operator<=>(T, T)
|
||||
//
|
||||
// C++2a [over.built]p15:
|
||||
//
|
||||
// For every pair of floating-point types L and R, there exists a candidate
|
||||
// operator function of the form
|
||||
//
|
||||
// std::partial_ordering operator<=>(L, R);
|
||||
//
|
||||
// FIXME: The current specification for integral types doesn't play nice with
|
||||
// the direction of p0946r0, which allows mixed integral and unscoped-enum
|
||||
// comparisons. Under the current spec this can lead to ambiguity during
|
||||
// overload resolution. For example:
|
||||
//
|
||||
// enum A : int {a};
|
||||
// auto x = (a <=> (long)42);
|
||||
//
|
||||
// error: call is ambiguous for arguments 'A' and 'long'.
|
||||
// note: candidate operator<=>(int, int)
|
||||
// note: candidate operator<=>(long, long)
|
||||
//
|
||||
// To avoid this error, this function deviates from the specification and adds
|
||||
// the mixed overloads `operator<=>(L, R)` where L and R are promoted
|
||||
// arithmetic types (the same as the generic relational overloads).
|
||||
//
|
||||
// For now this function acts as a placeholder.
|
||||
void addThreeWayArithmeticOverloads() {
|
||||
addGenericBinaryArithmeticOverloads();
|
||||
}
|
||||
|
||||
// C++ [over.built]p17:
|
||||
//
|
||||
// For every pair of promoted integral types L and R, there
|
||||
@ -8747,12 +8784,14 @@ void Sema::AddBuiltinOperatorCandidates(OverloadedOperatorKind Op,
|
||||
case OO_Greater:
|
||||
case OO_LessEqual:
|
||||
case OO_GreaterEqual:
|
||||
OpBuilder.addRelationalPointerOrEnumeralOverloads();
|
||||
OpBuilder.addGenericBinaryPointerOrEnumeralOverloads();
|
||||
OpBuilder.addGenericBinaryArithmeticOverloads();
|
||||
break;
|
||||
|
||||
case OO_Spaceship:
|
||||
llvm_unreachable("<=> expressions not supported yet");
|
||||
OpBuilder.addGenericBinaryPointerOrEnumeralOverloads();
|
||||
OpBuilder.addThreeWayArithmeticOverloads();
|
||||
break;
|
||||
|
||||
case OO_Percent:
|
||||
case OO_Caret:
|
||||
|
437
clang/test/CodeGenCXX/Inputs/std-compare.h
Normal file
437
clang/test/CodeGenCXX/Inputs/std-compare.h
Normal file
@ -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<signed char>(__v)) {}
|
||||
explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast<signed char>(__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
|
186
clang/test/CodeGenCXX/cxx2a-compare.cpp
Normal file
186
clang/test/CodeGenCXX/cxx2a-compare.cpp
Normal file
@ -0,0 +1,186 @@
|
||||
// RUN: %clang_cc1 -std=c++2a -emit-llvm %s -o - -triple %itanium_abi_triple | \
|
||||
// RUN: FileCheck %s \
|
||||
// RUN: '-DWE="class.std::__1::weak_equality"' \
|
||||
// RUN: '-DSO="class.std::__1::strong_ordering"' \
|
||||
// RUN: '-DSE="class.std::__1::strong_equality"' \
|
||||
// RUN: '-DPO="class.std::__1::partial_ordering"' \
|
||||
// RUN: -DEQ=0 -DLT=-1 -DGT=1 -DUNORD=-127 -DNE=1
|
||||
|
||||
#include "Inputs/std-compare.h"
|
||||
|
||||
// Ensure we don't emit definitions for the global variables
|
||||
// since the builtins shouldn't ODR use them.
|
||||
// CHECK-NOT: constant %[[SO]]
|
||||
// CHECK-NOT: constant %[[SE]]
|
||||
// CHECK-NOT: constant %[[WE]]
|
||||
// CHECK-NOT: constant %[[PO]]
|
||||
|
||||
// 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, i8 [[LT]], i8 [[GT]]
|
||||
// CHECK: %cmp.eq = icmp eq i32 %0, %1
|
||||
// CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 %sel.lt
|
||||
// CHECK: %__value_ = getelementptr inbounds %[[SO]], %[[SO]]* %retval, i32 0, i32 0
|
||||
// CHECK: store i8 %sel.eq, i8* %__value_, align 1
|
||||
// CHECK: %[[FINAL:.*]] = getelementptr inbounds %[[SO]], %[[SO]]* %retval
|
||||
// CHECK: %[[RET:.*]] = load i8, i8* %[[FINAL]]
|
||||
// CHECK: ret i8 %[[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, i8 [[LT]], i8 [[GT]]
|
||||
// CHECK: %cmp.eq = icmp eq i32 %0, %1
|
||||
// CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 %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, i8 [[EQ]], i8 [[UNORD]]
|
||||
// CHECK: %cmp.gt = fcmp ogt double %0, %1
|
||||
// CHECK: %sel.gt = select i1 %cmp.gt, i8 [[GT]], i8 %sel.eq
|
||||
// CHECK: %cmp.lt = fcmp olt double %0, %1
|
||||
// CHECK: %sel.lt = select i1 %cmp.lt, i8 [[LT]], i8 %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, i8 [[LT]], i8 [[GT]]
|
||||
// CHECK: %cmp.eq = icmp eq i32* %0, %1
|
||||
// CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 %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, i8 [[EQ]], i8 [[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]], i8 [[EQ]], i8 [[NE]]
|
||||
// CHECK: %retval
|
||||
// CHECK: %sel.eq
|
||||
return x <=> y;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z13test_constantv
|
||||
auto test_constant() {
|
||||
// CHECK: entry:
|
||||
// CHECK-NOT: icmp
|
||||
// CHECK: %__value_ = getelementptr inbounds %[[SO]], %[[SO]]* %retval
|
||||
// CHECK-NEXT: store i8 [[LT]], i8* %__value_
|
||||
// CHECK-NEXT: %[[TMP:.*]] = getelementptr inbounds %[[SO]], %[[SO]]* %retval
|
||||
// CHECK-NEXT: %[[RET:.*]] = load i8, i8* %[[TMP]]
|
||||
// CHECK-NEXT: ret i8 %[[RET]]
|
||||
const int x = 42;
|
||||
const int y = 101;
|
||||
return x <=> y;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z16test_nullptr_objPiDn
|
||||
auto test_nullptr_obj(int* x, decltype(nullptr) y) {
|
||||
// CHECK: %retval = alloca %[[SE]]
|
||||
// CHECK: %cmp.eq = icmp eq i32* %0, null
|
||||
// CHECK: %sel.eq = select i1 %cmp.eq, i8 [[EQ]], i8 [[NE]]
|
||||
// CHECK: %retval
|
||||
// CHECK: %sel.eq
|
||||
return x <=> y;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z18unscoped_enum_testijlm
|
||||
void unscoped_enum_test(int i, unsigned u, long l, unsigned long ul) {
|
||||
enum EnumA : int { A };
|
||||
enum EnumB : unsigned { B };
|
||||
// CHECK: %[[I:.*]] = load {{.*}} %i.addr
|
||||
// CHECK: icmp slt i32 {{.*}} %[[I]]
|
||||
(void)(A <=> i);
|
||||
|
||||
// CHECK: %[[U:.*]] = load {{.*}} %u.addr
|
||||
// CHECK: icmp ult i32 {{.*}} %[[U]]
|
||||
(void)(A <=> u);
|
||||
|
||||
// CHECK: %[[L:.*]] = load {{.*}} %l.addr
|
||||
// CHECK: icmp slt i64 {{.*}} %[[L]]
|
||||
(void)(A <=> l);
|
||||
|
||||
// CHECK: %[[U2:.*]] = load {{.*}} %u.addr
|
||||
// CHECK: icmp ult i32 {{.*}} %[[U2]]
|
||||
(void)(B <=> u);
|
||||
|
||||
// CHECK: %[[UL:.*]] = load {{.*}} %ul.addr
|
||||
// CHECK: icmp ult i64 {{.*}} %[[UL]]
|
||||
(void)(B <=> ul);
|
||||
}
|
||||
|
||||
namespace NullptrTest {
|
||||
using nullptr_t = decltype(nullptr);
|
||||
|
||||
// CHECK-LABEL: @_ZN11NullptrTest4testEDnDn(
|
||||
auto test(nullptr_t x, nullptr_t y) {
|
||||
// CHECK-NOT: select
|
||||
// CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %retval
|
||||
// CHECK-NEXT: store i8 [[EQ]], i8* %__value_
|
||||
// CHECK: ret
|
||||
return x <=> y;
|
||||
}
|
||||
} // namespace NullptrTest
|
||||
|
||||
namespace ComplexTest {
|
||||
|
||||
auto test_float(_Complex float x, _Complex float y) {
|
||||
// CHECK: %cmp.eq.r = fcmp oeq float %x.real, %y.real
|
||||
// CHECK: %cmp.eq.i = fcmp oeq float %x.imag, %y.imag
|
||||
// CHECK: %and.eq = and i1 %cmp.eq.r, %cmp.eq.i
|
||||
// CHECK: %sel.eq = select i1 %and.eq, i8 [[EQ]], i8 [[NE]]
|
||||
// CHECK: %__value_ = getelementptr inbounds %[[WE]], %[[WE]]* %retval
|
||||
// CHECK: store i8 %sel.eq, i8* %__value_, align 1
|
||||
return x <=> y;
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_ZN11ComplexTest8test_intECiS0_(
|
||||
auto test_int(_Complex int x, _Complex int y) {
|
||||
// CHECK: %cmp.eq.r = icmp eq i32 %x.real, %y.real
|
||||
// CHECK: %cmp.eq.i = icmp eq i32 %x.imag, %y.imag
|
||||
// CHECK: %and.eq = and i1 %cmp.eq.r, %cmp.eq.i
|
||||
// CHECK: %sel.eq = select i1 %and.eq, i8 [[EQ]], i8 [[NE]]
|
||||
// CHECK: %__value_ = getelementptr inbounds %[[SE]], %[[SE]]* %retval
|
||||
// CHECK: store i8 %sel.eq, i8* %__value_, align 1
|
||||
return x <=> y;
|
||||
}
|
||||
|
||||
} // namespace ComplexTest
|
437
clang/test/PCH/Inputs/std-compare.h
Normal file
437
clang/test/PCH/Inputs/std-compare.h
Normal file
@ -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<signed char>(__v)) {}
|
||||
explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast<signed char>(__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
|
28
clang/test/PCH/cxx2a-compare.cpp
Normal file
28
clang/test/PCH/cxx2a-compare.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
// 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
|
||||
// RUN: %clang_cc1 -pedantic-errors -std=c++2a -include-pch %t -emit-llvm %s -o -
|
||||
|
||||
|
||||
#ifndef HEADER
|
||||
#define HEADER
|
||||
|
||||
#include "Inputs/std-compare.h"
|
||||
constexpr auto foo() {
|
||||
return (42 <=> 101);
|
||||
}
|
||||
|
||||
inline auto bar(int x) {
|
||||
return (1 <=> x);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
// expected-no-diagnostics
|
||||
|
||||
static_assert(foo() < 0);
|
||||
|
||||
auto bar2(int x) {
|
||||
return bar(x);
|
||||
}
|
||||
|
||||
#endif
|
437
clang/test/SemaCXX/Inputs/std-compare.h
Normal file
437
clang/test/SemaCXX/Inputs/std-compare.h
Normal file
@ -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<signed char>(__v)) {}
|
||||
explicit constexpr strong_ordering(_OrdResult __v) noexcept : __value_(static_cast<signed char>(__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
|
@ -1,38 +1,43 @@
|
||||
// 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;
|
||||
int b[3], c[3];
|
||||
int *b = nullptr;
|
||||
|
||||
(void)(a <=> a); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}}
|
||||
(void)(b <=> b); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}}
|
||||
(void)(b <=> c); // expected-warning {{array comparison always evaluates to a constant}}
|
||||
}
|
||||
|
||||
void test0(long a, unsigned long b) {
|
||||
enum EnumA {A};
|
||||
enum EnumA : int {A};
|
||||
enum EnumB {B};
|
||||
enum EnumC {C = 0x10000};
|
||||
// (a,b)
|
||||
|
||||
// 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)((short)a <=> (unsigned short)b);
|
||||
|
||||
// (a,b)
|
||||
(void)(a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(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}}
|
||||
(void)((int)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((short)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((signed char)a <=> b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((long)a <=> (unsigned long)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((int)a <=> (unsigned int)b); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((short) a <=> (unsigned short) b);
|
||||
(void)((signed char) a <=> (unsigned char) b);
|
||||
|
||||
#if 0
|
||||
(void)(A < 42);
|
||||
// (A,b)
|
||||
(void)(A <=> (unsigned long) b);
|
||||
(void)(A <=> (unsigned int) b);
|
||||
@ -48,7 +53,7 @@ void test0(long a, unsigned long b) {
|
||||
(void)((signed char) A <=> (unsigned char) b);
|
||||
|
||||
// (a,B)
|
||||
(void)(a <=> (unsigned long) B);
|
||||
(void)(a <=> (unsigned long) B); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
|
||||
(void)(a <=> (unsigned int) B);
|
||||
(void)(a <=> (unsigned short) B);
|
||||
(void)(a <=> (unsigned char) B);
|
||||
@ -56,8 +61,8 @@ void test0(long a, unsigned long b) {
|
||||
(void)((int) a <=> B);
|
||||
(void)((short) a <=> B);
|
||||
(void)((signed char) a <=> B);
|
||||
(void)((long) a <=> (unsigned long) B);
|
||||
(void)((int) a <=> (unsigned int) B);
|
||||
(void)((long) a <=> (unsigned long) B); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
|
||||
(void)((int) a <=> (unsigned int) B); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'}}
|
||||
(void)((short) a <=> (unsigned short) B);
|
||||
(void)((signed char) a <=> (unsigned char) B);
|
||||
|
||||
@ -76,7 +81,7 @@ void test0(long a, unsigned long b) {
|
||||
(void)((signed char) C <=> (unsigned char) b);
|
||||
|
||||
// (a,C)
|
||||
(void)(a <=> (unsigned long) C);
|
||||
(void)(a <=> (unsigned long) C); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
|
||||
(void)(a <=> (unsigned int) C);
|
||||
(void)(a <=> (unsigned short) C);
|
||||
(void)(a <=> (unsigned char) C);
|
||||
@ -84,11 +89,10 @@ void test0(long a, unsigned long b) {
|
||||
(void)((int) a <=> C);
|
||||
(void)((short) a <=> C); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'short' is always 'std::strong_ordering::less'}}
|
||||
(void)((signed char) a <=> C); // expected-warning {{comparison of constant 'C' (65536) with expression of type 'signed char' is always 'std::strong_ordering::less'}}
|
||||
(void)((long) a <=> (unsigned long) C);
|
||||
(void)((int) a <=> (unsigned int) C);
|
||||
(void)((long) a <=> (unsigned long) C); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'long' to 'unsigned long'}}
|
||||
(void)((int) a <=> (unsigned int) C); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'}}
|
||||
(void)((short) a <=> (unsigned short) C);
|
||||
(void)((signed char) a <=> (unsigned char) C);
|
||||
#endif
|
||||
|
||||
// (0x80000,b)
|
||||
(void)(0x80000 <=> (unsigned long) b);
|
||||
@ -105,7 +109,7 @@ void test0(long a, unsigned long b) {
|
||||
(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}}
|
||||
(void)(a <=> (unsigned int) 0x80000);
|
||||
(void)(a <=> (unsigned short) 0x80000);
|
||||
(void)(a <=> (unsigned char) 0x80000);
|
||||
@ -113,19 +117,23 @@ void test0(long a, unsigned long b) {
|
||||
(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}}
|
||||
(void)((int)a <=> (unsigned int)0x80000); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(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) {
|
||||
enum EnumA { A };
|
||||
(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')}}
|
||||
(void)(b <=> A); // expected-error {{invalid operands to binary expression ('bool' and 'EnumA')}}
|
||||
|
||||
// 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 +150,13 @@ void test7(unsigned long other) {
|
||||
(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}}
|
||||
(void)((int)other <=> (unsigned long)(0x0000'0000'ffff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((int)other <=> (unsigned long)(0x0000'0000'0fff'ffff)); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
(void)((int)other <=> (unsigned)(0x8000'0000)); // expected-error {{argument to 'operator<=>' cannot be narrowed}}
|
||||
|
||||
// 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 +180,245 @@ void test7(unsigned long other) {
|
||||
(void)((unsigned char)other <=> (unsigned short)(0x100)); // expected-warning{{less}}
|
||||
(void)((unsigned short)other <=> (unsigned char)(0xff));
|
||||
}
|
||||
|
||||
void test8(void *vp, const void *cvp, int *ip) {
|
||||
(void)(vp <=> cvp); // OK, void* comparisons are allowed.
|
||||
(void)(vp <=> ip);
|
||||
(void)(ip <=> cvp);
|
||||
}
|
||||
|
||||
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_equality);
|
||||
|
||||
auto r3 = (fp <=> nullptr);
|
||||
ASSERT_EXPR_TYPE(r3, std::strong_equality);
|
||||
|
||||
auto r4 = (0 <=> fp);
|
||||
ASSERT_EXPR_TYPE(r4, std::strong_equality);
|
||||
|
||||
auto r5 = (nullptr <=> memp);
|
||||
ASSERT_EXPR_TYPE(r5, std::strong_equality);
|
||||
|
||||
auto r6 = (0 <=> memdp);
|
||||
ASSERT_EXPR_TYPE(r6, std::strong_equality);
|
||||
|
||||
auto r7 = (0 <=> nullptr);
|
||||
ASSERT_EXPR_TYPE(r7, std::strong_equality);
|
||||
}
|
||||
|
||||
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}}
|
||||
}
|
||||
|
||||
// Test that variable narrowing is deferred for value dependent expressions
|
||||
template <int Val>
|
||||
auto test_template_overflow() {
|
||||
// expected-error@+1 {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned long'}}
|
||||
return (Val <=> (unsigned long)0);
|
||||
}
|
||||
template auto test_template_overflow<0>();
|
||||
template auto test_template_overflow<-1>(); // expected-note {{requested here}}
|
||||
|
||||
void test_enum_integral_compare() {
|
||||
enum EnumA : int {A, ANeg = -1, AMax = __INT_MAX__};
|
||||
enum EnumB : unsigned {B, BMax = __UINT32_MAX__ };
|
||||
enum EnumC : int {C = -1, C0 = 0};
|
||||
|
||||
(void)(A <=> C); // expected-error {{invalid operands to binary expression ('EnumA' and 'EnumC')}}
|
||||
|
||||
(void)(A <=> (unsigned)0);
|
||||
(void)((unsigned)0 <=> A);
|
||||
(void)(ANeg <=> (unsigned)0); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
|
||||
(void)((unsigned)0 <=> ANeg); // expected-error {{cannot be narrowed}}
|
||||
|
||||
(void)(B <=> 42);
|
||||
(void)(42 <=> B);
|
||||
(void)(B <=> (unsigned long long)42);
|
||||
(void)(B <=> -1); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
|
||||
(void)(BMax <=> (unsigned long)-1);
|
||||
|
||||
(void)(C0 <=> (unsigned)42);
|
||||
(void)(C <=> (unsigned)42); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
|
||||
}
|
||||
|
||||
namespace EnumCompareTests {
|
||||
|
||||
enum class EnumA { A, A2 };
|
||||
enum class EnumB { B };
|
||||
enum class EnumC : unsigned { C };
|
||||
|
||||
void test_enum_enum_compare_no_builtin() {
|
||||
auto r1 = (EnumA::A <=> EnumA::A2); // OK
|
||||
ASSERT_EXPR_TYPE(r1, std::strong_ordering);
|
||||
(void)(EnumA::A <=> EnumA::A); // expected-warning {{self-comparison always evaluates to 'std::strong_ordering::equal'}}
|
||||
(void)(EnumA::A <=> EnumB::B); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumA' and 'EnumCompareTests::EnumB')}}
|
||||
(void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands}}
|
||||
}
|
||||
|
||||
template <int>
|
||||
struct Tag {};
|
||||
// expected-note@+1 {{candidate}}
|
||||
Tag<0> operator<=>(EnumA, EnumA) {
|
||||
return {};
|
||||
}
|
||||
Tag<1> operator<=>(EnumA, EnumB) {
|
||||
return {};
|
||||
}
|
||||
|
||||
void test_enum_ovl_provided() {
|
||||
auto r1 = (EnumA::A <=> EnumA::A);
|
||||
ASSERT_EXPR_TYPE(r1, Tag<0>);
|
||||
auto r2 = (EnumA::A <=> EnumB::B);
|
||||
ASSERT_EXPR_TYPE(r2, Tag<1>);
|
||||
(void)(EnumB::B <=> EnumA::A); // expected-error {{invalid operands to binary expression ('EnumCompareTests::EnumB' and 'EnumCompareTests::EnumA')}}
|
||||
}
|
||||
|
||||
void enum_float_test() {
|
||||
enum EnumA { A };
|
||||
(void)(A <=> (float)0); // expected-error {{invalid operands to binary expression ('EnumA' and 'float')}}
|
||||
(void)((double)0 <=> A); // expected-error {{invalid operands to binary expression ('double' and 'EnumA')}}
|
||||
(void)((long double)0 <=> A); // expected-error {{invalid operands to binary expression ('long double' and 'EnumA')}}
|
||||
}
|
||||
|
||||
enum class Bool1 : bool { Zero,
|
||||
One };
|
||||
enum Bool2 : bool { B2_Zero,
|
||||
B2_One };
|
||||
|
||||
void test_bool_enum(Bool1 A1, Bool1 A2, Bool2 B1, Bool2 B2) {
|
||||
(void)(A1 <=> A2);
|
||||
(void)(B1 <=> B2);
|
||||
}
|
||||
|
||||
} // namespace EnumCompareTests
|
||||
|
||||
namespace TestUserDefinedConvSeq {
|
||||
|
||||
template <class T, T Val>
|
||||
struct Conv {
|
||||
constexpr operator T() const { return Val; }
|
||||
operator T() { return Val; }
|
||||
};
|
||||
|
||||
void test_user_conv() {
|
||||
{
|
||||
using C = Conv<int, 0>;
|
||||
C c;
|
||||
const C cc;
|
||||
(void)(0 <=> c);
|
||||
(void)(c <=> -1);
|
||||
(void)((unsigned)0 <=> cc);
|
||||
(void)((unsigned)0 <=> c); // expected-error {{argument to 'operator<=>' cannot be narrowed from type 'int' to 'unsigned int'}}
|
||||
}
|
||||
{
|
||||
using C = Conv<int, -1>;
|
||||
C c;
|
||||
const C cc;
|
||||
(void)(c <=> 0);
|
||||
(void)(cc <=> (unsigned)0); // expected-error {{argument to 'operator<=>' evaluates to -1, which cannot be narrowed to type 'unsigned int'}}
|
||||
(void)(c <=> (unsigned)0); // expected-error {{cannot be narrowed from type 'int' to 'unsigned int'}}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace TestUserDefinedConvSeq
|
||||
|
||||
void test_array_conv() {
|
||||
int arr[5];
|
||||
int *ap = arr + 2;
|
||||
int arr2[3];
|
||||
(void)(arr <=> arr); // expected-error {{invalid operands to binary expression ('int [5]' and 'int [5]')}}
|
||||
(void)(+arr <=> arr);
|
||||
}
|
||||
|
||||
void test_mixed_float_int(float f, double d, long double ld) {
|
||||
extern int i;
|
||||
extern unsigned u;
|
||||
extern long l;
|
||||
extern short s;
|
||||
extern unsigned short us;
|
||||
auto r1 = (f <=> i);
|
||||
ASSERT_EXPR_TYPE(r1, std::partial_ordering);
|
||||
|
||||
auto r2 = (us <=> ld);
|
||||
ASSERT_EXPR_TYPE(r2, std::partial_ordering);
|
||||
|
||||
auto r3 = (s <=> f);
|
||||
ASSERT_EXPR_TYPE(r3, std::partial_ordering);
|
||||
|
||||
auto r4 = (0.0 <=> i);
|
||||
ASSERT_EXPR_TYPE(r4, std::partial_ordering);
|
||||
}
|
||||
|
||||
namespace NullptrTest {
|
||||
using nullptr_t = decltype(nullptr);
|
||||
void foo(nullptr_t x, nullptr_t y) {
|
||||
auto r = x <=> y;
|
||||
ASSERT_EXPR_TYPE(r, std::strong_equality);
|
||||
}
|
||||
} // namespace NullptrTest
|
||||
|
||||
namespace ComplexTest {
|
||||
|
||||
enum class StrongE {};
|
||||
enum WeakE { E_One,
|
||||
E_Two };
|
||||
|
||||
void test_diag(_Complex int ci, _Complex float cf, _Complex double cd, int i, float f, StrongE E1, WeakE E2, int *p) {
|
||||
(void)(ci <=> (_Complex int &)ci);
|
||||
(void)(ci <=> cf);
|
||||
(void)(ci <=> i);
|
||||
(void)(ci <=> f);
|
||||
(void)(cf <=> i);
|
||||
(void)(cf <=> f);
|
||||
(void)(ci <=> p); // expected-error {{invalid operands}}
|
||||
(void)(ci <=> E1); // expected-error {{invalid operands}}
|
||||
(void)(E2 <=> cf); // expected-error {{invalid operands}}
|
||||
}
|
||||
|
||||
void test_int(_Complex int x, _Complex int y) {
|
||||
auto r = x <=> y;
|
||||
ASSERT_EXPR_TYPE(r, std::strong_equality);
|
||||
}
|
||||
|
||||
void test_double(_Complex double x, _Complex double y) {
|
||||
auto r = x <=> y;
|
||||
ASSERT_EXPR_TYPE(r, std::weak_equality);
|
||||
}
|
||||
|
||||
} // namespace ComplexTest
|
||||
|
@ -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,194 @@ namespace ThreeWayComparison {
|
||||
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}}
|
||||
|
||||
constexpr void f() { // expected-error {{constant expression}}
|
||||
void(1 <=> 1); // expected-note {{constant expression}}
|
||||
static_assert(std::strong_ordering::equal == 0);
|
||||
|
||||
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));
|
||||
}
|
||||
{ // mixed nullptr tests
|
||||
using SO = std::strong_ordering;
|
||||
using SE = std::strong_equality;
|
||||
|
||||
int x = 42;
|
||||
int *xp = &x;
|
||||
|
||||
MemPtrT mf = nullptr;
|
||||
MemPtrT mf2 = &MemPtr::foo;
|
||||
auto r3 = (mf <=> nullptr);
|
||||
CHECK_TYPE(decltype(r3), std::strong_equality);
|
||||
CHECK(r3.test_eq(SE::equal));
|
||||
}
|
||||
|
||||
return true;
|
||||
}();
|
||||
|
||||
template <auto LHS, auto RHS, bool ExpectTrue = false>
|
||||
constexpr bool test_constexpr() {
|
||||
using nullptr_t = decltype(nullptr);
|
||||
using LHSTy = decltype(LHS);
|
||||
using RHSTy = decltype(RHS);
|
||||
// expected-note@+1 {{subexpression not valid in a constant expression}}
|
||||
auto Res = (LHS <=> RHS);
|
||||
if constexpr (__is_same(LHSTy, nullptr_t) || __is_same(RHSTy, nullptr_t)) {
|
||||
CHECK_TYPE(decltype(Res), std::strong_equality);
|
||||
}
|
||||
if (ExpectTrue)
|
||||
return Res == 0;
|
||||
return Res != 0;
|
||||
}
|
||||
int dummy = 42;
|
||||
int dummy2 = 101;
|
||||
|
||||
constexpr bool tc1 = test_constexpr<nullptr, &dummy>();
|
||||
constexpr bool tc2 = test_constexpr<&dummy, nullptr>();
|
||||
|
||||
// OK, equality comparison only
|
||||
constexpr bool tc3 = test_constexpr<&MemPtr::foo, nullptr>();
|
||||
constexpr bool tc4 = test_constexpr<nullptr, &MemPtr::foo>();
|
||||
constexpr bool tc5 = test_constexpr<&MemPtr::foo, &MemPtr::bar>();
|
||||
|
||||
constexpr bool tc6 = test_constexpr<&MemPtr::data, nullptr>();
|
||||
constexpr bool tc7 = test_constexpr<nullptr, &MemPtr::data>();
|
||||
constexpr bool tc8 = test_constexpr<&MemPtr::data, &MemPtr::data2>();
|
||||
|
||||
// expected-error@+1 {{must be initialized by a constant expression}}
|
||||
constexpr bool tc9 = test_constexpr<&dummy, &dummy2>(); // expected-note {{in call}}
|
||||
|
||||
template <class T, class R, class I>
|
||||
constexpr T makeComplex(R r, I i) {
|
||||
T res{r, i};
|
||||
return res;
|
||||
};
|
||||
|
||||
template <class T, class ResultT>
|
||||
constexpr bool complex_test(T x, T y, ResultT Expect) {
|
||||
auto res = x <=> y;
|
||||
CHECK_TYPE(decltype(res), ResultT);
|
||||
return res.test_eq(Expect);
|
||||
}
|
||||
static_assert(complex_test(makeComplex<_Complex double>(0.0, 0.0),
|
||||
makeComplex<_Complex double>(0.0, 0.0),
|
||||
std::weak_equality::equivalent));
|
||||
static_assert(complex_test(makeComplex<_Complex double>(0.0, 0.0),
|
||||
makeComplex<_Complex double>(1.0, 0.0),
|
||||
std::weak_equality::nonequivalent));
|
||||
static_assert(complex_test(makeComplex<_Complex double>(0.0, 0.0),
|
||||
makeComplex<_Complex double>(0.0, 1.0),
|
||||
std::weak_equality::nonequivalent));
|
||||
static_assert(complex_test(makeComplex<_Complex int>(0, 0),
|
||||
makeComplex<_Complex int>(0, 0),
|
||||
std::strong_equality::equal));
|
||||
static_assert(complex_test(makeComplex<_Complex int>(0, 0),
|
||||
makeComplex<_Complex int>(1, 0),
|
||||
std::strong_equality::nonequal));
|
||||
// TODO: defaulted operator <=>
|
||||
} // namespace ThreeWayComparison
|
||||
|
65
clang/test/SemaCXX/std-compare-cxx2a.cpp
Normal file
65
clang/test/SemaCXX/std-compare-cxx2a.cpp
Normal file
@ -0,0 +1,65 @@
|
||||
// Test diagnostics for ill-formed STL <compare> headers.
|
||||
|
||||
// RUN: %clang_cc1 -triple x86_64-apple-darwin -fcxx-exceptions -fsyntax-only -pedantic -verify -Wsign-compare -std=c++2a %s
|
||||
|
||||
void compare_not_found_test() {
|
||||
// expected-error@+1 {{cannot deduce return type of 'operator<=>' because type partial_ordering was not found; include <compare>}}
|
||||
(void)(0.0 <=> 42.123);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
inline namespace __1 {
|
||||
struct partial_ordering; // expected-note {{forward declaration}}
|
||||
}
|
||||
} // namespace std
|
||||
|
||||
auto compare_incomplete_test() {
|
||||
// expected-error@+1 {{incomplete type 'std::partial_ordering' where a complete type is required}}
|
||||
return (-1.2 <=> 123.0);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
inline namespace __1 {
|
||||
struct partial_ordering {
|
||||
unsigned value;
|
||||
};
|
||||
} // namespace __1
|
||||
} // namespace std
|
||||
|
||||
auto missing_member_test() {
|
||||
// expected-error@+1 {{standard library implementation of 'std::partial_ordering' is not supported; member 'equivalent' is missing}}
|
||||
return (1.0 <=> 1.0);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
inline namespace __1 {
|
||||
struct strong_ordering {
|
||||
long long value;
|
||||
static const strong_ordering equivalent; // expected-note {{declared here}}
|
||||
};
|
||||
} // namespace __1
|
||||
} // namespace std
|
||||
|
||||
auto test_non_constexpr_var() {
|
||||
// expected-error@+1 {{standard library implementation of 'std::strong_ordering' is not supported; member 'equivalent' does not have expected form}}
|
||||
return (1 <=> 0);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
inline namespace __1 {
|
||||
struct strong_equality {
|
||||
char value = 0;
|
||||
constexpr strong_equality() = default;
|
||||
// non-trivial
|
||||
constexpr strong_equality(strong_equality const &other) : value(other.value) {}
|
||||
};
|
||||
} // namespace __1
|
||||
} // namespace std
|
||||
|
||||
struct Class {};
|
||||
using MemPtr = void (Class::*)(int);
|
||||
|
||||
auto test_non_trivial(MemPtr LHS, MemPtr RHS) {
|
||||
// expected-error@+1 {{standard library implementation of 'std::strong_equality' is not supported; the type is not trivially copyable}}
|
||||
return LHS <=> RHS;
|
||||
}
|
Loading…
Reference in New Issue
Block a user