]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: Delete defaulted operator <=> if std::strong_ordering::equal doesn't convert...
authorJakub Jelinek <jakub@redhat.com>
Wed, 15 Jan 2025 07:56:40 +0000 (08:56 +0100)
committerJakub Jelinek <jakub@gcc.gnu.org>
Wed, 15 Jan 2025 07:59:46 +0000 (08:59 +0100)
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.

gcc/cp/method.cc
gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C
gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C
gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C [new file with mode: 0644]
gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C
libstdc++-v3/testsuite/25_algorithms/default_template_value.cc

index 304d11d145c5a8db53d8e65546de5a466da8d645..3914bbb1ef23add53a5d2f6f6ddb08af9a694cee 100644 (file)
@@ -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)
     {
index 57fbdb33088438884cfd2bac02d9ad50b03c5918..7e36df3ff9f30c089d3cb64a72e430f2d8219364 100644 (file)
@@ -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()
index d3f95e13f135469e332e204dc1e2b3fe459bdf60..f4df61ed50884873c8c95facc101f671a69017c6 100644 (file)
@@ -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 (file)
index 0000000..f3ef9fe
--- /dev/null
@@ -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'" }
+}
index a7793314ce1ebdf573746197c95635f6ec5d2ec5..050ea330db0d579f8783e60df6c229f230697536 100644 (file)
@@ -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
index 3cf51bc087b4add9a90508e170654a0cdae337ae..b05234e1f8dfb396d772c6d83ebd8ea9a73ac681 100644 (file)
@@ -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;
 };