Skip to content

Commit

Permalink
[Clang] Updating handling of defaulted comparison operators to reflec…
Browse files Browse the repository at this point in the history
…t 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: llvm#61238

Differential Revision: https://reviews.llvm.org/D146090
  • Loading branch information
shafik committed May 4, 2023
1 parent eee32d6 commit b4692f2
Show file tree
Hide file tree
Showing 8 changed files with 85 additions and 27 deletions.
6 changes: 6 additions & 0 deletions clang/docs/ReleaseNotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
19 changes: 14 additions & 5 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -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">;
Expand Down
15 changes: 14 additions & 1 deletion clang/lib/Sema/SemaDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
34 changes: 18 additions & 16 deletions clang/test/CXX/class/class.compare/class.compare.default/p3.cpp
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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}}
Expand Down Expand Up @@ -127,38 +129,38 @@ 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;
friend constexpr bool operator>=(const E2&, const E2&) = default;
};

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.
Expand Down
25 changes: 21 additions & 4 deletions clang/test/CXX/class/class.compare/class.compare.default/p4.cpp
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down Expand Up @@ -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@+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 {
Expand Down Expand Up @@ -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}}
}
3 changes: 3 additions & 0 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p3.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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}}
Expand All @@ -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
Expand Down
1 change: 1 addition & 0 deletions clang/test/CXX/dcl.dcl/dcl.spec/dcl.constexpr/p4.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
9 changes: 8 additions & 1 deletion clang/www/cxx_status.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit b4692f2

Please sign in to comment.