summaryrefslogtreecommitdiffstats
path: root/clang/lib
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib')
-rw-r--r--clang/lib/AST/ComparisonCategories.cpp35
-rw-r--r--clang/lib/Sema/SemaDeclCXX.cpp71
-rw-r--r--clang/lib/Sema/SemaExpr.cpp54
3 files changed, 113 insertions, 47 deletions
diff --git a/clang/lib/AST/ComparisonCategories.cpp b/clang/lib/AST/ComparisonCategories.cpp
index 3fb500c580e..9a07c2494ac 100644
--- a/clang/lib/AST/ComparisonCategories.cpp
+++ b/clang/lib/AST/ComparisonCategories.cpp
@@ -19,6 +19,41 @@
using namespace clang;
+Optional<ComparisonCategoryType>
+clang::getComparisonCategoryForBuiltinCmp(QualType T) {
+ using CCT = ComparisonCategoryType;
+
+ if (const ComplexType *CT = T->getAs<ComplexType>()) {
+ if (CT->getElementType()->hasFloatingRepresentation())
+ return CCT::WeakEquality;
+ // FIXME: Remove this, consistent with P1959R0.
+ return CCT::StrongEquality;
+ }
+
+ if (T->isIntegralOrEnumerationType())
+ return CCT::StrongOrdering;
+
+ if (T->hasFloatingRepresentation())
+ return CCT::PartialOrdering;
+
+ // C++2a [expr.spaceship]p7: If the composite pointer type is a function
+ // pointer type, a pointer-to-member type, or std::nullptr_t, the
+ // result is of type std::strong_equality
+ if (T->isFunctionPointerType() || T->isMemberPointerType() ||
+ T->isNullPtrType())
+ // FIXME: This case was removed by P1959R0.
+ return CCT::StrongEquality;
+
+ // C++2a [expr.spaceship]p8: If the composite pointer type is an object
+ // pointer type, p <=> q is of type std::strong_ordering.
+ // Note: this assumes neither operand is a null pointer constant.
+ if (T->isPointerType())
+ return CCT::StrongOrdering;
+
+ // TODO: Extend support for operator<=> to ObjC types.
+ return llvm::None;
+}
+
bool ComparisonCategoryInfo::ValueInfo::hasValidIntValue() const {
assert(VD && "must have var decl");
if (!VD->checkInitIsICE())
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 6d15e1e2025..7bbd0114b1e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -7214,12 +7214,18 @@ protected:
struct DefaultedComparisonInfo {
bool Deleted = false;
bool Constexpr = true;
+ ComparisonCategoryType Category = ComparisonCategoryType::StrongOrdering;
- static DefaultedComparisonInfo deleted() { return {true, false}; }
+ static DefaultedComparisonInfo deleted() {
+ DefaultedComparisonInfo Deleted;
+ Deleted.Deleted = true;
+ return Deleted;
+ }
bool add(const DefaultedComparisonInfo &R) {
Deleted |= R.Deleted;
Constexpr &= R.Constexpr;
+ Category = commonComparisonType(Category, R.Category);
return Deleted;
}
};
@@ -7353,19 +7359,43 @@ private:
// A defaulted comparison function is constexpr-compatible if [...]
// no overlod resolution performed [...] results in a non-constexpr
// function.
- if (FunctionDecl *FD = Best->Function) {
- assert(!FD->isDeleted() && "wrong overload resolution result");
+ if (FunctionDecl *BestFD = Best->Function) {
+ assert(!BestFD->isDeleted() && "wrong overload resolution result");
// If it's not constexpr, explain why not.
- if (Diagnose == ExplainConstexpr && !FD->isConstexpr()) {
+ if (Diagnose == ExplainConstexpr && !BestFD->isConstexpr()) {
if (Subobj.Kind != Subobject::CompleteObject)
S.Diag(Subobj.Loc, diag::note_defaulted_comparison_not_constexpr)
<< Subobj.Kind << Subobj.Decl;
- S.Diag(FD->getLocation(),
+ S.Diag(BestFD->getLocation(),
diag::note_defaulted_comparison_not_constexpr_here);
// Bail out after explaining; we don't want any more notes.
return Result::deleted();
}
- R.Constexpr &= FD->isConstexpr();
+ R.Constexpr &= BestFD->isConstexpr();
+ }
+
+ if (OO == OO_Spaceship && FD->getReturnType()->isUndeducedAutoType()) {
+ if (auto *BestFD = Best->Function) {
+ if (auto *Info = S.Context.CompCategories.lookupInfoForType(
+ BestFD->getCallResultType())) {
+ R.Category = Info->Kind;
+ } else {
+ if (Diagnose == ExplainDeleted) {
+ S.Diag(Subobj.Loc, diag::note_defaulted_comparison_cannot_deduce)
+ << Subobj.Kind << Subobj.Decl
+ << BestFD->getCallResultType().withoutLocalFastQualifiers();
+ S.Diag(BestFD->getLocation(),
+ diag::note_defaulted_comparison_cannot_deduce_callee)
+ << Subobj.Kind << Subobj.Decl;
+ }
+ return Result::deleted();
+ }
+ } else {
+ Optional<ComparisonCategoryType> Cat =
+ getComparisonCategoryForBuiltinCmp(Args[0]->getType());
+ assert(Cat && "no category for builtin comparison?");
+ R.Category = *Cat;
+ }
}
// Note that we might be rewriting to a different operator. That call is
@@ -7914,6 +7944,19 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
<< FD->getReturnTypeSourceRange();
return true;
}
+ // C++2a [class.spaceship]p2 [P2002R0]:
+ // Let R be the declared return type [...]. If R is auto, [...]. Otherwise,
+ // R shall not contain a placeholder type.
+ if (DCK == DefaultedComparisonKind::ThreeWay &&
+ FD->getDeclaredReturnType()->getContainedDeducedType() &&
+ !Context.hasSameType(FD->getDeclaredReturnType(),
+ Context.getAutoDeductType())) {
+ Diag(FD->getLocation(),
+ diag::err_defaulted_comparison_deduced_return_type_not_auto)
+ << (int)DCK << FD->getDeclaredReturnType() << Context.AutoDeductTy
+ << FD->getReturnTypeSourceRange();
+ return true;
+ }
// For a defaulted function in a dependent class, defer all remaining checks
// until instantiation.
@@ -7955,7 +7998,21 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
return false;
}
- // FIXME: Deduce the return type now.
+ // C++2a [class.spaceship]p2:
+ // The return type is deduced as the common comparison type of R0, R1, ...
+ if (DCK == DefaultedComparisonKind::ThreeWay &&
+ FD->getDeclaredReturnType()->isUndeducedAutoType()) {
+ SourceLocation RetLoc = FD->getReturnTypeSourceRange().getBegin();
+ if (RetLoc.isInvalid())
+ RetLoc = FD->getBeginLoc();
+ // FIXME: Should we really care whether we have the complete type and the
+ // 'enumerator' constants here? A forward declaration seems sufficient.
+ QualType Cat = CheckComparisonCategoryType(Info.Category, RetLoc);
+ if (Cat.isNull())
+ return true;
+ Context.adjustDeducedFunctionResultType(
+ FD, SubstAutoType(FD->getDeclaredReturnType(), Cat));
+ }
// C++2a [dcl.fct.def.default]p3 [P2002R0]:
// An explicitly-defaulted function that is not defined as deleted may be
diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp
index b97352e27e1..f6bcf899a7f 100644
--- a/clang/lib/Sema/SemaExpr.cpp
+++ b/clang/lib/Sema/SemaExpr.cpp
@@ -10521,8 +10521,6 @@ 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
@@ -10582,6 +10580,8 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
return S.InvalidOperands(Loc, LHS, RHS);
assert(Type->isArithmeticType() || Type->isEnumeralType());
+ // FIXME: Reject complex types, consistent with P1959R0.
+
bool HasNarrowing = checkThreeWayNarrowingConversion(
S, Type, LHS.get(), LHSType, LHS.get()->getBeginLoc());
HasNarrowing |= checkThreeWayNarrowingConversion(S, Type, RHS.get(), RHSType,
@@ -10591,20 +10591,8 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S,
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);
+ return S.CheckComparisonCategoryType(
+ *getComparisonCategoryForBuiltinCmp(Type), Loc);
}
static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS,
@@ -10729,34 +10717,20 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS,
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()) {
+ Optional<ComparisonCategoryType> CCT =
+ getComparisonCategoryForBuiltinCmp(CompositeTy);
+ if (!CCT)
+ return InvalidOperands(Loc, LHS, RHS);
+
+ if (CompositeTy->isPointerType() && LHSIsNull != RHSIsNull) {
// 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);
+ // FIXME: Reject this, consistent with P1959R0 + P0946R0.
+ CCT = ComparisonCategoryType::StrongEquality;
}
- // C++2a [expr.spaceship]p9: Otherwise, the program is ill-formed.
- // TODO: Extend support for operator<=> to ObjC types.
- return InvalidOperands(Loc, LHS, RHS);
- };
+ return CheckComparisonCategoryType(*CCT, Loc);
+ };
if (!IsRelational && LHSIsNull != RHSIsNull) {
bool IsEquality = Opc == BO_EQ;
OpenPOWER on IntegriCloud