From: Patrick Palka Date: Wed, 23 Jul 2025 12:38:12 +0000 (-0400) Subject: c++: name lookup for non-dep rewritten != expr [PR121179] X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7590c14b53a762ab30f5026148bd1cb9cf142264;p=thirdparty%2Fgcc.git c++: name lookup for non-dep rewritten != expr [PR121179] Here we're incorrectly rejecting the modules testcase (reduced from a std module example): $ cat 121179_a.C export module foo; enum class E { x }; bool operator==(E, int); export template void f() { E::x != 0; } $ cat 121179_b.C import foo; template void f(); $ g++ -fmodules 121179_*.C In module foo, imported at 121179_b.C:1: 121179_a.C: In instantiation of ‘void f@foo() [with T = int]’: 121179_b.C:3:9: required from here 121179_a.C:9:8: error: no match for ‘operator!=’ (operand types are ‘E@foo’ and ‘int’) This is ultimately because our non-dependent rewritten operator expression handling throws away the result of unqualified lookup at template parse time, and so we have to repeat the lookup at instantiation time which fails because the operator== isn't exported. This is a known deficiency, but it's easy enough to narrowly fix this for simple != to == rewrites by making build_min_non_dep_op_overload look through logical negation. PR c++/121179 gcc/cp/ChangeLog: * call.cc (build_new_op): Don't clear *overload for a simple != to == rewrite. * tree.cc (build_min_non_dep_op_overload): Handle TRUTH_NOT_EXPR appearing in a rewritten operator expression. gcc/testsuite/ChangeLog: * g++.dg/lookup/operator-8.C: Strengthen test and remove one XFAIL. Reviewed-by: Jason Merrill --- diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc index 37ad0a977c2..c925dd18ab4 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -7536,7 +7536,9 @@ build_new_op (const op_location_t &loc, enum tree_code code, int flags, if (cand->rewritten ()) { /* FIXME build_min_non_dep_op_overload can't handle rewrites. */ - if (overload) + if (code == NE_EXPR && !cand->reversed ()) + /* It can handle != rewritten to == though. */; + else if (overload) *overload = NULL_TREE; switch (code) { diff --git a/gcc/cp/tree.cc b/gcc/cp/tree.cc index a7b890884ab..c260efb7f6b 100644 --- a/gcc/cp/tree.cc +++ b/gcc/cp/tree.cc @@ -3696,6 +3696,7 @@ 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); non_dep = extract_call_expr (non_dep); nargs = call_expr_nargs (non_dep); @@ -3753,6 +3754,8 @@ build_min_non_dep_op_overload (enum tree_code op, CALL_EXPR_ORDERED_ARGS (call_expr) = CALL_EXPR_ORDERED_ARGS (non_dep); CALL_EXPR_REVERSE_ARGS (call_expr) = CALL_EXPR_REVERSE_ARGS (non_dep); + if (negated) + call = build_min (TRUTH_NOT_EXPR, boolean_type_node, call); if (obj) return keep_unused_object_arg (call, obj, overload); return call; diff --git a/gcc/testsuite/g++.dg/lookup/operator-8.C b/gcc/testsuite/g++.dg/lookup/operator-8.C index 64d8a97cdd0..7fe6a57061b 100644 --- a/gcc/testsuite/g++.dg/lookup/operator-8.C +++ b/gcc/testsuite/g++.dg/lookup/operator-8.C @@ -16,7 +16,8 @@ struct A { template void f() { A a; - (void)(a != 0, 0 != a); // { dg-bogus "deleted" "" { xfail *-*-* } } + (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 *-*-* } } @@ -31,4 +32,10 @@ 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();