c++, v2: Delete defaulted operator <=> if std::strong_ordering::equal doesn't convert to its rettype [PR118387]

Message ID Z4Y07BHie5BXZ/NC@tucnak
State New
Headers
Series c++, v2: Delete defaulted operator <=> if std::strong_ordering::equal doesn't convert to its rettype [PR118387] |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_gcc_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_gcc_check--master-arm success Test passed

Commit Message

Jakub Jelinek Jan. 14, 2025, 9:57 a.m. UTC
  On Mon, Jan 13, 2025 at 08:35:57PM -0500, Jason Merrill wrote:
> I'd rather hoist the build_static_cast from later; the rules for static cast
> aren't quite the same as can_convert.  See:
> 
> >       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);
> 
> Apparently this also needs to happen when !defining.

Ok, so like this then?

2025-01-14  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.



	Jakub
  

Comments

Jason Merrill Jan. 14, 2025, 1:16 p.m. UTC | #1
On 1/14/25 4:57 AM, Jakub Jelinek wrote:
> On Mon, Jan 13, 2025 at 08:35:57PM -0500, Jason Merrill wrote:
>> I'd rather hoist the build_static_cast from later; the rules for static cast
>> aren't quite the same as can_convert.  See:
>>
>>>        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);
>>
>> Apparently this also needs to happen when !defining.
> 
> Ok, so like this then?

OK.

> 2025-01-14  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.jj	2025-01-11 21:58:05.387588681 +0100
> +++ gcc/cp/method.cc	2025-01-14 09:45:14.064303749 +0100
> @@ -1635,6 +1635,18 @@ build_comparison_op (tree fndecl, bool d
>   	  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 d
>   	    }
>   	}
>         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)
>       {
> --- 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-14 10:27:12.311540443 +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 "invalid 'static_cast' from type 'const std::strong_ordering' to 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-14 10:32:00.246561725 +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 "invalid 'static_cast' from type 'const std::strong_ordering' to 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-14 10:27:49.739023268 +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 "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'" }
> --- 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-14 10:31:31.802954766 +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 "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'" }
> +}
> --- libstdc++-v3/testsuite/25_algorithms/default_template_value.cc.jj	2024-09-24 15:14:54.199160911 +0200
> +++ libstdc++-v3/testsuite/25_algorithms/default_template_value.cc	2025-01-14 10:40:02.304872699 +0100
> @@ -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;
>   };
> 
> 
> 	Jakub
>
  

Patch

--- gcc/cp/method.cc.jj	2025-01-11 21:58:05.387588681 +0100
+++ gcc/cp/method.cc	2025-01-14 09:45:14.064303749 +0100
@@ -1635,6 +1635,18 @@  build_comparison_op (tree fndecl, bool d
 	  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 d
 	    }
 	}
       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)
     {
--- 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-14 10:27:12.311540443 +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 "invalid 'static_cast' from type 'const std::strong_ordering' to 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-14 10:32:00.246561725 +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 "invalid 'static_cast' from type 'const std::strong_ordering' to 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-14 10:27:49.739023268 +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 "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'" }
--- 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-14 10:31:31.802954766 +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 "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'" }
+}
--- libstdc++-v3/testsuite/25_algorithms/default_template_value.cc.jj	2024-09-24 15:14:54.199160911 +0200
+++ libstdc++-v3/testsuite/25_algorithms/default_template_value.cc	2025-01-14 10:40:02.304872699 +0100
@@ -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;
 };