[clang] b4692f2 - [Clang] Updating handling of defaulted comparison operators to reflect changes from P2448R2
Shafik Yaghmour via cfe-commits
cfe-commits at lists.llvm.org
Thu May 4 11:07:25 PDT 2023
Author: Shafik Yaghmour
Date: 2023-05-04T11:07:16-07:00
New Revision: b4692f29263006c7ea519c7b11c9082384f0af53
URL: https://github.com/llvm/llvm-project/commit/b4692f29263006c7ea519c7b11c9082384f0af53
DIFF: https://github.com/llvm/llvm-project/commit/b4692f29263006c7ea519c7b11c9082384f0af53.diff
LOG: [Clang] Updating handling of defaulted comparison operators to reflect changes from P2448R2
Prior to P2448R2 we were more aggressive in diagnosing ill-formed
constexpr functions. Many of these restrictions were relaxed and now it
is not required for defaulted comparison operators to call constexpr
functions.
This behavior is extended to before C++23 and diagnostic for it's use
can be enabled w/ -pedantic or -Wc++2b-default-comp-relaxed-constexpr
This fixes: https://github.com/llvm/llvm-project/issues/61238
Differential Revision: https://reviews.llvm.org/D146090
Added:
Modified:
clang/docs/ReleaseNotes.rst
clang/include/clang/Basic/DiagnosticSemaKinds.td
clang/lib/Sema/SemaDeclCXX.cpp
clang/test/CXX/class/class.compare/class.compare.default/p3.cpp
clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
clang/www/cxx_status.html
Removed:
################################################################################
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 8fda486228dbc..e0ad5f5e3ae53 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -100,6 +100,12 @@ C++23 Feature Support
and `P2579R0 Mitigation strategies for P2036 <https://wg21.link/P2579R0>`_.
These proposals modify how variables captured in lambdas can appear in trailing return type
expressions and how their types are deduced therein, in all C++ language versions.
+- Implemented partial support for `P2448R2: Relaxing some constexpr restrictions <https://wg21.link/p2448r2>`_
+ Explicitly defaulted functions no longer have to be constexpr-compatible but merely constexpr suitable.
+ We do not support outside of defaulted special memeber functions the change that constexpr functions no
+ longer have to be constexpr compatible but rather support a less restricted requirements for constexpr
+ functions. Which include allowing non-literal types as return values and paremeters, allow calling of
+ non-constexpr functions and constructors.
Resolutions to C++ Defect Reports
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 7a20f2acbdede..f0ff7d0f3169b 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -9426,12 +9426,21 @@ 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 err_incorrect_defaulted_comparison_constexpr : Error<
+def ext_defaulted_comparison_constexpr_mismatch : Extension<
"defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
- "three-way comparison operator}0 "
- "cannot be declared %select{constexpr|consteval}2 because "
- "%select{it|the corresponding implicit 'operator=='}0 "
- "invokes a non-constexpr comparison function">;
+ "three-way comparison operator}0 that is "
+ "declared %select{constexpr|consteval}2 but"
+ "%select{|for which the corresponding implicit 'operator==' }0 "
+ "invokes a non-constexpr comparison function is a C++2b extension">,
+ InGroup<DiagGroup<"c++2b-default-comp-relaxed-constexpr">>;
+def warn_cxx2b_compat_defaulted_comparison_constexpr_mismatch : Warning<
+ "defaulted definition of %select{%sub{select_defaulted_comparison_kind}1|"
+ "three-way comparison operator}0 that is "
+ "declared %select{constexpr|consteval}2 but"
+ "%select{|for which the corresponding implicit 'operator==' }0 "
+ "invokes a non-constexpr comparison function is incompatible with C++ "
+ "standards before C++2b">,
+ InGroup<CXXPre2bCompat>, DefaultIgnore;
def note_defaulted_comparison_not_constexpr : Note<
"non-constexpr comparison function would be used to compare "
"%select{|member %1|base class %1}0">;
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index a746d8c50a0cc..5b7ee09ac4c72 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -8813,12 +8813,25 @@ bool Sema::CheckExplicitlyDefaultedComparison(Scope *S, FunctionDecl *FD,
// the requirements for a constexpr function [...]
// The only relevant requirements are that the parameter and return types are
// literal types. The remaining conditions are checked by the analyzer.
+ //
+ // We support P2448R2 in language modes earlier than C++23 as an extension.
+ // The concept of constexpr-compatible was removed.
+ // C++23 [dcl.fct.def.default]p3 [P2448R2]
+ // A function explicitly defaulted on its first declaration is implicitly
+ // inline, and is implicitly constexpr if it is constexpr-suitable.
+ // C++23 [dcl.constexpr]p3
+ // A function is constexpr-suitable if
+ // - it is not a coroutine, and
+ // - if the function is a constructor or destructor, its class does not
+ // have any virtual base classes.
if (FD->isConstexpr()) {
if (CheckConstexprReturnType(*this, FD, CheckConstexprKind::Diagnose) &&
CheckConstexprParameterTypes(*this, FD, CheckConstexprKind::Diagnose) &&
!Info.Constexpr) {
Diag(FD->getBeginLoc(),
- diag::err_incorrect_defaulted_comparison_constexpr)
+ getLangOpts().CPlusPlus2b
+ ? diag::warn_cxx2b_compat_defaulted_comparison_constexpr_mismatch
+ : diag::ext_defaulted_comparison_constexpr_mismatch)
<< FD->isImplicit() << (int)DCK << FD->isConsteval();
DefaultedComparisonAnalyzer(*this, RD, FD, DCK,
DefaultedComparisonAnalyzer::ExplainConstexpr)
diff --git a/clang/test/CXX/class/class.compare/class.compare.default/p3.cpp b/clang/test/CXX/class/class.compare/class.compare.default/p3.cpp
index 936ca7c5100a2..a64b8b895f61d 100644
--- a/clang/test/CXX/class/class.compare/class.compare.default/p3.cpp
+++ b/clang/test/CXX/class/class.compare/class.compare.default/p3.cpp
@@ -1,6 +1,8 @@
// This test is for the [class.compare.default]p3 added by P2002R0
+// Also covers modifications made by P2448R2 and extension warnings
// RUN: %clang_cc1 -std=c++2a -verify %s
+// RUN: %clang_cc1 -std=c++2a -Wc++2b-default-comp-relaxed-constexpr -verify=expected,extension %s
namespace std {
struct strong_ordering {
@@ -80,10 +82,10 @@ struct TestB {
};
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}} extension-note 2{{non-constexpr comparison function declared here}}
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}} extension-note 2{{non-constexpr comparison function declared here}}
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 +129,23 @@ struct TestD {
struct E {
A a;
- C c; // expected-note 2{{non-constexpr comparison function would be used to compare member 'c'}}
+ C c; // extension-note 2{{non-constexpr comparison function would be used to compare member '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; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
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; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
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 { // extension-note 2{{non-constexpr comparison function would be used to compare base class 'C'}}
+ friend constexpr bool operator==(const E2&, const E2&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
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; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
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 +153,14 @@ struct E2 : A, C { // expected-note 2{{non-constexpr comparison function would b
};
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&); // extension-note {{non-constexpr comparison function declared here}}
+ friend constexpr bool operator!=(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
+
+ friend std::strong_ordering operator<=>(const F&, const F&); // extension-note 4{{non-constexpr comparison function declared here}}
+ friend constexpr bool operator<(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
+ friend constexpr bool operator<=(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
+ friend constexpr bool operator>(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
+ friend constexpr bool operator>=(const F&, const F&) = default; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is a C++2b extension}}
};
// No implicit 'constexpr' if it's not the first declaration.
diff --git a/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp b/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
index 02adf3d51dede..fdb89f3da5352 100644
--- a/clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
+++ b/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 @@ void use_g(G g) {
}
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 at +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 is a C++2b extension}}
};
struct J {
@@ -144,3 +145,19 @@ namespace NoInjectionIfOperatorEqualsDeclared {
};
bool test_d = D() == D();
}
+
+namespace GH61238 {
+template <typename A> 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; // extension-warning {{declared constexpr but invokes a non-constexpr comparison function is 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<non_constexpr_type> obj; // extension-note {{in instantiation of template class 'GH61238::my_struct<GH61238::non_constexpr_type>' requested here}}
+}
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
index abfd76fd0b03c..d545f49fa5db3 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
@@ -41,6 +41,8 @@ struct T : SS, NonLiteral {
virtual constexpr int OutOfLineVirtual() const; // beforecxx20-error {{virtual function cannot be constexpr}}
// - its return type shall be a literal type;
+ // Once we support P2448R2 constexpr functions will be allowd to return non-literal types
+ // The destructor will also be allowed
constexpr NonLiteral NonLiteralReturn() const { return {}; } // expected-error {{constexpr function's return type 'NonLiteral' is not a literal type}}
constexpr void VoidReturn() const { return; } // beforecxx14-error {{constexpr function's return type 'void' is not a literal type}}
constexpr ~T(); // beforecxx20-error {{destructor cannot be declared constexpr}}
@@ -49,6 +51,7 @@ struct T : SS, NonLiteral {
constexpr F NonLiteralReturn2; // ok until definition
// - each of its parameter types shall be a literal type;
+ // Once we support P2448R2 constexpr functions will be allowd to have parameters of non-literal types
constexpr int NonLiteralParam(NonLiteral) const { return 0; } // expected-error {{constexpr function's 1st parameter type 'NonLiteral' is not a literal type}}
typedef int G(NonLiteral) const;
constexpr G NonLiteralParam2; // ok until definition
diff --git a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
index aed2f29f67c3a..b6f3e2e721f38 100644
--- a/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
+++ b/clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
@@ -242,6 +242,7 @@ constexpr int f(enable_shared_from_this<int>);
// - every constructor involved in initializing non-static data members and base
// class sub-objects shall be a constexpr constructor.
+// This will no longer be the case once we support P2448R2
struct ConstexprBaseMemberCtors : Literal {
Literal l;
diff --git a/clang/www/cxx_status.html b/clang/www/cxx_status.html
index 3750b573e79b5..e4ecb5f832e93 100755
--- a/clang/www/cxx_status.html
+++ b/clang/www/cxx_status.html
@@ -1480,7 +1480,14 @@ <h2 id="cxx23">C++23 implementation status</h2>
<tr>
<td>Relaxing some constexpr restrictions</td>
<td><a href="https://wg21.link/P2448R2">P2448R2</a></td>
- <td class="none" align="center">No</td>
+ <td class="partial" align="center">
+ <details><summary>Clang 17 (Partial)</summary>
+ We do not support outside of defaulted special memeber functions the change that constexpr functions no
+ longer have to be constexpr compatible but rather support a less restricted requirements for constexpr
+ functions. Which include allowing non-literal types as return values and paremeters, allow calling of
+ non-constexpr functions and constructors.
+ </details></td>
+ </td>
</tr>
<tr>
<td>Using unknown pointers and references in constant expressions</td>
More information about the cfe-commits
mailing list