aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakub Jelinek <jakub@redhat.com>2025-01-15 08:56:40 +0100
committerJakub Jelinek <jakub@gcc.gnu.org>2025-01-15 08:59:46 +0100
commit18f6bb9899209ccd41c476254d30596606c81dd7 (patch)
treed1bbbffe6f753d167c9b629e1b21fa3bfd5e7e85
parent64828272cde415bf2d7222eec87147ba84980f3d (diff)
downloadgcc-18f6bb9899209ccd41c476254d30596606c81dd7.zip
gcc-18f6bb9899209ccd41c476254d30596606c81dd7.tar.gz
gcc-18f6bb9899209ccd41c476254d30596606c81dd7.tar.bz2
c++: Delete defaulted operator <=> if std::strong_ordering::equal doesn't convert to its rettype [PR118387]
Note, the PR raises another problem. If on the same testcase the B b; line is removed, we silently synthetize operator<=> which will crash at runtime due to returning without a return statement. That is because the standard says that in that case it should return static_cast<int>(std::strong_ordering::equal); but I can't find anywhere wording which would say that if that isn't valid, the function is deleted. https://eel.is/c++draft/class.compare#class.spaceship-2.2 seems to talk just about cases where there are some members and their comparison is invalid it is deleted, but here there are none and it follows https://eel.is/c++draft/class.compare#class.spaceship-3.sentence-2 So, we synthetize with tf_none, see the static_cast is invalid, don't add error_mark_node statement silently, but as the function isn't deleted, we just silently emit it. Should the standard be amended to say that the operator should be deleted even if it has no elements and the static cast from https://eel.is/c++draft/class.compare#class.spaceship-3.sentence-2 On Fri, Jan 10, 2025 at 12:04:53PM -0500, Jason Merrill wrote: > That seems pretty obviously what we want, and is what the other compilers > implement. This patch implements it then. 2025-01-15 Jakub Jelinek <jakub@redhat.com> PR c++/118387 * method.cc (build_comparison_op): Set bad if std::strong_ordering::equal doesn't convert to rettype. * g++.dg/cpp2a/spaceship-err6.C: Expect another error. * g++.dg/cpp2a/spaceship-synth17.C: Likewise. * g++.dg/cpp2a/spaceship-synth-neg6.C: Likewise. * g++.dg/cpp2a/spaceship-synth-neg7.C: New test. * testsuite/25_algorithms/default_template_value.cc (Input::operator<=>): Use auto as return type rather than bool.
-rw-r--r--gcc/cp/method.cc26
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C2
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C58
-rw-r--r--gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C2
-rw-r--r--libstdc++-v3/testsuite/25_algorithms/default_template_value.cc2
6 files changed, 75 insertions, 17 deletions
diff --git a/gcc/cp/method.cc b/gcc/cp/method.cc
index 304d11d..3914bbb 100644
--- a/gcc/cp/method.cc
+++ b/gcc/cp/method.cc
@@ -1635,6 +1635,18 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain)
rettype = common_comparison_type (comps);
apply_deduced_return_type (fndecl, rettype);
}
+ tree retvaleq;
+ if (code == EQ_EXPR)
+ retvaleq = boolean_true_node;
+ else
+ {
+ tree seql = lookup_comparison_result (cc_strong_ordering,
+ "equal", complain);
+ retvaleq = build_static_cast (input_location, rettype, seql,
+ complain);
+ if (retvaleq == error_mark_node)
+ bad = true;
+ }
if (bad)
{
DECL_DELETED_FN (fndecl) = true;
@@ -1722,19 +1734,7 @@ build_comparison_op (tree fndecl, bool defining, tsubst_flags_t complain)
}
}
if (defining)
- {
- tree val;
- if (code == EQ_EXPR)
- val = boolean_true_node;
- else
- {
- tree seql = lookup_comparison_result (cc_strong_ordering,
- "equal", complain);
- val = build_static_cast (input_location, rettype, seql,
- complain);
- }
- finish_return_stmt (val);
- }
+ finish_return_stmt (retvaleq);
}
else if (code == NE_EXPR)
{
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C
index 57fbdb33..7e36df3 100644
--- a/gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C
@@ -10,7 +10,7 @@ class MyClass
public:
MyClass(int value): mValue(value) {}
- bool operator<=>(const MyClass&) const = default;
+ bool operator<=>(const MyClass&) const = default; // { dg-error "invalid 'static_cast' from type 'const std::strong_ordering' to type 'bool'" }
};
int main()
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C
index d3f95e1..f4df61e 100644
--- a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C
@@ -5,7 +5,7 @@
struct S {
int a; // { dg-error "three-way comparison of 'S::a' has type 'std::strong_ordering', which does not convert to 'int\\*'" }
- int *operator<=>(const S&) const = default;
+ int *operator<=>(const S&) const = default; // { dg-error "invalid 'static_cast' from type 'const std::strong_ordering' to type 'int\\*'" }
};
bool b = S{} < S{}; // { dg-error "use of deleted function 'constexpr int\\* S::operator<=>\\\(const S&\\\) const'" }
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C
new file mode 100644
index 0000000..f3ef9fe
--- /dev/null
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C
@@ -0,0 +1,58 @@
+// PR c++/118387
+// { dg-do compile { target c++20 } }
+
+#include <compare>
+
+struct A {
+ int operator<=> (const A &) const;
+};
+
+struct B {
+ A a;
+ int operator<=> (const B &) const = default; // { dg-message "'constexpr int B::operator<=>\\\(const B&\\\) const' is implicitly deleted because the default definition would be ill-formed:" }
+}; // { dg-error "invalid 'static_cast' from type 'const std::strong_ordering' to type 'int'" "" { target *-*-* } .-1 }
+
+struct C {
+ int operator<=> (const C &) const = default; // { dg-message "'constexpr int C::operator<=>\\\(const C&\\\) const' is implicitly deleted because the default definition would be ill-formed:" }
+}; // { dg-error "invalid 'static_cast' from type 'const std::strong_ordering' to type 'int'" "" { target *-*-* } .-1 }
+
+struct D {
+ auto operator<=> (const D &) const = default;
+};
+
+struct E {
+ D a; // { dg-error "three-way comparison of 'E::a' has type 'std::strong_ordering', which does not convert to 'int'" }
+ int operator<=> (const E &) const = default; // { dg-message "'constexpr int E::operator<=>\\\(const E&\\\) const' is implicitly deleted because the default definition would be ill-formed:" }
+}; // { dg-error "invalid 'static_cast' from type 'const std::strong_ordering' to type 'int'" "" { target *-*-* } .-1 }
+
+struct F {
+ A a;
+ int operator<=> (const F &) const = default;
+};
+
+struct G {
+ int operator<=> (const G &) const = default;
+};
+
+struct H {
+ D a;
+ int operator<=> (const H &) const = default;
+};
+
+auto
+foo (B a, B b)
+{
+ return a <=> b; // { dg-error "use of deleted function 'constexpr int B::operator<=>\\\(const B&\\\) const'" }
+}
+
+auto
+bar (C a, C b)
+{
+ return a <=> b; // { dg-error "use of deleted function 'constexpr int C::operator<=>\\\(const C&\\\) const'" }
+}
+
+auto
+baz (E a, E b)
+{
+ return a <=> b; // { dg-error "use of deleted function 'constexpr int E::operator<=>\\\(const E&\\\) const'" }
+}
diff --git a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C
index a779331..050ea33 100644
--- a/gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C
+++ b/gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C
@@ -8,7 +8,7 @@ struct B {};
struct A
{
B b; // { dg-error "no match for 'operator<=>' in '\[^\n\r]*' \\\(operand types are 'B' and 'B'\\\)" }
- int operator<=> (const A &) const = default;
+ int operator<=> (const A &) const = default; // { dg-error "invalid 'static_cast' from type 'const std::strong_ordering' to type 'int'" }
};
int
diff --git a/libstdc++-v3/testsuite/25_algorithms/default_template_value.cc b/libstdc++-v3/testsuite/25_algorithms/default_template_value.cc
index 3cf51bc..b05234e 100644
--- a/libstdc++-v3/testsuite/25_algorithms/default_template_value.cc
+++ b/libstdc++-v3/testsuite/25_algorithms/default_template_value.cc
@@ -27,7 +27,7 @@ struct Output
struct Input
{
Input(int, double);
- friend bool operator<=>(const Input &, const Input &) = default;
+ friend auto operator<=>(const Input &, const Input &) = default;
friend Input operator+(const Input &, const Input &);
operator Output() const;
};