diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index c07e6f9d7421a..33aa5d0483e9c 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9143,6 +9143,9 @@ def note_defaulted_comparison_cannot_deduce_undeduced_auto : Note< "%select{|member|base class}0 %1 declared here">; def note_defaulted_comparison_cannot_deduce_callee : Note< "selected 'operator<=>' for %select{|member|base class}0 %1 declared here">; +def note_defaulted_comparison_selected_invalid : Note< + "would compare %select{|member|base class}0 %1 " + "as %2, which does not support relational comparisons">; def err_incorrect_defaulted_comparison_constexpr : Error< "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|" "three-way comparison operator}0 " diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index d39837d277045..5109f1e877a26 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7749,16 +7749,11 @@ class DefaultedComparisonAnalyzer if (Args[0]->getType()->isOverloadableType()) S.LookupOverloadedBinOp(CandidateSet, OO, Fns, Args); - else if (OO == OO_EqualEqual || - !Args[0]->getType()->isFunctionPointerType()) { + else // FIXME: We determine whether this is a valid expression by checking to // see if there's a viable builtin operator candidate for it. That isn't // really what the rules ask us to do, but should give the right results. - // - // Note that the builtin operator for relational comparisons on function - // pointers is the only known case which cannot be used. S.AddBuiltinOperatorCandidates(OO, FD->getLocation(), Args, CandidateSet); - } Result R; @@ -7802,11 +7797,14 @@ class DefaultedComparisonAnalyzer return Result::deleted(); } - // C++2a [class.compare.default]p3 [P2002R0]: - // A defaulted comparison function is constexpr-compatible if [...] - // no overlod resolution performed [...] results in a non-constexpr - // function. + bool NeedsDeducing = + OO == OO_Spaceship && FD->getReturnType()->isUndeducedAutoType(); + if (FunctionDecl *BestFD = Best->Function) { + // C++2a [class.compare.default]p3 [P2002R0]: + // A defaulted comparison function is constexpr-compatible if + // [...] no overlod resolution performed [...] results in a + // non-constexpr function. assert(!BestFD->isDeleted() && "wrong overload resolution result"); // If it's not constexpr, explain why not. if (Diagnose == ExplainConstexpr && !BestFD->isConstexpr()) { @@ -7819,10 +7817,8 @@ class DefaultedComparisonAnalyzer return Result::deleted(); } R.Constexpr &= BestFD->isConstexpr(); - } - if (OO == OO_Spaceship && FD->getReturnType()->isUndeducedAutoType()) { - if (auto *BestFD = Best->Function) { + if (NeedsDeducing) { // If any callee has an undeduced return type, deduce it now. // FIXME: It's not clear how a failure here should be handled. For // now, we produce an eager diagnostic, because that is forward @@ -7848,10 +7844,9 @@ class DefaultedComparisonAnalyzer } return Result::deleted(); } - if (auto *Info = S.Context.CompCategories.lookupInfoForType( - BestFD->getCallResultType())) { - R.Category = Info->Kind; - } else { + auto *Info = S.Context.CompCategories.lookupInfoForType( + BestFD->getCallResultType()); + if (!Info) { if (Diagnose == ExplainDeleted) { S.Diag(Subobj.Loc, diag::note_defaulted_comparison_cannot_deduce) << Subobj.Kind << Subobj.Decl @@ -7862,12 +7857,25 @@ class DefaultedComparisonAnalyzer } return Result::deleted(); } - } else { - QualType T = Best->BuiltinParamTypes[0]; - assert(T == Best->BuiltinParamTypes[1] && - "builtin comparison for different types?"); - assert(Best->BuiltinParamTypes[2].isNull() && - "invalid builtin comparison"); + R.Category = Info->Kind; + } + } else { + QualType T = Best->BuiltinParamTypes[0]; + assert(T == Best->BuiltinParamTypes[1] && + "builtin comparison for different types?"); + assert(Best->BuiltinParamTypes[2].isNull() && + "invalid builtin comparison"); + + // The builtin operator for relational comparisons on function + // pointers is the only known case which cannot be used. + if (OO != OO_EqualEqual && T->isFunctionPointerType()) { + if (Diagnose == ExplainDeleted) + S.Diag(Subobj.Loc, diag::note_defaulted_comparison_selected_invalid) + << Subobj.Kind << Subobj.Decl << T; + return Result::deleted(); + } + + if (NeedsDeducing) { Optional Cat = getComparisonCategoryForBuiltinCmp(T); assert(Cat && "no category for builtin comparison?"); diff --git a/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp b/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp index 47b14cb5f9616..01fff692c0c85 100644 --- a/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp +++ b/clang/test/CXX/class/class.compare/class.spaceship/p2.cpp @@ -159,7 +159,7 @@ namespace BadDeducedType { namespace PR48856 { struct A { auto operator<=>(const A &) const = default; // expected-warning {{implicitly deleted}} - void (*x)(); // expected-note {{because there is no viable three-way comparison function for member 'x'}} + void (*x)(); // expected-note {{does not support relational comparisons}} }; struct B { @@ -191,4 +191,13 @@ namespace PR50591 { a2 f; }; std::partial_ordering cmp_b2 = b2() <=> b2(); + + struct a3 { + using fp = void (*)(); + operator fp() const; + }; + struct b3 { + auto operator<=>(b3 const &) const = default; // expected-warning {{implicitly deleted}} + a3 f; // expected-note {{would compare member 'f' as 'void (*)()', which does not support relational comparisons}} + }; }