https://gcc.gnu.org/g:7590c14b53a762ab30f5026148bd1cb9cf142264

commit r16-2448-g7590c14b53a762ab30f5026148bd1cb9cf142264
Author: Patrick Palka <ppa...@redhat.com>
Date:   Wed Jul 23 08:38:12 2025 -0400

    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<class T>
    void f() {
      E::x != 0;
    }
    
    $ cat 121179_b.C
    import foo;
    
    template void f<int>();
    
    $ 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 <ja...@redhat.com>

Diff:
---
 gcc/cp/call.cc                           | 4 +++-
 gcc/cp/tree.cc                           | 3 +++
 gcc/testsuite/g++.dg/lookup/operator-8.C | 9 ++++++++-
 3 files changed, 14 insertions(+), 2 deletions(-)

diff --git a/gcc/cp/call.cc b/gcc/cp/call.cc
index 37ad0a977c22..c925dd18ab41 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 a7b890884abd..c260efb7f6ba 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 64d8a97cdd00..7fe6a57061bd 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<class T>
 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<int>();

Reply via email to