Index: clang/include/clang/Basic/DiagnosticGroups.td =================================================================== --- clang/include/clang/Basic/DiagnosticGroups.td +++ clang/include/clang/Basic/DiagnosticGroups.td @@ -228,6 +228,7 @@ DiagCategory<"Deprecations">; def CXX20Designator : DiagGroup<"c++20-designator">; +def CXX2bDefCompCallsNonConstexpr : DiagGroup<"c++2b-default-comp-relaxed-constexpr">; // Allow -Wno-c99-designator to be used to turn off all warnings on valid C99 // designators (including the warning controlled by -Wc++20-designator). def C99Designator : DiagGroup<"c99-designator", [CXX20Designator]>; Index: clang/include/clang/Basic/DiagnosticSemaKinds.td =================================================================== --- clang/include/clang/Basic/DiagnosticSemaKinds.td +++ clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -9407,12 +9407,18 @@ "%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 err_incorrect_defaulted_comparison_constexpr : Error< +def ext_incorrect_defaulted_comparison_constexpr : Extension< "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|" "three-way comparison operator}0 " - "cannot be declared %select{constexpr|consteval}2 because " + "declared %select{constexpr|consteval}2 but " "%select{it|the corresponding implicit 'operator=='}0 " - "invokes a non-constexpr comparison function">; + "invokes a non-constexpr comparison function are a C++2b extension">, InGroup; +def warn_cxx2b_incorrect_defaulted_comparison_constexpr : Warning< + "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|" + "three-way comparison operator}0 " + "declared %select{constexpr|consteval}2 but " + "%select{it|the corresponding implicit 'operator=='}0 " + "invokes a non-constexpr comparison function are a C++2b extension">, InGroup, DefaultIgnore; def note_defaulted_comparison_not_constexpr : Note< "non-constexpr comparison function would be used to compare " "%select{|member %1|base class %1}0">; Index: clang/lib/Sema/SemaDeclCXX.cpp =================================================================== --- clang/lib/Sema/SemaDeclCXX.cpp +++ clang/lib/Sema/SemaDeclCXX.cpp @@ -8809,7 +8809,8 @@ CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) && !Info.Constexpr) { Diag(FD->getBeginLoc(), - diag::err_incorrect_defaulted_comparison_constexpr) + (getLangOpts().CPlusPlus2b ? diag::warn_cxx2b_incorrect_defaulted_comparison_constexpr : + diag::ext_incorrect_defaulted_comparison_constexpr )) << FD->isImplicit() << (int)DCK << FD->isConsteval(); DefaultedComparisonAnalyzer(*this, RD, FD, DCK, DefaultedComparisonAnalyzer::ExplainConstexpr) Index: clang/test/CXX/class/class.compare/class.compare.default/p3.cpp =================================================================== --- clang/test/CXX/class/class.compare/class.compare.default/p3.cpp +++ clang/test/CXX/class/class.compare/class.compare.default/p3.cpp @@ -80,10 +80,10 @@ }; struct C { - friend bool operator==(const C&, const C&); // expected-note {{previous}} expected-note 2{{here}} + friend bool operator==(const C&, const C&); // expected-note {{previous}} friend bool operator!=(const C&, const C&) = default; // expected-note {{previous}} - friend std::strong_ordering operator<=>(const C&, const C&); // expected-note {{previous}} expected-note 2{{here}} + friend std::strong_ordering operator<=>(const C&, const C&); // expected-note {{previous}} friend bool operator<(const C&, const C&) = default; // expected-note {{previous}} friend bool operator<=(const C&, const C&) = default; // expected-note {{previous}} friend bool operator>(const C&, const C&) = default; // expected-note {{previous}} @@ -127,23 +127,23 @@ struct E { A a; - C c; // expected-note 2{{non-constexpr comparison function would be used to compare member 'c'}} + C c; A b; - friend constexpr bool operator==(const E&, const E&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} + friend constexpr bool operator==(const E&, const E&) = default; friend constexpr bool operator!=(const E&, const E&) = default; - friend constexpr std::strong_ordering operator<=>(const E&, const E&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} + friend constexpr std::strong_ordering operator<=>(const E&, const E&) = default; friend constexpr bool operator<(const E&, const E&) = default; friend constexpr bool operator<=(const E&, const E&) = default; friend constexpr bool operator>(const E&, const E&) = default; friend constexpr bool operator>=(const E&, const E&) = default; }; -struct E2 : A, C { // expected-note 2{{non-constexpr comparison function would be used to compare base class 'C'}} - friend constexpr bool operator==(const E2&, const E2&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} +struct E2 : A, C { + friend constexpr bool operator==(const E2&, const E2&) = default; friend constexpr bool operator!=(const E2&, const E2&) = default; - friend constexpr std::strong_ordering operator<=>(const E2&, const E2&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} + friend constexpr std::strong_ordering operator<=>(const E2&, const E2&) = default; friend constexpr bool operator<(const E2&, const E2&) = default; friend constexpr bool operator<=(const E2&, const E2&) = default; friend constexpr bool operator>(const E2&, const E2&) = default; @@ -151,14 +151,14 @@ }; struct F { - friend bool operator==(const F&, const F&); // expected-note {{here}} - friend constexpr bool operator!=(const F&, const F&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} - - friend std::strong_ordering operator<=>(const F&, const F&); // expected-note 4{{here}} - friend constexpr bool operator<(const F&, const F&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} - friend constexpr bool operator<=(const F&, const F&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} - friend constexpr bool operator>(const F&, const F&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} - friend constexpr bool operator>=(const F&, const F&) = default; // expected-error {{cannot be declared constexpr because it invokes a non-constexpr comparison function}} + friend bool operator==(const F&, const F&); + friend constexpr bool operator!=(const F&, const F&) = default; + + friend std::strong_ordering operator<=>(const F&, const F&); + friend constexpr bool operator<(const F&, const F&) = default; + friend constexpr bool operator<=(const F&, const F&) = default; + friend constexpr bool operator>(const F&, const F&) = default; + friend constexpr bool operator>=(const F&, const F&) = default; }; // No implicit 'constexpr' if it's not the first declaration. Index: clang/test/CXX/class/class.compare/class.compare.default/p4.cpp =================================================================== --- clang/test/CXX/class/class.compare/class.compare.default/p4.cpp +++ clang/test/CXX/class/class.compare/class.compare.default/p4.cpp @@ -1,7 +1,9 @@ // RUN: %clang_cc1 -std=c++2a -verify %s +// RUN: %clang_cc1 -std=c++2a -Wc++2b-default-comp-relaxed-constexpr -verify=expected,extension %s // This test is for [class.compare.default]p3 as modified and renumbered to p4 // by P2002R0. +// Also covers modifications made by P2448R2 and extension warnings namespace std { struct strong_ordering { @@ -76,14 +78,13 @@ } struct H { - bool operator==(const H&) const; // expected-note {{here}} + bool operator==(const H&) const; // extension-note {{non-constexpr comparison function declared here}} constexpr std::strong_ordering operator<=>(const H&) const { return std::strong_ordering::equal; } }; struct I { - H h; // expected-note {{used to compare}} - // expected-error@+1 {{defaulted definition of three-way comparison operator cannot be declared constexpr because the corresponding implicit 'operator==' invokes a non-constexpr comparison function}} - constexpr std::strong_ordering operator<=>(const I&) const = default; + H h; // extension-note {{non-constexpr comparison function would be used to compare member 'h'}} + constexpr std::strong_ordering operator<=>(const I&) const = default; // extension-warning {{implicit 'operator==' invokes a non-constexpr comparison function are a C++2b extension}} }; struct J { @@ -144,3 +145,19 @@ }; bool test_d = D() == D(); } + +namespace GH61238 { +template struct my_struct { + A value; // extension-note {{non-constexpr comparison function would be used to compare member 'value'}} + + constexpr friend bool operator==(const my_struct &, const my_struct &) noexcept = default; // no diagnostic extension-warning {{declared constexpr but it invokes a non-constexpr comparison function are a C++2b extension}} +}; + +struct non_constexpr_type { + friend bool operator==(non_constexpr_type, non_constexpr_type) noexcept { // extension-note {{non-constexpr comparison function declared here}} + return false; + } +}; + +my_struct obj; // extension-note {{in instantiation of template class 'GH61238::my_struct' requested here}} +}