diff options
Diffstat (limited to 'clang/lib')
-rw-r--r-- | clang/lib/AST/ComparisonCategories.cpp | 35 | ||||
-rw-r--r-- | clang/lib/Sema/SemaDeclCXX.cpp | 71 | ||||
-rw-r--r-- | clang/lib/Sema/SemaExpr.cpp | 54 |
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; |