As a follow-up to r16-2448-g7590c14b53a762, this patch attempts to teach
build_min_non_dep_op_overload how to rebuild all rewritten comparison
operators, not just != -> == ones, so that we don't incorrectly repeat
the unqualified name lookup at instantiation time.
While working on this I noticed we'll seemingly never create a rewritten
operator expression that is in terms of a built-in operator, since we
could have used a built-in operator directly in the first place, which
simplifies things. I think this also means the extract_call_expr
handling of rewritten operators is wrong since it inspects for LT_EXPR,
SPACESHIP_EXPR etc directly, so this patch just removes it in passing.
gcc/cp/ChangeLog:
* call.cc (build_new_op): If the selected candidate is
rewritten, communicate the LOOKUP_REWRITTEN/REVERSED flags to
the caller via the *overload out-parameter, and stop clearing
*overload in that case.
(extract_call_expr): Remove seemingly unnecessary and incorrect
handling of C++20 rewritten comparison.
* tree.cc (build_min_non_dep_op_overload): Handle rebuilding any
C++20 rewritten comparison operator expressions.
gcc/testsuite/ChangeLog:
* g++.dg/lookup/operator-8.C: Remove XFAILs and properly
suppress all -Wunused-result warnings.
---
gcc/cp/call.cc | 38 +++-------
gcc/cp/tree.cc | 91 ++++++++++++++++++++----
gcc/testsuite/g++.dg/lookup/operator-8.C | 16 +++--
3 files changed, 98 insertions(+), 47 deletions(-)
diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index c925dd18ab41..7efbef736bc0 100644
--- a/gcc/cp/call.cc
+++ b/gcc/cp/call.cc
@@ -7486,7 +7486,16 @@ build_new_op (const op_location_t &loc, enum tree_code
code, int flags,
else if (TREE_CODE (cand->fn) == FUNCTION_DECL)
{
if (overload)
- *overload = cand->fn;
+ {
+ if (cand->rewritten ())
+ /* build_min_non_dep_op_overload needs to know whether the
+ candidate is rewritten/reversed. */
+ *overload = build_tree_list (build_int_cst (integer_type_node,
+ cand->flags),
+ cand->fn);
+ else
+ *overload = cand->fn;
+ }
if (resolve_args (arglist, complain) == NULL)
result = error_mark_node;
@@ -7535,11 +7544,6 @@ build_new_op (const op_location_t &loc, enum tree_code
code, int flags,
/* If this was a C++20 rewritten comparison, adjust the result. */
if (cand->rewritten ())
{
- /* FIXME build_min_non_dep_op_overload can't handle rewrites. */
- if (code == NE_EXPR && !cand->reversed ())
- /* It can handle != rewritten to == though. */;
- else if (overload)
- *overload = NULL_TREE;
switch (code)
{
case EQ_EXPR:
@@ -7891,28 +7895,6 @@ extract_call_expr (tree call)
call = TREE_OPERAND (call, 0);
if (TREE_CODE (call) == TARGET_EXPR)
call = TARGET_EXPR_INITIAL (call);
- if (cxx_dialect >= cxx20)
- switch (TREE_CODE (call))
- {
- /* C++20 rewritten comparison operators. */
- case TRUTH_NOT_EXPR:
- call = TREE_OPERAND (call, 0);
- break;
- case LT_EXPR:
- case LE_EXPR:
- case GT_EXPR:
- case GE_EXPR:
- case SPACESHIP_EXPR:
- {
- tree op0 = TREE_OPERAND (call, 0);
- if (integer_zerop (op0))
- call = TREE_OPERAND (call, 1);
- else
- call = op0;
- }
- break;
- default:;
- }
if (TREE_CODE (call) != CALL_EXPR
&& TREE_CODE (call) != AGGR_INIT_EXPR
diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc
index c260efb7f6ba..2bed60f8c949 100644
--- a/gcc/cp/tree.cc
+++ b/gcc/cp/tree.cc
@@ -3696,7 +3696,64 @@ build_min_non_dep_op_overload (enum tree_code op,
int nargs, expected_nargs;
tree fn, call, obj = NULL_TREE;
- bool negated = (TREE_CODE (non_dep) == TRUTH_NOT_EXPR);
+ releasing_vec args;
+ va_start (p, overload);
+
+ bool negated = false, rewritten = false, reversed = false;
+ if (cxx_dialect >= cxx20 && TREE_CODE (overload) == TREE_LIST)
+ {
+ /* Handle rebuilding a C++20 rewritten comparison operator expression,
+ e.g. !(x == y), y <=> x, (x <=> y) @ 0, etc, that resolved to a call
+ to a user-defined operator<=>/==. */
+ gcc_checking_assert (TREE_CODE_CLASS (op) == tcc_comparison
+ || op == SPACESHIP_EXPR);
+ int flags = TREE_INT_CST_LOW (TREE_PURPOSE (overload));
+ if (TREE_CODE (non_dep) == TRUTH_NOT_EXPR)
+ {
+ negated = true;
+ non_dep = TREE_OPERAND (non_dep, 0);
+ }
+ if (flags & LOOKUP_REWRITTEN)
+ rewritten = true;
+ if (flags & LOOKUP_REVERSED)
+ reversed = true;
+ if (rewritten
+ && DECL_OVERLOADED_OPERATOR_IS (TREE_VALUE (overload),
+ SPACESHIP_EXPR))
+ {
+ /* Handle (x <=> y) @ 0 and 0 @ (y <=> x) by recursing to first
+ rebuild the <=> and then rebuilding the outer operator expression.
+ Note that OVERLOAD in this case is the selected operator<=>, and
+ the provided variadic arguments are for this <=>. */
+
+ /* Clear LOOKUP_REWRITTEN for the recursive call. */
+ TREE_PURPOSE (overload)
+ = int_const_binop (BIT_AND_EXPR,
+ TREE_PURPOSE (overload),
+ build_int_cst (integer_type_node,
+ ~LOOKUP_REWRITTEN));
+
+ tree spaceship_non_dep = CALL_EXPR_ARG (non_dep, reversed ? 1 : 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);
+
+ /* Push the correct arguments for the operator OP expression, and set
+ OVERLOAD appropriately. */
+ tree op0 = build_min_non_dep_op_overload (SPACESHIP_EXPR,
+ spaceship_non_dep, overload,
+ spaceship_op0,
+ spaceship_op1);
+ tree op1 = CALL_EXPR_ARG (non_dep, reversed ? 0 : 1);
+ gcc_checking_assert (integer_zerop (op1));
+ vec_safe_push (args, op0);
+ vec_safe_push (args, op1);
+ overload = CALL_EXPR_FN (non_dep);
+ }
+ else
+ overload = TREE_VALUE (overload);
+ }
non_dep = extract_call_expr (non_dep);
nargs = call_expr_nargs (non_dep);
@@ -3717,32 +3774,40 @@ build_min_non_dep_op_overload (enum tree_code op,
expected_nargs += 1;
gcc_assert (nargs == expected_nargs);
- releasing_vec args;
- va_start (p, overload);
-
if (!DECL_OBJECT_MEMBER_FUNCTION_P (overload))
{
fn = overload;
- if (op == ARRAY_REF)
- obj = va_arg (p, tree);
- for (int i = 0; i < nargs; i++)
+ if (vec_safe_length (args) != 0)
+ /* The correct arguments were already pushed above. */
+ gcc_checking_assert (rewritten);
+ else
{
- tree arg = va_arg (p, tree);
- vec_safe_push (args, arg);
+ if (op == ARRAY_REF)
+ obj = va_arg (p, tree);
+ for (int i = 0; i < nargs; i++)
+ {
+ tree arg = va_arg (p, tree);
+ vec_safe_push (args, arg);
+ }
}
+ if (reversed)
+ std::swap ((*args)[0], (*args)[1]);
}
else
{
+ gcc_checking_assert (vec_safe_length (args) == 0);
tree object = va_arg (p, tree);
- tree binfo = TYPE_BINFO (TREE_TYPE (object));
- tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
- fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
- object, method, NULL_TREE);
for (int i = 0; i < nargs; i++)
{
tree arg = va_arg (p, tree);
vec_safe_push (args, arg);
}
+ if (reversed)
+ std::swap (object, (*args)[0]);
+ tree binfo = TYPE_BINFO (TREE_TYPE (object));
+ tree method = build_baselink (binfo, binfo, overload, NULL_TREE);
+ fn = build_min (COMPONENT_REF, TREE_TYPE (overload),
+ object, method, NULL_TREE);
}
va_end (p);
diff --git a/gcc/testsuite/g++.dg/lookup/operator-8.C
b/gcc/testsuite/g++.dg/lookup/operator-8.C
index 7fe6a57061bd..32d432dd8432 100644
--- a/gcc/testsuite/g++.dg/lookup/operator-8.C
+++ b/gcc/testsuite/g++.dg/lookup/operator-8.C
@@ -16,12 +16,16 @@ struct A {
template<class T>
void f() {
A a;
- (void)(a != 0); // We only handle this simple case, after PR121179
- (void)(0 != a); // { dg-bogus "deleted" "" { xfail *-*-* } }
- (void)(a < 0, 0 < a); // { dg-bogus "deleted" "" { xfail *-*-* } }
- (void)(a <= 0, 0 <= a); // { dg-bogus "deleted" "" { xfail *-*-* } }
- (void)(a > 0, 0 > a); // { dg-bogus "deleted" "" { xfail *-*-* } }
- (void)(a >= 0, 0 >= a); // { dg-bogus "deleted" "" { xfail *-*-* } }
+ (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
--
2.50.1.319.g90c0775e97