]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
c++: name lookup for non-dep rewritten != expr [PR121179]
authorPatrick Palka <ppalka@redhat.com>
Wed, 23 Jul 2025 12:38:12 +0000 (08:38 -0400)
committerPatrick Palka <ppalka@redhat.com>
Wed, 23 Jul 2025 12:38:12 +0000 (08:38 -0400)
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 <jason@redhat.com>
gcc/cp/call.cc
gcc/cp/tree.cc
gcc/testsuite/g++.dg/lookup/operator-8.C

index 37ad0a977c22bd9097440351ee692ea7b9785754..c925dd18ab4124fc5f0f4761f3926d824fdee01e 100644 (file)
@@ -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)
                {
index a7b890884abd3f3d56c6402ee2bb772f7baa06f2..c260efb7f6ba27f599dc4c08aa368580c92e8326 100644 (file)
@@ -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;
index 64d8a97cdd005e81cda1a8754719adf01eb96871..7fe6a57061bdf8e2d516ef72d8b78c07d4848414 100644 (file)
@@ -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>();