diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index b91fd262c9d3f1..d657c29adafd08 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -10145,8 +10145,8 @@ def warn_dispatch_body_ignored : Warning< // 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 ">; + "cannot %select{use builtin operator '<=>'|default 'operator<=>'}1 " + "because type '%0' was not found; include ">; def err_spaceship_argument_narrowing : Error< "argument to 'operator<=>' " "%select{cannot be narrowed from type %1 to %2|" diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 53db210a017759..6d0f79a2d2ce8b 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -5038,6 +5038,16 @@ class Sema final { IdentifierInfo *MemberOrBase); public: + enum class ComparisonCategoryUsage { + /// The '<=>' operator was used in an expression and a builtin operator + /// was selected. + OperatorInExpression, + /// A defaulted 'operator<=>' needed the comparison category. This + /// typically only applies to 'std::strong_ordering', due to the implicit + /// fallback return value. + DefaultedOperator, + }; + /// Lookup the specified comparison category types in the standard /// library, an check the VarDecls possibly returned by the operator<=> /// builtins for that type. @@ -5045,7 +5055,8 @@ class Sema final { /// \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); + SourceLocation Loc, + ComparisonCategoryUsage Usage); /// 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. diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp index ba4d450984a36d..239aaf65c86bad 100644 --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -7599,7 +7599,8 @@ class DefaultedComparisonSynthesizer // Per C++2a [class.spaceship]p3, as a fallback add: // return static_cast(std::strong_ordering::equal); QualType StrongOrdering = S.CheckComparisonCategoryType( - ComparisonCategoryType::StrongOrdering, Loc); + ComparisonCategoryType::StrongOrdering, Loc, + Sema::ComparisonCategoryUsage::DefaultedOperator); if (StrongOrdering.isNull()) return StmtError(); VarDecl *EqualVD = S.Context.CompCategories.getInfoForType(StrongOrdering) @@ -8057,7 +8058,8 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD, 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); + QualType Cat = CheckComparisonCategoryType( + Info.Category, RetLoc, ComparisonCategoryUsage::DefaultedOperator); if (Cat.isNull()) return true; Context.adjustDeducedFunctionResultType( @@ -10591,7 +10593,8 @@ struct InvalidSTLDiagnoser { } // namespace QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind, - SourceLocation Loc) { + SourceLocation Loc, + ComparisonCategoryUsage Usage) { assert(getLangOpts().CPlusPlus && "Looking for comparison category type outside of C++."); @@ -10620,7 +10623,7 @@ QualType Sema::CheckComparisonCategoryType(ComparisonCategoryType Kind, std::string NameForDiags = "std::"; NameForDiags += ComparisonCategories::getCategoryString(Kind); Diag(Loc, diag::err_implied_comparison_category_type_not_found) - << NameForDiags; + << NameForDiags << (int)Usage; return QualType(); } diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index 36eef4f425e5ef..a57ee7b0ff2147 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -10601,7 +10601,8 @@ static QualType checkArithmeticOrEnumeralThreeWayCompare(Sema &S, assert(!Type.isNull() && "composite type for <=> has not been set"); return S.CheckComparisonCategoryType( - *getComparisonCategoryForBuiltinCmp(Type), Loc); + *getComparisonCategoryForBuiltinCmp(Type), Loc, + Sema::ComparisonCategoryUsage::OperatorInExpression); } static QualType checkArithmeticOrEnumeralCompare(Sema &S, ExprResult &LHS, @@ -10738,7 +10739,8 @@ QualType Sema::CheckCompareOperands(ExprResult &LHS, ExprResult &RHS, CCT = ComparisonCategoryType::StrongEquality; } - return CheckComparisonCategoryType(*CCT, Loc); + return CheckComparisonCategoryType( + *CCT, Loc, ComparisonCategoryUsage::OperatorInExpression); }; if (!IsRelational && LHSIsNull != RHSIsNull) { diff --git a/clang/test/SemaCXX/std-compare-cxx2a.cpp b/clang/test/SemaCXX/std-compare-cxx2a.cpp index 941c4faeb7ffb1..3febb8ff439946 100644 --- a/clang/test/SemaCXX/std-compare-cxx2a.cpp +++ b/clang/test/SemaCXX/std-compare-cxx2a.cpp @@ -3,10 +3,24 @@ // 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 'std::partial_ordering' was not found; include }} + // expected-error@+1 {{cannot use builtin operator '<=>' because type 'std::partial_ordering' was not found; include }} (void)(0.0 <=> 42.123); } +struct deduction_compare_not_found { + // expected-error@+1 {{cannot default 'operator<=>' because type 'std::strong_ordering' was not found; include }} + friend auto operator<=>(const deduction_compare_not_found&, const deduction_compare_not_found&) = default; +}; + +struct comparable { + int operator<=>(comparable); +}; +struct default_compare_not_found { + // expected-error@+1 {{cannot default 'operator<=>' because type 'std::strong_ordering' was not found; include }} + friend int operator<=>(const default_compare_not_found&, const default_compare_not_found&) = default; +}; +bool b = default_compare_not_found() < default_compare_not_found(); // expected-note {{first required here}} + namespace std { inline namespace __1 { struct partial_ordering; // expected-note {{forward declaration}}