Tested on x86_64-pc-linux-gnu, does this look OK for trunk/16?
-- >8 --
When a user operator<=> returns an integral type smaller than int, the
rewritten form of x @ y contains an integer promotion: (int)(x <=> y) @ 0.
This patch teaches build_min_non_dep_op_overload to look through and
preserve this implicit cast.
PR c++/125378
gcc/cp/ChangeLog:
* tree.cc (build_min_non_dep_op_overload): Handle comparison
operator expressions rewritten from a <=> that contain an
integer promotion.
gcc/testsuite/ChangeLog:
* g++.dg/lookup/operator-8b.C: New test.
* g++.dg/lookup/operator-8c.C: New test.
* g++.dg/lookup/operator-9a.C: New test.
* g++.dg/lookup/operator-9b.C: New test.
---
gcc/cp/tree.cc | 13 ++++++++
gcc/testsuite/g++.dg/lookup/operator-8b.C | 40 +++++++++++++++++++++++
gcc/testsuite/g++.dg/lookup/operator-8c.C | 40 +++++++++++++++++++++++
gcc/testsuite/g++.dg/lookup/operator-9a.C | 19 +++++++++++
gcc/testsuite/g++.dg/lookup/operator-9b.C | 19 +++++++++++
5 files changed, 131 insertions(+)
create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8b.C
create mode 100644 gcc/testsuite/g++.dg/lookup/operator-8c.C
create mode 100644 gcc/testsuite/g++.dg/lookup/operator-9a.C
create mode 100644 gcc/testsuite/g++.dg/lookup/operator-9b.C
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index 02a62f78511c..8af3bd6f4641 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3855,6 +3855,17 @@ build_min_non_dep_op_overload (enum tree_code op,
tree spaceship_non_dep = (TREE_CODE (non_dep) == CALL_EXPR
? CALL_EXPR_ARG (non_dep, reversed ? 1 : 0)
: TREE_OPERAND (non_dep, reversed ? 1 : 0));
+
+ bool int_promotion = false;
+ if (TREE_CODE (spaceship_non_dep) == NOP_EXPR)
+ {
+ gcc_checking_assert (TREE_CODE (non_dep) != CALL_EXPR);
+ gcc_checking_assert (TREE_TYPE (spaceship_non_dep)
+ == integer_type_node);
+ spaceship_non_dep = TREE_OPERAND (spaceship_non_dep, 0);
+ int_promotion = true;
+ }
+
gcc_checking_assert (TREE_CODE (spaceship_non_dep) == CALL_EXPR);
tree spaceship_op0 = va_arg (p, tree);
tree spaceship_op1 = va_arg (p, tree);
@@ -3877,6 +3888,8 @@ build_min_non_dep_op_overload (enum tree_code op,
{
gcc_checking_assert (COMPARISON_CLASS_P (non_dep)
|| TREE_CODE (non_dep) == SPACESHIP_EXPR);
+ if (int_promotion)
+ op0 = build_nop (integer_type_node, op0);
if (reversed)
std::swap (op0, op1);
return build_min_non_dep (TREE_CODE (non_dep), non_dep, op0, op1);
diff --git a/gcc/testsuite/g++.dg/lookup/operator-8b.C
b/gcc/testsuite/g++.dg/lookup/operator-8b.C
new file mode 100644
index 000000000000..e08e879b08b4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/operator-8b.C
@@ -0,0 +1,40 @@
+// PR c++/125378
+// A version of operator-8a.C where the operator<=> returns char instead of
int.
+
+// { dg-do compile { target c++20 } }
+
+struct A {
+ bool operator==(int);
+ char operator<=>(int);
+};
+
+template<class T>
+void f() {
+ A a;
+ (void)(a != 0);
+ (void)(0 != a);
+ (void)(a < 0);
+ (void)(0 < a);
+ (void)(a <= 0);
+ (void)(0 <= a);
+ (void)(a > 0);
+ (void)(0 > a);
+ (void)(a >= 0);
+ (void)(0 >= a);
+}
+
+// These later-declared namespace-scope overloads shouldn't be considered
+// when instantiating f<int>.
+bool operator!=(A, int) = delete;
+bool operator<(A, int) = delete;
+bool operator<=(A, int) = delete;
+bool operator>(A, int) = delete;
+bool operator>=(A, int) = delete;
+
+bool operator!=(int, A) = delete;
+bool operator<(int, A) = delete;
+bool operator<=(int, A) = delete;
+bool operator>(int, A) = delete;
+bool operator>=(int, A) = delete;
+
+template void f<int>();
diff --git a/gcc/testsuite/g++.dg/lookup/operator-8c.C
b/gcc/testsuite/g++.dg/lookup/operator-8c.C
new file mode 100644
index 000000000000..251c516cc348
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/operator-8c.C
@@ -0,0 +1,40 @@
+// PR c++/125378
+// A version of operator-8a.C where the operator<=> returns long instead of
int.
+
+// { dg-do compile { target c++20 } }
+
+struct A {
+ bool operator==(int);
+ long operator<=>(int);
+};
+
+template<class T>
+void f() {
+ A a;
+ (void)(a != 0);
+ (void)(0 != a);
+ (void)(a < 0);
+ (void)(0 < a);
+ (void)(a <= 0);
+ (void)(0 <= a);
+ (void)(a > 0);
+ (void)(0 > a);
+ (void)(a >= 0);
+ (void)(0 >= a);
+}
+
+// These later-declared namespace-scope overloads shouldn't be considered
+// when instantiating f<int>.
+bool operator!=(A, int) = delete;
+bool operator<(A, int) = delete;
+bool operator<=(A, int) = delete;
+bool operator>(A, int) = delete;
+bool operator>=(A, int) = delete;
+
+bool operator!=(int, A) = delete;
+bool operator<(int, A) = delete;
+bool operator<=(int, A) = delete;
+bool operator>(int, A) = delete;
+bool operator>=(int, A) = delete;
+
+template void f<int>();
diff --git a/gcc/testsuite/g++.dg/lookup/operator-9a.C
b/gcc/testsuite/g++.dg/lookup/operator-9a.C
new file mode 100644
index 000000000000..ba49c8100e57
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/operator-9a.C
@@ -0,0 +1,19 @@
+// PR c++/125378
+// A version of operator-9.C where the operator<=> returns char instead of int.
+
+// { dg-do compile { target c++20 } }
+
+#include <compare>
+
+struct A {
+ bool operator==(int);
+ char operator<=>(int);
+};
+
+template<class T>
+void f() {
+ A a;
+ (void)(0 <=> a);
+}
+
+template void f<int>();
diff --git a/gcc/testsuite/g++.dg/lookup/operator-9b.C
b/gcc/testsuite/g++.dg/lookup/operator-9b.C
new file mode 100644
index 000000000000..2a46e49c9938
--- /dev/null
+++ b/gcc/testsuite/g++.dg/lookup/operator-9b.C
@@ -0,0 +1,19 @@
+// PR c++/125378
+// A version of operator-9.C where the operator<=> returns long instead of int.
+
+// { dg-do compile { target c++20 } }
+
+#include <compare>
+
+struct A {
+ bool operator==(int);
+ long operator<=>(int);
+};
+
+template<class T>
+void f() {
+ A a;
+ (void)(0 <=> a);
+}
+
+template void f<int>();
--
2.54.0.rc1.54.g60f07c4f5c