https://gcc.gnu.org/g:6eaecfb5923306e46ab0baafaf41f56f73dc685e
commit r16-9052-g6eaecfb5923306e46ab0baafaf41f56f73dc685e Author: Patrick Palka <[email protected]> Date: Wed Jun 3 18:04:12 2026 -0400 c++: non-dep cmp op rewritten from <=> returning char [PR125378] 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. Reviewed-by: Jason Merrill <[email protected]> Reviewed-by: Marek Polacek <[email protected]> (cherry picked from commit 5dc5dd6ed9b4759d278a07f95949c3f241626651) Diff: --- gcc/cp/tree.cc | 11 +++++++++ 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, 129 insertions(+) diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index c092c1e7f886..791232540e9b 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3853,6 +3853,15 @@ 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)); + + tree int_promotion = NULL_TREE; + if (TREE_CODE (spaceship_non_dep) == NOP_EXPR) + { + gcc_checking_assert (TREE_CODE (non_dep) != CALL_EXPR); + int_promotion = TREE_TYPE (spaceship_non_dep); + spaceship_non_dep = TREE_OPERAND (spaceship_non_dep, 0); + } + gcc_checking_assert (TREE_CODE (spaceship_non_dep) == CALL_EXPR); tree spaceship_op0 = va_arg (p, tree); tree spaceship_op1 = va_arg (p, tree); @@ -3875,6 +3884,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 (int_promotion, 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>();
