Bootstrapped and rgetested on x86_64-pc-linux-gnu, does this look OK for trunk?
In order to properly handle all rewritten operator expressions, I suppose we could teach build_min_non_dep_op_overload to carefully look through 'non_dep' and note when arguments are reversed etc. But rather than that, I wonder if we should just remove build_min_non_dep_op_overload and stick with the templated CALL_EXPR returned by build_new_op in the first place? Making build_min_non_dep_op_overload a no-op that just returns 'non_dep' seems to work nicely in for my tests, e.g. operator-8.C is now accepted completely. In any case this I reckon this narrow fix would be good to get in incrementally in order to fix the libstdc++/std module error. -- >8 -- 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 patch fixes this known deficiency in build_min_non_dep_op_overload narrowly for simple != to == rewrites. 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. --- 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 26296255b1ec..a25b6a2b31c0 100644 --- a/gcc/cp/call.cc +++ b/gcc/cp/call.cc @@ -7535,7 +7535,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..a0db961a5f1f 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 rewritten = (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 (rewritten) + 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..c5972d119d66 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 special 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>(); -- 2.50.1.319.g90c0775e97