On Fri, Jan 10, 2025 at 12:04:53PM -0500, Jason Merrill wrote:
> > 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
> > ?
> 
> That seems pretty obviously what we want, and is what the other compilers
> implement.

So like this?

Will you handle the defect report (unless you think nothing needs to be
clarified), or should I file something?

2025-01-13  Jakub Jelinek  <ja...@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.

--- gcc/cp/method.cc.jj 2025-01-11 21:58:05.387588681 +0100
+++ gcc/cp/method.cc    2025-01-13 16:19:09.896650756 +0100
@@ -1635,6 +1635,26 @@ build_comparison_op (tree fndecl, bool d
          rettype = common_comparison_type (comps);
          apply_deduced_return_type (fndecl, rettype);
        }
+      else if (code == SPACESHIP_EXPR && cat_tag_for (rettype) == cc_last)
+       {
+         /* The return value is ... and
+            static_cast<R>(std::strong_ordering::equal) otherwise.
+            Make sure to delete or diagnose if such a static cast is not
+            valid.  */
+         tree seql = lookup_comparison_result (cc_strong_ordering,
+                                               "equal", complain);
+         if (seql == error_mark_node)
+           bad = true;
+         else if (!can_convert (rettype, TREE_TYPE (seql), complain))
+           {
+             if (complain & tf_error)
+               error_at (info.loc,
+                         "%<std::strong_ordering::equal%> does not convert "
+                         "to %qD return type %qT",
+                         fndecl, rettype);
+             bad = true;
+           }
+       }
       if (bad)
        {
          DECL_DELETED_FN (fndecl) = true;
--- gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C.jj      2021-04-14 
19:19:14.050804249 +0200
+++ gcc/testsuite/g++.dg/cpp2a/spaceship-err6.C 2025-01-13 16:30:13.613331069 
+0100
@@ -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 
"'std::strong_ordering::equal' does not convert to 'constexpr bool 
MyClass::operator<=>\\\(const MyClass&\\\) const' return type 'bool'" }
 };
 
 int main()
--- gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C.jj   2025-01-11 
21:58:05.460587663 +0100
+++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth17.C      2025-01-13 
16:32:11.383677413 +0100
@@ -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 
"'std::strong_ordering::equal' does not convert to 'constexpr int 
A::operator<=>\\\(const A&\\\) const' return type 'int'" }
 };
 
 int
--- gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C.jj        2021-08-12 
20:37:12.696473756 +0200
+++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg6.C   2025-01-13 
16:48:22.482043534 +0100
@@ -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 
"'std::strong_ordering::equal' does not convert to 'constexpr int\\* 
S::operator<=>\\\(const S&\\\) const' return type 'int\\*'" }
 };
 
 bool b = S{} < S{};            // { dg-error "use of deleted function 
'constexpr int\\* S::operator<=>\\\(const S&\\\) const'" }
--- gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C.jj        2025-01-13 
16:19:09.897650742 +0100
+++ gcc/testsuite/g++.dg/cpp2a/spaceship-synth-neg7.C   2025-01-13 
16:51:00.093831428 +0100
@@ -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 "std::strong_ordering::equal' does not 
convert to 'constexpr int B::operator<=>\\\(const B&\\\) const' return 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 "std::strong_ordering::equal' does not 
convert to 'constexpr int C::operator<=>\\\(const C&\\\) const' return 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 "std::strong_ordering::equal' does not 
convert to 'constexpr int E::operator<=>\\\(const E&\\\) const' return 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'" }
+}


        Jakub

Reply via email to